Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

直接偏好优化:模型训练

上一章,我们构建了 DPO 的训练数据:每个样本包含一个提示词、一个接受答案和一个拒绝答案。现在,我们用这些数据对 SFT 模型进行直接偏好优化。

直接偏好优化

直接偏好优化的核心思想:让模型给接受答案打更高的分,给拒绝答案打更低的分。

序列对数概率

如何衡量模型对一个答案的打分?用序列对数概率(Sequence Log Probability):

log⁡P(y∣x)=∑t=1Tlog⁡P(yt∣x,y<t)\log P(y|x) = \sum_{t=1}^{T} \log P(y_t | x, y_{<t})

这里:

  • xx:提示词;

  • yy:答案,yty_t 是答案的第 tt 个词元,y<ty_{<t} 是它前面的所有词元;

  • TT:答案的词元数量。

也就是在提示词 xx 的条件下,模型生成答案中每个词元的对数概率之和。这个值越大,说明模型越“喜欢”这个答案。

DPO 损失函数

DPO 的损失函数基于训练模型和参考模型的对数概率比:

L=−log⁡σ(β(log⁡πθ(yc∣x)πref(yc∣x)−log⁡πθ(yr∣x)πref(yr∣x)))\mathcal{L} = -\log \sigma\left(\beta \left(\log \frac{\pi_\theta(y_c|x)}{\pi_{ref}(y_c|x)} - \log \frac{\pi_\theta(y_r|x)}{\pi_{ref}(y_r|x)} \right) \right)

这里:

  • πθ\pi_\theta:训练模型(Policy);

  • πref\pi_{ref}:参考模型(Reference),通常是 SFT 后的模型,训练时参数冻结;

  • ycy_c:接受答案(Chosen);

  • yry_r:拒绝答案(Rejected);

  • β\beta:系数,控制模型偏离参考模型的程度;

  • σ\sigma:Sigmoid 函数。

损失函数的目标:让 πθ\pi_\theta 给 ycy_c 的概率比 πref\pi_{ref} 给的更高,给 yry_r 的概率比 πref\pi_{ref} 给的更低。

参考模型

如果没有参考模型,训练模型可能会走极端:给接受答案无限高的概率,给拒绝答案无限低的概率,导致生成能力下降。参考模型相当于一个锚点,防止模型偏离太远。

数据集

SFT 数据集

和第 16 章相同的 SFT 数据集 SFTDataset,DPO 数据集在它的基础上扩展。

DPO 数据集

DPO 数据集 DPODataset 继承自 SFT 数据集,处理(提示词,接受答案,拒绝答案)三元组:

  • 将每个三元组拆成两个:(提示词,接受答案)和(提示词,拒绝答案);

  • 分别将两组数据编码成特征和标签,并添加掩码;

  • 返回六元组:(接受特征,接受标签,接受掩码,拒绝特征,拒绝标签,拒绝掩码)。

损失函数

DPO 二元交叉熵损失函数

DPO 损失函数 DPOLoss 实现上面的损失,也就是对边际取 Sigmoid 之后的二元交叉熵:

  • 计算差值:

pdiff=policychosen−policyrejectedp_{diff} = policy_{chosen} - policy_{rejected}
rdiff=referencechosen−referencerejectedr_{diff} = reference_{chosen} - reference_{rejected}
  • 计算边际:

margin=β⋅(pdiff−rdiff)margin = \beta \cdot (p_{diff} - r_{diff})
  • Sigmoid:

sigmoid=11+e−marginsigmoid = \frac{1}{1 + e^{- margin}}
  • 损失:返回 −log⁡(sigmoid)-\log(sigmoid) 的均值。

这里:

  • policychosenpolicy_{chosen}、policyrejectedpolicy_{rejected}:训练模型对接受答案、拒绝答案的序列对数概率;

  • referencechosenreference_{chosen}、referencerejectedreference_{rejected}:参考模型对接受答案、拒绝答案的序列对数概率;

  • β\beta:系数,对应代码中的 beta,默认值为 0.1。

训练器

DPO 训练器

DPO 训练器 DPOTrainer 同样继承自 GPT 训练器 GPTTrainer,有以下主要区别:

  • 双模型:DPO 训练器不仅有当前训练的模型 model,还有一个参考模型 reference。参考模型的参数冻结,不参与训练;

  • _sequence_log_prob(序列对数概率函数):计算模型对每组(特征,标签,掩码)的对数概率(只计算掩码为 1 的部分);

  • train(训练函数):同时计算训练模型和参考模型对接受答案、拒绝答案的打分,传给 DPO 损失函数。

💡 提示:参考模型和训练模型在训练开始时参数完全相同。训练过程中,训练模型的参数不断更新,参考模型的参数保持不变,当作对照组。注意训练模型处于训练模式(启用随机失活),参考模型处于推理模式(关闭随机失活),所以即使参数相同,两者给出的打分也不会完全一样。

超参数

学习率

偏好优化阶段通常使用一个比 SFT 阶段更小的学习率。本章取 0.000001,是 SFT 阶段的百分之一。

模型参数

其他模型参数需要保持和预训练阶段一致。

建模

加载模型

创建两个模型:

  • model:训练模型;

  • reference:参考模型。

两个模型都加载 SFT 模型参数。

加载 DPO 数据

训练

epoch 1 step 100/460 loss 0.6913 lr 0.000001
epoch 1 step 200/460 loss 0.6926 lr 0.000001
epoch 1 step 300/460 loss 0.6761 lr 0.000000
epoch 1 step 400/460 loss 0.6767 lr 0.000000
epoch 1 saved DPO model to ../../../../data/tinyshakespeare-dpo.npz

推理

ROMEO:

HORTENSIO:
Villor, will I come: did a hear,
Not our drow supposessiance, that you married haste,
Like a joy a Jack for joy her.

First Servingman:
I do go of year.

PITONES:
Hereford thy country.

PRUTUS:
Call now.

CoRLIFFORD:

SICINIAN:

BRUTUS:

IS:
N

本章,我们用 DPO 数据集对 SFT 模型进行了直接偏好优化训练:让模型给接受答案打更高的分,给拒绝答案打更低的分;参考模型作为锚点,防止模型偏离太远。

训练损失在 0.68 到 0.72 之间波动,没有明显下降。当训练模型和参考模型给出的打分完全相同时,损失正好等于 ln⁡2≈0.693\ln 2 \approx 0.693,所以这说明训练后的模型相对参考模型变化很小。我们的重点是理解 DPO 的流程,对这个很小的教学模型,不需要期待明显的效果。

至此,我们完成了模型微调,一步步让预训练模型学会和使用者对话:

  • SFT(监督微调):让模型学会回答问题;

  • DPO(直接偏好优化):让模型学会合理地回答问题。

课后练习

  1. 尝试把 DPO 损失函数的 beta 参数从 0.1 改成 0.5 或者 0.01,观察训练结果有什么变化?beta 参数用来控制什么?

  2. 尝试把学习率提高到 0.00001 或者 0.0001,观察训练损失的变化。