1import torch
2import torch.nn.functional as F
3
4# ---- Stage 1: Supervised fine-tuning (imitate human TL;DRs) ----
5def sft_step(policy, post, human_summary):
6 logits = policy(post, human_summary)
7 return F.cross_entropy(logits, human_summary.tokens) # plain imitation
8
9
10# ---- Stage 2: Reward model from human comparisons ----
11def reward_model_step(rm, post, winner, loser):
12 # Bradley-Terry: the human-preferred summary must score higher
13 gap = rm(post, winner) - rm(post, loser)
14 return -F.logsigmoid(gap) # loss = -log sigma(r_w - r_l)
15
16
17# ---- Stage 3: RL against the judge, on a KL leash ----
18def rlhf_reward(rm, policy, sft_policy, post, summary, beta=0.05):
19 proxy = rm(post, summary) # what the judge thinks
20
21 # The leash: pay for every token the SFT baseline finds weird
22 kl = (policy.logprob(summary, post)
23 - sft_policy.logprob(summary, post))
24
25 return proxy - beta * kl # the objective PPO maximizes
26
27
28# The closed-form optimum of stage 3 (what the lab visualizes):
29# pi*(y|x) ~ pi_sft(y|x) * exp( r(x,y) / beta )
30#
31# beta -> 0: highest-reward output takes everything -- including
32# reward-model MISTAKES. That's reward hacking.
33# beta -> inf: policy never moves; human feedback wasted.
34# The art of RLHF is the leash length in between.