DPO能用偏好数据调整模型行为,但不能因为损失下降,就宣布“百轮人格已经锁定”。在训练之前,应先回答一个更小的问题:我们累加的究竟是哪几个Token的条件概率?
因果语言模型在位置t输出的分布,预测的是后一个Token。若直接拿同位置的输入标签去gather,代码维度可能完全正确,目标却错了一位。提示词部分如果也算进偏好得分,还会混入与回答选择无关的长度和格式因素。
用四个Token看清shift
TRL DPO文档把样本区分为prompt、chosen和rejected。下面不加载TRL,也不训练模型,只用手写logits演示“下一词对齐后,仅计算回答部分”的约定。response_start指完整token序列里回答的起点。
import math def log_probability(row, target): m = max(row) log_z = m + math.log(sum(math.exp(x - m) for x in row)) return row[target] - log_z def response_logp(logits, tokens, response_start): if len(logits) != len(tokens): raise ValueError('one logit row per input position') if not 1 <= response_start < len(tokens): raise ValueError('response must have a preceding position') return sum(log_probability(logits[pos - 1], tokens[pos]) for pos in range(response_start, len(tokens))) def dpo_loss(pi_chosen, pi_rejected, ref_chosen, ref_rejected, beta): z = beta * ((pi_chosen - pi_rejected) - (ref_chosen - ref_rejected)) return max(0.0, -z) + math.log1p(math.exp(-abs(z))) tokens = [0, 1, 2, 1] # prompt=[0,1], response=[2,1] logits = [[9, 0, 0], [0, 0, 8], [0, 8, 0], [0, 0, 0]] score = response_logp(logits, tokens, 2) assert -0.01 < score < 0 # The first prompt-prediction row is excluded. changed = [[0, 9, 0]] + logits[1:] assert response_logp(changed, tokens, 2) == score assert abs(dpo_loss(0, 0, 0, 0, 0.1) - math.log(2)) < 1e-12 assert dpo_loss(-1, -3, -2, -2, 1) < math.log(2) print('causal shift, prompt mask, DPO scalar loss: PASS')对齐的是训练目标,行为仍需评测
本地验证证明了这个四Token样本的索引关系和标量损失公式,没有梯度更新、分词模板或长会话数据。真实训练还要处理EOS、padding、截断和prompt/chosen/rejected的模板一致性;参考模型与策略模型的有效回答掩码也应一致。
如果目标是保持说话风格,评测集应把“形式一致”与“服从错误设定”分开。用户改变偏好时,Agent应响应新指令,而不是固守旧人格。风格稳定不该覆盖事实更新和授权边界。
IBM Research 10月20日的ALTK-Evolve文章展示的是把学习到的指引加入提示词、没有更新权重的路线。它与DPO是不同干预方式,比较时要明确改的是参数还是上下文,不宜都归成“模型自己学会了记忆”。研究原文