Use SFTTrainer for Supervised Instruction Tuning
Learn to fine-tune LLMs with SFTTrainer for supervised instruction tuning. This tutorial covers setup, training, and evaluation.
Focus: use sfttrainer for supervised instruction tuning
Fine-tuning a large language model (LLM) with the SFTTrainer from Hugging Face's TRL library is the fastest, most reliable way to teach a pre-trained model to follow instructions. You've spent hours preparing your dataset, only to realize the model still answers with irrelevant text or ignores the prompt's format. The problem is that standard training loops weren't designed for conversational data—they don't automatically handle the special tokens, the padding, or the loss masking that instruction tuning demands. That's where SFTTrainer saves you, by wrapping all that complexity into a single Trainer-compatible API, so you can focus on your data and your model, not on reinventing the training wheel.
The problem this lesson solves
When you fine-tune an LLM for instruction following, you're not just training it to predict the next token. You're teaching it to respond to a prompt in a specific format, and to ignore the prompt when computing the loss. If you use a naive Trainer loop, you'll likely compute loss over the entire sequence, including the prompt. This causes the model to learn to mimic the prompt rather than to generate a helpful response. This is a common failure mode: the model becomes a "prompt parrot," repeating user input instead of answering.
Another pain point is the awkward integration of tokenizers and datasets. You need to define a collator that pads sequences, masks the loss on the prompt part, and handles the end-of-sequence token. It's easy to get lost in the details. SFTTrainer solves this by automatically masking the prompt tokens, handling the data collation, and providing sensible defaults for the training arguments—so you can launch a supervised fine-tuning run with just a few lines of code.
The bottom line: Without
SFTTrainer, you'll likely spend more time debugging your training loop than actually improving your model. This lesson shows you the mental model, the step-by-step process, and a hands-on example that will have you up and running in under 15 minutes.
Core concept / mental model
Think of SFTTrainer as a high-level training cockpit for supervised instruction tuning. It sits on top of the Trainer class from Transformers, but it's specialized: it assumes your dataset is a list of instruction-response pairs, and it prepares each example for you. The core idea is that during instruction tuning, you only want the model to learn from the response, not from the prompt. So SFTTrainer automatically masks the prompt tokens in the loss function, so the model's gradients only flow through the response part. It's like telling a student: "Read the question, but only learn from the answer."
Here's a mental diagram in words:
Dataset: {'text': '<prompt>\n<response>'}
→
SFTTrainer tokenizes the text, splits it into prompt/response (optional), pads to max length,
masks the prompt tokens from loss →
model learns to maximize only the likelihood of the response, given the prompt.
This approach has two main benefits: it prevents the model from overfitting to the prompt style, and it makes the convergence much faster because the loss signal is more focused.
How it works step by step
To use SFTTrainer for supervised instruction tuning, you follow a logical sequence:
- Load your dataset: A list of instruction-following examples, typically with
instruction,input(optional), andoutputfields, or a singletextfield that already contains the full prompt+response. - Choose your base model: A pretrained model (e.g.,
microsoft/phi-2,meta-llama/Llama-2-7b-hf). You'll need atokenizerto match. - Configure the training arguments:
TrainingArgumentsfrom Hugging Face Transformers — batch size, learning rate, number of steps, saving strategy, etc. - Wrap everything in
SFTTrainer: Pass the model, the train dataset, the tokenizer, the training args, and optionally a data collator. You can also specifymax_seq_lengthto truncate long examples. - Call
train(): This kicks off the training loop.SFTTrainerhandles the loss masking automatically. - Evaluate and save: You can use the
evaluate()method or just save the model for later inference.
The key is that SFTTrainer removes the manual step of creating a custom DataCollatorForLanguageModeling that masks the prompt. It does it under the hood, and it does it correctly with the tokenizer's padding side and attention mask.
Pro tip: Use
max_seq_lengthto limit the token count. This is crucial for GPU memory—shorter sequences are much cheaper to fine-tune.
Hands-on walkthrough
Let's get our hands dirty. Here's a complete, runnable example using a small model to illustrate the flow. We'll fine-tune gpt2 on a tiny synthetic instruction dataset. In practice, you'd use a larger model and a real dataset, but this shows the pattern.
First, install the required libraries:
pip install transformers trl datasets accelerate
Now, the core training script:
from trl import SFTTrainer
from transformers import TrainingArguments, AutoModelForCausalLM, AutoTokenizer
from datasets import load_dataset
# 1. Load a tiny model and tokenizer
model_name = "gpt2"
model = AutoModelForCausalLM.from_pretrained(model_name)
tokenizer = AutoTokenizer.from_pretrained(model_name)
tokenizer.pad_token = tokenizer.eos_token
# 2. Prepare a tiny dataset (in a real project, you'd load a proper instruction set)
dataset = load_dataset("json", data_files={"train": "instructions.json"})["train"]
def format_example(example):
return {
"text": f"### Instruction: {example['instruction']}\n\n### Response: {example['output']}"
}
dataset = dataset.map(format_example)
# 3. Configure training arguments
args = TrainingArguments(
output_dir="./sft-gpt2",
per_device_train_batch_size=4,
num_train_epochs=1,
logging_steps=10,
save_steps=100,
evaluation_strategy="steps",
eval_steps=100,
load_best_model_at_end=True,
report_to="none",
)
# 4. Create the SFTTrainer
trainer = SFTTrainer(
model=model,
args=args,
train_dataset=dataset,
tokenizer=tokenizer,
max_seq_length=128,
dataset_text_field="text",
)
# 5. Train!
trainer.train()
# 6. Save the fine-tuned model
trainer.save_model("./sft-gpt2-final")
Expected output (truncated):
***** Running training *****
Num examples = 100
Num Epochs = 1
Instantaneous batch size per device = 4
Total train batch size (w. parallel, distributed & accumulation) = 8
Total optimization steps = 25
{'loss': 1.9434, 'learning_rate': 0.0005, 'epoch': 0.1}
{'loss': 0.9877, 'learning_rate': 0.0005, 'epoch': 0.2}
...
If you want to see the loss masking in action, you can use the DataCollatorForCompletionOnlyLM to explicitly define which part is the prompt and which is the response. This is useful when your data isn't perfectly formatted. Here's how:
from trl import DataCollatorForCompletionOnlyLM
# Tell the collator what the response begins with
response_template = "### Response:"
collator = DataCollatorForCompletionOnlyLM(
response_template=response_template,
tokenizer=tokenizer,
)
trainer = SFTTrainer(
model=model,
args=args,
train_dataset=dataset,
tokenizer=tokenizer,
data_collator=collator,
max_seq_length=128,
)
Now the loss is only computed on the tokens after ### Response:. This is exactly what SFTTrainer does by default when you give it a prompt/response dataset—it automatically finds the response part and masks the rest.
Compare options / when to choose what
You might be wondering: "Why not use the plain Trainer with a manual loss-masking collator?" That's a valid alternative, but it requires more code and is error-prone. Let's compare:
| Approach | Pros | Cons | Use When |
|---|---|---|---|
SFTTrainer |
Built-in loss masking, handles tokenization, padding, and collation behind the scenes; works with TRL's peft support for LoRA/QLoRA | Less obvious how it works; hides the details | You want a quick, reliable path to instruction tuning with minimal boilerplate |
Plain Trainer + DataCollatorForLanguageModeling |
Full control over dataset formatting and loss masking | You must manually implement masking; easy to get padding and attention masks wrong | You're building a custom training pipeline or need very specific behavior |
Datasets + transformers.Trainer with custom collator |
Ultimate flexibility | Requires advanced knowledge and more debugging | Research or unusual training setups |
For 95% of instruction-tuning projects, SFTTrainer is your best choice. It's the default in many popular fine-tuning recipes (e.g., OpenHermes, Alpaca) and it seamlessly integrates with parameter-efficient methods like LoRA.
When to choose what:
- If you're fine-tuning on a single GPU with limited memory, use SFTTrainer + LoRA (covered in a later lesson).
- If you are only doing causal language modeling on raw text (no instruction structure), then the plain Trainer is more appropriate.
- If you need to control every detail of tokenization and collation, SFTTrainer may feel limiting—but you can still pass a custom data_collator to it.
Pro tip: For instruction tuning, always ensure your prompt and response are clearly separated with a template token like
### Response:soSFTTraineror your collator can find the boundary. This makes loss masking possible.
Troubleshooting & edge cases
Even with SFTTrainer, things can go wrong. Here are the common pitfalls and their fixes:
- The model never learns (loss stays high): This often happens because the
max_seq_lengthis too short and your data is being truncated, removing the response tokens. Fix: increasemax_seq_lengthor truncate your prompt intelligently, always keeping the response complete. ValueError: pad_token must be set: Some tokenizers (especially older ones like GPT-2) don't have a padding token by default. Fix: settokenizer.pad_token = tokenizer.eos_tokenas shown in the example.- Out-of-memory (OOM) on GPU: Your batch size or sequence length is too large. Fix: reduce
per_device_train_batch_size, use gradient accumulation, or reducemax_seq_length. For larger models, you'll need LoRA/QLoRA. - Loss masking not working: If you see the loss is too low or the model repeats the prompt, you may not have set up the response template correctly. Fix: use
DataCollatorForCompletionOnlyLMand double-check theresponse_templatestring matches your data. - Dataset format issues:
SFTTrainerexpects either atextcolumn or aprompt/completionpair. If your dataset has other names, usedataset_text_fieldor rename columns. - Evaluation loss after training is high: This could be due to a mismatch between training and evaluation format. Ensure your eval dataset has the same formatting function.
Pro tip: Always log a few training examples to see what the model sees. You can use
trainer.tokenizer.decode(trainer.train_dataset[0]['input_ids'])to verify the text and the padding.
What you learned & what's next
You've unlocked a superpower: the ability to fine-tune any pretrained LLM to follow instructions with just a handful of lines. Specifically, you can now:
- Explain why
SFTTraineris the go-to tool for supervised instruction tuning. - Set up a training pipeline with
SFTTrainer, including loading data, formatting it as instruction-response pairs, and running the training loop. - Understand how loss masking works and apply it using the built-in features or a custom collator.
- Troubleshoot common issues around tokenizers, memory, and data formatting.
You've completed a practical exercise that turned a base model into a model that's learning to respond to instructions. This is the heart of instruction tuning.
Next up in the track: Now that you know how to do full fine-tuning, the next lesson will show you how to do it efficiently on limited hardware using Parameter-Efficient Fine-Tuning (PEFT) with LoRA. You'll learn to adapt a 7B model on a single consumer GPU—a game-changer for real-world LLM engineering.
Keep this momentum—your next lesson is 'PEFT and LoRA: Efficient Fine-Tuning at Scale'. Happy tuning!
Practice recap
To solidify your skills, try fine-tuning a small model (like gpt2) on a dataset of 200 instruction-response pairs from a public source like the Alpaca dataset. Change the response template to something else (e.g., ### Answer:) and observe how the training loss changes. Then, if you have a GPU, attempt the same exercise with a 7B model using LoRA to see the memory savings. This hands-on practice will make the next lesson on PEFT a breeze.
Common mistakes
- Forgetting to set a padding token for models like GPT-2, causing a
ValueErrorat training start. - Setting
max_seq_lengthtoo low and truncating the response tokens, leading to poor learning and high loss. - Not using a distinct response template (e.g.,
### Response:) soSFTTrainercannot properly mask the prompt loss. - Mixing up the dataset column names and not using
dataset_text_field, resulting in aKeyErrorduring training. - Ignoring GPU memory limits and using a batch size too large, causing out-of-memory errors that halt training.
Variations
- Use
DataCollatorForCompletionOnlyLMto explicitly control loss masking when your dataset doesn't follow a standard format. - Integrate
SFTTrainerwith PEFT (LoRA) to fine-tune on limited GPU memory—just pass a PEFT model to the trainer. - Apply
SFTTraineron top of a QLoRA setup (quantized 4-bit base model) for even lower memory footprint.
Real-world use cases
- Fine-tuning a customer support LLM on your company's FAQ and past chat logs to generate accurate, on-brand responses.
- Adapting an open-source code model like CodeLlama to your team's internal coding standards by instruction-tuning on examples of approved code reviews.
- Creating a specialized medical or legal assistant by instruction-tuning a general LLM on domain-specific question-answer pairs from verified databases.
Key takeaways
SFTTraineris the standard tool for supervised instruction tuning—it masks prompt tokens, handles tokenization, and integrates with PEFT.- Your dataset must be formatted as instruction-response pairs, ideally with a clear template like
### Response:for loss masking. - Set a padding token and manage
max_seq_lengthcarefully to avoid truncation of responses and training roadblocks. - Training with
SFTTraineris as simple as configuringTrainingArgumentsand calling.train()—no custom collator needed for basic use. - Common pitfalls include OOM errors and poor loss masking, both solvable by adjusting batch size, sequence length, and using the proper response template.
- This lesson prepares you for the next step: using LoRA/PEFT to make fine-tuning feasible on consumer hardware.