RLHF, DPO & Preference Tuning
The stage that turns "imitates good answers" into "reliably prefers good answers over almost-as-good ones."
Rather than a single ideal response per prompt (what SFT uses), preference tuning starts from pairs: the same prompt with two candidate responses, and a label for which one is preferred. This is a much easier judgment to make reliably (both for human raters and for a judge model) than writing an ideal response from scratch, which is why it scales further than SFT data collection alone.
# one preference example
{
"prompt": "Explain recursion to a beginner.",
"chosen": "...", # the response humans/judges preferred
"rejected": "..." # the response they preferred less
}
# stage A: train a reward model on preference pairs
reward_model.train(chosen, rejected) # learns to score chosen > rejected
# stage B: use PPO (reinforcement learning) to update the LLM,
# using the reward model's score as the reward signal
for prompt in prompts:
response = llm.generate(prompt)
reward = reward_model.score(prompt, response)
llm.update_via_ppo(reward) # nudge toward higher-reward outputs
Direct Preference Optimization derives a loss function whose optimum is mathematically equivalent to what RLHF is trying to reach, but expresses it directly in terms of the model's own output probabilities on the chosen vs. rejected response — no separate reward model, no PPO, no RL instability. It's trained with ordinary supervised-learning-style gradient descent on the preference pairs directly.
| RLHF (reward model + PPO) | DPO | |
|---|---|---|
| Separate reward model needed | Yes | No |
| RL loop | Yes (PPO) | No — plain supervised-style training |
| Training stability | Sensitive to RL hyperparameters | Considerably more stable |
| Typical adoption today | Still used, especially at the largest labs | The more common default for teams doing this themselves |