Skip to content

Data basics: formats, chat templates and checks before you train

The vocabulary and file formats you need for the labs: instruction and chat data, JSONL, chat templates, loss masking, train and validation splits, and a script that validates a dataset before it wastes GPU time.

18 min·Verified 2026-11+50 XP
Share

Most fine-tuning failures are data failures. This lesson gives you the vocabulary and a validation script, so the labs are about learning, not debugging files.

#The vocabulary

Term Meaning
Example One training case: an input and the desired output
Instruction data Examples of "instruction in, response out"
Chat data A list of messages with roles: system, user, assistant
JSONL A text file with one JSON object per line
Chat template The rule that turns messages into the model's exact text format, with its special tokens
Loss masking Counting the loss only on the tokens you want learned (usually the assistant reply)
Epoch One pass over the training set
Train and validation split Data used to learn, and data held back to measure
Overfitting Memorising the training set; training loss falls while held-out quality gets worse

#The standard chat format

json
{"messages": [
  {"role": "system", "content": "You extract the city from a sentence."},
  {"role": "user", "content": "I flew into Lisbon on Monday."},
  {"role": "assistant", "content": "Lisbon"}
]}

One such object per line is a JSONL file. The labs and most training libraries (including TRL) accept this shape directly. The chat template of the base model then converts the messages to the text it expects.

#Validate before you train

Save this as validate.py. It writes a small file, reads it back and checks each line:

python
import json

examples = [
    {"messages": [
        {"role": "system", "content": "You extract the city from a sentence."},
        {"role": "user", "content": "I flew into Lisbon on Monday."},
        {"role": "assistant", "content": "Lisbon"}]},
    {"messages": [
        {"role": "user", "content": "She lives in Osaka."},
        {"role": "assistant", "content": "Osaka"}]},
]
with open("train.jsonl", "w") as f:
    for e in examples:
        f.write(json.dumps(e) + "\n")

bad = 0
with open("train.jsonl") as f:
    for n, line in enumerate(f, 1):
        row = json.loads(line)
        roles = [m["role"] for m in row["messages"]]
        if roles[-1] != "assistant" or any(r not in ("system", "user", "assistant") for r in roles):
            bad += 1; print("problem on line", n, roles)
print("examples:", n, "problems:", bad)

chars = sum(len(m["content"]) for e in examples for m in e["messages"] if m["role"] == "assistant")
print("assistant characters:", chars)

Expected output: examples: 2 problems: 0 and assistant characters: 11. On your own data, extend the checks: empty replies, duplicates, over-long examples, and a final role that is not assistant.

#Quality checklist

  • Every example shows the exact behaviour you want. If a few are sloppy, the model learns sloppy.
  • Consistent format. If half the answers are JSON and half are prose, the model will mix them.
  • No leakage. Do not let evaluation questions appear in the training file.
  • Diversity. Cover the real range of inputs, including awkward ones.
  • No private data you would not want a model to reproduce (privacy).
  • Length sanity. Check the longest examples against your maximum sequence length so they are not silently cut.

#Splitting

Hold out 5 to 20 percent as a validation set, chosen at random, and keep an even smaller test set that you do not look at until the end. If your data has natural groups (the same customer, the same document), split by group so near-duplicates do not appear on both sides.

#Chat templates in practice

Each model family has its own template with special tokens around roles. In Hugging Face tokenizers it is applied with tokenizer.apply_chat_template(messages, tokenize=False). Two rules:

  1. Use the template of the model you fine-tune. A template from another family changes the tokens the model sees.
  2. Use the same template at inference. The model learned the format it was trained on; sending a different one at serving time loses much of the benefit (serving adapters).

#How much data

Goal Typical starting size
A pipeline test 20 to 100 clean examples
A format or style change a few hundred
A narrow task hundreds to a few thousand
A broad behaviour change many thousands, plus careful evaluation

More is not always better; quality and coverage matter more, and the data lessons show how to generate and filter synthetic examples (synthetic data lab).

#Exercise

  1. Add a third example to the script with the wrong last role (user) and run it. What is reported?
  2. Why must the system message be the same at training and inference time if the training data always includes one?

Answers: 1. problems: 1 with the line number and the roles list. 2. The model learned to respond conditioned on that prompt; changing or dropping it shifts the input distribution, so behaviour can differ from what you evaluated.

Next: when to fine-tune, and when not to.

Quick quiz

+10 XP per correct answer, +25 XP for a perfect score

  1. 1. What is JSONL?

  2. 2. Why does a chat template matter?

  3. 3. What does loss masking do?

  4. 4. Why keep a validation set?

  5. 5. Which is a better first dataset size for a pipeline test?

Found this useful?

Send it to someone who is learning the same thing.

Share this lesson