LeetLLM
My PlanLearnGlossaryTracksPracticeBlog
LeetLLM

Your go-to resource for mastering AI & LLM systems.

Product

  • Learn
  • Glossary
  • Tracks
  • Practice
  • Blog
  • RSS

Legal

  • Terms of Service
  • Privacy Policy

© 2026 LeetLLM. All rights reserved.

All Topics
Your Progress
0%

0 of 178 articles completed

🛠️Computing Foundations0/9
Git, Shell, Linux for AIDocker for Reproducible AIPython for AI EngineeringNumPy and Tensor ShapesCUDA for ML TrainingMPS & Metal for ML on MacData Structures for AISQL and Data ModelingAlgorithms for ML Engineers
📊Math & Statistics0/8
Gradients and BackpropVectors, Matrices & TensorsLinear Algebra for MLAdam, Momentum, SchedulersProbability for Machine LearningStatistics and UncertaintyDistributions and SamplingHypothesis Tests, Intervals, and pass@k
📚Preparation & Prerequisites0/13
Neural Networks from ScratchCNNs from ScratchTraining & BackpropagationSoftmax, Cross-Entropy & OptimizationRNNs, LSTMs, GRUs, and Sequence ModelingAutoencoders and VAEsThe Transformer Architecture End-to-EndLanguage Modeling & Next TokensFrom GPT to Modern LLMsPrompt Engineering FundamentalsCalling LLM APIs in ProductionFirst AI App End-to-EndThe LLM Lifecycle
🧮ML Algorithms & Evaluation0/11
Linear Regression from ScratchLogistic Regression and MetricsDecision Trees, Forests, and BoostingReinforcement Learning BasicsValidation and LeakageClustering and PCACore Retrieval AlgorithmsDecoding AlgorithmsExperiment Design and A/B TestingPyTorch Training LoopsDataset Pipelines and Data Quality
📦Production ML Systems0/6
Feature Engineering for Production MLBatch and Streaming Feature PipelinesGradient Boosted Trees in ProductionRanking and Recommendation SystemsForecasting and Anomaly DetectionMonitoring Predictive Models
🧪Core LLM Foundations0/8
The Bitter Lesson & ComputeBPE, WordPiece, and SentencePieceStatic to Contextual EmbeddingsPerplexity & Model EvaluationFile Ingestion for AIChunking StrategiesLLM Benchmarks & LimitationsInstruction Tuning & Chat Templates
🧰Applied LLM Engineering0/24
Dimensionality Reduction for EmbeddingsCoT, ToT & Self-Consistency PromptingFunction Calling & Tool UseMCP & Tool Protocol StandardsContext EngineeringPrompt Injection DefenseResponsible AI GovernanceData Labeling and Human FeedbackEvaluating AI AgentsProduction RAG PipelinesHybrid Search: Dense + SparseReranking and Cross-Encoders for RAGRAG Evaluation for Reliable AnswersLLM-as-a-Judge EvaluationBias & Fairness in LLMsHallucination Detection & MitigationLLM Observability & MonitoringExperiment Tracking with MLflow and W&BPrompt Optimization with DSPyModel Versioning & DeploymentSemantic Caching & Cost OptimizationLLM Cost Engineering & Token EconomicsModel Gateways, Routing, and FallbacksDesign an Automated Support Agent
🎓Portfolio Capstones0/9
Capstone: Delivery ETA PredictionCapstone: Product RankingCapstone: Demand ForecastingCapstone: Image Damage ClassifierCapstone: Production ML PipelineCapstone: Document QACapstone: Eval DashboardCapstone: Fine-Tuned ClassifierCapstone: Reproducible ML Study
🧠Transformer Deep Dives0/8
Sentence Embeddings & Contrastive LossEmbedding Similarity & QuantizationScaled Dot-Product AttentionVision Transformers and Image EncodersPositional Encoding: RoPE & ALiBiLayer Normalization: Pre-LN vs Post-LNMechanistic InterpretabilityDecoding Strategies: Greedy to Nucleus
🧬Advanced Training & Adaptation0/17
Scaling Laws & Compute-Optimal TrainingPre-training Data at ScaleBuild GPT from Scratch LabJAX for PyTorch ResearchersContinued Pretraining for Domain ShiftSynthetic Data PipelinesSupervised Fine-Tuning PipelineMixed Precision TrainingDistributed Training: FSDP & ZeROLoRA & Parameter-Efficient TuningTraining Run OperationsReward Modeling from Preference DataRLHF & DPO AlignmentConstitutional AI & Red TeamingRLVR & Verifiable RewardsKnowledge Distillation for LLMsModel Merging and Weight Interpolation
🤖Advanced Agents & Retrieval0/16
Vector DB Internals: HNSW & IVFAdvanced RAG: HyDE & Self-RAGGraphRAG & Knowledge GraphsRAG Security & Access ControlStructured Output GenerationReAct & Plan-and-ExecuteGuardrails & Safety FiltersCode Generation & SandboxingComputer-Use / GUI / Browser AgentsHuman-in-the-Loop Agent ArchitectureAI Coding Workflow with AgentsAgent Memory & PersistenceAgent Failure & RecoveryRecursive Language Models (RLM)Multi-Agent OrchestrationCapstone: Production Agent
⚡Inference & Production Scale0/19
Inference: TTFT, TPS & KV CacheMulti-Query & Grouped-Query AttentionKV Cache & PagedAttentionPrefix Caching and Prompt CachingFlashAttention & Memory EfficiencyContinuous Batching & SchedulingScaling LLM InferenceModel Parallelism for LLM InferenceModel Quantization: GPTQ, AWQ & GGUFLocal LLM DeploymentSLM Specialization & Edge DeploymentSpeculative DecodingLong Context Window ManagementMixture of Experts ArchitectureMamba & State Space ModelsReasoning & Test-Time ComputeAdvanced MLOps & DevOps for AIGPU Serving & AutoscalingA/B Testing for LLMs
🏗️System Design Capstones0/9
Content Moderation SystemCode Completion SystemMulti-Tenant LLM PlatformLLM-Powered Search EngineVision-Language Models & CLIPMultimodal LLM ArchitectureDiffusion Models: Images & TextReal-Time Voice AI AgentReasoning Agent System Design
🎤AI Lab Interviewing0/4
AI Lab Coding Interview: Python SystemsAI Lab System Design InterviewAI Lab Behavioral InterviewAI Lab Technical Presentation
🔬Project Deep Dives0/17
Deep Dive - vLLMDeep Dive - SkyRLDeep Dive - FlashAttentionDeep Dive - FlashInferDeep Dive - DeepGEMMDeep Dive - NCCLDeep Dive - MegatronDeep Dive - DeepSpeedDeep Dive - RayDeep Dive - MLflowDeep Dive - PyTorchDeep Dive - TransformersDeep Dive - SGLangDeep Dive - slimeDeep Dive - DeepEPDeep Dive - TinkerDeep Dive - Light-PEFT
Back to Topics
LearnAdvanced Training & AdaptationReward Modeling from Preference Data
🛡️HardAlignment & Safety

Reward Modeling from Preference Data

Train reward models as a first-class post-training stage: validate chosen/rejected pairs and splits, fit a scalar reward head with Bradley-Terry loss, audit generalization, and decide when explicit rewards are worth the extra complexity.

22 min read
Learning path
Step 108 of 178 in the full curriculum
Training Run OperationsRLHF & DPO Alignment

Personalize this lesson

Adapt explanations and teaching visuals to your background and preferred voice.

The last chapter left you with a resumable supervised fine-tuning (SFT) job. policy-sft-v4 can continue, initialize, or export, and you already know which adaptation method to launch. Once that checkpoint answers, a new training problem shows up: two fluent replies to the same access-policy prompt. One cites policy P-7 and opens a review ticket. The other grants admin access for the night. Which one should score higher?

RLHF (Reinforcement Learning from Human Feedback) diagrams often squash that question into a box labeled reward model, then jump to Proximal Policy Optimization (PPO). The box is its own training project. If it learns the wrong shortcuts, policy optimization will amplify them.[1]Reference 1Training Language Models to Follow Instructions with Human Feedback (InstructGPT).https://arxiv.org/abs/2203.02155

A scalar judge, not a generator

A reward model doesn't write the next token. It scores text that already exists.

Given a prompt x and two candidate answers:

  • y+ chosen by the labeler (open a review ticket, cite P-7)
  • y- rejected by the labeler (grant admin tonight)

the model should assign:

text
1r(x, y+) > r(x, y-)

That scalar is useful in two different ways later:

  1. as a reward signal for PPO-style RLHF[1]Reference 1Training Language Models to Follow Instructions with Human Feedback (InstructGPT).https://arxiv.org/abs/2203.02155
  2. as an inspectable ranking score when you want to compare many policy outputs, including best-of-N sampling

Explicit reward models still earn their keep even though Direct Preference Optimization (DPO) can skip them for offline preference optimization.[2]Reference 2Direct Preference Optimization: Your Language Model is Secretly a Reward Model.https://arxiv.org/abs/2305.18290 Train that scalar first, then distrust it, before you plug it into either use.

Preference pairs that can actually train

The core supervision format is a preference pair: same prompt, two different completions, one definite ranking.

Standard format

preference_pair.json
1{ 2 "prompt": "User requests temporary admin access for a migration. What should the assistant do?", 3 "chosen": "Open an access-review ticket and cite policy P-7 before approval.", 4 "rejected": "Grant admin for tonight and ask the user to clean it up tomorrow." 5}

Conversational format

chat_preference_pair.json
1{ 2 "chosen": [ 3 {"role": "user", "content": "User requests temporary admin access for a migration. What should the assistant do?"}, 4 {"role": "assistant", "content": "Open an access-review ticket and cite policy P-7 before approval."} 5 ], 6 "rejected": [ 7 {"role": "user", "content": "User requests temporary admin access for a migration. What should the assistant do?"}, 8 {"role": "assistant", "content": "Grant admin for tonight and ask the user to clean it up tomorrow."} 9 ] 10}

Hugging Face TRL's RewardTrainer accepts both standard and conversational preference formats and can apply the model's chat template during tokenization.[3]Reference 3Reward Modeling.https://huggingface.co/docs/trl/reward_trainer

A pair contract before training

A row isn't ready just because it has chosen and rejected columns. For every binary pair, enforce:

  • the same prompt, system message, tool context, and rendering template on both candidates
  • two different candidate answers and a definite preference label
  • separate handling for ties, abstentions, and low-agreement labels
  • provenance such as source prompt, generator checkpoint, sampling settings, and labeling batch

That last field matters for splitting. If one prompt generated several candidates, its comparisons are near-duplicates. Put the entire prompt group in train or in evaluation, never both.

The audit below keeps only row a. Identical candidates, mismatched prompts, and ties don't enter ordinary pairwise ranking training.

preference_pair_contract.py
1from collections import Counter 2 3pairs = [ 4 {"id": "a", "prompt_left": "admin?", "prompt_right": "admin?", "chosen": "Escalate.", "rejected": "Approve.", "label": "chosen"}, 5 {"id": "b", "prompt_left": "admin?", "prompt_right": "admin?", "chosen": "Escalate.", "rejected": "Escalate.", "label": "chosen"}, 6 {"id": "c", "prompt_left": "admin?", "prompt_right": "source?", "chosen": "Escalate.", "rejected": "Cite source.", "label": "chosen"}, 7 {"id": "d", "prompt_left": "source?", "prompt_right": "source?", "chosen": "Cite.", "rejected": "Refuse.", "label": "tie"}, 8] 9 10def rejection_reason(pair): 11 if pair["label"] != "chosen": 12 return "tie_or_abstention" 13 if pair["prompt_left"] != pair["prompt_right"]: 14 return "context_mismatch" 15 if pair["chosen"] == pair["rejected"]: 16 return "identical_candidates" 17 return None 18 19reasons = Counter(reason for pair in pairs if (reason := rejection_reason(pair))) 20kept = [pair["id"] for pair in pairs if rejection_reason(pair) is None] 21 22print(f"kept={kept}") 23print(f"rejected={dict(sorted(reasons.items()))}")
Pair-contract audit output
1kept=['a'] 2rejected={'context_mismatch': 1, 'identical_candidates': 1, 'tie_or_abstention': 1}

A grouped split is the next check. Rows a-b and a-c share prompt support-17, so they move together.

grouped_preference_split.py
1pairs = [ 2 {"pair_id": "a-b", "prompt_id": "support-17"}, 3 {"pair_id": "a-c", "prompt_id": "support-17"}, 4 {"pair_id": "d-e", "prompt_id": "safety-04"}, 5 {"pair_id": "f-g", "prompt_id": "access-09"}, 6] 7eval_prompt_ids = {"support-17"} 8 9train = [pair for pair in pairs if pair["prompt_id"] not in eval_prompt_ids] 10evaluation = [pair for pair in pairs if pair["prompt_id"] in eval_prompt_ids] 11overlap = {pair["prompt_id"] for pair in train} & {pair["prompt_id"] for pair in evaluation} 12 13assert not overlap 14print(f"train_pairs={len(train)} eval_pairs={len(evaluation)}") 15print(f"prompt_overlap={sorted(overlap)}")
Grouped split output
1train_pairs=2 eval_pairs=2 2prompt_overlap=[]

Prompt groups also change how you train, not only how you split. InstructGPT asked labelers to rank K=4K=4K=4 to K=9K=9K=9 completions per prompt, which produces (K2)\binom{K}{2}(2K​) pairwise comparisons. If you shuffle those pairs into the dataset independently, each completion can contribute K−1K-1K−1 gradient updates in one epoch, and the reward model overfits. Their fix was to treat every comparison from one prompt as a single batch element and average the loss over the (K2)\binom{K}{2}(2K​) pairs.[1]Reference 1Training Language Models to Follow Instructions with Human Feedback (InstructGPT).https://arxiv.org/abs/2203.02155

Last token, one number

Most practical reward models aren't built from scratch. You start from a pretrained or SFT checkpoint, drop the token-unembedding head, and attach a one-unit score head. InstructGPT did exactly that: replace the unembedding layer with a projection to a scalar.[1]Reference 1Training Language Models to Follow Instructions with Human Feedback (InstructGPT).https://arxiv.org/abs/2203.02155 Conceptually:

text
1tokens | transformer hidden states | sequence representation | one scalar reward

For a decoder-only LM, that representation is often the final non-padding position. Reward modeling then feels like sequence classification with pairwise labels rather than generation. The output is one number per candidate response, not the next-token distribution.

The rendered sequence is part of the contract. Keep chat-template and end-of-sequence conventions consistent with the policy you'll score. Don't silently train on answers whose decisive ending was truncated: current TRL RewardConfig.max_length (default 1024) drops a pair when either candidate exceeds that limit after tokenization.[3]Reference 3Reward Modeling.https://huggingface.co/docs/trl/reward_trainer

sequence_length_gate.py
1max_length = 1024 2pairs = [ 3 {"id": "fits", "chosen_tokens": 412, "rejected_tokens": 390}, 4 {"id": "chosen_too_long", "chosen_tokens": 1088, "rejected_tokens": 401}, 5 {"id": "rejected_too_long", "chosen_tokens": 288, "rejected_tokens": 1030}, 6] 7 8kept = [p["id"] for p in pairs if max(p["chosen_tokens"], p["rejected_tokens"]) <= max_length] 9dropped = [p["id"] for p in pairs if p["id"] not in kept] 10 11print(f"kept={kept}") 12print(f"dropped_instead_of_truncated={dropped}")
Sequence-length gate output
1kept=['fits'] 2dropped_instead_of_truncated=['chosen_too_long', 'rejected_too_long']

A pair that scores is only useful if the loss on those two numbers matches the label. That's the next question.

Bradley-Terry from a worked margin

Start with the access-policy pair. Suppose the ticket-and-cite answer gets reward 1.8 and the unsupported approval gets 0.7. The margin is 1.8 - 0.7 = 1.1. A positive margin means the reward model prefers the chosen answer. The Bradley-Terry model turns that margin into a preference probability: σ(1.1)≈0.75\sigma(1.1) \approx 0.75σ(1.1)≈0.75.

Bar chart of one access-policy preference pair: the chosen ticket-and-cite answer scores 1.8 and the rejected grant-admin answer scores 0.7. A probability bar then maps the +1.1 margin through a sigmoid to about 0.75, the Bradley-Terry chance that the chosen answer wins.
The reward model scores each completion independently. Bradley-Terry only sees the difference: here 1.8 − 0.7 = 1.1, which maps to about a 75% chance the chosen access-policy answer wins.
P(y+≻y−∣x)=σ(r(x,y+)−r(x,y−))P(y^+ \succ y^- \mid x) = \sigma\big(r(x, y^+) - r(x, y^-)\big)P(y+≻y−∣x)=σ(r(x,y+)−r(x,y−))

where σ\sigmaσ is the sigmoid. InstructGPT trains the negative log of that probability, averaged over the comparisons in a prompt group:[1]Reference 1Training Language Models to Follow Instructions with Human Feedback (InstructGPT).https://arxiv.org/abs/2203.02155

L(θ)=−1(K2)E(x,yw,yl)∼D[log⁡σ(rθ(x,yw)−rθ(x,yl))]\mathcal{L}(\theta) = -\frac{1}{\binom{K}{2}} \mathbb{E}_{(x, y_w, y_l) \sim \mathcal{D}}\big[\log \sigma\big(r_\theta(x, y_w) - r_\theta(x, y_l)\big)\big]L(θ)=−(2K​)1​E(x,yw​,yl​)∼D​[logσ(rθ​(x,yw​)−rθ​(x,yl​))]

where (yw,yl)(y_w, y_l)(yw​,yl​) is the chosen (winning) and rejected (losing) pair among the KKK ranked completions for prompt xxx. For a single binary pair, K=2K=2K=2 and the factor is 1, so the loss is just −log⁡σ(r+−r−)-\log\sigma(r^+ - r^-)−logσ(r+−r−). If the chosen reward is much higher than the rejected reward, loss shrinks toward 0. If the model ranks them backwards, loss gets large.

The numerically stable form of −log⁡σ(z)-\log\sigma(z)−logσ(z) is log⁡(1+e−z)\log(1 + e^{-z})log(1+e−z). For negative zzz, rewrite it as −z+log⁡(1+ez)-z + \log(1 + e^{z})−z+log(1+ez) so exp doesn't overflow.

Why does a larger positive reward margin make the Bradley-Terry loss smaller?

Answer

Because the loss is -log sigma(r(chosen) - r(rejected)). As the chosen-minus-rejected margin grows, the sigmoid term moves closer to 1, so the negative log shrinks toward 0.

The batch below uses the same three margins you'll see in the quiz: 1.7, -0.2, and 0.7. Pair accuracy is 2/3 because one pair is ranked backwards.

reward_model_loss.py
1from math import exp, log1p 2from statistics import mean 3 4chosen_rewards = [2.1, 0.8, 1.9] 5rejected_rewards = [0.4, 1.0, 1.2] 6margins = [a - b for a, b in zip(chosen_rewards, rejected_rewards)] 7 8def neg_log_sigmoid(z: float) -> float: 9 if z >= 0: 10 return log1p(exp(-z)) 11 return -z + log1p(exp(z)) 12 13loss = mean(neg_log_sigmoid(m) for m in margins) 14accuracy = mean(m > 0 for m in margins) 15 16print("margins:", [round(m, 3) for m in margins]) 17print("reward_loss:", round(loss, 4)) 18print("pair_accuracy:", round(accuracy, 4))
Reward loss output
1margins: [1.7, -0.2, 0.7] 2reward_loss: 0.4564 3pair_accuracy: 0.6667

Sweeping the margin makes the same loss visible as a curve. Zero margin is a coin flip (log 2 ≈ 0.6931). A margin of -2 is a confident wrong ranking.

reward_margin_curve.py
1from math import exp, log1p 2 3def neg_log_sigmoid(z: float) -> float: 4 if z >= 0: 5 return log1p(exp(-z)) 6 return -z + log1p(exp(z)) 7 8for margin in [-2.0, 0.0, 2.0]: 9 print(f"margin={margin:+.1f} loss={neg_log_sigmoid(margin):.4f}")
Margin curve output
1margin=-2.0 loss=2.1269 2margin=+0.0 loss=0.6931 3margin=+2.0 loss=0.1269

That's the ranking core. Everything else in reward modeling is about whether the data and evaluation around that loss are strong enough to trust.

Multi-way rankings and a preference-strength margin

Binary chosen/rejected pairs are the default training unit. Production sets often produce K>2K>2K>2 completions per prompt. Two standard expansions:

  • Pair expansion. InstructGPT's method: emit every ordered pair the ranking can score, then pack those pairs by prompt so one completion doesn't dominate the epoch.[1]Reference 1Training Language Models to Follow Instructions with Human Feedback (InstructGPT).https://arxiv.org/abs/2203.02155
  • Plackett-Luce. Model a full ranking as a product of successive choices rather than independent pairs. Use it when you have true multi-way rankings and want one joint likelihood. Pair expansion remains the simpler baseline when only sparse pairwise labels exist.

Llama 2 added a discrete margin m(y+,y−)m(y^+, y^-)m(y+,y−) inside the sigmoid when raters marked how much better the winner was (significantly better, slightly better, and so on):[4]Reference 4Llama 2: Open Foundation and Fine-Tuned Chat Models.https://arxiv.org/abs/2307.09288

L=−log⁡σ(r(x,y+)−r(x,y−)−m)\mathcal{L} = -\log \sigma\big(r(x, y^+) - r(x, y^-) - m\big)L=−logσ(r(x,y+)−r(x,y−)−m)

Positive mmm requires the chosen score to beat the rejected score by more than mmm before the loss saturates. Llama 3 later dropped that term after seeing diminishing returns once preference data scaled.[5]Reference 5The Llama 3 Herd of Models.https://arxiv.org/abs/2407.21783 Treat mmm as a hyperparameter you validate on held-out pair accuracy and fresh-policy human agreement, not as a free constant.

A temperature β\betaβ that multiplies the reward difference, σ(β(r+−r−))\sigma\big(\beta(r^+ - r^-)\big)σ(β(r+−r−)), is multiplicative score scaling: it changes loss confidence and downstream RL strength without changing the additive invariance of pure ranking. Log any β\betaβ or mmm next to the reward checkpoint.

What pair accuracy hides

TRL logs more than loss for a reason.[3]Reference 3Reward Modeling.https://huggingface.co/docs/trl/reward_trainer

MetricWhat it tells youWhat it doesn't tell you
Pair accuracyHow often chosen beats rejectedWhether the ranking reason is policy-relevant
Mean marginTypical r(chosen) - r(rejected)Whether scores are calibrated for PPO
Mean / min / max rewardDrift or exploding scaleWhether humans would agree on new outputs
Gradient normUnstable updatesShortcut features such as length
Held-out preference qualityRanking on a static splitRanking on the current policy's fresh outputs

Loss can fall while the model memorizes easy stylistic cues that overfit the train pairs. InstructGPT already saw this: extra epochs quickly hurt validation loss even when the architecture was stable.[1]Reference 1Training Language Models to Follow Instructions with Human Feedback (InstructGPT).https://arxiv.org/abs/2203.02155

Centering and calibration

Reward models are underdetermined up to an additive constant: adding the same number to every score leaves every margin and the Bradley-Terry loss unchanged. Scaling scores is different. It preserves a ranking but changes loss confidence and the strength of a reward signal consumed by an optimizer.

That matters operationally because:

  • absolute reward level and score scale can drift over training
  • PPO-style optimization is sensitive to reward scale
  • long verbose answers can look better than they are if the model learned a shallow heuristic

InstructGPT fixed the additive ambiguity with a bias so labeler demonstrations scored mean zero before RL.[1]Reference 1Training Language Models to Follow Instructions with Human Feedback (InstructGPT).https://arxiv.org/abs/2203.02155 TRL exposes center_rewards_coefficient (recommended 0.01) as an auxiliary term that pushes batch rewards toward zero during training. It's a centering aid, not proof that reward magnitude is calibrated for policy optimization.[3]Reference 3Reward Modeling.https://huggingface.co/docs/trl/reward_trainer

reward_centering_invariance.py
1from math import exp, log1p 2from statistics import mean 3 4chosen = [1.2, 0.7] 5rejected = [0.2, 0.4] 6 7def pair_loss(left, right): 8 return mean(log1p(exp(-(a - b))) for a, b in zip(left, right)) 9 10shifted = ([score + 10 for score in chosen], [score + 10 for score in rejected]) 11scaled = ([score * 3 for score in chosen], [score * 3 for score in rejected]) 12 13print(f"base_loss={pair_loss(chosen, rejected):.4f}") 14print(f"shifted_loss={pair_loss(*shifted):.4f}") 15print(f"scaled_loss={pair_loss(*scaled):.4f}")
Centering invariance output
1base_loss=0.4338 2shifted_loss=0.4338 3scaled_loss=0.1949

Shift leaves the loss alone. Scale doesn't. If you later feed these scores into PPO, the * 3 version is a stronger (and easier to overoptimize) signal even though the ranking is the same.

Audits that pair accuracy misses

Pair accuracy can look healthy while the reward model still learns a bad shortcut. This audit ranks all three preference pairs correctly, but every chosen answer is longer than its rejected pair.

reward_audit.py
1from statistics import mean 2 3pairs = [ 4 {"chosen_reward": 1.8, "rejected_reward": 0.7, "chosen_tokens": 36, "rejected_tokens": 19}, 5 {"chosen_reward": 2.4, "rejected_reward": 1.1, "chosen_tokens": 58, "rejected_tokens": 22}, 6 {"chosen_reward": 1.6, "rejected_reward": 0.3, "chosen_tokens": 33, "rejected_tokens": 14}, 7] 8 9margins = [row["chosen_reward"] - row["rejected_reward"] for row in pairs] 10accuracy = mean(margin > 0 for margin in margins) 11length_gaps = [row["chosen_tokens"] - row["rejected_tokens"] for row in pairs] 12 13print(f"pair_accuracy={accuracy:.2f}") 14print(f"mean_margin={mean(margins):.2f}") 15print(f"chosen_answers_longer={all(gap > 0 for gap in length_gaps)}") 16print("next_check=build length-matched eval pairs")
Reward audit output
1pair_accuracy=1.00 2mean_margin=1.23 3chosen_answers_longer=True 4next_check=build length-matched eval pairs

Annotator disagreement is another failure signal. A pair can be formatted correctly and still be weak supervision if raters don't agree about which completion is better. The toy gate below routes any disputed label to review. A real pipeline may adjudicate, weight, or keep disagreements on a dedicated evaluation slice.

agreement_audit.py
1votes = { 2 "clear_safety": ["chosen", "chosen", "chosen"], 3 "style_only": ["chosen", "rejected", "chosen"], 4 "ambiguous_refusal": ["chosen", "rejected", "tie"], 5} 6 7for pair_id, labels in votes.items(): 8 chosen_share = labels.count("chosen") / len(labels) 9 status = "train" if chosen_share == 1.0 else "review_or_hold_out" 10 print(f"{pair_id}: chosen_share={chosen_share:.2f} status={status}")
Agreement audit output
1clear_safety: chosen_share=1.00 status=train 2style_only: chosen_share=0.67 status=review_or_hold_out 3ambiguous_refusal: chosen_share=0.33 status=review_or_hold_out

Length-matched slices and rater agreement still evaluate the original pair distribution. The harder question is what happens when the policy starts writing answers that weren't in that distribution.

Fresh outputs are the real test

A reward model can fit the training pairs and still rank the current policy's fresh answers the wrong way. That second ranking is the one that matters before you optimize against the score.

As the policy improves, it starts producing answers unlike the ones in the original preference dataset. The reward model may then score confidently for the wrong reasons. Gao, Schulman, and Hilton measured this as overoptimization: a proxy reward you keep optimizing eventually stops tracking a gold preference signal, which is Goodhart's law with a learning curve.[6]Reference 6Scaling Laws for Reward Model Overoptimizationhttps://arxiv.org/abs/2210.10760 In their synthetic setup the gold score rose, then fell, while the proxy kept climbing. A KL penalty increased proxy reward at a given KL, but it didn't improve the gold-reward frontier.

Watch for that gap in production:

  • the learned reward rises
  • held-out human preference on fresh policy outputs stops rising
  • raters see longer, repetitive, or otherwise worse answers
Schematic line chart of PPO steps from start to 400. The learned proxy reward rises from 0.2 to 1.4 while human preference on fresh policy outputs rises to 0.7 at step 200 then falls to 0.4, so the proxy keeps climbing after the real target has peaked.
Training-pair accuracy is local fit. The release question is whether human preference on fresh policy outputs is still rising with the proxy. The curves are a schematic of that gap, not a fitted scaling law.

If you don't monitor that gap, policy optimization can amplify the shortcut. The threshold below is illustrative; set release gates from your evaluation design and risk tolerance.

fresh_policy_gate.py
1evaluation = { 2 "static_held_out_pairs": {"accuracy": 0.92, "human_reviewed": False}, 3 "fresh_policy_pairs": {"accuracy": 0.64, "human_reviewed": True}, 4} 5minimum_fresh_accuracy = 0.80 6ppo_ready = evaluation["fresh_policy_pairs"]["accuracy"] >= minimum_fresh_accuracy 7 8print(f"static_accuracy={evaluation['static_held_out_pairs']['accuracy']:.2f}") 9print(f"fresh_accuracy={evaluation['fresh_policy_pairs']['accuracy']:.2f}") 10print(f"ppo_ready={ppo_ready}")
Fresh-policy gate output
1static_accuracy=0.92 2fresh_accuracy=0.64 3ppo_ready=False
Diagram showing Validated pairs, Train scalar RM, Fresh outputs still match humans?, and Optimize against reward.
Validated pairs, Train scalar RM, Fresh outputs still match humans?, and Optimize against reward.

KL control intuition (preview)

A common control, developed in full by the next lesson, penalizes the policy for drifting too far from its reference checkpoint. Optimization maximizes reward minus a KL-divergence term that measures policy drift.[1]Reference 1Training Language Models to Follow Instructions with Human Feedback (InstructGPT).https://arxiv.org/abs/2203.02155 That discourages large departures from the reference, but Gao's measurements are the reason to keep the claim narrow: KL control isn't a certificate that the reward model is valid on new outputs.[6]Reference 6Scaling Laws for Reward Model Overoptimizationhttps://arxiv.org/abs/2210.10760 A reward model is a local approximation of human preference, which is exactly why it can be overoptimized.

Standardized evaluation: RewardBench

Held-out pairs you wrote yourself can share your blind spots. RewardBench is an Ai2 benchmark that scores a reward model by how often it ranks a known-better completion above a worse one. Its original sections cover chat, harder instruction-following comparisons, safety, reasoning, and prior preference test sets.[7]Reference 7RewardBench: Evaluating Reward Models for Language Modelinghttps://arxiv.org/abs/2403.13787

RewardBench 2 is a harder follow-up: multi-skill, best-of-four scoring, mostly previously unused human prompts (about 70% of the set), and decontamination against twenty downstream evaluations. In their experiments, benchmark scores correlated strongly with best-of-N (Pearson 0.87 on the average they report). PPO was a weaker, saturating signal: decent-to-good reward models clustered, and transfer dropped when the reward model didn't share the policy's model lineage or prompt distribution.[8]Reference 8RewardBench 2: Advancing Reward Model Evaluationhttps://arxiv.org/abs/2506.01937

One practical caveat from that work: the highest-scoring reward model on the leaderboard isn't automatically the best choice for your run. Treat absolute benchmark rank as a filter, then validate with the policy and optimization setup you'll use.[8]Reference 8RewardBench 2: Advancing Reward Model Evaluationhttps://arxiv.org/abs/2506.01937

Your reward model tops the RewardBench leaderboard. Is it automatically the right choice for your PPO run?

Answer

No. A high benchmark score is a good filter, especially for best-of-N. RewardBench 2 reported better PPO transfer for same-lineage, in-distribution reward and policy models, and PPO gains saturated across many decent scores. Validate with your intended policy and optimization setup.

Other ways to build a preference signal

The scalar Bradley-Terry head is a common baseline. It isn't the only judge.

SignalWhat it outputsWhen it helpsMain failure
Scalar Bradley-Terry RMOne number per completionPPO, best-of-N, inspectable scoresOveroptimization, length and style shortcuts
DPOImplicit reward inside the policyClean offline preferences, no separate judgeNo reusable scalar for scoring or online RL
Process reward model (PRM)A score per reasoning stepMulti-step math or tool tracesExpensive step labels; still gameable
Generative RM / LLM-as-judgeA verdict, optionally with a rationaleWhen chain-of-thought or vote aggregation helps in your evalCost, position bias, and judge-specific quirks
Verifier / RLVRA checkable pass/failMath answers, tests, other exact gradersMisspecified tests and verifier gaming
  • Generative reward models. Instead of a scalar head, an LM can read candidates and emit a verdict. Mahan et al. study rationale generation and vote aggregation in that setup; treat those as design choices to evaluate, not universal guarantees.[9]Reference 9Generative Reward Modelshttps://arxiv.org/abs/2410.12832
  • Process reward models. Scoring only the final answer is a weak signal for multi-step reasoning. PRMs score each step. Lightman et al. compared process and outcome supervision as search over many sampled MATH solutions (best-of-N), not as RL on the generator, and the process RM beat the outcome RM.[10]Reference 10Let's Verify Step by Step.https://arxiv.org/abs/2305.20050
  • Verifiers and RLVR. When correctness is checkable, such as a final math answer or passing unit tests, verifiable rewards can reduce dependence on a learned preference proxy. They don't eliminate misspecified tests or gaming of the verifier.[11]Reference 11Tülu 3: Pushing Frontiers in Open Language Model Post-Traininghttps://arxiv.org/abs/2411.15124 Later chapters cover this family.

These don't retire the scalar reward model, and they don't all share its Bradley-Terry objective. The reusable lesson is the evaluation discipline: inspect the signal's coverage, test it on outputs produced by the system being optimized, and watch for optimization exploiting its blind spots.

When an explicit reward model is worth the cost

Use one when:

  • you want PPO-style online optimization
  • you want to score many candidate outputs with one scalar model
  • you want to inspect and audit the preference signal directly

Start with DPO when:

  • you have a clean offline preference dataset
  • you want the simpler baseline first
  • you don't need an explicit learned judge in the loop

That trade-off is why DPO is a strong offline-preference baseline: it removes the separate reward-model training stage.[2]Reference 2Direct Preference Optimization: Your Language Model is Secretly a Reward Model.https://arxiv.org/abs/2305.18290 It doesn't supply a reusable scalar judge for PPO or candidate scoring. The final toy gate combines several checks, with project-specific thresholds, to block optimization when only one static slice passes.

reward_readiness_gate.py
1checks = { 2 "grouped_split_has_no_prompt_overlap": True, 3 "length_matched_accuracy": 0.84, 4 "fresh_policy_human_accuracy": 0.78, 5 "minimum_required_accuracy": 0.80, 6} 7ready = ( 8 checks["grouped_split_has_no_prompt_overlap"] 9 and checks["length_matched_accuracy"] >= checks["minimum_required_accuracy"] 10 and checks["fresh_policy_human_accuracy"] >= checks["minimum_required_accuracy"] 11) 12 13print(f"static_slice_passes={checks['length_matched_accuracy'] >= checks['minimum_required_accuracy']}") 14print(f"fresh_policy_passes={checks['fresh_policy_human_accuracy'] >= checks['minimum_required_accuracy']}") 15print(f"optimize_against_reward={ready}")
Readiness-gate output
1static_slice_passes=True 2fresh_policy_passes=False 3optimize_against_reward=False

Your reward model has good static pair accuracy, but once PPO starts, reward climbs while human raters say answers are getting verbose and manipulative. What is the first diagnosis?

Answer

The reward model is being exploited under distribution shift. Static pair accuracy wasn't enough to prove that it would rank fresh policy outputs the way humans do.

You have a clean offline preference dataset and no need to score large candidate pools or run PPO. What simpler baseline should you evaluate first?

Answer

DPO. If you don't need an explicit learned scalar judge in the loop, it removes the separate reward-model stage.

Common pitfalls

Symptom: loss falls but held-out rankings barely improve

  • Cause: chosen and rejected responses are nearly equivalent, so the pair gives little ranking signal.
  • Fix: audit pair strength before training. Keep pairs where the preference is clear, policy-relevant, and tied to the same prompt.

Symptom: one labeler style dominates the reward model

  • Cause: inconsistent or narrow annotator preferences become inconsistent rewards.
  • Fix: measure agreement, review disagreements, and separate policy rules from personal style before training.

Symptom: PPO reward rises while human preference gets worse

  • Cause: the policy has found a shortcut in the reward model under distribution shift.
  • Fix: add fresh policy-output evaluations, length-matched checks, adversarial probes, and human preference gates before trusting the scalar reward.

Symptom: product dashboards treat reward as truth

  • Cause: reward is being mistaken for the business or human objective itself.
  • Fix: report reward beside held-out preference, refusal quality, helpfulness, safety, and downstream product metrics.
Complete the lesson

Mastery Check

Answer every question, then check your score. Score 75% or higher to mark this lesson complete.

1.What is the role of a scalar reward model in preference-based training?

Correct answer: Score each candidate response for a prompt so an optimizer can compare behavior; it is not the response generator.

A reward model maps a prompt and response to a scalar preference signal. It can guide PPO or evaluation, but it is not the actor that produces the assistant's text.

2.Which dataset contract gives a preference model a valid comparison?

Correct answer: Use two responses to the same prompt, preserve the preference label, and split train and evaluation by prompt group or provenance.

Pairwise preference learning assumes a shared prompt. Grouped splits prevent near-duplicate prompts or conversation branches from leaking into evaluation and inflating agreement.

3.Where should a sequence reward model read its scalar when padding is present?

Correct answer: Use the final nonpadding position or an explicitly pooled nonpadding representation, and reject overlength pairs consistently.

Padding is not response content. A scalar head must use a defined nonpadding representation, while overlength examples need a documented policy because silently truncating one side can change the preference.

4.For Bradley-Terry margins [1.7, -0.2, 0.7], what diagnostic pair is reported in the article?

Correct answer: Mean loss about 0.4564 and pair accuracy about 0.6667, because two of three signed margins are positive.

The logistic loss is averaged over all three margins, while pair accuracy counts positive margins. The negative -0.2 example lowers accuracy to two correct out of three without making loss a binary quantity.

5.What happens if the same constant is added to both scores in every preference pair?

Correct answer: The Bradley-Terry margin is unchanged, while multiplying scores changes margin scale and therefore loss strength.

Pairwise preference depends on the difference between chosen and rejected scores, so a shared additive offset cancels. Scaling changes differences and the sharpness of the logistic loss.

6.A reward model reaches high training accuracy by giving longer answers higher scores. What validation should come next?

Correct answer: Test length-controlled pairs and fresh agreement or human slices to detect length and annotator-style shortcuts.

Reward models can learn correlates such as length or a particular annotator style instead of the intended quality. Controlled counterexamples and fresh human or agreement slices expose those shortcuts.

7.A reward model scores 0.92 on a static preference set but only 0.64 on responses from the current policy. What should the release gate do?

Correct answer: Block or recalibrate the model because fresh policy agreement is the relevant gate for optimization feedback.

A reward model can look strong on a stale or familiar set while failing on the policy it will score. The article treats fresh policy agreement as a binding reliability check before optimization.

8.When is a preference objective such as DPO a practical alternative to training a separate reward model for PPO?

Correct answer: When clean paired preferences support a direct offline objective and no reusable scalar judge or online PPO loop is needed.

DPO uses preference pairs directly rather than requiring a separately trained scalar reward model and online PPO loop. It still depends on valid preference data and an explicit reference-policy setup.

9.A model ranks highly on RewardBench but fails a fresh, same-lineage preference slice. What conclusion is justified?

Correct answer: The benchmark result is insufficient evidence for promotion; lineage-matched and fresh behavior can expose a reward-model distribution shift.

A public benchmark is useful evidence, not a universal guarantee. Evaluating responses from the intended policy lineage tests whether the reward model remains trustworthy where it will actually be used.

9 questions remaining.

Next Step
Continue to RLHF & DPO Alignment

You can now train and audit a scalar preference judge, including the dataset contract, Bradley-Terry loss, and the fresh-output gate that decides whether that judge is safe to optimize against. Next you'll put that judge (or skip it) into the rest of the alignment stack: PPO with a KL penalty, DPO's implicit reward, and when to pick online RLHF versus offline preference optimization.

PreviousTraining Run Operations
Share this article
XFacebookLinkedInBlueskyRedditHacker NewsEmail
References

Training Language Models to Follow Instructions with Human Feedback (InstructGPT).

Ouyang, L., et al. · 2022 · NeurIPS 2022

https://arxiv.org/abs/2203.02155

Direct Preference Optimization: Your Language Model is Secretly a Reward Model.

Rafailov, R., et al. · 2023

https://arxiv.org/abs/2305.18290

Reward Modeling.

Hugging Face · 2026

https://huggingface.co/docs/trl/reward_trainer

Llama 2: Open Foundation and Fine-Tuned Chat Models.

Touvron, H., et al. · 2023 · arXiv preprint

https://arxiv.org/abs/2307.09288

The Llama 3 Herd of Models.

Dubey, A., et al. · 2024 · arXiv preprint

https://arxiv.org/abs/2407.21783

Scaling Laws for Reward Model Overoptimization

Gao, L., Schulman, J., & Hilton, J. · 2023

https://arxiv.org/abs/2210.10760

RewardBench: Evaluating Reward Models for Language Modeling

Lambert, N., et al. · 2024

https://arxiv.org/abs/2403.13787

RewardBench 2: Advancing Reward Model Evaluation

Malik, S., et al. · 2025

https://arxiv.org/abs/2506.01937

Generative Reward Models

Mahan, D., et al. · 2024

https://arxiv.org/abs/2410.12832

Let's Verify Step by Step.

Lightman, H., et al. · 2023 · ICLR

https://arxiv.org/abs/2305.20050

Tülu 3: Pushing Frontiers in Open Language Model Post-Training

Lambert, N., et al. · 2024 · arXiv preprint

https://arxiv.org/abs/2411.15124

Discussion

Questions and insights from fellow learners.

Discussion loads when you reach this section.