Dataset Curation
This skill assumes finetuning-method-selection
already routed here — the next step is preparing
data, not choosing a method. What follows: format
selection by target method, the template/packing
mechanics behind the most common silent training
failures, rules for mixing in synthetic data
without collapse, and the dataset card that closes
out Phase 2 before a run starts.
Input: raw examples (demonstrations, preference
judgments, or task prompts) plus a routing decision
from finetuning-method-selection.
Output format: a formatted, packed, validated
JSONL dataset plus a completed dataset card — the
Phase 2 artifact /finetune checks before launching
training.
Format Selection
| Method |
Shape |
Rows |
| SFT, single-turn |
Instruct (instruction/response or prompt/completion) |
~1,000+ floor |
| SFT, multi-turn |
Conversation / ChatML messages list |
~1,000+ floor |
| DPO / ORPO |
Preference pair (prompt, chosen, rejected) |
Method-dependent, see preference-optimization |
| KTO |
Unpaired (prompt, completion, label) |
Method-dependent, see preference-optimization |
| GRPO / RLVR |
Prompt-only (prompt + verifier metadata) |
Method-dependent, see grpo-rlvr-training |
~1,000+ rows is the recommended floor for SFT,
not a target. Below it, a handful of low-quality
or duplicate examples can dominate the gradient;
above it, quality over quantity — a smaller
verified, deduplicated set beats a larger noisy one.
The ChatML shape, for orientation; the other four
formats plus a ShareGPT conversion note live in
references/formats-and-templates.md:
{"messages": [
{"role": "user", "content": "..."},
{"role": "assistant", "content": "..."}
]}
Chat Templates and Loss Masking
Apply the target model's chat template before
any concatenation or packing, never after — packing
raw text and templating the packed blob afterward
corrupts turn boundaries, landing role markers in
the wrong place relative to each example.
Train on assistant responses only. Mask the
loss (-100 in the labels tensor) over system/user
turns and the template's own role markers — only
assistant-turn content tokens contribute to loss.
Template/tokenizer mismatches are a top silent
failure mode. A model trained against one chat
template but served or evaluated with a different
one degrades without erroring. Verify the same
template string used in training is applied at
inference and eval time.
Keep the dataset in messages shape and let
the trainer template and mask it
(assistant_only_loss=True in current TRL) —
pre-rendering to a flat text field destroys the
turn boundaries masking needs. Full code sketch:
references/formats-and-templates.md. Sanity-check
before training — decode only unmasked positions;
expect only assistant text:
keep = batch["labels"][0] != -100
print(tokenizer.decode(batch["input_ids"][0][keep]))
Packing
Without packing, 40–70% of compute is spent on
padding — variable-length examples batched at a
fixed sequence length waste the gap between each
example's length and the batch's max. Packing
concatenates multiple examples into one sequence
up to the max length, cutting most of that waste.
Packing changes batch semantics. A packed
sequence can contain several original examples, so
"steps per epoch" and any LR schedule keyed to
example count shift once packing is on — recompute
schedule milestones against packed-sequence count.
MANDATORY: decode and manually inspect 5–10
packed sequences before scaling to a full run.
Confirm example boundaries land where expected,
template markers are intact per sub-example, and
the loss mask is still assistant-only within each
packed sequence. Not optional — packing bugs are
silent (the loss curve looks normal) and only
surface in eval quality, hours later:
for seq in packed_dataset.select(range(10)):
print(tokenizer.decode(seq["input_ids"]))
Synthetic Data Rules
- Keep ≥25% real data as a collapse guard.
Training on a growing share of model-generated
data without a real-data floor drives measurable
quality collapse over successive generations —
25% real is the minimum that holds the line.
General-domain replay rows
count toward this floor —
"real" means "not generated
for this task from this
student," not "human-authored."
An all-synthetic-by-construction
dataset can meet the ≥25% floor
through replay alone (see
references/synthetic-data.md's
Replay-Mix Construction recipe);
state which rows count as "real"
in the dataset card rather than
leaving the floor structurally
unmeetable.
- Magpie and rejection sampling are the
workhorses. Magpie extracts prompts from the
model's own template prior; rejection sampling
generates several candidates per prompt and keeps
only the ones a filter passes. Both beat naive
single-shot generation.
- Targeted, student-aware generation beats static
generation by 1.3–2x sample efficiency — aiming
at the student's actual failure modes hits a
quality bar with fewer filtered examples.
- Typical accept rates after filtering run
10–30%. Plan volume accordingly — a 10,000-row
target at 15% accept needs ~65,000+ raw generations.
- Generation-method ranking, filter funnel, replay-
mix construction, and distillation pattern:
references/synthetic-data.md.
The Dataset Card
Every dataset that reaches training gets a card —
the required Phase 2 artifact /finetune checks
before launching. The card is not free-form
documentation; it MUST carry these fields:
- Provenance — where every row came from (real
source(s), synthetic method(s), or both),
traceable to
trace-to-training-data output.
- Counts — total rows, and rows per split
(train/eval/held-out) if split.
- Synthetic/real ratio — the measured ratio,
checked against the ≥25% real floor above.
- Dedup method — exact-match, semantic
(embedding threshold), or both; see the filter
funnel in
references/synthetic-data.md.
- Template used — the exact chat template
string/identifier, kept consistent through
inference and eval — this is what ties an
eval-harness-first run back to the checkpoint.
- Packing config — whether packing was used,
max sequence length, and confirmation the
5–10-sequence manual inspection above was done.
A dataset missing any of these six fields isn't
ready for /finetune — the card is a gate, not a
summary written after the fact.
Phase 2 Exit Checklist
Before handing off to /finetune, confirm:
- Format matches the method (table above).
- Template applied before concatenation.
- Loss masked to assistant turns only.
- 5–10 packed sequences decoded and read.
- ≥25% real data in the final mix.
- Dataset card complete — all six fields.
References
references/formats-and-templates.md — JSONL
examples per format, current-TRL masking code,
and the ShareGPT conversion note.
references/synthetic-data.md — generation-method
ranking, filter funnel, replay-mix construction,
and teacher→student distillation pattern.
Related skills: finetuning-method-selection routes
here; lora-qlora-recipes, vision-sft, and
preference-optimization consume the datasets this
skill produces; trace-to-training-data is the
provenance source for graded-trajectory datasets;
eval-harness-first grades the resulting checkpoint.
Source: wshobson/agents → plugins/llm-finetuning/skills/dataset-curation/SKILL.md
1---2name: dataset-curation3description: Prepare, format, and validate datasets for supervised fine-tuning and preference training. Use when converting raw data into training format, applying chat templates, configuring sequence packing, generating synthetic training data, or writing a dataset card before a run.4---567# Dataset Curation89This skill assumes `finetuning-method-selection`10already routed here — the next step is preparing11data, not choosing a method. What follows: format12selection by target method, the template/packing13mechanics behind the most common silent training14failures, rules for mixing in synthetic data15without collapse, and the dataset card that closes16out Phase 2 before a run starts.1718**Input:** raw examples (demonstrations, preference19judgments, or task prompts) plus a routing decision20from `finetuning-method-selection`.21**Output format:** a formatted, packed, validated22JSONL dataset plus a completed dataset card — the23Phase 2 artifact `/finetune` checks before launching24training.2526## Format Selection2728| Method | Shape | Rows |29|---|---|---|30| SFT, single-turn | Instruct (`instruction`/`response` or `prompt`/`completion`) | ~1,000+ floor |31| SFT, multi-turn | Conversation / ChatML `messages` list | ~1,000+ floor |32| DPO / ORPO | Preference pair (`prompt`, `chosen`, `rejected`) | Method-dependent, see `preference-optimization` |33| KTO | Unpaired (`prompt`, `completion`, `label`) | Method-dependent, see `preference-optimization` |34| GRPO / RLVR | Prompt-only (`prompt` + verifier metadata) | Method-dependent, see `grpo-rlvr-training` |3536- **~1,000+ rows is the recommended floor for SFT**,37 not a target. Below it, a handful of low-quality38 or duplicate examples can dominate the gradient;39 above it, **quality over quantity** — a smaller40 verified, deduplicated set beats a larger noisy one.41- The ChatML shape, for orientation; the other four42 formats plus a ShareGPT conversion note live in43 `references/formats-and-templates.md`:4445 ```json46 {"messages": [47 {"role": "user", "content": "..."},48 {"role": "assistant", "content": "..."}49 ]}50 ```5152## Chat Templates and Loss Masking5354Apply the target model's chat template **before**55any concatenation or packing, never after — packing56raw text and templating the packed blob afterward57corrupts turn boundaries, landing role markers in58the wrong place relative to each example.5960- **Train on assistant responses only.** Mask the61 loss (`-100` in the labels tensor) over system/user62 turns and the template's own role markers — only63 assistant-turn content tokens contribute to loss.64- **Template/tokenizer mismatches are a top silent65 failure mode.** A model trained against one chat66 template but served or evaluated with a different67 one degrades without erroring. Verify the same68 template string used in training is applied at69 inference and eval time.70- **Keep the dataset in `messages` shape** and let71 the trainer template and mask it72 (`assistant_only_loss=True` in current TRL) —73 pre-rendering to a flat text field destroys the74 turn boundaries masking needs. Full code sketch:75 `references/formats-and-templates.md`. Sanity-check76 before training — decode only unmasked positions;77 expect only assistant text:7879 ```python80 keep = batch["labels"][0] != -10081 print(tokenizer.decode(batch["input_ids"][0][keep]))82 ```8384## Packing8586**Without packing, 40–70% of compute is spent on87padding** — variable-length examples batched at a88fixed sequence length waste the gap between each89example's length and the batch's max. Packing90concatenates multiple examples into one sequence91up to the max length, cutting most of that waste.9293- **Packing changes batch semantics.** A packed94 sequence can contain several original examples, so95 "steps per epoch" and any LR schedule keyed to96 example count shift once packing is on — recompute97 schedule milestones against packed-sequence count.98- **MANDATORY: decode and manually inspect 5–1099 packed sequences before scaling to a full run.**100 Confirm example boundaries land where expected,101 template markers are intact per sub-example, and102 the loss mask is still assistant-only within each103 packed sequence. Not optional — packing bugs are104 silent (the loss curve looks normal) and only105 surface in eval quality, hours later:106107 ```python108 for seq in packed_dataset.select(range(10)):109 print(tokenizer.decode(seq["input_ids"]))110 ```111112## Synthetic Data Rules113114- **Keep ≥25% real data as a collapse guard.**115 Training on a growing share of model-generated116 data without a real-data floor drives measurable117 quality collapse over successive generations —118 25% real is the minimum that holds the line.119 **General-domain replay rows120 count toward this floor** —121 "real" means "not generated122 for this task from this123 student," not "human-authored."124 An all-synthetic-by-construction125 dataset can meet the ≥25% floor126 through replay alone (see127 `references/synthetic-data.md`'s128 Replay-Mix Construction recipe);129 state which rows count as "real"130 in the dataset card rather than131 leaving the floor structurally132 unmeetable.133- **Magpie and rejection sampling are the134 workhorses.** Magpie extracts prompts from the135 model's own template prior; rejection sampling136 generates several candidates per prompt and keeps137 only the ones a filter passes. Both beat naive138 single-shot generation.139- **Targeted, student-aware generation beats static140 generation by 1.3–2x sample efficiency** — aiming141 at the student's actual failure modes hits a142 quality bar with fewer filtered examples.143- **Typical accept rates after filtering run144 10–30%.** Plan volume accordingly — a 10,000-row145 target at 15% accept needs ~65,000+ raw generations.146- Generation-method ranking, filter funnel, replay-147 mix construction, and distillation pattern:148 `references/synthetic-data.md`.149150## The Dataset Card151152Every dataset that reaches training gets a card —153the required Phase 2 artifact `/finetune` checks154before launching. The card is not free-form155documentation; it MUST carry these fields:156157- **Provenance** — where every row came from (real158 source(s), synthetic method(s), or both),159 traceable to `trace-to-training-data` output.160- **Counts** — total rows, and rows per split161 (train/eval/held-out) if split.162- **Synthetic/real ratio** — the measured ratio,163 checked against the ≥25% real floor above.164- **Dedup method** — exact-match, semantic165 (embedding threshold), or both; see the filter166 funnel in `references/synthetic-data.md`.167- **Template used** — the exact chat template168 string/identifier, kept consistent through169 inference and eval — this is what ties an170 `eval-harness-first` run back to the checkpoint.171- **Packing config** — whether packing was used,172 max sequence length, and confirmation the173 5–10-sequence manual inspection above was done.174175A dataset missing any of these six fields isn't176ready for `/finetune` — the card is a gate, not a177summary written after the fact.178179### Phase 2 Exit Checklist180181Before handing off to `/finetune`, confirm:1821831. Format matches the method (table above).1842. Template applied before concatenation.1853. Loss masked to assistant turns only.1864. 5–10 packed sequences decoded and read.1875. ≥25% real data in the final mix.1886. Dataset card complete — all six fields.189190## References191192- `references/formats-and-templates.md` — JSONL193 examples per format, current-TRL masking code,194 and the ShareGPT conversion note.195- `references/synthetic-data.md` — generation-method196 ranking, filter funnel, replay-mix construction,197 and teacher→student distillation pattern.198199Related skills: `finetuning-method-selection` routes200here; `lora-qlora-recipes`, `vision-sft`, and201`preference-optimization` consume the datasets this202skill produces; `trace-to-training-data` is the203provenance source for graded-trajectory datasets;204`eval-harness-first` grades the resulting checkpoint.205206---207208**Source:** [`wshobson/agents`](https://github.com/wshobson/agents) → `plugins/llm-finetuning/skills/dataset-curation/SKILL.md`