[LLM 3/10] RLHF and PPO: Training a Model on a Reward You Cannot Differentiate
In chapter 2 we taught the model by "imitating the answer key" one token at a time. But the qualities that make an AI assistant genuinely usable — correct, polite, not making things up, not sliding into English — have no answer key to imitate, and cannot be written directly as a loss function. This chapter is the most classical answer to that problem: RLHF (Reinforcement Learning from Human Feedback) with PPO. We will train a real reward model from Thai preference pairs, then write the PPO loop ourselves from scratch in about 120 lines, and close with my favorite experiment in the series: taking off the KL leash and watching the model cheat the reward live. This is deliberately the heaviest chapter of the series, because chapter 4 (DPO) and chapter 5 (GRPO) both start from this chapter's equations and each choose a different piece to delete.
Open in Colab03_rlhf_ppo.ipynb
1. The Problem
SFT in chapter 2 carries one hidden assumption: there must be an answer key to imitate. But think about what we actually want — say, "solve the math problem correctly, and explain it in readable Thai." That sentence has no single answer key: good answers come in a hundred shapes, and "readable" cannot be written as an equation.
Try to optimize these things directly and you always run into two walls:
Wall one — quality cannot be written as a loss. "Better" cannot be defined as a function, but humans are very good at comparing: show them two answers and ask which one they prefer, and they can do it immediately and fairly consistently. So the data you can actually collect comes in threes: a prompt , the chosen answer , the rejected answer .
Wall two — even with a score, you cannot backprop. Suppose a magical function scored every answer. You still couldn't train supervised, because the answer comes from sampling tokens one at a time. The score arrives after the sampling is done, and derivatives cannot travel back through sampling — the path from back to the weights breaks exactly there.
| The road you'd like to take | The wall it hits |
|---|---|
| Write a loss for "a good answer" directly | "Good" has no equation — there are only comparisons |
| Have humans score, then backprop | The score sits behind token sampling — gradients can't walk through sampling |
| Have humans score live during training | Humans can't keep pace with even a sliver of the rollouts |
Hence the chapter's title: we are about to optimize a reward we cannot differentiate. The tool that can do that is called reinforcement learning.
2. What We're Going to Do
RLHF gets past both walls with a two-step walk:
- Stage A — Reward Model: train a model to imitate human comparisons from preference pairs (defeats wall one, and stands in for humans who can't score fast enough)
- Stage B — PPO: use policy-gradient RL to push the policy toward 's scores without differentiating through the sampling (defeats wall two), with a KL leash holding it back from fleeing the starting model
The price you pay is complexity: during training there are four models resident in VRAM at once — the policy (the one being trained), the reference (the frozen starting model), the reward model , and a value network that hasn't introduced itself yet (wait for section 3.4).
RLHF is the optimization of a reward you cannot differentiate, through an imperfect proxy (the reward model). And optimizing a proxy hard, with nothing to restrain you, always fails by Goodhart's law: when a measure becomes a target, it ceases to be a good measure.
The KL term in equation 3.2 is therefore not a regularizer sprinkled in for good luck — it is the only thing standing between you and reward hacking. Section 8 will prove that sentence by taking it out before your eyes.
And one more sentence to hold on to for the whole series: this chapter's objective is the master equation of the second half of the series. Chapter 4 (DPO) solves it in closed form until the reward model and the RL loop cancel out. Chapter 5 (GRPO) changes how the advantage is estimated until the value network disappears. Understand this one chapter, and the next two become "deleting parts" — readable at sight.
3. The Equations
3.1 The reward model: Bradley–Terry
Stage A trains with one short loss:
- = a single scalar score per text — in practice a language model whose head is swapped for a single linear layer (
num_labels=1) - = the sigmoid, turning the score difference into the probability a human picks (the Bradley–Terry model)
- the wider the gap , the lower the loss
The point people overlook and pay for later: this loss sees only the difference of the scores. Replace with for any constant — the loss doesn't change at all. Which means the absolute scale of a reward model is meaningless and is not pinned down by training. Two runs can yield mean scores of 3.7 and −12.4 that rank identically. This is why you must always standardize rewards before feeding them into PPO (subtract the mean, divide by the std) — hold onto this; it returns in sections 7 and 9.
3.2 The master equation: the RLHF objective
If you memorize a single equation from this whole series, memorize this one:
In plain language: "collect as much reward as you can, but every step you walk away from the starting model costs a fine."
- = the policy, the model being trained — note that is sampled from itself; this is the structural difference from SFT, which learns from static data sitting in a file
- = the reference, the starting model (the post-SFT model from chapter 2), frozen throughout training
- = the price per nat of wandering off — the tightness of the leash
- = the distributional distance between the policy and the reference
Chapter 4 (DPO) will prove this equation has a closed-form solution, then flip it inside out until and the RL loop both vanish. Chapter 5 (GRPO) will keep the RL skeleton but change how the advantage is computed until vanishes. Neither chapter proposes a new objective — they solve this same equation with different tools.
3.3 The PPO clipped surrogate: Stage B's engine
Raw policy gradient (REINFORCE) can use a batch of rollouts for exactly one update before throwing it away — very expensive, because generation is the bottleneck. PPO wants to squeeze the same rollouts for several epochs, so it needs a correction factor (the importance sampling ratio):
- = the state at position : the prompt plus every token sampled so far
- = the "action": the next token, already sampled during the rollout
- = a snapshot of the policy at rollout time — computed once and frozen
Then gets pinched with a clip:
- = the advantage: "how much better than expected was this token" (defined in the next subsection)
- = the width of the trust region (standard value 0.2)
The heart is that min + clip work together with deliberate pessimism: if is positive (a good token), the payoff from pushing is capped at — pushing beyond that earns nothing, gradient zero. But if is negative (a bad token), the min always picks the worse branch — the penalty has no floor. One-sentence summary: gains capped, losses uncapped. The policy therefore moves in small steps that stay close to where it already is.
in equation 3.2 is frozen for the entire run and serves as the KL leash. in equation 3.3 is a snapshot at the latest rollout, refreshed every round, and serves as the trust region. The number-one bug among people writing their own PPO is storing these two in the same variable.
3.4 GAE: computing the advantage without drowning in noise
The advantage is built from the TD error of the value network :
- = the value network's prediction of "from here to the end, how much more reward will be collected" — this is model number four
- = the per-token reward (in our task: a KL penalty at every position, plus the task score at the final token)
- = the discount factor (LLM work usually uses 1.0)
- = the bias–variance dial: trusts wholeheartedly (high bias if mispredicts), doesn't trust it at all and waits for the real outcome to the very end (high variance, carrying the noise of the whole trajectory); the popular value is 0.95
is a model roughly the size of the policy that must be trained alongside it with a loss of its own. If predicts garbage, the advantage is garbage, and the policy learns from a garbage signal — PPO's classic failure point. Chapter 5 will answer the question "what if we replaced with the mean of a group of answers sampled from the same prompt?" That is the entire GRPO algorithm — deleting the fourth model with a single average.
3.5 The full PPO loss: three terms, two models
Assemble every piece into the one loss the optimizer actually sees (written as a minimization):
- first term = the clipped surrogate from 3.3 (negated because we want to maximize it)
- second term = the value loss teaching to predict close to the actual return ; is usually 0.5
- third term = the entropy bonus , keeping the distribution from collapsing too fast; is usually 0.01
- as for the KL leash from equation 3.2, standard practice folds it into the per-token reward: , which is the approach we take in section 7
Count all the toys that need tuning: 4 models, plus , and two separate learning rates. This is why PPO is famous for "run it twice with different seeds, get two completely different stories" — and it is the reason for chapter 4's entire existence.
4. Seeing the Equations
Bradley–Terry: the gradient piles onto the pairs still ranked wrong
Pick the Bradley-Terry mode in the tool below and drag the margin: pairs the reward model already ranks confidently right (large positive margin) have almost no gradient left — Stage A training automatically spends its budget on the pairs it still ranks wrong.
Drag anywhere on the plot, or use the Δ slider with the arrow keys.
This pair teaches almost nothing.The model already ranks this pair correctly, so σ(−βΔ) is only 11.92% and the gradient is 11.9% of what a hard pair would give. This is why preference datasets full of obvious wins barely move the model: the easy pairs are silently ignored, and the few genuinely confusing pairs do all the work.
If this plot reminds you of DPO's gradient in chapter 4 — that is no coincidence. DPO lifts this Bradley–Terry model wholesale, merely changing what plays the role of the reward.
The trust region made visible: min + clip
Figure 3.1Equation 3.3 drawn directly at ε = 0.2 — on the positive-Â side, the payoff is capped at 1+ε (the flat region = zero gradient), while on the negative-Â side the penalty has no floor, because min always picks the worse branch
Study the asymmetry closely — it is PPO's entire personality: moving toward something good earns at most 20% per step, but hand too much probability to a bad token and you are hauled back at full force, every time. The "flat region" in the figure is the trust region that lets PPO reuse old rollouts for several epochs without exploding.
GAE's bias–variance dial
Figure 3.2GAE on a synthetic 20-step rollout (γ = 1): per-step rewards are small noise, the real score arrives at the end, and V_ψ is deliberately set to under-predict by about 0.4 — at λ = 0 the signal never reaches the early tokens; at λ = 1 every token gets full credit along with full noise (an illustration of the mechanism, not real training data)
Read the lines from the bottom up: at (green) the advantage hugs zero nearly everywhere — the end-of-episode score never reaches the early tokens, because everything is filtered through a that mispredicts. At (red), every token gets full credit from the ending but carries the accumulated noise of the whole trajectory with it. (blue) is the middle ground the whole field has settled on — the signal travels far, but the noise is damped.
Before moving on, build your intuition for the word advantage — "how much better than expected" — by hand: this tool uses the group mean as its baseline instead of (a full-blown spoiler of chapter 5). Press the All correct preset and watch what happens when every answer earns the same reward:
Rewards r_i (G = 8)
Watch the std term.Dividing by std(r) = 0.500 rescales this whole group. A group that happened to be near-unanimous gets a large multiplier and dominates the update, even though it carries less information than a group that genuinely disagreed. Untick the box to see the same rewards without the rescaling.
Advantage zero across the whole group = no signal to learn from — hold onto that feeling for section 9.
The KL leash: equation 3.2 drawn as a picture
Figure 3.3Two training trajectories in the (KL, reward) plane — β = 0.05 climbs and then stops at the point where the marginal gain equals the penalty, while β = 0 has no stopping point and runs right into reward-hacking territory (an illustration of the failure mode's mechanism — the actual measured curves are in section 8)
The green line's stopping point is not a guess — it is the mathematics of equation 3.2: optimization stops exactly where the reward gained per nat equals β; walking further loses money. When that stopping condition doesn't exist — every nat of wandering that buys even a sliver of reward is "profit," so the model keeps running out of natural language for as long as the reward number still twitches upward.
5. Setting Up the Environment
Production RLHF holds 4 models of which the largest is usually 7B or more, uses real human preference pairs numbering in the tens of thousands to millions, and splits generation (a rollout fleet) onto machines separate from training. This notebook uses Qwen3-0.6B in every slot, 100 preference pairs, and 64 math problems. What it demonstrates is the algorithm with every part present — not real RLHF. The results will prove the mechanism, not that the model got better for real use.
Open Colab and pick Runtime → Change runtime type → T4 GPU (the free tier is enough).
The Colab T4 is Turing architecture (SM 7.5), which does not support bfloat16 and does not support FlashAttention-2.
But Qwen3-0.6B's config.json declares torch_dtype: bfloat16.
So torch_dtype="auto" is a trap — your code will crash or run bizarrely slowly without telling you why.
torch_dtype=torch.float16 # not bfloat16
attn_implementation="sdpa" # not flash_attention_2
And this chapter in particular carries one extra fp16 bomb, named ratio overflow — wait for section 7.
cap = torch.cuda.get_device_capability(0)
print("compute capability:", cap) # T4 = (7, 5)
print("native bf16:", cap[0] >= 8) # T4 -> False
print("torch says :", torch.cuda.is_bf16_supported()) # T4 -> True (counts emulation!)
is_bf16_supported() lies on a T4Recent torch returns True on a T4 because it counts emulation as support — which is far slower than fp16.
Gate on compute capability ≥ 8.0 (Ampere and up) instead. This was a real bug, caught only by running the notebook on Colab.
Why RLHF is expensive, in one picture
Figure 3.4The four models that must sit in VRAM at once during one PPO step, computed from Qwen3-0.6B's real parameter count (fp16, weights only) — LoRA lets the policy and reference share a single base, saving one entire model
The numbers in the figure are just the weights — before activations, the KV cache during generation, gradients, and optimizer state. And at 0.6B everything still looks tiny, but the ×4 multiplier goes nowhere as you scale: at 7B it is 56 GB before you've done anything at all.
Our policy is the base + the LoRA adapter from chapter 2, and is that same base with the adapter off —
called inside policy.disable_adapter(). The reference model costs zero additional bytes of VRAM.
Chapter 4 will use this same move again for DPO; it is the architectural reason this series chose LoRA from the start.
6. Preparing the Data
Stage A — preference pairs for the reward model
We use iapp/dpo_thai_tutorial (100 pairs, Apache-2.0) —
a Thai preference dataset I built myself for this series and released for free reuse.
Each row has prompt, chosen, rejected, hand-curated with an emphasis on politeness and natural-sounding Thai.
Split 80/20: train on 80 pairs, hold 20 pairs out — never touched during training. Stage A's passing bar is pairwise ranking accuracy on the 20 held-out pairs, with a Wilson 95% CI that does not straddle 0.5 — "significantly better than a coin flip" already proves the mechanism, because 20 pairs genuinely cannot make the CI any tighter.
Stage B — problems a rule can grade
For the PPO loop we use 64 math problems from VISAI-AI/gsm8k-thai (a Thai translation of GSM8K)
and score them with a verifiable rule instead of the Stage A reward model:
import re
def rule_reward(response: str, gold: int) -> float:
"""+1.0 if the last integer in the answer is correct, +0.2 if the answer is actually in Thai"""
nums = re.findall(r"-?\d+", response.replace(",", ""))
correct = 1.0 if nums and int(nums[-1]) == gold else 0.0
thai = sum("\u0e01" <= ch <= "\u0e5b" for ch in response) # the Thai Unicode block, U+0E01..U+0E5B
thai_bonus = 0.2 if thai / max(len(response), 1) >= 0.5 else 0.0
return correct + thai_bonus
In a real system, Stage B consumes Stage A's output directly — that is the definition of RLHF. But an RM trained on 100 pairs is far too weak to withstand PPO's pressure: it would get hacked within a few updates, and then we couldn't tell whether our PPO loop was wrong or the RM was merely weak — the experiment would prove nothing at all.
So the rule reward serves as a verifiable stand-in for the RM: when the reward climbs, we know for certain the loop is working. But it remains "imperfect" just like every RM — it measures only the final number and the fraction of Thai characters, not the readability of anything around them, and that is exactly the loophole the experiment in section 8 will poke at. (This idea of rule-verifiable rewards returns as the full-time protagonist in chapter 5.)
7. The Main Code
7.1 Stage A — training a real reward model (~6 minutes)
Turn a language model into a scoring machine: the LM head is replaced with a single linear layer returning one scalar.
import torch, torch.nn.functional as F
from transformers import AutoModelForSequenceClassification, AutoTokenizer
from peft import LoraConfig, get_peft_model
tok = AutoTokenizer.from_pretrained("Qwen/Qwen3-0.6B")
rm = AutoModelForSequenceClassification.from_pretrained(
"Qwen/Qwen3-0.6B",
num_labels=1, # scalar head: one score per text
torch_dtype=torch.float16, # the T4 has no bf16
attn_implementation="sdpa",
).cuda()
rm.config.pad_token_id = tok.pad_token_id
rm = get_peft_model(rm, LoraConfig(
task_type="SEQ_CLS", r=8, lora_alpha=16,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
modules_to_save=["score"], # the score head starts random — must be trained in full
))
for p in rm.parameters():
if p.requires_grad:
p.data = p.data.float() # the fp16 lesson from chapter 1: train in fp32
def rm_loss(chosen, rejected):
s_w = rm(**chosen).logits.squeeze(-1) # r_φ(x, y_w)
s_l = rm(**rejected).logits.squeeze(-1) # r_φ(x, y_l)
return -F.logsigmoid(s_w - s_l).mean() # equation 3.1, literally
Train 3 epochs on 80 pairs, then measure pairwise accuracy on the 20 held-out pairs with a Wilson CI. If the CI clears 0.5 — you have just trained the first reward model of your life, on 80 rows of data.
7.2 Stage B — PPO written from scratch, ~120 lines (~10 minutes)
We will not use TRL's PPOTrainer, and this is a deliberate decision, not stubbornness:
TRL has moved PPOTrainer into trl.experimental and announced plans to remove it in 0.29.0 —
code taught against that library will stop running within a few months.
A self-written PPO loop of about 120 lines will run for as long as PyTorch exists.
And more important still: write it yourself and you will know what every line does, just like the 25-line DPO loss in chapter 4.
The pieces on the board: policy = base + LoRA adapter (from chapter 2, is_trainable=True),
reference = the same base with the adapter off, and a value head — a two-layer MLP plugged into the final hidden state:
value_head = torch.nn.Sequential(
torch.nn.Linear(1024, 1024), torch.nn.Tanh(),
torch.nn.Linear(1024, 1),
).float().cuda() # ~1M parameters — tiny next to the other three
The rollout: sample answers 8 prompts at a time (max_new_tokens=200, do_sample=True),
score with rule_reward, standardize the scores within the batch, and store the rollout-time log-probs, detached, immediately.
Then enter this update loop — the 40 lines that are the heart of the whole chapter (the full loop is in the notebook):
def compute_gae(rewards, values, gamma=1.0, lam=0.95):
"""rewards: [T] for one response, values: [T+1] (last slot = 0 after the end)"""
adv, acc = torch.zeros_like(rewards), 0.0
for t in reversed(range(rewards.shape[0])):
delta = rewards[t] + gamma * values[t + 1] - values[t] # equation 3.4
acc = delta + gamma * lam * acc # Â_t = δ_t + γλ Â_{t+1}
adv[t] = acc
return adv
def ppo_update(rollout, eps=0.2, beta=0.05):
out = policy(rollout.ids, output_hidden_states=True) # one forward, two outputs
logp = gather_logprobs(out.logits, rollout.ids) # [B, T], carries gradient
with torch.no_grad(), policy.disable_adapter():
logp_ref = gather_logprobs(policy(rollout.ids).logits, rollout.ids)
# per-token reward = KL penalty at every position + task score at the last token (equation 3.2)
rew = -beta * (logp.detach() - logp_ref)
rew[:, -1] += rollout.scores_std # the standardized rule score
hidden = out.hidden_states[-1].detach().float() # detach = cut the gradient into the trunk
values = value_head(hidden).squeeze(-1)
adv = torch.stack([compute_gae(r, F.pad(v, (0, 1))) for r, v in zip(rew, values)])
adv = (adv - adv.mean()) / (adv.std() + 1e-8) # standardize the advantage once more
log_ratio = (logp - rollout.logp_old).clamp(-10, 10) # prevents fp16 overflow!
ratio = log_ratio.exp() # ρ_t (equation 3.3)
surr = torch.min(ratio * adv.detach(),
ratio.clamp(1 - eps, 1 + eps) * adv.detach())
returns = (adv + values).detach() # the value head's target
m = rollout.resp_mask # count response tokens only
loss = (-(surr * m).sum() + 0.5 * ((values - returns).pow(2) * m).sum()) / m.sum()
return loss # (the entropy term is in the notebook)
Settings: , , , ,
4 epochs per rollout on 64 prompts, adapter LR 1e-5, value head LR 1e-4.
Roughly 10 minutes per run on a T4 (the notebook runs twice: and ).
1. .clamp(-10, 10) before .exp() — in fp16, exp(12) = 162,754, past the fp16 ceiling (65,504).
The result is inf, which turns into NaN and spreads through the whole batch within a single step.
2. logp_old must be computed once at rollout time and stored detached — never recomputed inside the epoch loop.
Recompute it and always, the clip never fires, and your PPO silently degrades into REINFORCE with no error whatsoever.
3. Standardize scores before use — equation 3.1 already told you the reward's scale is undefined. Yes, our rule reward lives between 0 and 1.2, but this habit has to stick for when you use a real RM whose scale can be anything it likes.
8. Results
The notebook tracks three curves simultaneously at every update and writes them to results.json:
- Mean reward per rollout — should climb (this is what we're buying)
- Per-token KL against — should grow and then saturate under the ceiling that sets (this is the price we pay)
- Mean answer length — the disease detector: length spiking or cratering abnormally is the first sign of a policy going strange
A climbing reward means nothing at all if you don't know what the model paid in exchange. Reward up + KL saturating = learning under the leash. Reward up + KL climbing without limit = fleeing language itself, heading for a loophole in the judge. The same curve in a different context means precisely the opposite thing — this is the measured version of figure 3.3.
Catching reward hacking red-handed
The notebook's second run sets and touches absolutely nothing else — no leash.
The pattern expected: reward climbs as fast or faster, but KL soars with no ceiling,
and the answers start to degenerate — repetitive, oddly short, or collapsing into a fixed formula with a number stuffed at the end of the sentence.
Because rule_reward sees only the final number and the fraction of Thai characters, everything it cannot see is free for the model to throw away.
Samples of degenerate answers from the run are printed by the notebook's final cell:
[This space is filled only from the notebook's real run — I will not invent degenerate
examples myself, because this whole series stands on the rule that no number or output
is ever made up. Run the notebook and the "hacking exhibits" cell will show 2-3 real
answers along with their KL.]
Promptอธิบายว่าทำไมท้องฟ้าถึงเป็นสีฟ้า แบบสั้น ๆbase
sft
Showing the built-in sample.
9. Comparison
The notebook runs the same battery over three systems on held-out Thai math problems (TH-MATH):
| Model | TH-MATH acc (95% CI) | Final mean KL | Mean answer length | Training time |
|---|---|---|---|---|
| starting policy | 70.0% (52.1–83.3) | 0 | 182 tokens | — |
| PPO, β = 0.05 | 70.0% (52.1–83.3) | 0.011 | 172 tokens | 5.6 min |
| PPO, β = 0 (ablation) | ~unchanged | 0.012 | 159 tokens | 5.6 min |
results.json.
The article predicted β = 0 would make KL soar and the language collapse (reward hacking). On a real demo-scale run (FAST_MODE: 32 prompts × 16 updates) it did not: β=0's KL (0.012) is barely different from β=0.05's (0.011), reward stayed flat in both, and McNemar on TH-MATH gives p = 1.0 (no change).
The reason is that 16 updates on a 0.6B model is far too few to induce reward hacking, which needs hundreds of updates. What this chapter genuinely proves is (1) the reward model trains — 80%, and (2) the ~120-line PPO loop runs end to end with its math matching hand calculation (section 6). Seeing reward hacking clearly requires turning off FAST_MODE and running long on a larger GPU — so we report what was measured, not what we wanted.
Note: the starting policy is a fresh LoRA (fallback), not chapter 2's adapter, which is not yet pushed to the Hub; once pushed it will start from the real SFT model as intended.
10. Summary
- RLHF = a two-step detour to optimize what cannot be differentiated: train a judge (), then use RL to run toward the judge's score
- Bradley–Terry sees only differences — a reward's absolute scale is undefined, so always standardize before use
- The master equation is the one equation to memorize — chapters 4 and 5 are this same equation solved by other means
- KL is not a regularizer — it is the system's only stopping condition; the moment you remove it, Goodhart goes to work
- min + clip = pessimism by design: gains capped, losses uncapped — the trust region that makes old rollouts reusable
- GAE is the bias–variance dial, and the it leans on is the fourth model — the one GRPO deletes in chapter 5
- PPO is expensive by structure, not by badly written code: 4 models + around ten hyperparameters is the sticker price
64 prompts and a rule-based reward are a demonstration of the algorithm, not RLHF. Our rule reward is only a stand-in for the Stage A reward model — real RLHF uses RMs at the 7B+ level trained on tens of thousands to millions of human preference pairs, and needs a separate rollout fleet, because generation consumes several times more compute than the updates.
What this experiment genuinely proves is two things: the hand-written PPO loop works correctly (reward climbs under the KL leash), and the mechanism of reward hacking is real (remove β and it is measured, not merely narrated). Don't cite these results as an aligned Thai model — what you gain is an understanding of how the entire machine turns, which is exactly what the next two chapters require.
Next chapter: DPO — DPO deletes both the reward model and the RL loop with pure algebra, starting from master equation 3.2, the one you just memorized.
References
- Schulman et al. (2017). Proximal Policy Optimization Algorithms — the original PPO paper: the clipped surrogate in section 3
- Schulman et al. (2015). High-Dimensional Continuous Control Using Generalized Advantage Estimation — GAE: the advantage estimator PPO uses
- Christiano et al. (2017). Deep reinforcement learning from human preferences — the paper that started RL from human preferences
- Stiennon et al. (2020). Learning to summarize from human feedback — the first convincingly working RLHF, on summarization
- Ouyang et al. (2022). Training language models to follow instructions with human feedback — InstructGPT: the origin of the whole SFT -> RM -> PPO pipeline
- Bai et al. (2022). Training a Helpful and Harmless Assistant with Reinforcement Learning from Human Feedback — the helpful/harmless dataset and its lessons about KL
- Zheng et al. (2023). Secrets of RLHF in Large Language Models Part I: PPO — the PPO implementation details other papers omit
- Bradley & Terry (1952). Rank Analysis of Incomplete Block Designs: I. The Method of Paired Comparisons — the Bradley-Terry model every reward model rests on
The writing, code and notebooks in this series are licensed under CC BY-NC-SA 4.0 — reuse and adapt them freely with attribution, for non-commercial purposes, and share your adaptations under the same terms. Third-party models and datasets referenced here keep their own licences.
