RLHF, DPO and Preference Tuning
Reward model plus PPO against DPO and its family. Losses, beta and KL settings, failure modes and fixes.
- Concepts and terms
- Tasks and commands
- Annotated code
- Diagrams
- Comparisons
- Defaults and numbers
- Troubleshooting
- Questions and tips
RLHF against DPO#
| Factor | RLHF (PPO) | DPO |
|---|---|---|
| Models to train | 3: SFT, reward, policy | 1: policy only |
| Training loops | 3: SFT, RM, PPO | 2: SFT, DPO |
| Hyperparameters | lr, KL coeff, clip ratio, RM lr, epochs | lr, beta, epochs |
| Reward model | Separate training | Implicit in model probabilities |
| Algorithm | PPO, complex and unstable | Supervised learning, stable |
| Models in memory | 3-4 during PPO | 2: current and reference |
| Compute | Three loops | About one-third of RLHF |
| Data | Online loop with new ratings | Fixed preference dataset |
From Lesson 10.08
Use X when#
- DPO: 5,000-20,000 pairs, limited compute, or many dataset experiments.
- RLHF: large scale, multi-objective rewards, or an iterative online rating loop.
- SimPO: length bias is evident. It also drops the reference model.
- KTO: you only have unpaired good or bad labels.
- ORPO: you want one stage from the base model, with no SFT checkpoint.
- BPO: DPO logs show degraded chosen log-probs.
- IPO: preference strengths vary widely and DPO saturates.
From Lesson 10.08, Lesson 18.03, Lesson 09.09
RLHF pipeline#
From Lesson 10.07, Lesson 09.09
One DPO epoch (capstone)#
DPO loss and reference freeze#
diff_w = logp_w_pol - logp_w_refchosen log-ratiodiff_l = logp_l_pol - logp_l_refrejected log-ratiomargin = diff_w - diff_limplicit reward gap over betaz = beta * marginloss = -F.logsigmoid(z)stable -log sigmoidpolicy.load_state_dict(reference.state_dict())start from equal weightsfor p in reference.parameters(): p.requires_grad = Falsereference never movestarget = 1.0 / (2.0 * beta) if beta > 0 else 0.0loss = (margin - target) ** 2IPO variantFrom 19.40 code/main.py
Reward model and KL reward (toy)#
p = sigmoid(r_pos - r_neg)Bradley-Terry P(chosen wins)grad_scale = 1 - pw[t] += lr * grad_scale * cchosen tokens up, rejected downkl_term = kl(probs_new, probs_ref)reward = rm_score - beta * kl_termKL-penalized rewardadvs = [(r - mean_r) / sd_r for r in rewards]normalized advantagesratio = math.exp(logp - log_pi_old)clipped = (adv > 0 and ratio > 1 + eps) or (adv < 0 and ratio < 1 - eps)From 09.09 code/main.py
Tasks in code#
| Task | Code |
|---|---|
| Train a Bradley-Terry RM | train_rm(n_pairs= |
| Toy PPO-RLHF loop | rlhf_loop(w, |
| TRL PPO with adaptive KL | PPOConfig(init_kl_coef= |
| Train DPO, lesson 10.08 | dpo_train(..., |
| Sweep beta | betas= |
| Response log-prob | sequence_log_prob(policy, |
| Compare six losses | python3 code/ |
| Capstone end to end | sys.exit(run_demo()) |
From 09.09 code/main.py, Lesson 09.09, 10.08 code/main.py, 18.03 code/main.py, 19.40 code/main.py
Losses#
- RM loss
- -\log \sigma(R_\phi(x, y_+) - R_\phi(x, y_-)), the Bradley-Terry pairwise logistic.
- Implicit reward
- r = \beta \log \frac{\pi^*(y \mid x)}{\pi_{\text{ref}}(y \mid x)} + \beta \log Z(x). Z(x) cancels.
- DPO
- -\log \sigma(\beta (\log \frac{\pi(y_w)}{\pi_{\text{ref}}(y_w)} - \log \frac{\pi(y_l)}{\pi_{\text{ref}}(y_l)}))
- IPO
- squared error of the log-ratio gap against \frac{1}{2\beta}. The gap stays bounded.
- SimPO
- length-normalized \frac{\beta}{|y|} \log \pi(y \mid x) with a margin \gamma. No reference.
- Gradient sign
- \partial L / \partial \log \pi_\theta(y_w) = -\beta (1 - \sigma(z)). Chosen goes up.
Defaults#
| Setting | Default | Note |
|---|---|---|
| RLHF KL β | 0.01-0.05 | Typical range |
| InstructGPT KL β | 0.02 | lr=1.5e-5, 4 PPO epochs |
| PPO clip ε | 0.2 | Ratio clipped to 1 ± ε |
TRL init_kl_coef | 0.05 | With target_kl=6.0 |
| DPO β, lesson 10.08 | 0.1 | Sweet spot 0.1 to 0.3 |
| DPO lr, lesson 10.08 | 5e-6 | 5 epochs |
Capstone DPOConfig beta | 0.2 | lr 1e-3, 30 epochs |
Capstone warmup_epochs | 8 | Reference pretrain first |
From Lesson 09.09, Lesson 10.07, Lesson 10.08, 19.40 code/main.py
Troubleshooting#
| Symptom | Cause | Fix |
|---|---|---|
| Reward climbs, human eval flat | Reward hacking | Stop early, raise β, broaden RM data |
| Responses get longer | Length hacking | Length-normalized reward, or SimPO |
| Mean KL past ~5-10 | Policy drifted from SFT | Adaptive β that targets a fixed KL |
| Policy barely changes | β too high | Lower β |
| Chosen log-prob falls in DPO | Degraded Chosen Response | BPO penalty on the chosen response |
| DPO gap blows up | Implicit reward gap is unbounded | IPO, gap capped at 1/(2 beta) |
| RM scores unreliable | RM smaller than the policy | RM at least as large as the policy |
| ~30% noisy labels | Ambiguous preferences | Agreement-filtered data, BT temperature |
Questions#
- Why pairs, not absolute scores?
- People rank two answers well. They score one answer badly.
- Why the KL term?
- It keeps the policy near the data where the RM was trained.
- Why does the RM read the last token?
- The causal mask lets it see the whole sequence.
- How many forward passes per DPO pair?
- Four: two models times two responses.
- Do DAAs escape over-optimization?
- No. They over-optimize the reference ratio instead.