The Training Loop with TRL

Module FT11 · Course 3 — LLM Fine-Tuning Masterclass

90 minutes · The capstone of Pillar 2 — run a complete, instrumented SFT job end-to-end.

Prereq: FT08 (LoRA/QLoRA), FT10 (Full FT vs PEFT decision).

Pillar 2 — Parameter-Efficient Fine-Tuning

TRL — the canonical post-training library

Transformers Reinforcement Learning. v1.0 released March 31, 2026. The library you reach for to steer a transformer model.

75+

training methods & trainer variants

~3M

downloads / month

Ships a Stability Contract (pinned API) and a production CLI: trl sft, trl dpo, trl grpo.

Design principle = the FT00 thesis: every trainer is a steering tool. None inject knowledge.

Why TRL, not raw transformers

SFT is not just "language modeling on a new dataset." Three things make it different — TRL handles all three:

ProblemWhat TRL does
Chat templatesApplies the model's tokenizer template; trains only on the completion (masks the prompt). Learn to respond, not parrot.
PEFT integrationAttach a LoraConfig → model wrapped, base frozen, adapter trained & saved. No PEFT glue.
PackingConcatenates short examples into one sequence (with attention masking). Often ~2x throughput.

Cost: learn the config surface (SFTConfig). Benefit: stop debugging glue code, start debugging data & hyperparameters.

The SFTTrainer, end to end

Six stages, in the order they run on your machine.

1. Dataset  ·  chat-format, train/eval split
2. Model  ·  bf16 + FlashAttention 2
3. PEFT config  ·  LoraConfig (or None for full FT)
4. SFTConfig  ·  the TrainingArguments levers
5. trainer.train()  ·  loss curve, eval every N steps
6. Save / Merge / Load  ·  adapter or full model

Reading a training loss curve

The EKG of your run. Three failure modes + the healthy baseline.

CurveDiagnosisFirst check
Healthy — steep drop, gentle decline, flattenConvergingConfirm eval tracks train
Not decreasing — flat from step 1Not learningLR too low · data/template bug (FT07)
Exploding — NaN / spikeUnstableLR too high · FP16 overflow (use BF16) · EOS bug
Plateau — descended, then flatConverged or stuckEval flat low = done · eval rising = overfitting
Watch grad norm too. A 10x spike between two logging steps = about to explode, even if loss looks fine. Kill it, lower LR, check clipping.

The levers — learning rate & batch

Learning rate (the big one)

MethodLR range
Full FT1e-55e-5
LoRA / QLoRA1e-45e-4

LoRA is 10x higher — adapters are small & random, need faster learning vs the frozen base.

Effective batch

effective_batch =
  per_device_batch_size
  × grad_accum_steps
  × world_size

VRAM-limited? Lower per-device batch, raise accumulation. Same optimizer math, slower wall-clock.

Cardinal error: full-FT LR on a LoRA run (loss barely moves) or LoRA LR on full FT (loss explodes). The method (FT10) sets the LR band.

The levers — schedule, memory, optimizer

Warmup & scheduler

  • Warmup ratio 0.03–0.1 — stabilize early training
  • cosine — the common default (smooth decay)
  • linear — straight-line to zero
  • constant_with_warmup — flat, pick best checkpoint

Memory & speed

  • gradient_checkpointing — ~60-70% mem, ~30% slower (FT01)
  • FlashAttention 2/3 — effectively mandatory, 2-4x speedup
  • bf16=True — NOT fp16 (overflow → NaN)

Optimizer

OptionWhen
adamwDefault. Robust. Most memory (2 momentum states/param).
adamw_8bitMemory-tight. Quantized states, small accuracy cost.
paged_adamw_8bitQLoRA default. CUDA paging offloads states to CPU.

Save · Merge · Load

Three operations, three purposes. Confuse them → ship the wrong artifact.

Save (what was trained)

trainer.save_model(path)

With PEFT: the adapter (~100s of MB). Without: the full model (GBs).

Merge (for deployment)

merged = model.merge_and_unload()
merged.save_pretrained(dir)

Adapter folded into base → single standalone model. Quantize (FT19) or serve (FT20).

Load (hot-swap at inference): PeftModel.from_pretrained(base, adapter) + load_adapter() to swap. One base, many adapters. Layer 2 detaches from Layer 1 — the FT00 swappability property, in code.

Logging — what to watch

If it isn't logged, it didn't happen. If you can't see the loss curve, you are not training — you are hoping.

Backends

  • W&Breport_to="wandb" — rich dashboards
  • Trackioreport_to="trackio" — HF Spaces-backed
  • TensorBoard/JSONL — zero dependencies

What to log

  • train_loss, lr, grad_norm — every step
  • eval_loss — every eval step
  • throughput — spot slowdowns
The one plot: train loss & eval loss on the same chart. The gap = overfitting signal. Stop at the eval-loss minimum. load_best_model_at_end=True.

Anti-patterns

No eval (flying blind). Can't tell convergence from overfitting, can't compare checkpoints, ship a memorizing model. Eval is not optional — 200 examples is enough.
Wrong LR for the method. Full-FT LR on LoRA = no learning. LoRA LR on full FT = explosion. The method sets the band.
No logging / ignoring grad norm. A NaN'd run and a converged run look identical from the final checkpoint. Grad norm spike = the canary before the NaN.
FP16 instead of BF16. Same memory, FP16 overflows, BF16 doesn't. On Ampere+ there is no reason to use FP16 for training.

The lab — the full SFT loop

The capstone of Pillar 2. Run a real SFT job.

  • Model: Qwen2.5-3B-Instruct or MiniCPM3-4B (your choice)
  • Data: your FT05/FT06 output, or the provided trl-lib/Capybara
  • Log to W&B or Trackio, eval every N steps
  • Produce: loss curve, eval curve, merged model
  • Generate before/after samples — see the steering
If your run NaNs — good. The failure is the lesson. Diagnose via the loss curve & grad norm, fix the lever, re-run. That diagnosis is the skill this module teaches.

Consumer GPU (RTX 4090 / 24GB) or Colab. Runnable Python in 07-lab-spec.md.

What you can now do

  1. Run a complete SFT job with TRL end to end — dataset, model, PEFT, config, train, eval, save.
  2. Read a loss curve and diagnose the three failure modes (not decreasing, exploding, plateau).
  3. Choose the TrainingArguments levers for a given hardware budget and method.
  4. Save, merge, and load a fine-tuned model — adapter, merge_and_unload, PeftModel.from_pretrained.
  5. Wire up logging and know what to watch — loss, eval, LR, grad norm, throughput.

Next: FT12 — SFT: The Baseline