什么是DPO训练
更新时间:2024-04-09
DPO(Direct Preference Optimization):
直接偏好优化方法,通过直接优化语言模型来实现对大模型输出的精确把控,不用进行强化学习,也可以准确判断和学习到使用者的偏好,且效果更加突出。
千帆大模型平台已预置DPO模型训练方式,快开启您的模型训练之旅吧~
优势
- 操作更便捷:更容易实施和训练,只需匹配专有数据集和训练模型,即可发布服务。
- 运行更稳定:最大力度避免陷入局部最优,保证训练过程更可信、可靠。
- 效率更明显:与强化学习相比,节省训练步骤,需要的计算资源和数据更少,实现训练轻量化。
- 输出更有效:在情感控制、内容摘要和对话输出等语言任务中,效果优于RLHF。
工作步骤
DPO微调包含以下两个步骤:
-
数据集收集:prompt+chosen+rejected数据集,样例下载。
-
prompt:
- ①单轮对话场景,Prompt字段仅包含每轮对话的提问;
- ②多轮对话场景,Prompt字段包含前几轮对话的问答及最后一轮的提问
- chosen:Prompt字段对应的正偏好回答。
- rejected:Prompt字段对应的负偏好回答。
-
- 优化训练:基于预置或SFT后的大模型,调整部分参数,训练您的偏好大模型。
常见问题
Q:是不是DPO不含奖励模型训练的过程?
A:严格意义上讲,DPO是将奖励模型训练和强化学习融合在同个阶段进行,其目标函数是优化模型参数以最大化奖励的函数。
一、为什么要提出DPO
在之前,我们已经了解到基于人类反馈的强化学习RLHF分为三个阶段:全监督微调(SFT)、奖励模型(RM)、强化学习(PPO)。但是RLHF面临缺陷:RLHF 是一个复杂且经常不稳定的过程,首先拟合反映人类偏好的奖励模型,然后使用强化学习微调大型无监督 LM,以最大化这种估计奖励,而不会偏离原始模型太远。为解决这一问题,提出一个直接偏好优化 (DPO) 的新算法:通过利用奖励函数与最优策略之间的映射关系,证明这个受限的奖励最大化问题可以通过单阶段的策略训练来精确优化,本质上是在人类偏好数据上解决一个分类问题。DPO是稳定的、性能和计算成本轻量级的,无需拟合奖励模型,在微调期间从 LM 中采样,或执行显着的超参数调整。通过实验表明:DPO 进行微调超过了 RLHF 效果,并提高了摘要和单轮对话的响应质量。
二、什么是DPO
DPO,一种基于人类偏好优化语言模型的新方法。与RLHF不同,DPO不依赖于明确的奖励建模或强化学习。它针对与RLHF相同的目标,但提供了一种更简单、更直接的培训方法。
DPO的工作原理:增加偏好样本的对数概率与减小非偏好样本响应的对数概率。它结合了动态加权机制,以避免仅使用概率比目标时遇到的模型退化问题。
DPO依赖于理论上的偏好模型,如Bradley-Terry模型,来测量奖励函数与经验偏好数据的对齐程度。与传统方法不同,传统方法使用偏好模型来训练奖励模型,然后基于该奖励模型训练策略,DPO直接根据策略定义偏好损失。给定一个关于模型响应的人类偏好数据集,DPO可以使用简单的二元交叉熵目标来优化策略,无需在训练过程中明确学习奖励函数或从策略中采样。具体推导见链接1
(1)原RLHF的优化目标:最大化奖励和最小化参考策略的KL散度
(2)DPO优化目标:利用了从奖励函数到最优策略的解析映射,允许直接使用人类偏好数据进行简化的优化过程
该目标增加了对偏好数据的可能性,并减少了非偏好数据��的可能性。这些示例按照隐式奖励模型的评级加权,由�缩放.
DPO重参数化等效于具有隐式奖励函数:
参数模型��的优化等效于在此变量更改下的奖励模型优化。
(3)DPO在干什么?
为了从原理上理解 DPO,分析损失函数的梯度����。 相对于参数 θ 的梯度可以写为:
其中
是由语言模型��和参考模型����隐式定义的奖励函数。直观上,损失函数 ����的梯度增加了偏好�� 的可能性,并降低了非偏好��的可能性。更重要的是,样例的权重是通过: 隐式奖励模型�^�对非偏好的评分高多少来衡量的,即�^�(�,��)−�^�(�,��),按 β 进行缩放,即隐式奖励模型认为策略模型错误的程度。 我们的实验表明了这种加权的重要性,因为没有加权系数的这种方法的简单版本可能会导致语言模型退化。
(4)DPO outline
步骤1)是在构造数据集,通过对同一问题的两种回复的倾向性:chosen or rejected,反映人类偏好。
步骤2)在于优化,具体过程大概是,对于同一个question prompt,模型在两种模型:language/policy model 和 reference model下分别生成,对应chosen 和 rejected label真值标签的生成概率,因此可以获得四种概率值:policy_chosen_logps, policy_rejected_logps, reference_chosen_logps, reference_rejected_logps, 用于DPO loss计算。
1、DPO trainer 期望数据集具有非常特定的格式。 给定两个句子时,模型将被训练为直接优化偏好:那一个句子最相关。
数据集由三部分组成:
prompt
chosen
rejected
可以由prompt 模板: Human: prompt. Assistant: chosen/rejected 构成如下数据:Anthropic/hh-rlhf dataset
2、 预期模型格式
与 PPO 期望 AutoModelForCausalLMWithValueHead 作为值函数相比,DPO 训练器期望 AutoModelForCausalLM 模型。
3、使用 DPOTrainer 源码
有关详细示例,请查看 Examples/scripts/dpo.py 脚本。 在较高级别上,我们需要使用我们希望训练的模型、参考 ref_model 来初始化 DPOTrainer,我们将使用它来计算首选和拒绝响应的隐式奖励,beta 指隐式奖励的超参数, 数据集包含上面列出的 3 个条目。 请注意,模型和 ref_model 需要具有相同的架构(即仅解码器或编码器-解码器)。
dpo_trainer = DPOTrainer( model, model_ref, args=training_args, beta=0.1, train_dataset=train_dataset, tokenizer=tokenizer, )
之后就可以调用:
dpo_trainer.train()
请注意,β 是 DPO 损失的温度参数,通常在 0.1 到 0.5 范围内。 当beta -> 0 ,意味着忽略参考模型。
4、损失函数
给定偏好数据,我们可以根据 Bradley-Terry 模型拟合二元分类器,事实上,DPO 作者通过 Logsigmoid 提出标准化似然的 sigmoid 损失来拟合逻辑回归。
def dpo_loss( self, policy_chosen_logps: torch.FloatTensor, policy_rejected_logps: torch.FloatTensor, reference_chosen_logps: torch.FloatTensor, reference_rejected_logps: torch.FloatTensor, reference_free: bool = False, ) -> Tuple[torch.FloatTensor, torch.FloatTensor, torch.FloatTensor]: """Compute the DPO loss for a batch of policy and reference model log probabilities. Args: policy_chosen_logps: Log probabilities of the policy model for the chosen responses. Shape: (batch_size,) policy_rejected_logps: Log probabilities of the policy model for the rejected responses. Shape: (batch_size,) reference_chosen_logps: Log probabilities of the reference model for the chosen responses. Shape: (batch_size,) reference_rejected_logps: Log probabilities of the reference model for the rejected responses. Shape: (batch_size,) reference_free: If True, we ignore the _provided_ reference model and implicitly use a reference model that assigns equal probability to all responses. Returns: A tuple of three tensors: (losses, chosen_rewards, rejected_rewards). The losses tensor contains the DPO loss for each example in the batch. The chosen_rewards and rejected_rewards tensors contain the rewards for the chosen and rejected responses, respectively. """ pi_logratios = policy_chosen_logps - policy_rejected_logps if reference_free: ref_logratios = 0 else: ref_logratios = reference_chosen_logps - reference_rejected_logps pi_logratios = pi_logratios.to(self.accelerator.device) ref_logratios = ref_logratios.to(self.accelerator.device) logits = pi_logratios - ref_logratios # The beta is a temperature parameter for the DPO loss, typically something in the range of 0.1 to 0.5. # We ignore the reference model as beta -> 0. The label_smoothing parameter encodes our uncertainty about the labels and # calculates a conservative DPO loss. if self.loss_type == "sigmoid": losses = ( -F.logsigmoid(self.beta * logits) * (1 - self.label_smoothing) - F.logsigmoid(-self.beta * logits) * self.label_smoothing ) elif self.loss_type == "hinge": losses = torch.relu(1 - self.beta * logits) elif self.loss_type == "ipo": # eqn (17) of the paper where beta is the regularization parameter for the IPO loss, denoted by tau in the paper. losses = (logits - 1 / (2 * self.beta)) ** 2 elif self.loss_type == "kto_pair": # eqn (7) of the HALOs paper chosen_KL = (policy_chosen_logps - reference_chosen_logps).mean().clamp(min=0) rejected_KL = (policy_rejected_logps - reference_rejected_logps).mean().clamp(min=0) chosen_logratios = policy_chosen_logps - reference_chosen_logps rejected_logratios = policy_rejected_logps - reference_rejected_logps # As described in the KTO report, the KL term for chosen (rejected) is estimated using the rejected (chosen) half. losses = torch.cat( ( 1 - F.sigmoid(self.beta * (chosen_logratios - rejected_KL)), 1 - F.sigmoid(self.beta * (chosen_KL - rejected_logratios)), ), 0, ) else: raise ValueError( f"Unknown loss type: {self.loss_type}. Should be one of ['sigmoid', 'hinge', 'ipo', 'kto_pair']" ) chosen_rewards = ( self.beta * ( policy_chosen_logps.to(self.accelerator.device) - reference_chosen_logps.to(self.accelerator.device) ).detach() ) rejected_rewards = ( self.beta * ( policy_rejected_logps.to(self.accelerator.device) - reference_rejected_logps.to(self.accelerator.device) ).detach() ) return losses, chosen_rewards, rejected_rewards
其他改进的损失函数:
RSO 作者建议在 SLiC 论文中的归一化似然上使用 hinge损失。 DPOTrainer 可以通过 loss_type="hinge" 参数切换到此损失,这种情况下的 beta 是margin的倒数。
IPO 作者对 DPO 算法提供了更深入的理论理解,并识别了过度拟合的问题,并提出了一种替代损失,可以通过训练器的 loss_type="ipo" 参数来使用。
cDPO 是对 DPO 损失的调整,其中我们假设偏好标签有一定的噪声,可以通过 label_smoothing 参数(0 到 0.5 之间)传递到 DPOTrainer,然后使用保守的 DPO 损失。 使用 loss_type="cdpo" 参数给训练器来使用它。
KTO 损失的导出是为了直接最大化 LLM 代的效用,而不是偏好的对数似然。 因此,数据集不一定是偏好,而是期望的完成与不期望的完成。 对于 DPOTrainer 所需的配对偏好数据,请使用训练器的 loss_type="kto_pair" 参数来利用此损失,而对于所需和不需要的数据的更一般情况,请使用尚未实现的 KTOTrainer。
5、指标:在训练和评估时,记录以下奖励指标:
rewards/chosen
: the mean difference between the log probabilities of the policy model and the reference model for the chosen responses scaled by betarewards/rejected
: the mean difference between the log probabilities of the policy model and the reference model for the rejected responses scaled by betarewards/accuracies
: mean of how often the chosen rewards are > than the corresponding rejected rewardsrewards/margins
: the mean difference between the chosen and corresponding rejected rewards
def get_batch_loss_metrics( self, model, batch: Dict[str, Union[List, torch.LongTensor]], train_eval: Literal["train", "eval"] = "train", ): """Compute the DPO loss and other metrics for the given batch of inputs for train or test.""" metrics = {} ( policy_chosen_logps, policy_rejected_logps, policy_chosen_logits, policy_rejected_logits, ) = self.concatenated_forward(model, batch) # if reference_chosen_logps and reference_rejected_logps in batch use them, otherwise use the reference model if "reference_chosen_logps" in batch and "reference_rejected_logps" in batch: reference_chosen_logps = batch["reference_chosen_logps"] reference_rejected_logps = batch["reference_rejected_logps"] else: with torch.no_grad(): if self.ref_model is None: with self.null_ref_context(): ( reference_chosen_logps, reference_rejected_logps, _, _, ) = self.concatenated_forward(self.model, batch) else: ( reference_chosen_logps, reference_rejected_logps, _, _, ) = self.concatenated_forward(self.ref_model, batch) losses, chosen_rewards, rejected_rewards = self.dpo_loss( policy_chosen_logps, policy_rejected_logps, reference_chosen_logps, reference_rejected_logps, ) reward_accuracies = (chosen_rewards > rejected_rewards).float() prefix = "eval_" if train_eval == "eval" else "" metrics[f"{prefix}rewards/chosen"] = chosen_rewards.mean().cpu() metrics[f"{prefix}rewards/rejected"] = rejected_rewards.mean().cpu() metrics[f"{prefix}rewards/accuracies"] = reward_accuracies.mean().cpu() metrics[f"{prefix}rewards/margins"] = (chosen_rewards - rejected_rewards).mean().cpu() metrics[f"{prefix}logps/rejected"] = policy_rejected_logps.detach().mean().cpu() metrics[f"{prefix}logps/chosen"] = policy_chosen_logps.detach().mean().cpu() metrics[f"{prefix}logits/rejected"] = policy_rejected_logits.detach().mean().cpu() metrics[f"{prefix}logits/chosen"] = policy_chosen_logits.detach().mean().cpu() return losses.mean(), metrics
这个公式表示的是一种损失函数,通常用于机器学习中的策略优化问题,特别是在强化学习或者对话生成模型中。这种损失函数的目标是通过最小化损失来优化策略 𝜋𝜃πθ。
我们来逐步解释这个公式:
符号解释
- 𝐿DPO(𝜋𝜃;𝜋ref)LDPO(πθ;πref): DPO损失函数,表示目标策略 𝜋𝜃πθ 和参考策略 𝜋refπref 之间的损失。
- 𝐸(𝑥,𝑦𝑤,𝑦𝑙)∼𝐷 E(x,yw,yl)∼D: 对从数据集 𝐷D 中采样的三元组 (𝑥,𝑦𝑤,𝑦𝑙)(x,yw,yl) 的期望。
- 𝑥x: 输入样本。
- 𝑦𝑤yw: 在给定 𝑥x 的情况下由策略产生的较好的输出。
- 𝑦𝑙yl: 在给定 𝑥x 的情况下由策略产生的较差的输出。
- 𝜋𝜃πθ: 目标策略,参数为 𝜃θ。
- 𝜋refπref: 参考策略。
- 𝜎σ: Sigmoid 函数,定义为 𝜎(𝑧)=1/1+𝑒−𝑧 σ(z)=1+e−z1。
- 𝛽β: 标量系数,用于调整对数比值的权重。
公式解析
公式的整体结构是一个负的期望值,期望值的内容是一个对数和Sigmoid函数的组合。具体步骤如下:
-
对数比值计算:
log(𝜋𝜃(𝑦𝑤∣𝑥)𝜋ref(𝑦𝑤∣𝑥))−log(𝜋𝜃(𝑦𝑙∣𝑥)𝜋ref(𝑦𝑙∣𝑥))log(πref(yw∣x)πθ(yw∣x))−log(πref(yl∣x)πθ(yl∣x))
这个部分计算了目标策略 𝜋𝜃πθ 和参考策略 𝜋refπref 在较好输出 𝑦𝑤yw 和较差输出 𝑦𝑙yl 上的对数比值。
-
加权和:
𝛽log(𝜋𝜃(𝑦𝑤∣𝑥)𝜋ref(𝑦𝑤∣𝑥))−𝛽log(𝜋𝜃(𝑦𝑙∣𝑥)𝜋ref(𝑦𝑙∣𝑥))βlog(πref(yw∣x)πθ(yw∣x))−βlog(πref(yl∣x)πθ(yl∣x))
乘以标量系数 𝛽β 对这些对数比值进行加权。
-
应用Sigmoid函数:
log𝜎(𝛽log(𝜋𝜃(𝑦𝑤∣𝑥)𝜋ref(𝑦𝑤∣𝑥))−𝛽log(𝜋𝜃(𝑦𝑙∣𝑥)𝜋ref(𝑦𝑙∣𝑥)))logσ(βlog(πref(yw∣x)πθ(yw∣x))−βlog(πref(yl∣x)πθ(yl∣x)))
计算结果传递给Sigmoid函数,并取对数。
-
期望值和负号:
−𝐸(𝑥,𝑦𝑤,𝑦𝑙)∼𝐷[…]−E(x,yw,yl)∼D[…]
最后取负号并对所有样本的期望值。
总结
这个损失函数的目的是通过最小化损失 𝐿DPOLDPO 来优化目标策略 𝜋𝜃πθ,使其在给定输入 𝑥x 的情况下,更倾向于产生较好的输出 𝑦𝑤yw,而不是较差的输出 𝑦𝑙yl。通过这种方式,可以逐步提高目标策略的质量。
标签: 大模型和强化学习
为什么要有标量系数调整对数比值的权重
GPT-4o
Poe
在损失函数中引入标量系数 𝛽β 用于调整对数比值的权重有几个重要的原因:
1. 控制更新步长
标量系数 𝛽β 可以控制损失函数对策略更新的敏感度。通过调整 𝛽β,可以改变损失函数对策略参数 𝜃θ 的梯度大小,从而影响策略更新的步长。较大的 𝛽β 会放大对数比值的影响,使策略更新更剧烈;反之,较小的 𝛽β 会使策略更新更平缓。
2. 平衡探索与利用
在强化学习中,探索和利用之间的平衡是一个关键问题。通过调整 𝛽β,可以影响策略的探索行为。较大的 𝛽β 倾向于强化当前策略的行为(利用),而较小的 𝛽β 则可能鼓励更多的探索。
3. 稳定数值计算
对数比值的数值范围可能非常大,直接使用这些值进行计算可能会导致数值不稳定。引入 𝛽β 可以适当缩放这些值,使得在数值计算上更加稳定,减少溢出或者下溢的风险。
4. 调整损失函数的敏感度
在某些情况下,可能希望损失函数对特定样本的敏感度不同。通过调整 𝛽β,可以控制损失函数对不同样本的敏感度。例如,在一些对话生成模型中,可能需要对某些关键对话的输出更加敏感,这时可以通过调整 𝛽β 来实现。
5. 适应不同的任务需求
不同的任务可能对损失函数的要求不同。通过引入可调节的标量系数 𝛽β,可以根据具体任务的需求来调整损失函数,使其更适合当前的任务。例如,在某些任务中,可能需要更强的约束来保证输出质量,这时可以通过增加 𝛽β 来实现。
总结
总的来说,标量系数 𝛽β 提供了一种灵活的机制,可以根据具体需求调整损失函数的特性,从而更好地控制策略优化过程。这种调整不仅有助于提高模型的性能,还能在一定程度上保证训练过程的稳定性。
为什么是对数比值
GPT-4o
Poe
在机器学习和统计学中,对数比值(log-ratio)有几个重要的优势,这些优势使得它在很多损失函数和优化目标中被广泛使用。以下是一些原因:
1. 比率的对称性
对数比值提供了一种对称的衡量方法。例如,对于两个概率 𝑝p 和 𝑞q,比率 𝑝/𝑞p/q 和 𝑞/𝑝q/p 是互为倒数的,但其对数比值 log(𝑝/𝑞)log(p/q) 和 log(𝑞/𝑝)log(q/p) 是相反数。这种对称性在很多应用中非常有用,因为它提供了一种平衡的视角。
2. 稳定数值计算
直接使用概率比值进行计算可能会导致数值不稳定,尤其是在概率值非常接近0或1的情况下。通过取对数,可以将乘法运算转变为加法运算,减小数值范围,从而提高计算的稳定性和精度。
3. 可解释性和线性化
对数比值在解释概率变化方面更加直观。例如,若 log(𝑝/𝑞)>0log(p/q)>0,则意味着 𝑝>𝑞p>q;若 log(𝑝/𝑞)<0log(p/q)<0,则意味着 𝑝<𝑞p<q。此外,对数比值可以将指数增长的关系线性化,这样在优化过程中更容易处理。
4. 避免极端值的影响
在一些优化问题中,直接使用概率比值可能会导致极端值对结果的影响过大。对数比值通过平滑这些极端值,使得优化过程更加稳健。
5. 应用于信息论
对数比值在信息论中的应用广泛。例如,Kullback-Leibler (KL) 散度和交叉熵等度量方法都涉及对数比值。它们用于衡量两个概率分布之间的差异,是很多机器学习算法的基础。
6. 相对变化的度量
对数比值能够很好地量化相对变化。例如,如果 𝜋𝜃(𝑦∣𝑥)πθ(y∣x) 和 𝜋ref(𝑦∣𝑥)πref(y∣x) 是两个概率分布,通过比较它们的对数比值 log(𝜋𝜃(𝑦∣𝑥)/𝜋ref(𝑦∣𝑥))log(πθ(y∣x)/πref(y∣x)),可以更清晰地看到它们的相对变化,而不是绝对变化。
应用到公式中的解释
在具体的公式中:
𝐿DPO(𝜋𝜃;𝜋ref)=−𝐸(𝑥,𝑦𝑤,𝑦𝑙)∼𝐷[log𝜎(𝛽log(𝜋𝜃(𝑦𝑤∣𝑥)𝜋ref(𝑦𝑤∣𝑥))−𝛽log(𝜋𝜃(𝑦𝑙∣𝑥)𝜋ref(𝑦𝑙∣𝑥)))]LDPO(πθ;πref)=−E(x,yw,yl)∼D[logσ(βlog(πref(yw∣x)πθ(yw∣x))−βlog(πref(yl∣x)πθ(yl∣x)))]
这个公式中使用对数比值可以解释为:
- 比较目标策略和参考策略:通过对数比值,可以比较目标策略 𝜋𝜃πθ 和参考策略 𝜋refπref 在不同输出 𝑦𝑤yw 和 𝑦𝑙yl 下的相对变化。
- 放大差异:对数比值可以放大目标策略和参考策略之间的差异,从而更有效地指导优化过程。
- 数值稳定性:对数比值可以帮助避免概率值过小或过大的数值不稳定问题。
总的来说,对数比值的使用提供了稳定、对称和可解释的优化目标,有助于提高模型的训练效率和效果。