"Should we fine-tune?" usually comes up in the second week of an LLM project. The prompt is getting long, the outputs are not quite right, and someone remembers that you can train the model on your own data.
Sometimes that is the right call. More often the problem is missing context (retrieval) or a vague task definition (prompting and evals), and fine-tuning would only bake the confusion into weights.
This post is a cheatsheet for that decision, and then for the case where fine-tuning wins: how LoRA and QLoRA work, which knobs matter, a runnable recipe with Hugging Face PEFT and TRL, rough memory math, what the research says, and the failure modes that show up after training.
1. The vocabulary
| Term | Meaning | Why it matters |
|---|---|---|
| Prompting | Steering a fixed model with instructions, examples and context in the input | Cheapest to change. Every fix ships in minutes. |
| RAG | Retrieval-augmented generation: fetch relevant documents at query time and put them in the context | The standard way to give a model knowledge it does not have, with citations and freshness. |
| Full fine-tuning | Updating every weight of the model on your data | Highest capacity, highest cost. Needs memory for weights, gradients and optimizer state. |
| PEFT | Parameter-efficient fine-tuning: train a small number of extra or selected parameters, freeze the rest | Makes fine-tuning possible on one GPU, and adapters are megabytes, not gigabytes. |
| SFT | Supervised fine-tuning on input/target pairs (often chat transcripts) | The default fine-tuning method. Teaches format, style and task behavior. |
| Preference tuning (DPO) | Training on pairs of preferred vs. rejected answers | For "this answer is better than that one" signals, when you cannot write a single target. |
| RFT | Reinforcement fine-tuning: a grader scores model outputs and the model is optimized for that score | For tasks with checkable answers. Needs a reliable grader more than lots of data. |
| LoRA | Low-Rank Adaptation: learn each weight update as the product of two thin matrices | The most widely used PEFT method. |
| QLoRA | LoRA trained on top of a base model quantized to 4 bits | Cuts memory again, so larger models fit on one GPU. |
| Catastrophic forgetting | Losing general abilities while learning the new task | The main hidden cost of fine-tuning. Measure it, do not assume it away. |
2. Prompting vs RAG vs fine-tuning
The shortest rule: RAG changes what the model knows at answer time. Fine-tuning changes how the model behaves. Prompting does a bit of both, cheaply, until the prompt gets too long or too fragile.
| Problem you see | Likely fix | Why |
|---|---|---|
| Model does not know your products, policies or recent data | RAG | Knowledge changes; retrieval updates instantly, gives citations, and respects access control per document. |
| Answers are right but format, tone or structure drift | Prompting first, then SFT | Clear instructions and examples fix most of it. Fine-tune when you need consistency at scale or a shorter prompt. |
| Task is narrow, high volume, and a big model with a long prompt is too slow or expensive | Fine-tune a smaller model | Distilling a good prompted behavior into a small model is one of the most reliable wins. |
| Model must follow a domain-specific output style or label set exactly | SFT (plus structured outputs if it is JSON) | Hundreds of consistent examples beat a page of instructions. |
| Answers are verifiable (tests pass, number matches) and quality is the bottleneck | RFT | Optimizes directly for the grader. |
| Requirements are still changing weekly | Prompting | Every fine-tune freezes this week's definition of the task. |
| You do not have an eval yet | Build the eval | Without it you cannot tell whether any option helped. |
What the research says about knowledge injection
Two results are worth knowing before you try to "teach the model our docs" with fine-tuning:
- Retrieval beats fine-tuning for facts. Ovadia et al. (December 2023) compared unsupervised fine-tuning and RAG for injecting knowledge, and found RAG consistently ahead, for knowledge seen in pre-training and for entirely new facts. Fine-tuning learned new facts poorly, though exposing the model to several paraphrases of each fact helped.
- New facts in SFT data can increase hallucination. Gekhman et al. (May 2024) found that fine-tuning examples containing knowledge the model did not already have are learned much more slowly, and once learned, they increase the model's tendency to hallucinate. Their reading: fine-tuning mostly teaches a model to use what it already knows.
Practical consequence: fine-tune for behavior, retrieve for knowledge. The common production shape is a fine-tuned model that is good at answering from retrieved context, not one that memorized the wiki.
3. How LoRA works
A transformer layer has large weight matrices, for example a 4,096 x 4,096 attention projection. Full fine-tuning learns an update to all of it. LoRA (Hu et al., June 2021) assumes the update has low rank and learns it as two thin matrices instead:
h = W0 x + (alpha / r) * B A x
W0 : frozen pretrained weight, d x k
A : trainable, r x k (random init)
B : trainable, d x r (zero init, so training starts exactly at the base model)
r : rank, typically 8-64, much smaller than d and k
For a 4,096 x 4,096 matrix at r = 16, that is about 131 thousand trainable parameters instead of about 16.8 million, under 1%.
What the paper reported for GPT-3 175B compared with full fine-tuning with Adam:
| Claim | Number |
|---|---|
| Trainable parameters | about 10,000x fewer |
| GPU memory during training | about 3x less (1.2 TB down to 350 GB) |
| Checkpoint size (r = 4, query and value only) | 350 GB down to 35 MB |
| Quality | on par with or better than full fine-tuning on their benchmarks |
| Inference latency | none added, because B A can be merged into W0 |
The original paper adapted only the attention query and value matrices. Later practice, including the QLoRA paper, found that applying LoRA to all linear layers in the transformer block matters more than the exact rank.
QLoRA in one table
QLoRA (Dettmers et al., May 2023) keeps the LoRA adapters in 16-bit but stores the frozen base model in 4 bits:
| Idea | What it does |
|---|---|
| NF4 (4-bit NormalFloat) | A 4-bit data type designed for normally distributed weights, better than plain 4-bit floats or ints |
| Double quantization | Quantizes the quantization constants too, saving a bit more memory per parameter |
| Paged optimizers | Uses unified memory to absorb optimizer-state spikes instead of crashing with out-of-memory |
Headline results: fine-tuning a 65B model on a single 48 GB GPU while matching 16-bit fine-tuning on their tasks, and a model (Guanaco) that reached 99.3% of ChatGPT's score on the Vicuna benchmark after 24 hours on one GPU. That benchmark used model-based judging, so read it as "close", not "equal".
Variants worth recognizing
| Variant | Idea | When to try it |
|---|---|---|
| rsLoRA (November 2023) | Scale by alpha / sqrt(r) instead of alpha / r |
When you raise rank and see no gain; it keeps higher ranks learning. use_rslora=True in PEFT. |
| DoRA (February 2024) | Splits each weight into magnitude and direction, applies LoRA to the direction | When plain LoRA lags full fine-tuning. use_dora=True in PEFT; slower to train. |
| LoRA+ (February 2024) | Different learning rates for A and B | Small, cheap gains in training speed and quality. |
4. The hyperparameters that matter
| Knob | What it controls | Practical starting point |
|---|---|---|
r (rank) |
Capacity of each adapter | 16. Go to 32-64 for harder tasks or larger data; lower rarely saves much. |
lora_alpha |
Scale of the update (alpha / r) |
Often set to r or 2 * r. Change it together with the learning rate, not independently. |
target_modules |
Which layers get adapters | "all-linear" in PEFT. Attention-only is cheaper but usually weaker. |
lora_dropout |
Regularization on the adapter input | 0.0-0.1. Matters more on small datasets. |
| Learning rate | Step size | Around 1e-4 to 2e-4 is a common LoRA range; full fine-tuning uses much lower rates. |
| Epochs | Passes over the data | 1-3. Watch eval loss; more epochs on small data is the fastest way to overfit. |
| Data | Everything | A few hundred clean, consistent examples beat thousands of noisy ones. |
On data size, OpenAI's fine-tuning guide is a good calibration point even if you train elsewhere: a hard minimum of 10 examples, and a recommendation to start with about 50 well-crafted demonstrations, evaluate, then add more.
5. A runnable QLoRA recipe (PEFT, TRL, bitsandbytes)
Versions current at the time of writing: PEFT 0.19.1 (April 2026), TRL 1.5.1 (May 2026), bitsandbytes 0.49.2 (February 2026). Pin them; these libraries rename arguments often.
uv pip install "peft==0.19.1" "trl==1.5.1" "bitsandbytes==0.49.2" transformers datasets accelerate
The dataset is a JSONL file of chat transcripts, one per line:
{"messages": [{"role": "system", "content": "Classify the support ticket. Reply with one label."}, {"role": "user", "content": "I was charged twice for May."}, {"role": "assistant", "content": "billing_duplicate_charge"}]}
import torch
from datasets import load_dataset
from peft import LoraConfig
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from trl import SFTConfig, SFTTrainer
model_id = "Qwen/Qwen2.5-7B-Instruct"
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_use_double_quant=True,
bnb_4bit_compute_dtype=torch.bfloat16,
)
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(
model_id,
quantization_config=bnb_config,
dtype=torch.bfloat16,
device_map="auto",
)
peft_config = LoraConfig(
r=16,
lora_alpha=32,
lora_dropout=0.05,
target_modules="all-linear",
task_type="CAUSAL_LM",
)
data = load_dataset("json", data_files={"train": "train.jsonl", "eval": "eval.jsonl"})
args = SFTConfig(
output_dir="out/ticket-classifier-lora",
num_train_epochs=2,
per_device_train_batch_size=4,
gradient_accumulation_steps=4,
learning_rate=2e-4,
lr_scheduler_type="cosine",
warmup_ratio=0.03,
max_length=1024,
bf16=True,
gradient_checkpointing=True,
logging_steps=10,
eval_strategy="steps",
eval_steps=50,
save_strategy="steps",
save_steps=50,
)
trainer = SFTTrainer(
model=model,
args=args,
train_dataset=data["train"],
eval_dataset=data["eval"],
processing_class=tokenizer,
peft_config=peft_config,
)
trainer.model.print_trainable_parameters()
trainer.train()
trainer.save_model("out/ticket-classifier-lora/final") # saves only the adapter
To serve without the PEFT wrapper, merge the adapter into a 16-bit copy of the base model (not the 4-bit one):
import torch
from peft import PeftModel
from transformers import AutoModelForCausalLM
base = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-7B-Instruct", dtype=torch.bfloat16)
merged = PeftModel.from_pretrained(base, "out/ticket-classifier-lora/final").merge_and_unload()
merged.save_pretrained("out/ticket-classifier-merged")
Or keep adapters separate: inference servers such as vLLM can serve many LoRA adapters on one base model, which is how per-customer or per-task variants stay cheap.
6. Rough memory math
Back-of-the-envelope numbers for a 7B model, before activations (which grow with batch size and sequence length):
| Setup | What sits in GPU memory | Rough total (planning estimate) |
|---|---|---|
| Inference, bf16 | 2 bytes per weight | about 14 GB |
| Full fine-tuning, mixed precision with Adam | weights, gradients, fp32 master weights and two optimizer moments: about 16 bytes per weight | about 112 GB, multi-GPU territory |
| LoRA on a bf16 base | frozen weights (2 bytes each) plus a small adapter with its optimizer state | about 15-16 GB |
| QLoRA on a 4-bit base | about 0.5 bytes per frozen weight plus quantization overhead and the adapter | under 10 GB |
7. What the research says about LoRA vs full fine-tuning
"LoRA matches full fine-tuning" is true for many instruction-tuning and classification tasks. It is less true when the target domain is far from pre-training.
Biderman et al., LoRA Learns Less and Forgets Less (May 2024), compared LoRA and full fine-tuning on code and math with large training sets:
- LoRA substantially underperformed full fine-tuning on the target domain.
- LoRA preserved the base model's performance outside the target domain better, and did so better than common regularizers such as weight decay and dropout.
- Full fine-tuning learned weight updates with a rank 10-100x higher than typical LoRA configurations, a plausible reason for the gap.
How to use that:
- For narrow behavior (format, style, labels, tool-call patterns), LoRA is usually enough, and the reduced forgetting is a feature.
- For teaching a new domain at scale, expect a gap; try higher rank, all-linear targets, DoRA, or full fine-tuning if you have the compute.
- Either way, evaluate general ability before and after, not just the target task.
8. Hosted fine-tuning in mid-2026
| Platform | What is offered | Notes |
|---|---|---|
| OpenAI | Supervised fine-tuning, DPO, and reinforcement fine-tuning (RFT opened for o4-mini to verified organizations in May 2025) | On May 7, 2026, OpenAI notified developers that it is winding down self-serve fine-tuning, starting with no access for organizations that have never used it. Existing fine-tuned models keep serving until their base model is deprecated. Check the deprecations page before planning new work on it. |
| Amazon Bedrock (Claude 3 Haiku) | Supervised fine-tuning of Claude 3 Haiku | Preview July 2024, generally available November 2024 in US West (Oregon). |
| Open-weight models | Anything above, on your own or rented GPUs | Most control and portability: the adapter is a file you own. |
The lesson: a hosted fine-tune ties your behavior to one vendor's base model and lifecycle. Keep data, eval set and recipe in version control so you can reproduce the result elsewhere.
9. Failure modes after training
| Symptom | Likely cause | Fix |
|---|---|---|
| Great on eval, poor in production | Eval data leaked from train, or eval does not match real traffic | Split by source or time, not randomly; sample eval from production logs. |
| Model repeats phrases from training data | Overfitting on a small dataset | Fewer epochs, more diverse examples, early stopping on eval loss. |
| General ability or safety behavior degraded | Forgetting | Mix in general instruction data, lower learning rate, smaller rank, run a general benchmark before and after. |
| More confident wrong facts | Training on knowledge the model did not have | Move facts to retrieval; train on answering from context. |
| Fine-tune is no better than the prompt | Data inconsistent, or the prompt was already near the ceiling | Audit labels; compare against a few-shot baseline with the same eval. |
| Behavior changes after a base model update | Adapters are tied to the exact base weights | Retrain on the new base; never load an adapter onto a different base. |
Why this matters
- Most "fine-tune it" requests are retrieval or prompt problems. Diagnosing that first saves weeks.
- Fine-tuning teaches behavior, not facts. Research on knowledge injection points to RAG for facts and fine-tuning for how to use them.
- LoRA and QLoRA make fine-tuning a single-GPU job. The barrier is now data quality and evaluation, not hardware.
- LoRA forgets less, and learns less. Pick it for narrow behavior; expect a gap on large new domains.
- Portability is a design choice. Owning data, recipe and adapter protects you from vendor and base-model changes.
Final Thoughts
Prompting, retrieval and fine-tuning are not rungs on a ladder. They are tools for different problems: unclear instructions, missing knowledge, and inconsistent behavior. Most good systems use all three, in that order of effort.
When you do fine-tune, LoRA and QLoRA make the training the easy part. The hard part is the same as everywhere else in AI engineering: a clean dataset, an honest eval, and a before-and-after comparison that includes what you might have broken.
Sources
- Hu et al., LoRA: Low-Rank Adaptation of Large Language Models (June 2021): low-rank updates, 10,000x fewer trainable parameters, 3x less memory, no added latency, query and value adaptation.
- Dettmers et al., QLoRA: Efficient Finetuning of Quantized LLMs (May 2023): NF4, double quantization, paged optimizers, 65B on one 48 GB GPU, Guanaco at 99.3% on the Vicuna benchmark, all-linear adapters.
- Kalajdzievski, A Rank Stabilization Scaling Factor for Fine-Tuning with LoRA (November 2023): rsLoRA scaling.
- Liu et al., DoRA: Weight-Decomposed Low-Rank Adaptation (February 2024): magnitude and direction decomposition.
- Hayou et al., LoRA+: Efficient Low Rank Adaptation of Large Models (February 2024): separate learning rates for A and B.
- Biderman et al., LoRA Learns Less and Forgets Less (May 2024): LoRA vs. full fine-tuning on code and math, forgetting, update rank.
- Lewis et al., Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks (May 2020): the original RAG formulation.
- Ovadia et al., Fine-Tuning or Retrieval? Comparing Knowledge Injection in LLMs (December 2023): RAG vs. unsupervised fine-tuning for new and existing knowledge.
- Gekhman et al., Does Fine-Tuning LLMs on New Knowledge Encourage Hallucinations? (May 2024): new knowledge learned slowly and linked to more hallucination.
- Hugging Face, PEFT LoRA reference and peft on PyPI (0.19.1, April 2026):
LoraConfig,all-linear,use_rslora,use_dora. - Hugging Face, trl on PyPI (1.5.1, May 2026) and bitsandbytes on PyPI (0.49.2, February 2026).
- Hugging Face, Transformers bitsandbytes quantization docs:
BitsAndBytesConfig, NF4, nested quantization, compute dtype. - OpenAI, Supervised fine-tuning guide: minimum 10 examples, start with about 50 demonstrations.
- OpenAI, Fine-tuning updates: reinforcement fine-tuning now available (May 2025): RFT for o4-mini.
- OpenAI, Deprecations (May 2026): update to self-serve fine-tuning.
- Anthropic, Fine-tune Claude 3 Haiku in Amazon Bedrock (July 2024), and AWS, Fine-tuning for Anthropic's Claude 3 Haiku in Amazon Bedrock is now generally available (November 2024).