十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AReaL 直接偏好优化(DPO)训练完全指南:原理、HH-RLHF 实战与源码解析

AReaL 直接偏好优化(DPO)训练完全指南:原理、HH-RLHF 实战与源码解析 AReaL 直接偏好优化DPO训练完全指南原理、HH-RLHF 实战与源码解析【免费下载链接】AReaLThe RL Bridge for LLM-based Agent Applications. Made Simple Flexible.项目地址: https://gitcode.com/GitHub_Trending/are/AReaLAReaL 是一套面向 LLM Agent 应用的 RL 训练框架其DPOTrainer提供基于 FSDP2 的**直接偏好优化Direct Preference Optimization, DPO**离线对齐能力无需奖励模型、无需在线采样直接在 chosen/rejected 偏好对上完成对齐训练。本文以 AReaL 的 DPO 算法文档为主线结合 DPO 引擎实现、损失函数工具、数据管线 与 单元测试完整讲解 DPO 目标函数的推导直觉、参考模型共卡部署的配置方式、单机/多机训练命令、关键超参数与训练指标解读帮助你在 AReaL 上快速复现一份可用的 DPO 对齐训练。DPO 是什么一种无需奖励模型的离线对齐算法直接偏好优化是一种离线对齐算法直接在人类偏好数据chosen / rejected 对上优化语言模型不需要训练奖励模型也不需要在线 RL 采样。给定偏好数据集 $\mathcal{D} {(x, y_w, y_l)}$$y_w$ 为 chosen 回复、$y_l$ 为 rejected 回复DPO 通过最大化 chosen 相对 rejected 的隐式奖励差距来调整策略。与经典 RLHFPPO相比DPO 的优势体现在三个方面更简单没有奖励模型、没有价值网络、没有在线生成环节数据准备完即可离线训练更稳定只优化一个监督式损失规避了 PPO 中 reward 归一化、GAE、clip 等一系列工程细节的调参成本更高效每个 batch 只需两次前向 一次反向策略模型一次前向 参考模型一次前向反向仅发生在策略上。AReaL 实现了基于FSDP2的 DPO 训练引擎并支持参考模型与策略模型共卡部署colocation将参考模型作为冻结模型与 actor 放在同一批 GPU 上显著节省显存占用这是它在推理成本上优于另起一组参考模型资源设计的关键。核心思想从 KL 正则化 RLHF 到隐式奖励DPO 目标函数DPO 优化的目标函数为$$ \mathcal{L}{\text{DPO}}(\pi\theta; \pi_{\text{ref}}) -\mathbb{E}{(x, y_w, y_l) \sim \mathcal{D}} \left[\log \sigma!\left(\beta \left( \log \frac{\pi\theta(y_w | x)}{\pi_{\text{ref}}(y_w | x)}\log \frac{\pi_\theta(y_l | x)}{\pi_{\text{ref}}(y_l | x)} \right)\right)\right] $$其中 $\pi_\theta$ 为训练中的策略模型$\pi_{\text{ref}}$ 为冻结的参考模型$\beta$ 控制 KL 惩罚强度越大越贴近参考模型。该目标函数可以从带 KL 正则的 RLHF 最优策略的闭式解 Bradley-Terry 偏好模型推导得出奖励函数由策略与参考模型隐式定义因此完全不需要单独训练一个奖励模型。隐式奖励、奖励边际与奖励准确率训练过程中AReaL 以 $r(x, y) \beta (\log \pi_\theta(y|x) - \log \pi_{\text{ref}}(y|x))$ 作为隐式奖励。由此衍生出两个可直接观测的训练信号奖励边际reward margin$r(x, y_w) - r(x, y_l)$。边际大于 0 表示模型当前正确偏好 chosen 回复奖励准确率reward accuracy边际为正的样本对占全体样本对的比例衡量模型偏好判断的正确率。这两个指标由 DPOEngine 在计算损失的同时以torch.no_grad()统计并随训练日志输出。AReaL 源码中的损失实现损失的实际计算落在两个函数上functional.pydpo_pair_logratios从打包packedbatch 中按序列边界聚合策略与参考的逐 token log 概率按 chosen/rejected 两两配对dpo_preference_loss根据loss_type计算偏好损失。sigmoid分支为-logsigmoid(beta * logits)即原始 DPO 损失ipo分支为(logits - 1/(2*beta))^2即 IPO 的平方损失。在 dpo_engine.py 的 compute_dpo_loss 中两类损失的具体拼装方式为sigmoidlogits (policy_logps_chosen - policy_logps_rejected) - (ref_logps_chosen - ref_logps_rejected)IPO先按 completion 长度对每条序列的 logratio 做逐 token 平均即除以completion_lens再取 chosen 与 rejected 的平均 logratio 之差作为logits最后代入平方损失。这一点与 TRL 经原作者确认的实现约定一致保证 $\beta$ 在不同长度序列之间具有可比性。compute_dpo_loss同时会过滤掉空序列对_dpo_valid_pairs检查每个 pair 两条序列长度均非 0若整批都是空对则记录空统计并返回零损失避免数值污染。运行示例在 HH-RLHF 上跑通 DPOAReaL 仓库在examples/alignment/下提供了开箱即用的 DPO 示例数据、脚本、配置齐全入口脚本为 hhrlhf_dpo.py配置为 hhrlhf_dpo.yaml。训练入口三行代码的 Trainer 封装hhrlhf_dpo.py 展示了 AReaL DPO 训练的最小调用范式from areal import DPOTrainer from areal.api.cli_args import DPOConfig, load_expr_config from areal.dataset import get_custom_dataset from areal.utils.hf_utils import load_hf_tokenizer config, _ load_expr_config(args, DPOConfig) tokenizer load_hf_tokenizer(config.tokenizer_path) train_dataset get_custom_dataset(splitconfig.train_dataset.split, ...) valid_dataset get_custom_dataset(splitconfig.valid_dataset.split, ...) with DPOTrainer(config, train_datasettrain_dataset, valid_datasetvalid_dataset) as trainer: trainer.train()从源码结构看DPOTrainerareal/trainer/dpo_trainer.py负责调度器初始化、actor 与 ref 两个引擎的创建与初始化、DataLoader 构建以及训练/验证循环真正计算损失的是DPOEngine与DPOController。单机训练8 GPUpython3 examples/alignment/hhrlhf_dpo.py \ --config examples/alignment/hhrlhf_dpo.yaml \ scheduler.typelocal--config指定 YAML 配置文件命令行后缀scheduler.typelocal以覆盖式参数覆盖配置中的调度器类型AReaL 的配置系统支持这种点路径覆盖语法。配置文件逐段解读hhrlhf_dpo.yaml 的完整结构如下注释为理解辅助实际文件中以 YAML 形式给出experiment_name: hhrlhf-dpo trial_name: trial0 seed: 1 enable_offload: false total_train_epochs: 1 tokenizer_path: ${actor.path} # 复用 actor 的模型路径作为分词器路径 cluster: n_nodes: 1 # 单机 n_gpus_per_node: 8 fileroot: /tmp/areal/experiments # 实验产物checkpoint、日志根目录 name_resolve: type: nfs # 多机场景下通过 NFS 做节点名解析 nfs_record_root: /tmp/areal/name_resolve scheduler: type: local # 本地进程调度多机改为 ray actor: backend: fsdp:d8p1t1 # FSDP2 后端8 张卡、TP1、PP1 experiment_name: ${experiment_name} trial_name: ${trial_name} path: Qwen/Qwen2.5-7B # 遵循原论文直接在 base 模型上训练 init_from_scratch: false is_critic: false # DPO 要求策略是语言模型不能是 critic beta: 0.1 # KL 惩罚系数 disable_dropout: true # DPO 稳定性所必需禁用 dropout 保证 logp 确定性 gradient_checkpointing: true dtype: bfloat16 mb_spec: max_tokens_per_mb: 4096 # 每个微批最大 token 数 granularity: 2 # DPO 必须为 2chosen rejected 成对调度 optimizer: type: adam lr: 5e-6 weight_decay: 0.01 beta1: 0.9 beta2: 0.999 eps: 1e-8 lr_scheduler_type: cosine warmup_steps_proportion: 0.1 min_lr_ratio: 0.1 gradient_clipping: 10.0 scheduling_spec: # actor 占用的进程资源规格 - task_type: worker port_count: 2 gpu: 1 mem: 32 cmd: python3 -m areal.infra.rpc.rpc_server env_vars: {} ref: backend: ${actor.backend} # 与 actor 相同的 FSDP2 后端 experiment_name: ${experiment_name} trial_name: ${trial_name} path: ${actor.path} # 参考模型与 actor 同源初始化 init_from_scratch: false disable_dropout: true dtype: ${actor.dtype} mb_spec: max_tokens_per_mb: 10240 optimizer: null # 冻结模型不配置优化器 scheduling_strategy: type: colocation # 共卡部署 target: actor # 与 actor 共用同一批 GPU节省显存 scheduling_spec: ${actor.scheduling_spec} train_dataset: batch_size: 64 shuffle: true pin_memory: true num_workers: 4 path: Anthropic/hh-rlhf # HF 数据集路径配置值训练时联网加载 type: dpo # 数据集类型dpo max_length: 2048 valid_dataset: batch_size: 64 pin_memory: true num_workers: 4 path: Anthropic/hh-rlhf type: dpo max_length: 2048 saver: # 保存策略按 epoch 保存 experiment_name: ${experiment_name} trial_name: ${trial_name} fileroot: ${cluster.fileroot} freq_epochs: 1 freq_steps: null freq_secs: null recover: # 恢复策略本次禁用但定义了每小时一次的恢复检查 mode: disabled experiment_name: ${experiment_name} trial_name: ${trial_name} fileroot: ${cluster.fileroot} freq_epochs: 1 freq_steps: null freq_secs: 3600 evaluator: experiment_name: ${experiment_name} trial_name: ${trial_name} fileroot: ${cluster.fileroot} freq_epochs: 1 freq_steps: null freq_secs: null stats_logger: # 指标记录 experiment_name: ${experiment_name} trial_name: ${trial_name} fileroot: ${cluster.fileroot} wandb: mode: disabled其中几个值得特别注意的要点mb_spec.granularity: 2是 DPO 的硬性要求。AReaL 的数据管线会把每个样本展开成两条序列chosen、rejectedgranularity2保证成对的序列在同一个 DP rank 上被一起调度否则 pair 关系会被打散。DPOController在 RPC 调用train_dpo/evaluate_dpo/compute_logp时同样以group_size2传递参数见 dpo_engine.py。ref.optimizer: null表示参考模型完全冻结ref.scheduling_strategy.type: colocationtarget: actor是共卡部署的开关。actor.disable_dropout: true是 DPO 稳定性的前提。DPO 损失依赖精确的 log 概率差若训练中随机 dropout 改变 logp 计算路径隐式奖励会被噪声污染导致训练不稳定。actor.is_critic: false的校验在配置层完成DPOConfig.__post_init__会显式拒绝is_critictrue的配置并给出报错提示见 cli_args.py。数据集准备token 级最长公共前缀划分 promptget_hhrlhf_dpo_datasetareal/dataset/hhrlhf.py对 HH-RLHF 数据的处理方式非常巧妙它对 chosen/rejected 的原始文本直接分词然后逐 token 比较两条序列取token 级最长公共前缀作为 prompt 边界公共前缀之后的部分标记为响应loss_mask1。为什么可行HH-RLHF 的每个数据对共享完全相同的多轮 prompt 文本只有最后一条 assistant 回复不同因此公共前缀恰好就是 prompt。具体流程为tokenizer.encode(sample[chosen] tokenizer.eos_token)与 rejected 同法编码逐 token 求公共前缀长度prompt_len生成chosen_loss_mask/rejected_loss_maskprompt 部分为 0、响应部分为 1max_length过滤掉任一条序列超长的样本。在 DPOTrainer 的 dpo_modeling_collate_fn 中每个样本被展开成chosen 在前、rejected 在后的两条序列 dict最终在引擎侧按[chosen_0, rejected_0, chosen_1, rejected_1, ...]的顺序打包成 batch——这正是dpo_pair_logratios能以view(-1, 2)方式配对的前提。参考模型的 log 概率无需预计算DPOEngine.compute_logp会在训练过程中在线由 ref 引擎前向得到ref_logprobs并注入输入见 dpo_engine.py。多机训练Ray当单机 8 卡显存不够或需要扩大数据吞吐时改用 Ray 调度并声明集群规模与共享文件系统python3 examples/alignment/hhrlhf_dpo.py \ --config examples/alignment/hhrlhf_dpo.yaml \ cluster.n_nodes2 cluster.n_gpus_per_node8 \ cluster.fileroot/path/to/nfs \ scheduler.typeray多机训练依赖共享文件系统NFS用于 checkpoint 与节点名解析cluster.fileroot需指向所有节点可访问的同一路径cluster.name_resolve.typenfs配合nfs_record_root完成节点间的地址发现。关键参数一览参数默认值说明actor.beta0.1KL 惩罚系数。越大越接近参考模型。典型范围 0.05–0.5。actor.loss_typesigmoid损失变体。sigmoid为原始 DPORafailov et al. 2023ipo使用逐 token 平均的平方损失Azar et al. 2023。actor.optimizer.lr5e-6学习率。DPO 对 LR 敏感建议 5e-7 – 5e-6。actor.disable_dropouttrue禁用 dropout 以确保 log 概率计算确定性。actor.mb_spec.granularity2微批粒度。DPO 必须为 2chosenrejected 成对。ref—参考模型配置必填建议与 actor 同路径初始化并冻结。参数校验细节可在 DPOEngineConfig 中确认beta默认0.1loss_type仅接受sigmoid/ipo两个取值非法值在__post_init__阶段直接抛ValueError避免训练中途才发现配置错误。训练指标解读dpo/ 前缀的五个信号训练与验证过程中会记录以下指标前缀dpo/由 compute_dpo_loss 在torch.no_grad()下按样本对为分母统计dpo/lossDPO 损失对样本对取均值dpo/chosen_rewardchosen 序列的隐式奖励均值dpo/rejected_rewardrejected 序列的隐式奖励均值dpo/reward_marginchosen_reward - rejected_reward的均值训练中应持续上升dpo/reward_accuracy边际为正的样本对占比反映模型偏好判断的准确率。仓库自带的训练曲线examples/alignment/dpo_curve.png展示了 5 个子图随训练步数的演化dpo/loss_avg从约 0.8 单调下降并收敛到约 0.4dpo/reward_margin_avg从接近 0 逐步上升到约 0.3dpo/reward_accuracy_avg稳定在 0.7–0.8dpo/rejected_reward_avg下降而dpo/chosen_reward_avg趋稳说明模型在拉大 chosen 与 rejected 之间的偏好差距。从源码看实现细节成对打包、fp64 聚合与空对处理序列打包与逐 token 对齐next-token shiftDPO 引擎的输入是打包packed序列因此 dpo_pair_logratios 需要自己处理下一条 token的对齐将loss_mask向后平移一位得到shifted_maskshifted_mask[:-1] loss_mask[1:]以对齐 next-token 预测的 logp通过index_fill_把每个序列的最后一个位置强制清零——它没有下一条 token绝不能计入损失用repeat_interleave构造seq_ids通过index_add_把逐 token logp 按序列聚合成序列级总和。tests/test_dpo.py中TestDPOLossIntraSequenceShift专门回归验证了这一对齐逻辑如果使用朴素的全局torch.rollchosen 序列末尾的响应 token 会泄漏进相邻序列的 prompt 区域破坏损失计算而序列内平移 末位清零方案杜绝了这种跨序列污染。fp64 聚合避免长序列精度翻转另一个容易被忽视的工程细节是policy_logps与ref_logps的聚合使用fp64 scatter-addtorch.float64。对于 2k token 的长偏好对fp32 累加会累积足够大的舍入误差甚至翻转 log-ratio 的符号——也就是说哪怕模型其实正确偏好了 chosen误差也可能让损失看起来反向。使用 fp64 聚合从根上规避了这类精度问题。空对过滤与测试保障compute_dpo_loss 通过_dpo_valid_pairs检查每个 pair 的两条序列是否都非空空对不会进入损失与统计若整批为空则返回零损失。配套的单测覆盖了损失为标量/有限/非负、chosen 被偏好时损失更低、beta放大损失差异、参考模型偏移作用、prompt token 不参与损失、缺失ref_logprobs报错、IPO 的长度归一化不变量、非法loss_type抛错等场景tests/test_dpo.py可作为验证自己改动或理解语义的参考。参考Rafailov 等2023.Direct Preference Optimization: Your Language Model is Secretly a Reward Model. NeurIPS 2023arXiv:2305.18290Azar 等2023.A General Theoretical Paradigm to Understand Learning from Human FeedbackarXiv:2310.12036IPO 损失来源Anthropic HH-RLHF 数据集训练/验证配置中的path: Anthropic/hh-rlhf即对应数据源AReaL 仓库内关联实现DPO 引擎、DPO Trainer、损失与配对工具、HH-RLHF 数据集、示例配置、示例脚本、DPO 单测【免费下载链接】AReaLThe RL Bridge for LLM-based Agent Applications. Made Simple Flexible.项目地址: https://gitcode.com/GitHub_Trending/are/AReaL创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表