RLHF Reward Model Loss - Reinforcement Learning
Implement RLHF Reward Model Loss in Python with NumPy. hard exercise with examples, hints, and instant feedback. No setup or signup required.
RLHF Reward Model Loss
What you'll build
The Bradley–Terry pairwise loss used to train the reward model in RLHF.
Background
Humans find it much easier to say which of two responses is better than to give an absolute score. RLHF (InstructGPT, 2022) therefore collects pairs (y_chosen, y_rejected) for a prompt x and trains a reward model r(x, y), usually an LLM with a scalar head, so that chosen responses score higher. That reward model then supplies the reward for PPO (see PPO-RLHF Objective) or best-of-n sampling.
Formula
Bradley–Terry model of a preference:
P(chosen ≻ rejected) = σ(r_chosen - r_rejected)
Negative log-likelihood over N pairs:
L = -(1/N) · Σ_i log σ(r_chosen_i - r_rejected_i)
Numerically stable form: log σ(x) = -log(1 + e^(-x)) = -np.logaddexp(0, -x).
Inputs
r_chosen: (N,) reward-model scores of the preferred responsesr_rejected: (N,) scores of the rejected responses (same order)
Expected return format
A Python float: the mean loss, rounded to 3 decimals.
Edge cases and conventions
- Equal scores give log 2 ≈ 0.693 per pair (a coin flip).
- Only the difference of scores matters: adding a constant to both leaves the loss unchanged.
- Must stay exact for large margins: for r_chosen - r_rejected = -100 the loss is 100.0. Don't add an eps inside the log (
log(p + 1e-10)caps the loss near 23).
Constraints
NumPy only. Don't call a library BCE.
Follow-up
- This is binary cross-entropy with logits on the score difference and label 1. Why is the reward model only identified up to a constant shift?
- How would you use a margin, or more than two ranked responses (Plackett–Luce)?
Examples
Input: r_chosen = [2.0, 1.5], r_rejected = [1.0, 0.5] Output: 0.313
Both margins are 1: -log σ(1) = 0.313
Input: r_chosen = [0.0], r_rejected = [0.0] Output: 0.693
Equal rewards: -log 0.5 = log 2
Practice in Python
import numpy as np
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def reward_model_loss(r_chosen: np.ndarray, r_rejected: np.ndarray) -> float:
"""
Compute the Bradley-Terry reward model loss.
Args:
r_chosen: (N,) scores of the preferred responses
r_rejected: (N,) scores of the rejected responses
Returns:
mean of -log sigmoid(r_chosen - r_rejected) as a float, rounded to 3 decimals
"""
r_chosen = np.array(r_chosen, dtype=float)
r_rejected = np.array(r_rejected, dtype=float)
# Your code here
pass
The interactive editor loads with Python and NumPy. Run your implementation against 5 tests, or use the progressive hints.