Pretraining a model (fundamentals Module 15) costs millions and months. But that model already learned grammar, facts, and reasoning. Fine-tuning takes that pretrained model and continues training it — briefly — on your specific data, so it picks up your style, domain, or task.
From scratch = raising a child from birth to teach them your business. Fine-tuning = hiring a knowledgeable graduate and giving them a few days of on-the-job training in your way of doing things. Same brain, quick specialization.
Slide the target quality and watch the two approaches' cost/time. Fine-tuning reaches good results for a tiny fraction of the effort — because it starts from something that already works.
Good news: fine-tuning is the Module 3 training loop — just started from pretrained weights instead of random ones, with a small learning rate (you don't want to erase what it knows).
from transformers import AutoModelForCausalLM, AutoTokenizer
model = AutoModelForCausalLM.from_pretrained("gpt2") # pretrained! not random
tok = AutoTokenizer.from_pretrained("gpt2")
optimizer = torch.optim.AdamW(model.parameters(), lr=5e-5) # small lr!
for epoch in range(3): # just a few passes
for batch in your_data_loader: # YOUR data (Module 4)
loss = model(**batch).loss # forward + loss
loss.backward() # autograd (Module 2)
optimizer.step() # update
optimizer.zero_grad()Use a much smaller learning rate than training from scratch (e.g. 5e-5 not 1e-3). Big steps would overwrite the model's hard-won knowledge — called catastrophic forgetting. Small steps gently nudge it toward your data while keeping what it knows.
Same prompt, same model — but after fine-tuning on a specific style. Pick a target style and watch the "before" (generic GPT-2) become the "after" (your specialized model).
The #1 practical detail: your data must be in the right shape for the task. Different goals need different formats. Click each to see the structure.
A common trick: freeze most of the pretrained layers (keep them fixed) and only train the top few. This is faster, uses less memory, and reduces overfitting on small data. Click layers to freeze/unfreeze them.
# freeze everything, then unfreeze just the last block
for p in model.parameters():
p.requires_grad = False # ❄️ frozen
for p in model.transformer.h[-1].parameters():
p.requires_grad = True # 🔥 train only thisBecause the model already knows language, fine-tuning loss starts low and drops fast — very different from the high starting loss of training from scratch. Run it and see.
Training can crash, or you might overfit (Module 3). So you save checkpoints — snapshots of the model's weights — periodically. Then you can resume, or roll back to the best one. Click a checkpoint to "load" it and see its validation score.
# save a checkpoint
model.save_pretrained("./my-model-epoch3") # weights + config
tok.save_pretrained("./my-model-epoch3")
# load it back later (or the best one)
model = AutoModelForCausalLM.from_pretrained("./my-model-epoch3")Track validation loss (Module 3) and keep the checkpoint where it was lowest — not necessarily the final epoch. The last checkpoint may already be overfitting. This is early stopping + checkpointing working together.
Next up → Module 8: LoRA & PEFT — even fine-tuning all of a big model is expensive. LoRA fine-tunes just a tiny set of "adapter" weights (often <1% of the model), and quantization shrinks models to fit one GPU. This is how fine-tuning is actually done today.