WARNING
🧪 Beta公测版本提示:教程主体已完成,正在优化细节,欢迎大家提Issue反馈问题或建议。
s21 RLHF:当强化学习遇见大模型 — exercise.py 练习指南
练习目标
通过补全三个 TODO,在 LLM 记号下再用一遍已经学过的零件:
- PPO 裁剪 —— 公式见 PPO 练习,这里换成 token 概率比
- DPO 损失 —— 本章新内容,绕过显式奖励模型
- GAE —— 公式见 PPO 章,这里用在稀疏的序列末尾奖励上
预备知识
- PPO 裁剪目标:
- 概率比:
- DPO 损失:
- GAE 递推:
,
任务清单
TODO 1:实现 PPO 裁剪目标(ppo_clipped_objective 函数)
任务:实现
实现步骤:
surr1 = ratio * advantage—— 未裁剪的原始目标clipped_ratio = torch.clamp(ratio, 1 - clip_epsilon, 1 + clip_epsilon)—— 限制比率surr2 = clipped_ratio * advantage—— 裁剪后目标objective = torch.min(surr1, surr2).mean()—— 取 min 确保保守更新
关键理解:取 min 是 PPO 的精髓。无论 advantage 是正还是负,min 都确保不会因为过大的
预期输出(clip_epsilon=0.2):
测试 1 [正优势 Â > 0]:
ratio = [0.5, 1.0, 1.5, 2.0, 3.0]
objective ≈ 1.2200
预期: (1.0 + 1.0 + 1.2 + 1.2 + 1.2) / 5 = 1.22
第3-5个 ratio > 1.2 被裁剪为 1.2
测试 2 [负优势 Â < 0]:
min 选择更负的目标值,裁剪防止 ratio 过度降低
ratio < 0.8 时被裁剪到 0.8TODO 2:实现 DPO 损失函数(dpo_loss 函数)
任务:实现
实现步骤:
log_ratio_w = log_p_w - ref_log_p_w—— 偏好回复的对数比率log_ratio_l = log_p_l - ref_log_p_l—— 不偏好回复的对数比率diff = beta * (log_ratio_w - log_ratio_l)—— 加权差值loss = -F.logsigmoid(diff)—— DPO 损失
为什么用 F.logsigmoid 而非 -torch.log(torch.sigmoid(diff)):F.logsigmoid 在内部做了数值稳定处理。当 diff 很小时,sigmoid(diff) 接近 0,log(接近0) 会下溢;F.logsigmoid 用 -softplus(-diff) 的方式避免了此问题。
预期输出:
测试 1 [策略偏好 y_w (正确方向)]:
log_ratio_w = -2 - (-3) = +1, log_ratio_l = -5 - (-3) = -2
diff = 0.1 * (1 - (-2)) = 0.3
loss ≈ -log σ(0.3) ≈ 0.555 (损失小 = 好)
测试 2 [策略偏好 y_l (错误方向)]:
log_ratio_w = -5 - (-3) = -2, log_ratio_l = -2 - (-3) = +1
diff = 0.1 * (-2 - 1) = -0.3
loss ≈ -log σ(-0.3) ≈ 0.854 (损失大 = 差)
loss(正确方向) < loss(错误方向) ✓TODO 3:实现 GAE 优势估计(compute_gae 函数)
任务:实现递推形式的 GAE 计算。
核心递推公式(从后往前):
其中
实现步骤:
python
for t in reversed(range(T)):
# 1. 确定 V(s_{t+1})
if t == T - 1:
next_v = next_value # 最后一步:终止后 value=0 或 last_value
else:
next_v = values[t + 1] # 一般情况
# 2. 计算 TD 误差
delta = rewards[t] + gamma * next_v - values[t]
# 3. 递推 GAE
gae = delta + gamma * gae_lambda * gae
# 4. 存储
advantages[t] = gae关键参数:
next_value:如果 episode 终止(done=True),;否则 gae_lambda:控制偏差-方差折中(0=低方差高偏差,1=高方差低偏差)
预期输出(5 步 episode,最后一步奖励=10.0,其他=0.0):
GAE 优势: 大约 [6.99, 7.45, 7.94, 8.46, 9.0]
最后一步优势 ≈ 9.0(因为 δ₄ = 10 + 0 - 1 = 9.0)
所有优势 > 0: 是(GAE 将未来奖励反向传播到了前几步)完成后的验证
全部三个 TODO 通过测试后,运行 python code/demo.py 观察:
- PPO 训练过程中的 RM 分数上升(策略在变好)
- KL 散度的变化(策略偏离了多少)
- 策略熵的变化(探索程度是否在下降)
- DPO 的偏好边际(y_w - y_l 的分数差距)是否在扩大
源码位置
clone 后打开(相对仓库根目录):
docs/nn-decision/rl/rlhf/code/exercise.py