大模型修炼秘籍 第十三章:直指人心——DPO之革新

张开发
2026/5/14 14:35:22 15 分钟阅读
大模型修炼秘籍 第十三章:直指人心——DPO之革新
第十三章直指人心——DPO之革新直指人心DPO跳过奖励直接学。【本章导读】DPODirect Preference Optimization直接偏好优化是一种革命性的对齐方法。它跳过了奖励模型训练直接从偏好数据学习更简单、更稳定、更高效。一、DPO的诞生【RLHF的问题】RLHF虽然有效但存在明显问题问题描述复杂需要训练奖励模型 PPO优化不稳定PPO训练敏感容易崩溃成本高需要多个模型同时运行调参难KL系数、学习率等难以调整【DPO的洞察】DPO的核心洞察可以直接从偏好数据优化策略无需显式训练奖励模型。二、DPO的数学原理【从奖励到策略】在RLHF中最优策略与奖励函数的关系π∗(y∣x)1Z(x)πref(y∣x)exp⁡(1βr(x,y))\pi^*(y|x) \frac{1}{Z(x)} \pi_{ref}(y|x) \exp\left(\frac{1}{\beta}r(x,y)\right)π∗(y∣x)Z(x)1​πref​(y∣x)exp(β1​r(x,y))由此可以反推奖励函数r(x,y)βlog⁡π(y∣x)πref(y∣x)βlog⁡Z(x)r(x,y) \beta \log\frac{\pi(y|x)}{\pi_{ref}(y|x)} \beta \log Z(x)r(x,y)βlogπref​(y∣x)π(y∣x)​βlogZ(x)【DPO损失函数】将奖励代入Bradley-Terry模型得到DPO损失LDPO−E[log⁡σ(βlog⁡π(yw∣x)πref(yw∣x)−βlog⁡π(yl∣x)πref(yl∣x))]L_{DPO} -\mathbb{E}\left[\log\sigma\left(\beta\log\frac{\pi(y_w|x)}{\pi_{ref}(y_w|x)} - \beta\log\frac{\pi(y_l|x)}{\pi_{ref}(y_l|x)}\right)\right]LDPO​−E[logσ(βlogπref​(yw​∣x)π(yw​∣x)​−βlogπref​(yl​∣x)π(yl​∣x)​)]其中ywy_wyw​chosen更好的回答yly_lyl​rejected更差的回答π\piπ当前策略πref\pi_{ref}πref​参考策略SFT模型【直观理解】DPO的目标让模型提高chosen的概率降低rejected的概率同时不要偏离参考模型太远。优化前: P(chosen) 0.3 P(rejected) 0.5 优化后: P(chosen) 0.6 ↑ P(rejected) 0.2 ↓三、DPO vs RLHF【对比】方面RLHFDPO流程SFT → RM → PPOSFT → DPO模型数量3-4个2个训练稳定性不稳定稳定计算成本高低调参难度困难简单效果好相当或更好【流程对比】RLHF流程: SFT模型 → 训练奖励模型 → PPO优化 → 对齐模型 (需要额外训练) (需要多个模型) DPO流程: SFT模型 → 直接偏好优化 → 对齐模型 (一步到位)四、DPO代码实现【完整代码】importtorchimporttorch.nnasnnimporttorch.nn.functionalasFfromtransformersimportAutoModelForCausalLM,AutoTokenizerclassDPOTrainer:def__init__(self,model,ref_model,tokenizer,beta0.1,lr1e-6):self.modelmodel self.ref_modelref_model# 参考模型冻结self.tokenizertokenizer self.betabeta self.optimizertorch.optim.Adam(model.parameters(),lrlr)defget_logprobs(self,model,input_ids,attention_mask):计算序列的对数概率outputsmodel(input_idsinput_ids,attention_maskattention_mask)logitsoutputs.logits[:,:-1,:]# 去掉最后一个tokenlabelsinput_ids[:,1:]# 去掉第一个tokenlog_probsF.log_softmax(logits,dim-1)token_logprobslog_probs.gather(2,labels.unsqueeze(-1)).squeeze(-1)# 只计算回答部分的概率returntoken_logprobs.sum(dim-1)defcompute_dpo_loss(self,batch):计算DPO损失# 编码chosen_idsself.tokenizer(batch[prompt]batch[chosen],return_tensorspt,paddingTrue,truncationTrue).input_ids.to(self.model.device)rejected_idsself.tokenizer(batch[prompt]batch[rejected],return_tensorspt,paddingTrue,truncationTrue).input_ids.to(self.model.device)# 计算当前策略的对数概率policy_chosen_logprobsself.get_logprobs(self.model,chosen_ids,None)policy_rejected_logprobsself.get_logprobs(self.model,rejected_ids,None)# 计算参考策略的对数概率不计算梯度withtorch.no_grad():ref_chosen_logprobsself.get_logprobs(self.ref_model,chosen_ids,None)ref_rejected_logprobsself.get_logprobs(self.ref_model,rejected_ids,None)# 计算对数比率chosen_logratiospolicy_chosen_logprobs-ref_chosen_logprobs rejected_logratiospolicy_rejected_logprobs-ref_rejected_logprobs# DPO损失logitsself.beta*(chosen_logratios-rejected_logratios)loss-F.logsigmoid(logits).mean()returnlossdeftrain_step(self,batch):训练一步self.optimizer.zero_grad()lossself.compute_dpo_loss(batch)loss.backward()self.optimizer.step()returnloss.item()deftrain(self,dataset,epochs1,batch_size4):完整训练fromtorch.utils.dataimportDataLoader dataloaderDataLoader(dataset,batch_sizebatch_size,shuffleTrue)forepochinrange(epochs):total_loss0forbatchindataloader:lossself.train_step(batch)total_lossloss avg_losstotal_loss/len(dataloader)print(fEpoch{epoch1}, Loss:{avg_loss:.4f})【使用示例】fromtransformersimportAutoModelForCausalLM,AutoTokenizer# 加载模型modelAutoModelForCausalLM.from_pretrained(sft_model)ref_modelAutoModelForCausalLM.from_pretrained(sft_model)# 参考模型tokenizerAutoTokenizer.from_pretrained(sft_model)# 冻结参考模型forparaminref_model.parameters():param.requires_gradFalse# 创建训练器trainerDPOTrainer(model,ref_model,tokenizer,beta0.1)# 训练trainer.train(preference_dataset,epochs1)五、DPO的变体【IPOIdentity Preference Optimization】解决DPO可能过拟合的问题LIPOE[((β−1(rw−rl)−log⁡π(yw∣x)π(yl∣x))2]L_{IPO} \mathbb{E}\left[\left((\beta^{-1}(r_w - r_l) - \log\frac{\pi(y_w|x)}{\pi(y_l|x)}\right)^2\right]LIPO​E[((β−1(rw​−rl​)−logπ(yl​∣x)π(yw​∣x)​)2]【KTOKahneman-Tversky Optimization】不需要成对偏好数据只需要标注好或坏LKTOλy⋅(1−σ(β(log⁡π(y∣x)πref(y∣x)−z(x))))L_{KTO} \lambda_y \cdot (1 - \sigma(\beta(\log\frac{\pi(y|x)}{\pi_{ref}(y|x)} - z(x))))LKTO​λy​⋅(1−σ(β(logπref​(y∣x)π(y∣x)​−z(x))))【ORPOOdds Ratio Preference Optimization】将SFT和偏好优化合并为一个目标LORPOLSFTλ⋅LORL_{ORPO} L_{SFT} \lambda \cdot L_{OR}LORPO​LSFT​λ⋅LOR​六、DPO的最佳实践【超参数选择】超参数推荐值说明β\betaβ0.1 - 0.5KL惩罚强度学习率1e-6 ~ 1e-5比SFT更小Batch Size64-256越大越稳定训练轮数1-3轮避免过拟合【数据质量】确保偏好标注一致chosen和rejected差异明显覆盖多种场景【常见问题】问题原因解决方案模型变笨β\betaβ太小增大β\betaβ学习太慢β\betaβ太大减小β\betaβ过拟合训练太久早停、正则化七、本章心法总结【口诀】DPO直指人心跳过奖励模型。简单稳定效率高偏好数据直接学。【要点回顾】要点说明核心思想直接从偏好数据优化策略优势简单、稳定、高效损失函数最大化chosen与rejected的对数概率差超参数β\betaβ控制KL惩罚强度变体IPO、KTO、ORPO等【下一章预告】下一章我们将学习红队测试与安全防御了解如何发现模型的安全漏洞并构建防御机制。

更多文章