Dataset Curation
Seth HobsonCommunityCreated by Seth Hobson
- Skill
- Workflow
- Data and AI
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured procedure for curating datasets for model training. It covers format selection based on the training method, mechanics for chat templates and loss masking, strategies for synthetic data generation, and the mandatory requirements for a dataset card. It serves as a gatekeeper for the training phase, ensuring that data is properly formatted, packed, and validated before a run begins.
What it does
- Converts raw data into specific formats for SFT, DPO, ORPO, KTO, or GRPO training.
- Applies chat templates to conversation data before sequence packing.
- Configures sequence packing to reduce padding and improve compute efficiency.
- Generates synthetic training data using methods like Magpie and rejection sampling.
- Validates datasets against a 25% real-data floor to prevent model collapse.
- Enforces the completion of a six-field dataset card required for training.
When to use it
- When preparing raw demonstrations or preference judgments for a fine-tuning run.
- When configuring datasets for multi-turn chat or preference optimization.
- When integrating synthetic data into a training set.
- When preparing to launch a training job that requires a validated dataset card.
Advantages
- Prevents silent training failures by enforcing template and masking rules.
- Optimizes compute usage through structured sequence packing.
- Provides a clear checklist for dataset readiness before training.
- Ensures provenance and consistency through mandatory dataset card fields.
Usage examples
- “How do I format a multi-turn conversation dataset for SFT?”
- The agent provides the ChatML messages list structure and explains the requirement to apply the chat template before packing.
- “What are the requirements for a dataset card before I start training?”
- The agent lists the six mandatory fields: Provenance, Counts, Synthetic/real ratio, Dedup method, Template used, and Packing config.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/wshobson/dataset-curation/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip dataset-curation.zip -d .agents/skills
Documentation by Seth Hobson
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 (
-100in 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
messagesshape and let the trainer template and mask it (assistant_only_loss=Truein 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-dataoutput.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-firstrun 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.
Files
- ATTRIBUTION.mdAdded by AutoGineer976 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md7.8 KiB
- references/formats-and-templates.md6.4 KiB
- references/synthetic-data.md12.5 KiB
references/formats-and-templates.md
Dataset Formats and Template Application
Concrete JSONL examples for every format in
SKILL.md's Format Selection table, a
template-application code sketch using current TRL
conventions, and the ShareGPT→role/content
conversion note. Base models are never named here —
every code example uses a BASE_MODEL placeholder;
see finetuning-method-selection's
references/model-catalog.md for which actual
checkpoint to load.
Instruct (SFT, Single-Turn)
One JSONL row per example. Either key pair works; pick one and use it consistently across the dataset:
{"instruction": "Summarize the following text in one sentence.", "input": "Q3 revenue grew 14% year-over-year, driven primarily by...", "output": "Q3 revenue grew 14% YoY on strong core-segment demand."}{"prompt": "Summarize the following text in one sentence: Q3 revenue grew 14%...", "completion": "Q3 revenue grew 14% YoY on strong core-segment demand."}ChatML Conversation (SFT, Multi-Turn)
A messages list per row — the shape SFTTrainer
templates and loss-masks natively (see Applying the
Chat Template below):
{"messages": [
{"role": "system", "content": "You are a concise technical assistant."},
{"role": "user", "content": "What does a KV cache do?"},
{"role": "assistant", "content": "It stores attention keys/values from prior tokens so decoding doesn't recompute them each step."},
{"role": "user", "content": "Does it grow with context length?"},
{"role": "assistant", "content": "Yes, linearly — that's why long-context serving is memory-bound on cache size, not compute."}
]}Only the final two assistant turns' content
tokens should carry loss after masking — see
SKILL.md's Chat Templates and Loss Masking
section.
DPO / ORPO — Chosen/Rejected Pair
{"prompt": "Explain why the sky is blue.", "chosen": "Sunlight scatters off air molecules; shorter (blue) wavelengths scatter more, so blue dominates what reaches your eyes from all directions.", "rejected": "Because the sky reflects the ocean."}chosen and rejected are both full responses to
the same prompt — not a diff or a ranking score.
See preference-optimization's Pair Construction
section for how to select rejected from a graded
trajectory set (μ−2σ of the reward distribution,
not the naive minimum).
KTO — Unpaired Binary Feedback
{"prompt": "Draft a one-line commit message for a null-check fix.", "completion": "Fix null pointer exception in user lookup", "label": true}{"prompt": "Draft a one-line commit message for a null-check fix.", "completion": "misc changes", "label": false}No pairing between rows is required or expected —
label: true marks desirable, label: false
undesirable. A healthy KTO dataset needs both
labels represented across the set.
GRPO / RLVR — Prompt-Only
{"prompt": "Solve: 17 * 24 = ?", "answer": "408", "verifier": "exact_match"}No response is stored — GRPO samples completions
from the policy at train time and scores them
against answer via the named verifier (or a
reward function). See grpo-rlvr-training for
reward-function design and the manual-inspection
requirement before a GRPO run.
Applying the Chat Template (Current TRL API)
Keep the dataset in messages shape and let
SFTTrainer apply the template. Do not
pre-render conversations to a flat text field —
flattening destroys the message boundaries TRL
needs to mask loss to assistant turns. Given a
messages-shaped dataset, current TRL applies
the tokenizer's chat template per example (before
any packing concatenation, satisfying SKILL.md's
template-before-concatenation rule) and masks loss
to assistant spans when assistant_only_loss=True:
from transformers import AutoTokenizer
from trl import SFTConfig, SFTTrainer
tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL)
sft_args = SFTConfig(
output_dir="./outputs-sft",
max_length=2048,
packing=True, # see SKILL.md Packing section before enabling
assistant_only_loss=True, # mask loss to assistant turns
)
trainer = SFTTrainer(
model=BASE_MODEL,
args=sft_args,
train_dataset=dataset, # messages-shaped — no pre-rendered text field
processing_class=tokenizer, # current TRL — not tokenizer=
)(processing_class, not tokenizer= — see
lora-qlora-recipes's
references/unsloth-trl-mapping.md for the full
Unsloth↔TRL kwarg mapping.)
assistant_only_loss=True requires the tokenizer's
chat template to mark assistant spans (the
{% generation %} keyword). If the template lacks
it, TRL raises rather than silently training on
everything — fix the template, don't fall back to
flat text.
apply_chat_template(..., tokenize=False) is still
the right tool for inspecting what the template
produces — decode-and-read checks like the packing
inspection in SKILL.md — just not for building
the training dataset.
The Flat-Text Path Does NOT Mask
The older pattern — pre-rendering each conversation
with apply_chat_template(..., tokenize=False)
into a text column and pointing
SFTConfig(dataset_text_field="text") at it —
still runs, but computes loss over the entire
sequence, user turns and template markers
included. That is exactly the silent
train-on-everything failure SKILL.md's Chat
Templates and Loss Masking section warns about.
It is only appropriate when full-sequence loss is
actually intended (CPT-style continued pretraining
on raw text), never for conversational SFT.
ShareGPT → role/content Conversion
Older datasets often ship in ShareGPT's
conversations shape (from/value keys, human/
gpt roles) rather than the messages
(role/content) shape current TRL expects.
Convert before templating, not during:
ROLE_MAP = {"human": "user", "gpt": "assistant", "system": "system"}
def sharegpt_to_messages(example):
messages = [
{"role": ROLE_MAP[turn["from"]], "content": turn["value"]}
for turn in example["conversations"]
]
return {"messages": messages}
dataset = dataset.map(sharegpt_to_messages, remove_columns=["conversations"])Run this conversion — and spot-check a handful of
converted rows — before the Chat Templates section's
"apply before concatenation" rule applies; a
ShareGPT dataset that gets packed or templated
still in from/value shape produces malformed
turns that a template call won't error on.
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs Python
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- Seth Hobson
- Publisher
- Seth Hobson (Community)
- Source
- wshobson/agents · plugins/llm-finetuning/skills/dataset-curation · commit 4bde316
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Seth Hobson.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download dataset-curation.zip