任务、先修与推导范围
本章把第四章BT和第五章KL最优策略连接起来,得到直接偏好优化DPO。读者应能重建奖励差的sigmoid似然,以及正支持参考下的指数倾斜解。我们不会把“无需显式奖励网络”说成没有统计假设;DPO仍使用偏好模型、固定参考与一组已给定比较。项目有两个实现:八参数策略提供可穷举和全参数有限差分参考;完整微型decoder用真实prompt/response路径概率训练全部模型参数。二者共享损失定义,但模型与可计算的评价空间不同。
令参考策略为q,策略为pi,严格正,采用第五章=的约定。由最优策略公式,逐回答奖励可以写成乘,加 。当同一x下比较w与l,公共的 完全相消,奖励差只剩两个相对参考log概率之差。它是关于最优分布和相应奖励的变量关系;用一个有限参数策略替代后优化偏好似然,是建模与估计步骤,不能倒过来声称任意训练中间参数已经达到原奖励目标最优。
代入BT并定义可计算损失
BT胜率为sigmoid奖励差。代入上述关系,定义margin m为乘“winner的logpi减logq”再减“loser的logpi减logq”。每条观测winner胜出的负log概率是softplus(−m)。对固定比较集平均,就是DPO损失。这里q必须与pi在同一个提示、同一回答文本、同一tokenizer与边界规则下评分;把两个模型不同格式的logprob硬相减没有共同事件含义。reference不是另外一条任意字符串,也不是只有一个全局常数。
若以和为中间变量,损失对前者导数为乘(sigmoid(m)减一),对后者为其相反数;reference项不参与求导。再沿各自完整回答的生成图反传到。margin低或符号错误时权重大,已经大正时权重小,这是logistic似然自身给出的动态系数;不能用“永远把winner概率加固定量”替代它。所有pair有共同可微参数时,梯度贡献还需相加,不是分别得到互不影响的最优响应。
算例一:策略等于参考仍有学习信号
令策略起点完全复制参考,则两个相对log比都是零,m=0,损失log2,而sigmoid(m)减一为负二分之一。若=0.5,损失对winner logprob导数为−0.25,对loser为+0.25。损失不为零,梯度也不为零;DPO正是从参考起点开始改变相对偏好。把初始log比零误读为“训练不会动”混淆了函数值与导数。
在有限线性策略中,同一x的logpi差等于logit差,因为softmax公共归一化项相消。取x=2,winner为一、loser为零,梯度只更新这两个动作的偏置与斜率:winner [−0.25,−0.5],loser [0.25,0.5],其余为零。学习率0.2时winner参数增加[0.05,0.1],loser减少同量。独立检查一是八参数中心差分,独立检查二是新margin为正且loss下降。某个pair对部分参数零梯度在此是公式结果,不是断链。
完整decoder不能直接照搬“仅两个输出行”的简化。不同回答在首个不同token后拥有不同上下文,后续softmax归一化项不再共同相消;logprob求和的梯度经过embedding、注意力、位置、前馈和输出头。项目序列版对两个完整回答分别前向、按response+EOS求logprob和,再用自动微分传播损失,检查十七个参数张量全部有有限梯度与实际更新。这个实现关系会在第七章逐API展开。
算例二:相对偏好改善不保证winner绝对概率上升
设参考对winner、loser的概率分别0.4与0.2。当前策略变为0.2与0.02,其余质量分给其他回答。winner相对参考的比率是0.5,loser是0.1,差分log比为log5;=0.5时m约0.804719,BT预测偏好胜率约0.690983,loss约0.369640。相比参考起点log2,DPO改善了,但winner绝对概率从0.4下降到0.2。
这个例子并非数值异常:DPO数据约束的是winner相对loser的参考校正比,不单独固定其他所有回答的质量。若loser降得更快,margin可改善,即使winner也降。独立检查是分别打印两个绝对概率、两个log比和最终margin,避免只看正margin就宣布所有想要的行为都增加。在开放文本里,未比较回答可能吸收概率,因此偏好损失与独立生成评价必须并列保留。
beta、优化与不能保证的单调关系
在推导中连接奖励尺度与偏离参考的代价;在损失实现中它还缩放margin和梯度。若固定隐式奖励函数,较大对应更强KL代价、策略更近参考;但实际训练是有限步拟合数据,改变同时改变梯度尺度与饱和速度,实测KL未必随严格单调。项目有限参考列0.1、0.5、2的验证结果,不拿小网格当定理。序列版固定0.1,不用test挑选它。
为零时margin恒零,策略梯度也零;因此实现应拒绝非正,不能把“零约束”理解为零后自动无限追奖励。真实“无KL奖励最大化”的极限与DPO参数化的训练动力学不是直接把代码系数设零。另一个极端是很大,少量log比变化就令sigmoid饱和,对浮点稳定与学习率都更敏感;项目用softplus避免直接计算负logsigmoid造成溢出。
回答概率必须是一整个事件
对自回归回答,logpi(y|x)等于response每个token连同EOS的log概率之和,提示token作为条件不计分。winner与loser长度不同仍取总和,因为它们分别是完整字符串事件的概率。若改成每token均值,会重新定义分数,不再是原DPO推导中的同一完整回答概率;这种变体可以研究,但必须改名字、说明目标,不能悄悄除长度后继续引用完全相同的理论结论。
总logprob天然随长度变化,但reference校正会抵消一部分两模型共有的长度效应,不保证消除所有偏好数据长度偏差。比如SFT示范包含长回答,DPO偏好简短,EOS行为会受直接梯度影响。序列项目严格保留EOS、首回答token和不同长度mask,并将全回答NLL、每回答目标NLL与greedy短答正确率分别报告;三者不是同一目标,也不会被一个“综合分”隐藏冲突。
有限参考策略在四动作上完整归一化,可以精确算全分布KL;decoder可以对有限长度已给定字符串路径计算有效概率,但无法枚举全部文本。理论上可把生成预算末尾未结束路径登记为abort结果,使有限生成树的概率总质量闭合;本项目没有对这棵巨大树求和。四条固定候选的再归一化只用于候选条件诊断,不能拿它声称已经测得整文本KL或满足某个全局KL约束。
固定参考是目标的一部分
冻结参考需要三件事:数值快照不再改变,参考前向不建梯度图,模型处于确定的评价模式。只用no_grad不会阻止其参数对象被别处更新;只设eval不会冻结参数;只复制变量名可能仍指向同一可变列表或模块。项目有限策略用deepcopy,序列版deepcopy后所有reference参数requires_grad为False并eval,DPO前向在no_grad中读取,训练前后对完整state做哈希比较。
若reference每步跟随当前策略,即使每次都detach,margin每次重回零,日志loss会一直log2,但梯度仍可能持续推动策略,因为只阻断了参考分支的导数;若两分支都连着同一计算图,则它们相消,梯度可能为零。两种错误不同,单看loss均可误判。独立检查应同时断言对象身份、参数冻结、参考hash与策略更新,而非只核对某个标量是否有限。
BUILD THE MECHANISM
实现完整回答评分与DPO
先按response+EOS的mask求路径logprob,再实现固定reference的DPO损失。 下载本课完整项目,在项目根目录运行;只编辑 learner/starter.py,已有构造框架由项目提供。
输入与输出 prompt和padding不计分,回答路径用总和,不按长度平均。
beta>0;reference的路径分数detach,policy winner/loser保留梯度。
# response_scores
def response_scores(logits,targets,mask):
raise NotImplementedError('dpo')
# dpo_loss
def dpo_loss(policy_w,policy_l,reference_w,reference_l,beta):
raise NotImplementedError('dpo')提示 1
先把两种不等长回答写成概率乘积,再取log。
提示 2
margin=beta*((policy_w-policy_l)-(ref_w-ref_l))。
提示 3
margin为0时loss=log2,winner导数负、loser正;reference不应收到梯度。
独立检查 .venv/bin/python -m learner.check --stage dpo --output runs/learner-dpo.json
迁移检验 让EOS概率单独变化,再给prompt增加一token;解释前者影响完整回答概率、后者在固定回答logits夹具中不改评分。
参考实现与解释
# response_scores
def response_scores(logits,targets,mask):
if logits.ndim!=3 or targets.shape!=logits.shape[:2] or mask.shape!=targets.shape:raise ValueError('shape mismatch')
if not torch.isfinite(logits).all():raise ValueError('finite logits required')
safe=targets.masked_fill(~mask,0);return (logits.log_softmax(-1).gather(-1,safe[...,None]).squeeze(-1)*mask).sum(-1)
# dpo_loss
def dpo_loss(policy_w,policy_l,reference_w,reference_l,beta):
if not beta>0:raise ValueError('positive beta')
margin=beta*((policy_w-policy_l)-(reference_w.detach()-reference_l.detach()));return torch.nn.functional.softplus(-margin).mean()本章项目增量
本章交付DPO损失、完整梯度与两个能揭露误解的算例。有限参考训练二百步、0.5、学习率0.2,以固定SFT快照为参考;序列版采用六十步SFT再四十步DPO,0.1,用全参数decoder计算真实回答路径。两个预算和模型不同,不能把它们的数字混成同一性能曲线。下一章会把mask、logprob、冻结与状态保存接成真实可执行的训练工程,并保留所有格式迁移失败。
完整回答比率中的reference校正不能省略。若只使用减再做sigmoid,优化的是另一种偏好分类目标;它没有保持相对固定参考的奖励变量关系。即使训练pair准确率同样上升,也不能用相同推导解释其KL行为。项目同时保存policy与reference两份评分,测试策略等于参考时所有margin必须为零,能发现reference误用、提示不一致或回答mask错配。
理论偏好模型假定观测胜率由某个奖励差的sigmoid给出,真实数据若含循环或标注规则随上下文遗漏而变化,DPO同样继承这层错配。省掉显式BT拟合没有消除BT的表达假设。有限数据又可能在参数空间完全可分,继续优化会把margin增大、损失趋零,而不保证其他响应质量受控。第八章的概率外流正是需要独立生成与绝对路径指标的原因。
求导时只有参考被当常量,仍是固定超参数,不对其反向。若研究学习,需要新目标,否则模型可能仅靠改变尺度降低训练loss而非改变期望行为。当前有限与序列版本各自固定和步数,不在训练中自动调节。二者使用不同不是矛盾,而是两个已分别记录的实验;不能把它们的绝对loss数值直接横向比较优劣。
离线pair的平均也有样本权重。某提示的winner与三个loser比较三次,并不等于拿三个独立用户的同一反馈;这些比较共享winner和上下文,统计相关。当前训练把十八条pair等权作为经验目标,测试仅报告这组指定关系,不从十八条当独立样本推导精确置信区间。若要估计人类总体偏好,需要另有采样与标注模型,不能用本课合成规则代替。
反向传播到EOS尤为重要:短回答优于长回答时,提升结束概率可能是模型满足偏好的主要路径。若漏EOS,却仍声称给完整回答评分,模型可能学会正确开头但不停止;若把所有EOS都当padding删除,则DPO与SFT都缺失结束事件。第七章在有效目标成员层面逐项验证EOS,而不只看字符串前缀是否正确。
import math
softplus=lambda t:max(t,0)+math.log1p(math.exp(-abs(t)))
sigmoid=lambda t:1/(1+math.exp(-t))
beta=.5;q_w,q_l=.4,.2;p_w,p_l=.2,.02
m=beta*((math.log(p_w)-math.log(q_w))-(math.log(p_l)-math.log(q_l)))
assert p_w<q_w and m>0 and softplus(-m)<math.log(2)
print('winner fell',q_w,'to',p_w,'margin',m,'preference probability',sigmoid(m),'loss',softplus(-m))
grad_w=-beta/2;grad_l=beta/2
assert grad_w<0 and grad_l>0
print('at reference loss',math.log(2),'winner/loser logprob gradients',grad_w,grad_l)
TRY IT YOURSELF
练习一:公共常数相消
若winner和loser来自不同x,是否仍可消去beta logZ?为什么?
提示 1
提示 2
对照参考解答
TRY IT YOURSELF
练习二:长度均值变体
winner有2个回答目标、loser有10个。为什么不能未经说明把两者logprob各除长度再用原DPO理论?
提示 1
提示 2
对照参考解答
TRY IT YOURSELF
练习三:参考漂移故障
若每步把reference替换成当前策略并detach,loss一直log2是否证明训练没更新?如何验收?