@ericrisco/rsc 0.1.39 → 0.1.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +8 -2
  2. package/manifest.json +181 -3
  3. package/package.json +1 -1
  4. package/scripts/lib/domains.js +1 -0
  5. package/scripts/lib/recommend.js +6 -0
  6. package/skills/deep-learning/SKILL.md +263 -0
  7. package/skills/deep-learning/evals/README.md +11 -0
  8. package/skills/deep-learning/evals/cases.yaml +48 -0
  9. package/skills/deep-learning/references/distributed.md +105 -0
  10. package/skills/deep-learning/references/training-recipes.md +135 -0
  11. package/skills/finetuning/SKILL.md +244 -0
  12. package/skills/finetuning/evals/README.md +13 -0
  13. package/skills/finetuning/evals/cases.yaml +50 -0
  14. package/skills/finetuning/references/hyperparameters-and-eval.md +74 -0
  15. package/skills/finetuning/references/methods.md +189 -0
  16. package/skills/harness/references/providers.yaml +35 -0
  17. package/skills/huggingface/SKILL.md +1 -1
  18. package/skills/machine-learning/SKILL.md +260 -0
  19. package/skills/machine-learning/evals/README.md +15 -0
  20. package/skills/machine-learning/evals/cases.yaml +54 -0
  21. package/skills/machine-learning/references/gbdt-and-tuning.md +83 -0
  22. package/skills/machine-learning/references/metrics-and-imbalance.md +82 -0
  23. package/skills/machine-learning/references/pipelines-and-cv.md +135 -0
  24. package/skills/nlp/SKILL.md +225 -0
  25. package/skills/nlp/evals/README.md +10 -0
  26. package/skills/nlp/evals/cases.yaml +45 -0
  27. package/skills/nlp/references/evaluation.md +107 -0
  28. package/skills/nlp/references/tokenization.md +95 -0
  29. package/skills/open-weights/SKILL.md +239 -0
  30. package/skills/open-weights/evals/README.md +14 -0
  31. package/skills/open-weights/evals/cases.yaml +42 -0
  32. package/skills/open-weights/references/licenses.md +123 -0
  33. package/skills/open-weights/references/model-families.md +83 -0
  34. package/skills/open-weights/references/sizing-and-quant.md +89 -0
  35. package/skills/roast-me/tools/adapters/__pycache__/__init__.cpython-312.pyc +0 -0
  36. package/skills/roast-me/tools/adapters/__pycache__/base.cpython-312.pyc +0 -0
  37. package/skills/roast-me/tools/adapters/__pycache__/claude.cpython-312.pyc +0 -0
  38. package/skills/roast-me/tools/adapters/__pycache__/codex.cpython-312.pyc +0 -0
  39. package/skills/roast-me/tools/adapters/__pycache__/gemini.cpython-312.pyc +0 -0
  40. package/skills/roast-me/tools/adapters/__pycache__/registry.cpython-312.pyc +0 -0
  41. package/skills/training-data/SKILL.md +226 -0
  42. package/skills/training-data/evals/README.md +13 -0
  43. package/skills/training-data/evals/cases.yaml +41 -0
  44. package/skills/training-data/references/formats.md +118 -0
  45. package/skills/training-data/references/synthesis-dedup-quality.md +134 -0
  46. package/skills/unsloth/SKILL.md +254 -0
  47. package/skills/unsloth/evals/README.md +10 -0
  48. package/skills/unsloth/evals/cases.yaml +42 -0
  49. package/skills/unsloth/references/export.md +75 -0
  50. package/skills/unsloth/references/grpo.md +91 -0
  51. package/skills/unsloth/references/masking-and-templates.md +91 -0
  52. package/skills/vllm/SKILL.md +235 -0
  53. package/skills/vllm/evals/README.md +9 -0
  54. package/skills/vllm/evals/cases.yaml +47 -0
  55. package/skills/vllm/references/flags-and-endpoints.md +105 -0
  56. package/skills/vllm/references/memory-and-throughput.md +78 -0
@@ -0,0 +1,134 @@
1
+ # Synthesis, dedup, decontamination, quality, licensing
2
+
3
+ Depth overflow for SKILL.md §3–§7. Library versions and, above all, model licenses change
4
+ monthly — verify at author time; nothing here is a standing legal fact.
5
+
6
+ ## Synthetic data
7
+
8
+ **Self-Instruct**: hand-write a small seed set of diverse instructions+responses, prompt a
9
+ strong model to generate many more conditioned on the seeds, then filter for validity, dedup,
10
+ and difficulty. **Evol-Instruct**: iteratively rewrite each instruction to be harder
11
+ (add constraints, deepen, broaden) so the corpus spans a difficulty gradient. **AI feedback /
12
+ UltraFeedback-style**: have a model rate candidate responses on axes (helpfulness, honesty,
13
+ correctness) to build preference pairs.
14
+
15
+ ### distilabel skeleton (Argilla / Hugging Face, v1.x — verify)
16
+
17
+ ```python
18
+ from distilabel.pipeline import Pipeline
19
+ from distilabel.steps import LoadDataFromDicts
20
+ from distilabel.steps.tasks import TextGeneration
21
+ # LLM client import paths shift between releases — check current distilabel docs.
22
+
23
+ with Pipeline(name="self-instruct") as pipe:
24
+ seeds = LoadDataFromDicts(data=[{"instruction": "Explain X."}, ...])
25
+ gen = TextGeneration(llm=...) # plug an LLM client
26
+ seeds >> gen
27
+
28
+ distiset = pipe.run(parameters={...})
29
+ distiset.push_to_hub("me/synthetic-sft") # pushes data + the pipeline definition + a card
30
+ ```
31
+
32
+ Distilabel serializes the whole pipeline to YAML/JSON and (on push) writes it into the dataset
33
+ card, so a run is reproducible. Output is a `Distiset` (a `datasets.DatasetDict` variant).
34
+
35
+ ### Distillation licensing trap (do this before generating)
36
+
37
+ Training on *another model's outputs* is governed by that model/provider's terms:
38
+
39
+ - **Proprietary API outputs** may be contractually barred from training competing models — read
40
+ the current provider ToS, not a blog.
41
+ - **Open-weight licenses** vary and are **not** all OSI-open: Llama ships under the Meta Llama
42
+ Community License (with acceptable-use + naming/derivative terms), Gemma has custom Google
43
+ terms, some models (e.g. Codestral-class) are non-production/research-only, and Qwen license
44
+ terms vary by size. **Never state a license as bare fact** — open the specific model card,
45
+ licenses change.
46
+ - Record per-example provenance (which model/source produced it) so an audit can trace it.
47
+
48
+ ## Deduplication
49
+
50
+ Exact dedup: hash normalized text, drop collisions. Near-dup (the one that matters) —
51
+ **MinHash + LSH** over token/char shingles:
52
+
53
+ ```python
54
+ from datasketch import MinHash, MinHashLSH
55
+
56
+ def minhash(text, num_perm=128, k=5):
57
+ m = MinHash(num_perm=num_perm)
58
+ toks = text.split()
59
+ for i in range(len(toks) - k + 1):
60
+ m.update(" ".join(toks[i:i+k]).encode())
61
+ return m
62
+
63
+ lsh, keep = MinHashLSH(threshold=0.8, num_perm=128), []
64
+ for i, ex in enumerate(dataset):
65
+ mh = minhash(ex["text"])
66
+ if not lsh.query(mh): # no near-duplicate already kept
67
+ lsh.insert(str(i), mh); keep.append(i)
68
+ dedup = dataset.select(keep)
69
+ ```
70
+
71
+ For very large corpora use a batched/distributed dedup (e.g. HF `datatrove`, `text-dedup`)
72
+ rather than an in-memory loop. Tune `threshold` on a sample — too high keeps paraphrase dupes,
73
+ too low deletes legitimately similar-but-distinct examples.
74
+
75
+ ## Decontamination (against eval/benchmark test sets)
76
+
77
+ Leakage of test items into training inflates your eval — you measure memorization. Remove any
78
+ training row that overlaps a test item, by **n-gram overlap** (the common convention is a long
79
+ n-gram, e.g. 13-gram, exact match; also embed-and-threshold for paraphrased leakage):
80
+
81
+ ```python
82
+ def ngrams(text, n=13):
83
+ t = text.lower().split()
84
+ return {" ".join(t[i:i+n]) for i in range(len(t) - n + 1)}
85
+
86
+ contam = set().union(*(ngrams(x["text"]) for x in eval_set)) # build from EVERY eval you report
87
+ clean = dataset.filter(lambda ex: ngrams(ex["text"]).isdisjoint(contam))
88
+ ```
89
+
90
+ Decontaminate against **every** benchmark you will quote *and* your own private held-out set —
91
+ including any public dataset you synthesized from, since its test split may be in your training
92
+ mix. `lm-eval-harness` and dataset-curation toolkits ship decontamination utilities; prefer a
93
+ maintained one over hand-rolled for scale.
94
+
95
+ ## Quality filtering
96
+
97
+ **LIMA** (*Less Is More for Alignment*, arXiv 2305.11206): ~1,000 curated examples produced a
98
+ strong instruction-follower — for teaching **behavior/style/format**, curation and diversity
99
+ beat raw volume. Caveat: LIMA is about *alignment*, not injecting large new *knowledge* — a
100
+ broad domain-knowledge shift still wants scale. Practical filter ladder:
101
+
102
+ 1. **Format/structural** — drop empty/truncated turns, malformed JSON, wrong role order, missing
103
+ final assistant turn (SFT), identical `chosen`/`rejected` (preference).
104
+ 2. **Length** — cut extreme-short and runaway-long outliers (often degenerate/repetitive).
105
+ 3. **Dedup + decontam** (above).
106
+ 4. **Diversity** — embed and cluster; downsample over-represented intents so one task is not 80%
107
+ of the set. (Lilac was built for exactly this triage — but its OSS repo was archived
108
+ ~July 2025 after the Databricks acquisition; **[verify]** before depending on it.)
109
+ 5. **Scoring** — reward model or LLM-as-judge rates helpfulness/correctness; keep the top slice.
110
+ Audit the judge: it has position/length/self-preference biases; spot-check by hand.
111
+
112
+ Always keep a discarded-rows sample so filtering decisions are reviewable, not a black hole.
113
+
114
+ ## Licensing — two separate questions
115
+
116
+ 1. **Your dataset's license** — what you release the JSONL under, and whether you *may* (you
117
+ cannot relicense others' data by aggregating it).
118
+ 2. **Source-usage restrictions** — the terms attached to where each row came from: scraped-site
119
+ ToS, the license of any base dataset, and the model-output ToS above. These bind even if you
120
+ never publish. A `source`/`license` provenance column per row makes an audit tractable.
121
+
122
+ Write a **data card** (HF dataset README front-matter): license, sources, generation method,
123
+ dedup/decontam done, known biases, intended use.
124
+
125
+ ## Tooling summary
126
+
127
+ | Tool | Use | Note |
128
+ | --- | --- | --- |
129
+ | distilabel | synthetic data / AI-feedback pipelines | Argilla/HF, v1.x, → `Distiset` |
130
+ | Argilla | human annotation/review UI | vet and label examples |
131
+ | HF `datasets` | load/map/filter/push | the substrate |
132
+ | datatrove / text-dedup | large-scale dedup | when datasketch loop is too small |
133
+ | lm-eval-harness | eval + decontamination utils | report metrics on the clean split |
134
+ | Lilac | dataset exploration/clustering | **OSS archived ~2025-07 [verify]** |
@@ -0,0 +1,254 @@
1
+ ---
2
+ name: unsloth
3
+ description: "Use when fine-tuning an open-weight LLM fast on ONE GPU with low VRAM — Unsloth's FastLanguageModel/FastModel + 4-bit QLoRA + trl SFTTrainer, response-only loss masking via get_chat_template/train_on_responses_only, GRPO reasoning FT, and export to merged-16bit / GGUF / push_to_hub for Ollama or vLLM. Triggers: 'fine-tune Llama/Qwen/Gemma/gpt-oss on a single 16GB/24GB GPU', 'QLoRA OOMs on my GPU, make it fit', 'train ~2x faster with less VRAM', 'why is my loss covering the prompt / how do I mask the user turn', 'save my LoRA as GGUF Q4_K_M for Ollama', 'ajustar un modelo en una sola GPU sense quedar-me sense VRAM'. NOT whether/why/which method to fine-tune or trl/peft theory (that is finetuning), NOT running the exported GGUF locally (that is ollama), NOT serving-engine flags/throughput (that is vllm), NOT building the JSONL dataset (that is training-data)."
4
+ tags: [unsloth, qlora, lora, single-gpu, gguf, fine-tuning]
5
+ recommends: [finetuning, training-data, open-weights, ollama, huggingface]
6
+ origin: risco
7
+ ---
8
+
9
+ # Unsloth — fast, low-VRAM fine-tuning on one GPU
10
+
11
+ Unsloth is a fine-tuning *backend*: hand-written Triton kernels + a patched LoRA/QLoRA path that make
12
+ `transformers` + `trl` training run faster and fit a much bigger model on a single consumer GPU. You
13
+ reach for it when the *decision to fine-tune is already made* and the problem is now "make this run on
14
+ the one GPU I have." This skill owns the backend + the export mechanics. It does **not** decide
15
+ whether fine-tuning is even the right move (`finetuning`), and it does **not** run the model you
16
+ export (`ollama` / `vllm`).
17
+
18
+ ## Read this first (the two things that bite everyone)
19
+
20
+ 1. **Single-GPU is the free-tier assumption.** The open (Apache-2.0) core is built for one GPU.
21
+ Multi-GPU / multi-node "works but a better version is coming" per the docs, and the polished
22
+ multi-GPU + full-finetuning path is gated behind the paid Pro/Enterprise tiers — **verify the
23
+ current split at docs.unsloth.ai before you promise anyone `torchrun --nproc 8`.** If the plan is
24
+ truly multi-node from day one, that is an `axolotl`/native-`trl`/`accelerate` job, not this.
25
+ 2. **You must mask the prompt or your loss is wrong.** Fine-tuning a chat model means computing loss
26
+ on the *assistant* turn only. Use Unsloth's `get_chat_template` for the format and
27
+ `train_on_responses_only` for the mask. Skip it and the model trains on predicting the user's
28
+ words too — loss looks fine, behaviour is subtly broken (and on some templates you hit a
29
+ zero-loss trap). This is the single most common mistake; it is section 4 for a reason.
30
+
31
+ ### Version / performance reality (fast-moving — verify at author time)
32
+
33
+ The headline, straight off docs.unsloth.ai (accessed 2026-07): **~2x faster training with ~70% less
34
+ VRAM, no accuracy loss**, on a single GPU with the free core. Treat that as a *class* of improvement,
35
+ not a contract:
36
+
37
+ - The baseline is a **standard Hugging Face + FlashAttention-2 QLoRA** pipeline, not "raw PyTorch."
38
+ - Numbers are **model-, GPU-, and config-specific.** Some pages/reviews cite ~60% VRAM; GRPO/RL
39
+ claims ~80% less VRAM; MoE (e.g. gpt-oss, Qwen3-family MoE) shows much larger multipliers on
40
+ specific hardware (up to ~7–12x on a B200 in Unsloth's own MoE post). **Do not quote a single
41
+ number as gospel — cite the docs page you read and hedge.**
42
+ - Licensing/pricing also moves: core is Apache-2.0 and free; a paid Pro tier and an Enterprise
43
+ (contact-sales) tier add multi-GPU/multi-node, full-parameter training, and faster kernels. **Check
44
+ unsloth.ai/pricing for the live tiers and figures.**
45
+
46
+ ### Setup
47
+
48
+ Notebook-centric by design — the fastest path is one of the maintained Colab/Kaggle notebooks
49
+ (`unslothai/notebooks`). Locally:
50
+
51
+ ```bash
52
+ pip install unsloth # pulls unsloth + unsloth_zoo; expects a recent PyTorch + CUDA
53
+ python -c "import unsloth; print(unsloth.__version__)"
54
+ ```
55
+
56
+ NVIDIA is the first-class target (min ~CUDA-capable GPU, works down to ~a free-Colab T4 for small
57
+ models). AMD (ROCm) and Intel GPU support have landed as install targets — **verify your hardware on
58
+ the docs' requirements page before assuming it works.** Don't pin a brittle version in your head;
59
+ `unsloth` ships frequently — install fresh and read its startup banner (it prints the versions it
60
+ patched).
61
+
62
+ ## Supported models (verify the live list)
63
+
64
+ Unsloth advertises **500+ models** across text, vision, and TTS/embeddings. Families you can expect
65
+ (confirm the specific checkpoint at docs.unsloth.ai/models — new releases land within days):
66
+
67
+ - **Text:** Llama, Qwen, Gemma, Mistral/Mixtral, Phi, DeepSeek, GLM, and **gpt-oss** (OpenAI's
68
+ open-weight MoE).
69
+ - **Vision (VLM):** e.g. Qwen-VL, Gemma vision, Llama-vision — via `FastVisionModel`.
70
+ - Plus TTS and embedding fine-tunes in the notebook zoo.
71
+
72
+ Prefer Unsloth's **pre-quantized 4-bit repos** (`unsloth/<model>-unsloth-bnb-4bit`) — faster download,
73
+ fewer OOMs. Which base model + **which license** is right for you is an `open-weights` question, not
74
+ this one: **never assert a model's license from memory** (Llama = Meta Community license, Gemma =
75
+ custom terms, gpt-oss/Qwen vary by size) — read the model card.
76
+
77
+ ## Canonical flow
78
+
79
+ Three steps: **load 4-bit → attach LoRA → SFTTrainer**. `FastModel` is the newer unified loader (text
80
+ + vision); `FastLanguageModel` is the text path and still owns `.get_peft_model`.
81
+
82
+ ```python
83
+ from unsloth import FastLanguageModel
84
+ import torch
85
+ from trl import SFTTrainer, SFTConfig
86
+ from datasets import load_dataset
87
+
88
+ max_seq_length = 2048 # Unsloth does RoPE scaling internally — pick what you need
89
+
90
+ # 1) Load a (pre-quantized) base in 4-bit. This is the QLoRA memory win.
91
+ model, tokenizer = FastLanguageModel.from_pretrained(
92
+ model_name = "unsloth/Meta-Llama-3.1-8B-Instruct-bnb-4bit", # verify current id
93
+ max_seq_length= max_seq_length,
94
+ load_in_4bit = True, # QLoRA. False + load_in_16bit=True => 16-bit LoRA
95
+ # load_in_8bit / load_in_16bit / full_finetuning are the other switches
96
+ # token = "hf_...", # only for gated repos
97
+ )
98
+
99
+ # 2) Attach LoRA adapters — you train ~1% of weights.
100
+ model = FastLanguageModel.get_peft_model(
101
+ model,
102
+ r = 16, # rank: 8/16/32; higher = more capacity, more VRAM
103
+ lora_alpha = 16, # a common default is alpha == r (some recipes use 2*r)
104
+ target_modules = ["q_proj","k_proj","v_proj","o_proj",
105
+ "gate_proj","up_proj","down_proj"],
106
+ lora_dropout = 0, # 0 is the optimized path
107
+ bias = "none", # "none" is the optimized path
108
+ use_gradient_checkpointing = "unsloth", # "unsloth" = ~30% less VRAM, fits longer context
109
+ random_state = 3407,
110
+ )
111
+
112
+ # 3) Train with trl's SFTTrainer (see section 4 before you call .train()).
113
+ dataset = load_dataset("json", data_files="train.jsonl", split="train") # your data
114
+ trainer = SFTTrainer(
115
+ model = model, tokenizer = tokenizer, train_dataset = dataset,
116
+ args = SFTConfig(
117
+ max_seq_length = max_seq_length,
118
+ per_device_train_batch_size = 2,
119
+ gradient_accumulation_steps = 4, # effective batch = 2*4
120
+ warmup_steps = 10,
121
+ max_steps = 60, # or num_train_epochs = 1
122
+ learning_rate = 2e-4,
123
+ logging_steps = 1,
124
+ optim = "adamw_8bit", # 8-bit optimizer = more VRAM saved
125
+ output_dir = "outputs",
126
+ seed = 3407,
127
+ ),
128
+ )
129
+ trainer.train()
130
+ ```
131
+
132
+ MoE caveat: 4-bit QLoRA is **not** supported for MoE models yet — load MoE in 16-bit and LoRA the
133
+ `gate_up_proj` / `down_proj` layers. (`load_in_4bit=False` for gpt-oss/Qwen3-MoE.) Verify on
134
+ docs.unsloth.ai/basics/faster-moe.
135
+
136
+ ## Chat templates + response-only loss (do not skip)
137
+
138
+ Format with Unsloth's template helper — **not** a hand-written string — so the special tokens match
139
+ what the base model was trained on:
140
+
141
+ ```python
142
+ from unsloth.chat_templates import get_chat_template
143
+
144
+ tokenizer = get_chat_template(tokenizer, chat_template = "llama-3.1") # match your base model
145
+ # then map your messages -> a "text" column via tokenizer.apply_chat_template(...)
146
+ ```
147
+
148
+ Then wrap the trainer so **loss is computed on the assistant turn only**:
149
+
150
+ ```python
151
+ from unsloth.chat_templates import train_on_responses_only
152
+
153
+ trainer = train_on_responses_only(
154
+ trainer,
155
+ instruction_part = "<|start_header_id|>user<|end_header_id|>\n\n", # Llama-3
156
+ response_part = "<|start_header_id|>assistant<|end_header_id|>\n\n",
157
+ )
158
+ # Gemma-3 would use: instruction_part="<start_of_turn>user\n", response_part="<start_of_turn>model\n"
159
+ ```
160
+
161
+ The `instruction_part` / `response_part` strings are the **template's own turn markers** — they must
162
+ match the chat template you applied, per model. Verify the mask worked before spending GPU-hours:
163
+
164
+ ```python
165
+ # labels are -100 where masked. Decoding the non-masked tokens should show ONLY the answer.
166
+ print(tokenizer.decode(trainer.train_dataset[0]["input_ids"]))
167
+ print(tokenizer.decode([tokenizer.pad_token_id if x == -100 else x
168
+ for x in trainer.train_dataset[0]["labels"]]))
169
+ ```
170
+
171
+ More templates, thinking-mode (`enable_thinking`), and the vision path are in
172
+ [references/masking-and-templates.md](references/masking-and-templates.md).
173
+
174
+ ## GRPO / reasoning fine-tuning (brief)
175
+
176
+ Unsloth supports RL (GRPO and variants) with the same low-VRAM story — it plugs into `trl`'s
177
+ `GRPOTrainer` / `GRPOConfig` and can use a built-in vLLM engine (`fast_inference=True`) for the
178
+ rollout generation. Instead of imitating a target string, GRPO optimizes **reward functions** you
179
+ write (e.g. "answer matches ground truth", "output obeys the `<reasoning>/<answer>` format"). The docs
180
+ cite ~80% less VRAM for GRPO vs a standard setup — verify. This is how you turn a base model into a
181
+ reasoning model on one GPU. The *choice* of SFT vs DPO vs GRPO is a `finetuning` decision; the
182
+ mechanics + a runnable GSM8K reward example live in [references/grpo.md](references/grpo.md).
183
+
184
+ ## Export (the other half of this skill)
185
+
186
+ After `trainer.train()` you have LoRA adapters. Pick an export by where it's going:
187
+
188
+ ```python
189
+ # A) Merge LoRA into the base at 16-bit — the portable, high-quality artifact (vLLM, re-hosting).
190
+ model.save_pretrained_merged("model_16bit", tokenizer, save_method = "merged_16bit")
191
+ model.push_to_hub_merged("user/model", tokenizer, save_method = "merged_16bit", token = "hf_...")
192
+
193
+ # B) Keep just the adapters (small, hot-swappable).
194
+ model.save_pretrained_merged("model_lora", tokenizer, save_method = "lora")
195
+
196
+ # C) GGUF for llama.cpp / Ollama — choose the quant that trades size vs quality.
197
+ model.save_pretrained_gguf("model_gguf", tokenizer, quantization_method = "q4_k_m")
198
+ model.push_to_hub_gguf("user/model-gguf", tokenizer,
199
+ quantization_method = ["q4_k_m", "q8_0", "f16"], token = "hf_...")
200
+ ```
201
+
202
+ Then **running** the GGUF is an `ollama` job (`ollama create` from the file, `ollama run`), and
203
+ serving the merged-16bit at scale is a `vllm` job. Quant guidance: **Q4_K_M** is the everyday
204
+ size/quality sweet spot, **Q8_0** near-lossless, **f16** the unquantized ceiling — lower quant = smaller
205
+ + faster but real quality loss. Do **not** merge to 4-bit as your keeper artifact (quality drops;
206
+ it's a niche path). Full export matrix + the Ollama/llama.cpp handoff:
207
+ [references/export.md](references/export.md).
208
+
209
+ ## Guardrails / gotchas
210
+
211
+ - **Single-GPU assumption (OSS).** Don't design a multi-node run on the free core; verify the paid
212
+ multi-GPU/multi-node status at docs.unsloth.ai first. `CUDA_VISIBLE_DEVICES` to one GPU if unsure.
213
+ - **No mask = wrong training.** Without `train_on_responses_only`, loss covers the prompt; some
214
+ templates then show ~0 loss. Always decode-check the labels once.
215
+ - **Wrong chat template = garbage.** The `chat_template` and the mask's `instruction_part`/
216
+ `response_part` must match the *base model's* markers. Use `get_chat_template`; never hand-roll.
217
+ - **MoE ≠ 4-bit yet.** Load MoE models in 16-bit; QLoRA-4bit is unsupported for them (verify).
218
+ - **GGUF quant is lossy.** Q4_K_M for size, Q8_0/f16 when quality matters. Merged-4bit is discouraged.
219
+ - **`import unsloth` first.** Import it before `transformers`/`trl` so its patches apply; heed the
220
+ startup banner that prints patched versions.
221
+ - **Numbers drift.** The 2x/70% headline is a *class*, not a guarantee — cite the docs page and hedge.
222
+
223
+ ## Related skills
224
+
225
+ - **`finetuning`** — the method layer: FT-vs-RAG-vs-prompt, SFT/DPO/GRPO choice, hyperparameters, the
226
+ backend-agnostic `trl`/`peft` theory. Unsloth is *one fast backend* under it; go there for "should
227
+ I / how much / which method." This skill is "make it run on my GPU."
228
+ - **`training-data`** — build the JSONL messages / preference pairs you feed the trainer. Data shape
229
+ and quality live there; this skill assumes you already have a dataset.
230
+ - **`open-weights`** — choose the base model + read its license/size tradeoffs before you fine-tune.
231
+ - **`ollama`** — run the GGUF you export, on one box. Export here, run there.
232
+ - **`huggingface`** — get the base weights and host/push the result; `vllm` serves the merged-16bit at
233
+ throughput. This skill produces the artifact; those consume it.
234
+
235
+ ## Checklist
236
+
237
+ - [ ] Fine-tuning is actually the right move and method is chosen (confirmed via `finetuning`).
238
+ - [ ] Base model + license verified on its card (`open-weights`); using a current `unsloth/*-4bit` id.
239
+ - [ ] `from_pretrained(load_in_4bit=True, max_seq_length=…)` → `get_peft_model(r, target_modules, …)`.
240
+ - [ ] `get_chat_template` applied with the model's correct template.
241
+ - [ ] `train_on_responses_only` applied AND the label mask decode-checked (answer-only).
242
+ - [ ] Fits the one GPU (batch × grad-accum, `use_gradient_checkpointing="unsloth"`, `adamw_8bit`).
243
+ - [ ] Exported for the target: merged_16bit (vLLM/re-host) or GGUF Q4_K_M/Q8_0 (Ollama/llama.cpp).
244
+ - [ ] Any speed/VRAM/tier/model claim I stated is hedged + cited to docs.unsloth.ai (not memory).
245
+
246
+ ## References
247
+
248
+ - [references/masking-and-templates.md](references/masking-and-templates.md) — `get_chat_template`
249
+ options, per-model `instruction_part`/`response_part` pairs, thinking-mode, the vision path, and the
250
+ label-mask sanity check.
251
+ - [references/grpo.md](references/grpo.md) — GRPO end-to-end: `GRPOConfig`/`GRPOTrainer`, `vLLM`
252
+ `fast_inference`, a GSM8K reward-function set, and loss-type/DAPO knobs.
253
+ - [references/export.md](references/export.md) — full export matrix (merged_16bit / lora / merged_4bit
254
+ / GGUF), quant-method table, `push_to_hub_*`, manual `convert_hf_to_gguf.py`, and the Ollama handoff.
@@ -0,0 +1,10 @@
1
+ # Evals for the `unsloth` skill
2
+
3
+ `cases.yaml` is a routing + capability fixture, not an automated harness. For each `should_trigger`
4
+ prompt, confirm the router picks `unsloth` (fast low-VRAM single-GPU fine-tuning + GGUF export). For
5
+ each `should_not_trigger`, confirm it routes to the named sibling — method choice to `finetuning`,
6
+ dataset to `training-data`, running the GGUF to `ollama`, throughput serving to `vllm`, base-model
7
+ choice to `open-weights` — and not here. For the `capability` case, check the answer covers every
8
+ `must_include` bullet: the from_pretrained→get_peft_model→SFTTrainer flow, response-only masking, GGUF
9
+ export + the ollama handoff, the hedged speed/VRAM claim, and the single-GPU guardrail. No GPUs or
10
+ network needed.
@@ -0,0 +1,42 @@
1
+ skill: unsloth
2
+
3
+ should_trigger:
4
+ - prompt: "I want to QLoRA fine-tune Llama-3.1-8B but it OOMs on my 16GB GPU — how do I make it fit?"
5
+ why: Core niche — fast, low-VRAM single-GPU fine-tuning; the fit problem is exactly what load_in_4bit + get_peft_model + adamw_8bit + unsloth gradient checkpointing solve.
6
+ - prompt: "How do I fine-tune gpt-oss on a single 24GB GPU roughly 2x faster with less VRAM?"
7
+ why: Names the backend's headline (2x/less-VRAM), a single GPU, and a supported MoE model — squarely this skill.
8
+ - prompt: "My chat fine-tune's loss looks like it's covering the user turn — how do I mask the prompt so it only trains on the response?"
9
+ why: The train_on_responses_only / get_chat_template masking problem, the load-bearing gotcha owned here.
10
+ - prompt: "After training my LoRA with Unsloth, how do I export it to GGUF Q4_K_M for Ollama?"
11
+ why: Export mechanics (save_pretrained_gguf, quant choice, handoff to ollama) are half of this skill.
12
+ - prompt: "Turn a base Qwen into a reasoning model with GRPO on one GPU using reward functions."
13
+ why: GRPO/reasoning FT via GRPOTrainer + fast_inference vLLM, the low-VRAM RL path this skill covers.
14
+ - prompt: "Vull ajustar un model a una sola GPU sense quedar-me sense VRAM amb Unsloth."
15
+ why: Catalan; single-GPU low-VRAM fine-tuning with Unsloth by name — the exact trigger.
16
+
17
+ should_not_trigger:
18
+ - prompt: "Should I fine-tune, use RAG, or just prompt-engineer for my support bot? And SFT vs DPO?"
19
+ route_to: finetuning
20
+ why: The whether/why/which-method decision is the method layer, not the backend that makes it run.
21
+ - prompt: "How do I format my conversations into a JSONL messages dataset for fine-tuning?"
22
+ route_to: training-data
23
+ why: Dataset construction/quality; this skill assumes the dataset already exists.
24
+ - prompt: "I exported a GGUF — how do I run it locally, write a Modelfile, and size VRAM in Ollama?"
25
+ route_to: ollama
26
+ why: Running the exported artifact locally is Ollama's job; Unsloth stops at producing the file.
27
+ - prompt: "Serve my merged 16-bit model at high throughput with tensor parallelism and continuous batching."
28
+ route_to: vllm
29
+ why: Serving-engine flags/throughput are the serving skill, not the training backend.
30
+ - prompt: "Which open model and license should I pick as my base — Llama vs Qwen vs Gemma?"
31
+ route_to: open-weights
32
+ why: Base-model selection + license tradeoffs are the choosing layer, not the fine-tuning backend.
33
+
34
+ capability:
35
+ - scenario: "I have one RTX 4090 (24GB). Fine-tune Llama-3.1-8B-Instruct on my chat JSONL with Unsloth so loss is only on the assistant turns, then export a Q4_K_M GGUF to run in Ollama. It keeps OOMing."
36
+ must_include:
37
+ - "FastLanguageModel.from_pretrained with load_in_4bit=True and max_seq_length, using a pre-quantized unsloth/*-bnb-4bit base id (verify the current id)."
38
+ - "get_peft_model with r/lora_alpha/target_modules and use_gradient_checkpointing='unsloth'; plus VRAM levers for OOM (batch size + gradient_accumulation_steps, optim='adamw_8bit')."
39
+ - "get_chat_template for the correct Llama-3.1 template AND train_on_responses_only with matching instruction_part/response_part so loss is response-only — with a decode/label sanity check."
40
+ - "Export via save_pretrained_gguf(quantization_method='q4_k_m') and the note that running it is an ollama job (create from the GGUF, run); merge to 16bit for vLLM instead."
41
+ - "The honesty hedge: the ~2x faster / ~70% less VRAM headline is model/GPU/config-specific vs a HF+FA2 QLoRA baseline — verify current numbers at docs.unsloth.ai and don't quote one number as gospel."
42
+ - "The single-GPU guardrail (OSS core is single-GPU; multi-GPU/multi-node is the paid/verify-live tier) and the boundary that method choice is finetuning, not this."
@@ -0,0 +1,75 @@
1
+ # Export: from trained LoRA to a runnable artifact
2
+
3
+ After `trainer.train()` you hold LoRA adapters on top of a (possibly 4-bit) base. Export depends on
4
+ where it runs next. Tracks docs.unsloth.ai inference-and-deployment pages (accessed 2026-07).
5
+
6
+ ## The matrix
7
+
8
+ | Target | Method | Notes |
9
+ | -------------------------- | ------------------------------------------------------------- | ----- |
10
+ | vLLM / re-host / portable | `save_pretrained_merged(dir, tok, save_method="merged_16bit")`| Base + LoRA merged at 16-bit. The high-quality keeper. |
11
+ | Small, hot-swappable | `save_pretrained_merged(dir, tok, save_method="lora")` | Adapters only (~MBs). Load onto the base at run time. |
12
+ | Ollama / llama.cpp | `save_pretrained_gguf(dir, tok, quantization_method="q4_k_m")`| Converts + quantizes to GGUF. |
13
+ | DPO / niche 4-bit inference| `save_method="merged_4bit"` | **Discouraged** — quality loss; only if a tool needs it. |
14
+
15
+ ## Merge to 16-bit (recommended keeper)
16
+
17
+ ```python
18
+ model.save_pretrained_merged("model_16bit", tokenizer, save_method = "merged_16bit")
19
+ # push instead of / in addition to saving:
20
+ model.push_to_hub_merged("user/model", tokenizer,
21
+ save_method = "merged_16bit", token = "hf_...")
22
+ ```
23
+
24
+ `merged_16bit` is the format vLLM and most serving stacks expect. Prefer it over `merged_4bit` — merge
25
+ at 16-bit, quantize downstream if you need to.
26
+
27
+ ## LoRA adapters only
28
+
29
+ ```python
30
+ model.save_pretrained("finetuned_lora"); tokenizer.save_pretrained("finetuned_lora")
31
+ # or save_pretrained_merged(..., save_method="lora")
32
+ ```
33
+
34
+ Tiny and hot-swappable (e.g. vLLM LoRA hot-swap). Requires the base at inference time.
35
+
36
+ ## GGUF for Ollama / llama.cpp
37
+
38
+ ```python
39
+ # one quant:
40
+ model.save_pretrained_gguf("model_gguf", tokenizer, quantization_method = "q4_k_m")
41
+ # several at once, pushed to the Hub:
42
+ model.push_to_hub_gguf("user/model-gguf", tokenizer,
43
+ quantization_method = ["q4_k_m", "q8_0", "f16"], token = "hf_...")
44
+ ```
45
+
46
+ Quant guide (size vs quality — this is a real tradeoff, not free):
47
+
48
+ | Method | ~bits/weight | Use it for |
49
+ | -------- | ------------ | ---------- |
50
+ | `q4_k_m` | ~4.9 | Everyday default: ~half the size for a few % quality loss. |
51
+ | `q5_k_m` | ~5.5 | A notch more quality than Q4 for a bit more size. |
52
+ | `q8_0` | ~8.5 | Near-lossless; larger. |
53
+ | `f16` | 16 | Unquantized ceiling; biggest. |
54
+
55
+ Lower than Q4 (Q2/Q3) drops quality sharply — avoid unless desperate for size.
56
+
57
+ ### Manual conversion (when the helper can't, or you want control)
58
+
59
+ Merge to 16-bit first, then run llama.cpp's converter:
60
+
61
+ ```bash
62
+ python llama.cpp/convert_hf_to_gguf.py model_16bit \
63
+ --outfile model-F16.gguf --outtype f16 --split-max-size 50G
64
+ # then quantize with llama.cpp's quantize tool to q4_k_m / q8_0, etc.
65
+ ```
66
+
67
+ ## Handoff — this skill stops at the file
68
+
69
+ - **Run the GGUF locally →** `ollama`. Roughly: `ollama create <name> -f Modelfile` with
70
+ `FROM ./model-Q4_K_M.gguf`, then `ollama run <name>`. The Modelfile/quant/VRAM details are its job.
71
+ Unsloth's docs also show `OLLAMA_MODELS=unsloth ollama run merged_file.gguf` for a quick check.
72
+ - **Serve merged-16bit at throughput →** `vllm` (engine flags, batching, LoRA hot-swap).
73
+ - **Host/share the weights →** `huggingface` (the `push_to_hub_*` calls above land there).
74
+
75
+ Export is the last thing this skill owns; running and serving belong to those siblings.
@@ -0,0 +1,91 @@
1
+ # GRPO / reasoning fine-tuning with Unsloth
2
+
3
+ Mechanics only. **Whether** to do RL vs SFT vs DPO is a `finetuning` decision. Tracks
4
+ docs.unsloth.ai's RL guide (accessed 2026-07); reward APIs and loss-type flags move — verify.
5
+
6
+ ## The idea
7
+
8
+ GRPO (Group Relative Policy Optimization) doesn't imitate a target string. It samples several
9
+ completions per prompt, scores each with **reward functions you write**, and pushes weights toward the
10
+ higher-reward samples. That's how you train a model to *reason* (produce correct answers in a required
11
+ format) rather than memorize outputs. Unsloth's win here is memory — the docs cite ~80% less VRAM for
12
+ GRPO vs a standard setup (verify), and it can drive generation through a built-in vLLM engine.
13
+
14
+ ## Load with fast inference
15
+
16
+ ```python
17
+ from unsloth import FastLanguageModel
18
+
19
+ model, tokenizer = FastLanguageModel.from_pretrained(
20
+ model_name = "unsloth/Llama-3.1-8B-Instruct", # verify current id
21
+ max_seq_length = 1024, # room for the reasoning trace
22
+ load_in_4bit = True,
23
+ fast_inference = True, # use the built-in vLLM engine for rollouts
24
+ gpu_memory_utilization = 0.8, # lower if you OOM
25
+ )
26
+ model = FastLanguageModel.get_peft_model(model, r = 32, lora_alpha = 64,
27
+ target_modules = ["q_proj","k_proj","v_proj","o_proj",
28
+ "gate_proj","up_proj","down_proj"],
29
+ use_gradient_checkpointing = "unsloth", random_state = 3407)
30
+ # For memory-efficient GRPO with vLLM you may also set:
31
+ # os.environ["UNSLOTH_VLLM_STANDBY"] = "1"
32
+ ```
33
+
34
+ ## Reward functions (GSM8K-style)
35
+
36
+ Each reward fn takes `completions` (+ `prompts`, `answer`, `**kwargs`) and returns a list of floats,
37
+ one per completion. Combine several — correctness plus format shaping:
38
+
39
+ ```python
40
+ import re
41
+
42
+ def correctness_reward(prompts, completions, answer, **kw):
43
+ responses = [c[0]["content"] for c in completions]
44
+ got = [extract_xml_answer(r) for r in responses]
45
+ return [2.0 if g == a else 0.0 for g, a in zip(got, answer)]
46
+
47
+ def strict_format_reward(completions, **kw):
48
+ pattern = r"^<reasoning>\n.*?\n</reasoning>\n<answer>\n.*?\n</answer>\n$"
49
+ responses = [c[0]["content"] for c in completions]
50
+ return [0.5 if re.match(pattern, r, re.S) else 0.0 for r in responses]
51
+
52
+ def extract_xml_answer(text):
53
+ return text.split("<answer>")[-1].split("</answer>")[0].strip()
54
+ ```
55
+
56
+ Keep rewards simple and hard to game — a sloppy reward degrades the model (reward hacking).
57
+
58
+ ## Train
59
+
60
+ ```python
61
+ from trl import GRPOConfig, GRPOTrainer
62
+
63
+ trainer = GRPOTrainer(
64
+ model = model, processing_class = tokenizer,
65
+ reward_funcs = [correctness_reward, strict_format_reward],
66
+ train_dataset = dataset, # prompts + ground-truth "answer"
67
+ args = GRPOConfig(
68
+ learning_rate = 5e-6,
69
+ per_device_train_batch_size = 1,
70
+ gradient_accumulation_steps = 4,
71
+ num_generations = 8, # completions sampled per prompt (the "group")
72
+ max_prompt_length = 256,
73
+ max_completion_length = 512,
74
+ max_steps = 250,
75
+ output_dir = "outputs_grpo",
76
+ # loss_type = "grpo" | "dr_grpo" | "bnpo" | "dapo"
77
+ # epsilon = 0.2, epsilon_high = 0.28, delta = 1.5 # DAPO / two-sided knobs
78
+ # mask_truncated_completions = True
79
+ ),
80
+ )
81
+ trainer.train()
82
+ ```
83
+
84
+ Notes:
85
+ - `num_generations` is the group size — bigger gives a better reward signal but costs generation time
86
+ and VRAM.
87
+ - Advanced objectives (DAPO, Dr. GRPO) are `loss_type` + `epsilon`/`delta` flags on `GRPOConfig` —
88
+ verify the current names on the RL guide.
89
+ - Reasoning traces need context headroom: budget `max_prompt_length + max_completion_length` under
90
+ `max_seq_length`.
91
+ - Export the result exactly like an SFT run — see [export.md](export.md).