Use when assembling or curating the corpus a fine-tune trains on — turning raw examples into the JSONL shape a trainer expects (instruction, conversational, preference-pair or binary-label), matching the data to the model's chat template, generating synthetic or distilled examples, deduplicating and decontaminating against eval sets, and quality-filtering. NOT cleaning tabular rows, nulls and dtypes (that is `data-cleaning`), NOT building a retrieval corpus of chunks and embeddings (that is `...
Scanned 9/2/2026
Install to Claude Code
npx -y skills add ericrisco/rsc-harness --skill training-data --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Training Data?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/ericrisco-training-data)More formats (shields.io, HTML) on the badges page.
---
name: training-data
description: "Use when assembling or curating the corpus a fine-tune trains on — turning raw examples into the JSONL shape a trainer expects (instruction, conversational, preference-pair or binary-label), matching the data to the model's chat template, generating synthetic or distilled examples, deduplicating and decontaminating against eval sets, and quality-filtering. NOT cleaning tabular rows, nulls and dtypes (that is `data-cleaning`), NOT building a retrieval corpus of chunks and embeddings (that is `embeddings-search`), NOT running the trainer or picking hyperparameters (that is `finetuning`)."
tags: [training-data, fine-tuning-dataset, jsonl, chat-template, preference-data, dpo, kto, synthetic-data, decontamination, dataset-curation]
recommends: [finetuning, unsloth, huggingface, data-cleaning]
origin: risco
---
# training-data — the corpus a fine-tune actually eats
You own the **training corpus**: the JSONL of chat turns, instruction triples, or preference
pairs that a trainer reads. The deliverable is a validated, deduplicated, decontaminated,
license-clean file in the **exact shape the trainer expects**, rendered through the **target
model's chat template**. You stop the moment that file loads cleanly and round-trips through
`apply_chat_template`. You do not choose LoRA rank or launch the run — that is
[`finetuning`](../finetuning/SKILL.md) / [`unsloth`](../unsloth/SKILL.md).
**Loud boundary.** This is *LLM training corpora* — messages, instruction triples, preference
pairs. It is **not**:
- Tabular row cleaning — nulls, dtypes, dedupe of CSV rows, category normalization → [`data-cleaning`](../data-cleaning/SKILL.md).
- A *retrieval* corpus — chunking documents and embedding them for search → [`embeddings-search`](../embeddings-search/SKILL.md).
- Actually training or serving — hyperparameters, the run, export → [`finetuning`](../finetuning/SKILL.md), [`unsloth`](../unsloth/SKILL.md), [`huggingface`](../huggingface/SKILL.md).
**Version reality (verified July 2026 — re-verify, these move monthly).** TRL is on the **v1.x**
line (its dataset-formats doc was tagged `v1.8.0` at author time); `transformers` is in the
**4.57+** era (mixed text+vision data needs ≥4.57); `datasets` is **4.x** (the `Json()` feature
type needs ≥4.7). Pin whatever you install — do not trust these numbers as current.
## 1. The format the trainer expects (pick by trainer, not by taste)
The trainer dictates the columns. Get this wrong and TRL either errors or, worse, trains on a
mangled string. Two axes: **format** (`standard` = plain strings vs `conversational` =
`messages` lists) and **type** (the task). One JSON object per line = JSONL.
| Trainer | Dataset type | Required keys |
| --- | --- | --- |
| `SFTTrainer` | language-modeling **or** prompt-completion | `messages` / `text`, or `prompt`+`completion` |
| `DPOTrainer`, `ORPOTrainer`, `CPOTrainer` | preference (explicit prompt recommended) | `prompt`, `chosen`, `rejected` |
| `KTOTrainer`, `BCOTrainer` | unpaired preference (binary label) | `prompt`, `completion`, `label` |
| `RewardTrainer` | preference (implicit prompt) | `chosen`, `rejected` |
| `GRPOTrainer`, `RLOOTrainer`, `PPOTrainer` | prompt-only | `prompt` |
Tiny JSONL of each (conversational values are **lists of `{role, content}`**; `label` is a JSON
boolean):
```jsonl
# Alpaca instruction (standard) — classic; NOT a native TRL type, see below
{"instruction": "Classify the sentiment.", "input": "The battery dies in an hour.", "output": "negative"}
# Conversational messages (SFT) — the default for chat fine-tunes
{"messages": [{"role": "system", "content": "You are a terse support agent."}, {"role": "user", "content": "My order never arrived."}, {"role": "assistant", "content": "Sorry about that — what is your order number?"}]}
# Preference pair (DPO) — chosen beats rejected for the same prompt
{"prompt": [{"role": "user", "content": "Define a hash map in one sentence."}], "chosen": [{"role": "assistant", "content": "A hash map stores key-value pairs and finds a value by hashing its key to a bucket, giving average O(1) lookup."}], "rejected": [{"role": "assistant", "content": "It's a fast dictionary thing."}]}
# KTO / unpaired preference — one completion + a good/bad boolean label
{"prompt": [{"role": "user", "content": "Define a hash map in one sentence."}], "completion": [{"role": "assistant", "content": "It's a fast dictionary thing."}], "label": false}
```
**Alpaca is not a native TRL type.** `{instruction, input, output}` is the Stanford-Alpaca
convention, still common in Unsloth notebooks, but TRL trains on `text`/`messages`/`prompt`+
`completion`. You must either (a) map it into `messages` (instruction+input → user, output →
assistant), or (b) render it into a single `text` string via a prompt template — **and append
the EOS token yourself**, or the model never learns to stop (the #1 Unsloth-Alpaca bug). Prefer
(a) `messages` for chat models. Full field matrix, tool-calling (`tools` column) and vision
(`images`) extras, and every type→type conversion live in `references/formats.md`.
## 2. Chat templates — the silent run-wrecker
A chat template is a **Jinja** string stored in the tokenizer (in `tokenizer_config.json` under
`chat_template`, or a standalone `chat_template.jinja` in newer tokenizers). It maps a `messages`
list to the exact token string the model was trained on — special tokens (`<|im_start|>`,
`[INST]`, `<|start_header_id|>`, …) and all. You render it, you never hand-type it:
```python
from transformers import AutoTokenizer
tok = AutoTokenizer.from_pretrained("<target-model>") # the model you will fine-tune
# TRAINING: no trailing generation prompt — the assistant turn is already in the data
text = tok.apply_chat_template(msgs, tokenize=False, add_generation_prompt=False)
# INFERENCE: add_generation_prompt=True appends the assistant turn-start so the model continues
prompt = tok.apply_chat_template(msgs, tokenize=False, add_generation_prompt=True)
```
Three ways this silently destroys a run — **no error, just a worse model**:
- **Hand-formatting the tokens.** Writing `<|im_start|>user\n…` strings yourself and getting one
token, one newline, or the BOS wrong. Train-time string ≠ inference-time string → the model
learns a distribution it is never served. Always render via `apply_chat_template`.
- **Using the wrong model's template.** The template must be the one of the model you are
fine-tuning. Copy Llama's template onto a Qwen fine-tune and every example is subtly malformed.
- **A base model with no template at all.** Base (non-instruct) checkpoints often ship
`chat_template = None`. `apply_chat_template` then raises — you must **choose and attach** a
template (e.g. ChatML) and use that same one at inference forever after.
Also decide **loss masking**: for chat SFT you usually train only on the assistant tokens
(`completion_only_loss` / assistant-only masking in SFTTrainer, or a completion-only collator),
so the model is not penalized for "predicting" the user's words. TRL applies the template for
you when the dataset is conversational — let it, rather than pre-flattening to `text`.
## 3. Synthetic data & distillation
Not enough real examples? Generate them. Two workhorses: **Self-Instruct** (seed a few
hand-written examples, prompt a strong model to produce more, filter) and **Evol-Instruct**
(iteratively mutate prompts to be harder/deeper). Wrap them in a pipeline framework rather than
ad-hoc loops (see §7).
**Licensing trap — read before you distill.** Generating your training data from *another
model's outputs* ("distillation") is a **terms-of-service question, not just a quality one**.
Some providers' terms restrict using their outputs to train competing models; some open-weight
licenses carry naming/derivative obligations (e.g. Llama-derived data/models may inherit naming
requirements). Never assert a model's license from memory — check the specific model card and
provider ToS at author time (licenses change). If in doubt, distill from an
openly-licensed-for-this-use model, and record the provenance per example.
## 4. Dedup + decontamination (skip these and your numbers lie)
- **Dedup.** Exact dedup is trivial (hash the text). Real corpora need **near-dup** removal:
**MinHash + LSH** (Jaccard similarity over shingles) catches templated/boilerplate repeats
that inflate a few patterns. Dupes waste compute and bias the model toward whatever is
over-represented.
- **Decontamination — the one people forget.** Remove any training example that overlaps your
**eval / benchmark test sets** (n-gram overlap, e.g. long-n-gram match against MMLU, GSM8K,
your own held-out set). If test items leak into training, your eval score is **inflated** and
meaningless — you measured memorization, not capability. Decontaminate *against every metric
you will report*, including your private eval. Code for both in `references/synthesis-dedup-quality.md`.
## 5. Quality filtering — a few thousand clean beats a noisy dump
**LIMA** (*Less Is More for Alignment*, arXiv **2305.11206**) is the anchor: ~1,000 carefully
curated examples produced a strong instruction-follower — for **alignment/style** SFT, quality
and diversity dominate raw volume. (This is about teaching *behavior/format*, not injecting a
lot of new *knowledge* — a broad knowledge shift still wants scale.) Cheap, high-leverage
filters, applied before you spend GPU hours:
- **Length/format**: drop empty or truncated turns, runaway-length outliers, malformed JSON,
wrong-role sequences (two `assistant` turns in a row, missing final assistant turn for SFT).
- **Dedup + decontam** from §4.
- **Diversity**: cluster/embed and prune near-identical intents so the set is not 80% one task.
- **Model/heuristic scoring**: rate helpfulness/correctness (a reward model or an LLM judge) and
keep the top slice — but audit the judge, LLM-as-judge has its own biases.
## 6. Licensing — two separate questions
1. **The dataset's own license** — what *you* release the JSONL under, and whether you *can*
release it (aggregating others' data does not launder their licenses).
2. **Source-usage restrictions** — the terms on where each example *came from*: scraped-site
ToS, the license of any base dataset you built on, and the model-output ToS from §3. These
bind even if you never publish. Keep a provenance column so an audit can trace every row.
State the license *class* and point at the source; never freeze a license as bare fact.
## 7. Tooling
- **distilabel** (Argilla, now under Hugging Face) — the go-to synthetic-data / AI-feedback
pipeline framework: composable `Step`/`Task` graphs (`TextGeneration`, `UltraFeedback`,
`EvolInstruct`), serializable to YAML/JSON, outputs a `Distiset` you push to the Hub. v1.x.
- **Argilla** — human-in-the-loop annotation/review UI to label and vet examples.
- **HF `datasets`** — load/`map`/`filter`/`push_to_hub`; the substrate everything else speaks.
- **Lilac** — dataset exploration/clustering for quality triage. **[verify — the open-source
repo was archived (read-only) around July 2025 after the Databricks acquisition]**; treat as
unmaintained OSS and confirm before depending on it.
## Worked lifecycle (build → validate → dedup → decontaminate → format → push)
```python
from datasets import load_dataset
from transformers import AutoTokenizer
ds = load_dataset("json", data_files="raw.jsonl", split="train")
tok = AutoTokenizer.from_pretrained("<target-model>")
# 1. VALIDATE shape + render every row through the template (catches template errors NOW,
# not after 3 GPU-hours). A base model with chat_template=None raises here — attach one.
def render(ex):
return {"text": tok.apply_chat_template(ex["messages"], tokenize=False,
add_generation_prompt=False)}
ds = ds.filter(lambda ex: isinstance(ex.get("messages"), list) and ex["messages"]
and ex["messages"][-1]["role"] == "assistant") # SFT: must end on assistant
ds = ds.map(render)
# 2. DEDUP (near-dup) and 3. DECONTAMINATE against your eval set — see references for MinHash
# + n-gram code; both are one filter pass each.
# 4. PUSH with a data card recording license + provenance.
ds.push_to_hub("me/support-sft", private=True)
```
Deep code — MinHash/LSH dedup, n-gram decontamination, a distilabel Self-Instruct pipeline, and
the full conversion matrix — is in `references/`.
## Guardrails / gotchas
- **Wrong format for the trainer** = hard error or silent garbage. Match the table in §1 to your
trainer before generating a single row.
- **Hand-typed chat tokens** train a distribution you never serve. Render via `apply_chat_template`.
- **No EOS in Alpaca-`text` formatting** → the model never stops. Append it.
- **Skipped decontamination** → inflated eval; you measured leakage. Non-negotiable.
- **Distilling model outputs** can violate ToS. Provenance + license check first.
- **Quantity worship** — a noisy 500k dump loses to a curated few-thousand for alignment (LIMA).
- `label` in KTO/unpaired data is a JSON boolean (`true`/`false`), not the strings `"true"`/`"1"`.
## Related skills
- [`finetuning`](../finetuning/SKILL.md) — consumes this corpus: chooses SFT vs DPO vs KTO,
LoRA/QLoRA, hyperparameters, runs trl/peft. You hand it the file; it trains.
- [`unsloth`](../unsloth/SKILL.md) — one fast single-GPU training backend + GGUF export;
its notebooks expect exactly the Alpaca/`messages` shapes you produce here.
- [`huggingface`](../huggingface/SKILL.md) — the Hub you `push_to_hub` the dataset to, model
cards, and hosted/routed inference of the *result*.
- [`data-cleaning`](../data-cleaning/SKILL.md) — upstream when your raw source is dirty *tabular*
rows; it hands you clean rows, you turn rows into training examples.
## Checklist
- [ ] Format matches the target **trainer** (§1 table); one JSON object per line.
- [ ] Every row renders through the **target model's** `apply_chat_template` without error.
- [ ] SFT rows end on an `assistant` turn; preference rows have distinct `chosen`/`rejected`; KTO `label` is a boolean.
- [ ] Loss masking / EOS handling decided (assistant-only loss; EOS appended if flattening to `text`).
- [ ] Near-duplicates removed (MinHash/LSH); exact dupes gone.
- [ ] **Decontaminated** against every eval/benchmark you will report.
- [ ] Quality-filtered (length/format/diversity/score) — curated over bulk.
- [ ] Dataset license set **and** source-usage/model-output ToS checked; provenance recorded.
- [ ] Data card written; pushed (private first).
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!