- 示例工程
【免费下载链接】DeepSpeedExamples
Example models using DeepSpeed
本指南聚焦 DeepSpeed-Chat 训练流水线第二步——Direct Preference Optimization(DPO)偏好对齐微调中training_scripts/目录下的全部训练脚本:你将学会如何用一条命令在单卡、单机多卡、多节点场景下启动 DPO 训练,如何通过--model_name_or_path一键切换facebook/opt、EleutherAI/gpt-neo、meta-llama/Llama-2等模型家族,理解 ZeRO 阶段、CPU 卸载、LoRA、梯度检查点等关键参数的作用,并深入 DPO 损失函数的源码实现原理。
一、训练脚本目录总览
在 applications/DeepSpeed-Chat/training/step2_dpo_finetuning/ 下,training_scripts/目录按模型家族与运行规模组织脚本,其结构为:
training_scripts/ ├── llama2/ │ ├── run_llama2_7b.sh # Llama-2-7B 全参数 DPO │ └── run_llama2_7b_lora.sh # Llama-2-7B LoRA 高效 DPO └── opt/ ├── multi_node/ │ └── run_350m.sh # 多节点分布式 ├── single_gpu/ │ └── run_350m.sh # 单 GPU └── single_node/ ├── run_350m.sh # 单机多卡 └── sweep/ ├── run_step2_sweep.sh # 超参扫描入口 ├── run_single.sh # 单次运行封装 └── README.md如 training_scripts/README.md 所述:每个文件夹中的 bash 脚本默认都以 "facebook/opt" 家族为示例。所有脚本最终都调用同一个入口 main.py,区别仅在于传入的命令行参数组合,因此理解一个脚本即可掌握整套脚本的用法。
二、DPO 微调简介:为什么训练参数与 Reward Model 微调几乎相同
DPO(Direct Preference Optimization)是一种直接偏好学习算法:它不再显式训练奖励模型、再用强化学习(RLHF 第三步)优化策略,而是利用奖励模型的特定参数化形式,在闭式解中提取对应的最优策略,通过一个简单的分类损失让语言模型直接与人类偏好对齐,避开了 RLHF 的复杂性与不稳定性。
正如 DPO 论文标题所言"Your Language Model is Secretly a Reward Model"——由于语言模型本身就蕴含奖励建模能力,DPO 的训练参数与训练流程绝大部分与 step2 "Reward Model (RM) finetuning" 相同(这也是两个 step 目录脚本结构几乎一致的原因)。训练完成后,你会得到一个已经与人类偏好对齐的语言模型。这一背景在 step2 DPO README 中有完整说明。
三、快速上手:OPT-350m 单机训练
training_scripts/opt/single_node/run_350m.sh 是官方推荐的入门脚本,在 step2 目录下直接执行:
bash training_scripts/opt/single_node/run_350m.sh脚本接受两个位置参数:OUTPUT(输出目录,默认./output)与ZERO_STAGE(默认0)。脚本核心命令如下:
deepspeed main.py \ --data_path Dahoas/rm-static Dahoas/full-hh-rlhf Dahoas/synthetic-instruct-gptj-pairwise yitingxie/rlhf-reward-datasets \ --data_split 2,4,4 \ --model_name_or_path facebook/opt-350m \ --per_device_train_batch_size 4 \ --per_device_eval_batch_size 4 \ --max_seq_len 512 \ --learning_rate 5e-5 \ --weight_decay 0.1 \ --num_train_epochs 1 \ --dropout 0.0 \ --gradient_accumulation_steps 1 \ --lr_scheduler_type cosine \ --num_warmup_steps 0 \ --seed 1234 \ --zero_stage $ZERO_STAGE \ --deepspeed \ --output_dir $OUTPUT \ &> $OUTPUT/training.log要点解读:
- 数据:
--data_path可同时传入多个偏好数据集(Dahoas/rm-static、Dahoas/full-hh-rlhf、Dahoas/synthetic-instruct-gptj-pairwise、yitingxie/rlhf-reward-datasets),多个数据集会被混合拼接;--data_split 2,4,4表示三个训练阶段(SFT/RM/PPO)按 2:4:4 划分同一份数据,DPO 使用其中的 phase 2 部分,与 main.py 中train_phase = 2的设定 对应。 - 精度与硬件:默认 ZeRO Stage 0(
--zero_stage 0),OPT-350m 规模下单机即可训练;--dtype默认fp16(可切bf16)。 - 日志:所有输出重定向到
$OUTPUT/training.log,便于后台观察训练进度。
四、切换模型:--model_name_or_path 就是唯一开关
这是 training_scripts/README.md 的核心指导:所有脚本都默认使用facebook/opt-350m,想换成其他模型,只需替换--model_name_or_path参数。例如把 OPT-350m 换成 EleutherAI 的 GPT-Neo 系列:
# 原命令 --model_name_or_path facebook/opt-350m # 替换为 --model_name_or_path EleutherAI/gpt-neo-125m由于脚本内部通过 Transformers 的AutoModelForCausalLM与load_hf_tokenizer自动加载模型和分词器(见 main.py 中模型创建与分词器加载),因此任何 Hugging Face 上的因果语言模型(causal LM)原则上都可以通过这一个参数接入,例如将EleutherAI/gpt-j-6b作为目标模型。关于项目完整支持(含 llama2 7B/13B、llama2-70b 等)的模型清单,请查阅 DeepSpeed-Chat 支持的模型列表(即训练脚本 README 中所指 "our landing page")。
更换模型时需要同步考虑两点:一是显存规模,二是学习率——大模型通常需要更小的学习率(见下文 Llama-2 脚本中9.65e-6的取值)。
五、Llama-2-7B:全参数训练与 LoRA 高效微调
5.1 全参数训练
training_scripts/llama2/run_llama2_7b.sh 展示了 7B 级模型的配置思路,默认输出目录为./output_step2_llama_7b_epoch1_lr9.65e-6,默认ZERO_STAGE=3:
deepspeed main.py \ --data_path Dahoas/rm-static \ --data_split 2,4,4 \ --model_name_or_path meta-llama/Llama-2-7b-hf \ --per_device_train_batch_size 8 \ --per_device_eval_batch_size 8 \ --max_seq_len 512 \ --learning_rate 9.65e-6 \ --weight_decay 0.1 \ --num_train_epochs 1 \ --gradient_accumulation_steps 1 \ --lr_scheduler_type cosine \ --num_warmup_steps 0 \ --seed 1234 \ --gradient_checkpointing \ --zero_stage $ZERO_STAGE \ --deepspeed \ --offload \ --output_dir $OUTPUT \ &> $OUTPUT/training.log与 OPT-350m 脚本相比,Llama-2 脚本的关键差异:
--zero_stage 3+--offload:ZeRO-3 将模型参数、梯度、优化器状态分片到所有 GPU,--offload再把优化器状态等卸载到 CPU(对应DeepSpeedCPUAdam优化器路径,见 main.py 优化器选择),使 7B 模型在有限显存下可训练。--gradient_checkpointing:以少量计算换取显存,进一步降低激活值占用。--learning_rate 9.65e-6:远低于 OPT-350m 的5e-5,符合大模型 DPO 微调常见做法。
5.2 LoRA 高效微调
training_scripts/llama2/run_llama2_7b_lora.sh 在上一脚本基础上叠加 LoRA 参数:
--lora_dim 128 \ --lora_module_name "layers." \对应 main.py 的 LoRA 分支:当--lora_dim > 0时,调用convert_linear_layer_to_lora将指定模块范围内的线性层替换为 LoRA 低秩分解结构;配合--only_optimize_lora可以只优化 LoRA 参数(冻结基座模型),配合--lora_learning_rate(默认5e-4)单独设置 LoRA 学习率。训练结束保存时,main.py 会调用convert_lora_to_linear_layer将 LoRA 权重合并回原模型,再以标准 HF 格式保存。
六、多节点与单卡场景
多节点:training_scripts/opt/multi_node/run_350m.sh 与单节点脚本参数一致,仅将per_device_train_batch_size降到2(多卡场景下总 batch 由 world size 放大),配合 DeepSpeed 的分布式启动方式运行。
单卡:training_scripts/opt/single_gpu/run_350m.sh 是最精简的示例,通过deepspeed --num_gpus 1 main.py显式限定单 GPU,并将--gradient_accumulation_steps提升到4以模拟较大 batch,同时开启 TensorBoard 记录:
deepspeed --num_gpus 1 main.py --model_name_or_path facebook/opt-350m \ --weight_decay 0.1 --dropout 0.0 --gradient_accumulation_steps 4 --zero_stage $ZERO_STAGE \ --enable_tensorboard \ --tensorboard_path $OUTPUT \ --deepspeed --output_dir $OUTPUT &> $OUTPUT/training.log其中--enable_tensorboard/--tensorboard_path对应 main.py 的 TensorBoard 日志参数,默认路径为step2_tensorboard,供后续用 TensorBoard 观察损失曲线。
七、超参扫描:sweep 脚本自动化跑批
DPO 脚本还内置了一套超参扫描工具,见 training_scripts/opt/single_node/sweep/。其 README.md 说明:当前扫描覆盖ZeRO Stage ∈ {2, 3}与Offload ∈ {True, False}共 4 种组合。
入口脚本 run_step2_sweep.sh 双层循环生成 4 个组合,逐个调用 run_single.sh:
for z in {2..3} do for offload in true false do cmd="bash training_scripts/opt/single_node/sweep/run_single.sh \ ${z} ${offload} z${z}_offload_${offload}" $cmd pkill -9 python sleep 60 done done每次跑完一组后脚本会pkill -9 python并休眠 60 秒清理显存,run_single.sh则把 ZeRO 阶段与 Offload 开关拼进deepspeed main.py命令(Offload 为 true 时追加--offload)。该框架可以很容易地扩展扫描维度(学习率、weight decay 等),只需在run_single.sh中追加参数变量即可。运行扫描只需一条命令(在 step2 目录下):
bash training_scripts/opt/single_node/sweep/run_step2_sweep.sh八、main.py 核心参数全解
所有脚本共享 main.py 的parse_args中定义的参数体系,下表按功能分组整理关键参数:
| 类别 | 参数 | 默认值 | 说明 |
|---|---|---|---|
| 数据 | --data_path | Dahoas/rm-static | 训练数据路径,支持多个数据集混合 |
| 数据 | --data_split | 2,4,4 | 三阶段数据划分比例,DPO 取 phase 2 |
| 数据 | --data_output_path | /tmp/data_files/ | 预处理缓存(shuffle index、tokenized .pt 文件)存放位置,建议放在节点本地存储 |
| 模型 | --model_name_or_path | 必填 | 预训练模型路径或 HF 模型 ID |
| 模型 | --max_seq_len | 512 | 最大序列长度 |
| 模型 | --dropout | None | 覆盖模型默认 dropout,脚本中常用0.0 |
| 训练 | --per_device_train_batch_size | 16 | 每设备训练 batch |
| 训练 | --per_device_eval_batch_size | 16 | 每设备评估 batch |
| 训练 | --num_train_epochs | 1 | 训练轮数 |
| 训练 | --gradient_accumulation_steps | 1 | 梯度累积步数 |
| 优化 | --learning_rate | 1e-3 | 初始学习率(OPT 脚本用5e-5,Llama-2 用9.65e-6) |
| 优化 | --weight_decay | 0.0 | 权重衰减,脚本常用0.1 |
| 优化 | --lr_scheduler_type | cosine | 调度器类型:linear/cosine/cosine_with_restarts/polynomial/constant/constant_with_warmup |
| 优化 | --num_warmup_steps | 0 | 预热步数 |
| DPO 损失 | --beta | 1e-1 | DPO 损失温度参数,典型取值 0.1~0.5;beta 趋近 0 时等价于忽略参考模型 |
| DPO 损失 | --label_smoothing | 0.0 | 标签平滑,假设偏好存在噪声(以该概率翻转) |
| DeepSpeed | --zero_stage | 0 | ZeRO 优化阶段(Actor 模型) |
| DeepSpeed | --offload | 关闭 | 启用 ZeRO CPU Offload |
| DeepSpeed | --offload_reference_model | 关闭 | 为参考模型单独启用 Offload |
| DeepSpeed | --dtype | fp16 | 训练精度,可选fp16/bf16 |
| 显存 | --gradient_checkpointing | 关闭 | 梯度检查点 |
| LoRA | --lora_dim | 0 | 大于 0 时启用 LoRA 高效训练 |
| LoRA | --lora_module_name | decoder.layers. | LoRA 作用模块范围 |
| LoRA | --only_optimize_lora | 关闭 | 只优化 LoRA 参数 |
| LoRA | --lora_learning_rate | 5e-4 | LoRA 学习率 |
| 精度 | --compute_fp32_loss | 关闭 | 低精度(fp16/bf16)下用 fp32 计算损失 |
| 日志 | --enable_tensorboard | 关闭 | 开启 TensorBoard |
| 日志 | --tensorboard_path | step2_tensorboard | TensorBoard 日志目录 |
| 其他 | --seed | 1234 | 随机种子,保证可复现 |
| 其他 | --local_rank | -1 | 分布式训练 local_rank,-1 表示单机 |
两个需要展开的 DPO 专属参数(其含义直接对应损失函数源码):
--beta(温度参数):控制对参考模型(未对齐的初始策略)偏离的惩罚强度。beta越大,模型越不偏离参考模型;beta -> 0时损失退化为仅最大化 chosen 与 rejected 的对数概率差,等价于忽略参考模型。--label_smoothing:用于处理偏好标签噪声——假设偏好以该概率被翻转,对应损失中logsigmoid(logits) * (1 - label_smoothing)与logsigmoid(-logits) * label_smoothing两项的加权。
此外注意:main.py会通过deepspeed.add_config_arguments(parser)挂接 DeepSpeed 的通用配置参数,因此还可在命令行直接传入--deepspeed_config等标准 DeepSpeed 参数。
九、源码层面:DPO 损失是如何计算的
理解训练脚本背后的计算逻辑,能帮助你正确调参。DPO 训练需要同时前向两个模型——被训练的策略模型与冻结的参考模型,main.py 中的ref_model构建逻辑 显示:参考模型与 Actor 使用同一权重初始化,且当zero_stage != 3时参考模型以 ZeRO-0 加载(假设显存足够容纳),只有 ZeRO-3 场景才为参考模型复用分片配置,并支持--offload_reference_model将参考模型卸载到 CPU。
每次训练 step 的核心流程(main.py 训练循环):
DataCollatorReward(见 dschat/utils/data/data_utils.py 中的实现)将每个样本的 chosen 与 rejected 序列拼接到同一 batch,input_ids前半为 chosen、后半为 rejected;- 对每条样本计算 chosen 与 rejected 首个分叉 token 位置
divergence_ind,将该位置之前的label_mask置 0(只对分歧之后的部分计算对数概率,避免 prompt 前缀抵消); get_batch_logps(main.py L219-L229)通过torch.gather从 logits 的 log_softmax 中取出每个真实 token 的对数概率并按 label_mask 求和,得到序列级对数概率;- 分别对策略模型(
model)与参考模型(ref_model,torch.no_grad())计算chosen_logps、rejected_logps、ref_chosen_logps、ref_rejected_logps; - 按如下公式计算 DPO 损失:
logits = beta * ((chosen_logps - ref_chosen_logps) - (rejected_logps - ref_rejected_logps)) loss = -logsigmoid(logits) * (1 - label_smoothing) - logsigmoid(-logits) * label_smoothing其中beta * (logps - ref_logps)正是隐式奖励的估计,logsigmoid项则让 chosen 隐式奖励高于 rejected,从而驱动策略对齐人类偏好。main.py 注释表明该实现参考了 DPO 开源实现与 Hugging Face TRL 的dpo_trainer。
评估阶段(main.pyevaluation函数)以同样的方式计算验证集损失,并额外输出chosen_rewards与rejected_rewards(即隐式奖励均值),供训练前后对比模型对齐程度。
十、数据集格式:每个样本一对 chosen / rejected
由于 DPO 把语言模型当作奖励模型使用,其数据集格式与 Reward Model 微调完全一致(见 step2 DPO README 的 Datasets 小节):同一输入 prompt 对应两条输出——一条 "chosen"(被人类偏好)与一条 "rejected"(被拒绝)。训练时模型的目标就是增大 chosen 序列的相对概率、压低 rejected 序列的相对概率。仓库默认使用的Dahoas/rm-static、Dahoas/full-hh-rlhf等开源偏好数据集均符合该格式。
十一、训练后评估与模型输出
DPO 训练产出的 checkpoint本质上就是一个对齐后的语言模型,因此可以像 step1 "Supervised Finetuning" 那样直接评估(如做生成测试、下游任务评测)。模型保存逻辑见 main.py 保存部分:默认以标准 HF 格式保存(save_hf_format),而当--zero_stage 3时因每个 GPU 只持有模型分片,会额外调用save_zero_three_model完成 ZeRO-3 的权重聚合保存。
总结:如何选择训练脚本
| 你的场景 | 推荐脚本 |
|---|---|
| 入门验证 / 单机多卡 | training_scripts/opt/single_node/run_350m.sh |
| 单 GPU 显存受限 | training_scripts/opt/single_gpu/run_350m.sh(配合--gradient_accumulation_steps) |
| 多节点集群 | training_scripts/opt/multi_node/run_350m.sh |
| 7B 级模型全参数 | training_scripts/llama2/run_llama2_7b.sh(ZeRO-3 + offload) |
| 7B 级模型显存紧张 | training_scripts/llama2/run_llama2_7b_lora.sh(LoRA) |
| 自动跑多组配置 | training_scripts/opt/single_node/sweep/run_step2_sweep.sh |
所有脚本只需修改--model_name_or_path即可切换到其他因果语言模型;动手前记得同步评估显存规模并相应调整 ZeRO 阶段、Offload、batch size 与学习率。
- 示例工程
【免费下载链接】DeepSpeedExamples
Example models using DeepSpeed
相关推荐
DeepSpeed-Chat Step 2 奖励模型微调训练脚本实战:模型替换、参数全解与打分原理
DeepSpeed Chat Step 2 奖励模型微调训练脚本实战:模型替换、参数全解与打分原理 本文以 step2_reward_model_finetun
示例工程DeepSpeed-Chat 第三阶段 RLHF 微调实战:从训练脚本到混合引擎的原理与参数解析
DeepSpeed Chat 第三阶段 RLHF 微调实战:从训练脚本到混合引擎的原理与参数解析 导读 本文以 DeepSpeedExamples 仓库中 st
示例工程ROCm 安装向导中的 GPU Selector 深度解析:Instinct / Radeon / Ryzen 显卡与 gfx 架构映射全指南
ROCm 安装向导中的 GPU Selector 深度解析:Instinct / Radeon / Ryzen 显卡与 gfx 架构映射全指南 本文聚焦 AMD
示例工程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考