DeepSpeed:混合引擎驱动的 RLHF 训练全链路拆解实战指南
【免费下载链接】DeepSpeedDeepSpeed is a deep learning optimization library that makes distributed training and inference easy, efficient, and effective.项目地址: https://gitcode.com/GitHub_Trending/de/DeepSpeed
在做 DeepSpeed 之前,想训一个 ChatGPT 类模型,得自己用 HuggingFace 手动拼装"推理生成 + 训练反传"两套代码,替 PPO 里四五个模型副本手动倒腾显存,吞吐常常只有硬件能力的 5% 不到。DeepSpeed 的 Hybrid Engine 把训练引擎和推理引擎合进了同一个模型对象里,让 RLHF 训练中的"生成经验"和"梯度更新"能在同一份权重上无缝来回切换,这也是它能用单张消费级 GPU 训 13B 模型的核心原因。
1. 项目全景:它到底在解决什么
DeepSpeed 是微软开源的深度学习优化库,定位是"让分布式训练和推理变得简单、高效"。它的 RLHF 相关能力由三块拼成:训练侧的 ZeRO 显存优化、推理侧的 KV-Cache 与高性能 Transformer 内核、以及把两者统一起来的 Hybrid Engine。整个 RLHF 流水线对齐 InstructGPT 的三步:先用人工标注数据做 SFT(监督微调),再用"好答案/坏答案"对训练一个奖励模型 RW,最后用 PPO 算法拿 RW 的反馈继续微调 actor 模型——可选地叠加 EMA checkpoint 与混合预训练目标。
核心特性一览:
- 一键三阶段训练:单个脚本走完 SFT、奖励模型、PPO 全流程
- Hybrid Engine 双模切换:同一模型对象内训练/推理内核热切换
- ZeRO + LoRA + 张量并行可组合:训练分片与推理切分自动切换
- EMA 与混合训练内置:与 InstructGPT 配方完全一致
- 多数据源抽象与混合:统一格式后再切分到三阶段
想引用其 RLHF 部分,官方建议引用论文 arXiv:2308.01320(DeepSpeed-Chat)。
2. 最小可运行路径:内置测试 10 分钟跑通
最短链路是仓库自带的混合引擎测试:单卡加载 OPT-350M,验证"训练前向 → 推理生成 → 切回训练前向"的完整切换。下面这条 bash 序列做的事是:克隆仓库、安装 deepspeed、用内置配置跑这个最小测试(仓库地址仅在需要 clone 时给出)。
git clone https://gitcode.com/GitHub_Trending/de/DeepSpeed.git && cd DeepSpeed pip install deepspeed>=0.9.0 # 内置最小测试:OPT-350M 单卡,enable_hybrid_engine=True deepspeed --num_gpus 1 tests/hybrid_engine/hybrid_engine_test.py \ --deepspeed_config tests/hybrid_engine/hybrid_engine_config.json跑通后你会看到终端打印模型的 logits 张量及其范数,以及see_memory_usage输出的显存占用统计,说明模型在 train/eval 两种模式间都完成了前向。
这张表回答的是"不同部署形态该传什么参数、预期多久、至少要什么卡"(数据来自官方示例仓库的实测,模型规模越大越需要 ZeRO + Hybrid Engine 组合发力):
| 场景 | 关键参数 | 预期耗时 | 最低硬件 |
|---|---|---|---|
| 单卡试跑 1.3B | --actor-model facebook/opt-1.3b --deployment-type single_gpu | 约 2.2 小时 | 1× 48GB 消费级 GPU |
| 单节点 13B | --actor-model facebook/opt-13b --deployment-type single_node | 约 13.6 小时 | 8× A100-40G |
| 多节点 66B | --actor-model facebook/opt-66b --deployment-type multi_node | 约 9 小时 | 64× A100-80G(8 节点) |
3. 核心机制拆解:训练/推理双模切换与显存管理
Hybrid Engine 的源码只有一个文件为主战场:deepspeed/runtime/hybrid_engine.py。DeepSpeedHybridEngine继承自标准DeepSpeedEngine,初始化时除了建训练引擎,还会调用create_inference_module()给模型里每一层建一个平行的"推理容器"(_inference_containers),并把每层的原始 forward 存进_orig_fwds备查——权重只有一份,跑哪条路径由前向入口决定。
机制一:eval/train 驱动的双模热切换
一句话白话:用两行eval()/train()决定此刻每层前向走推理内核还是训练内核。
源码定位:deepspeed/runtime/hybrid_engine.py 中的eval()与train()重载(约 L448–L504)。
关键片段:这是它相对"调两次 HuggingFace API"的本质区别——切换发生在 forward 函数指针层面:
def eval(self): # 进入推理模式 for orig_module, container in zip(self._orig_modules, self._inference_containers): orig_module.forward = container.module.forward # 每层前向换成推理内核 container.transform_for_inference() # 分配 KV-Cache 等推理侧状态 if self._decode_graphs is not None: self.module.forward = self._decode_graphs # 可选:decode 走 CUDA Graph 缓存 def train(self, mode=True): # 切回训练模式 if mode and len(self._orig_modules) > 0: for container, orig_module, orig_fwd in zip(self._inference_containers, self._orig_modules, self._orig_fwds): container.transform_for_training() # 推理权重还原成训练形态 orig_module.forward = orig_fwd # 换回原始前向 super().train(mode)旁边这张配置表是hybrid_engine配置块的全字段速查(定义在 deepspeed/runtime/config.py 的HybridEngineConfig,L515–L522):
| 配置项 | 类型/默认值 | 作用 |
|---|---|---|
enabled | bool /False | 开启 Hybrid Engine |
max_out_tokens | int /512 | 生成最大长度,决定推理容器 KV-Cache 容量 |
inference_tp_size | int /1 | 推理张量并行规模,>1时按组切分权重 |
release_inference_cache | bool /False | 生成后释放推理 workspace,显存归还训练 |
pin_parameters | bool /True | ZeRO-3 下生成前 gather 全部参数驻留显存 |
tp_gather_partition_size | int /8 | ZeRO-3 + TP 时按每 8 层分组 gather 的步长 |
enable_cuda_graph | bool /False | decode 阶段复用 CUDA Graph 缓存 |
数据流:训练循环里调engine.eval(),各层 forward 被换成推理容器,engine.generate()即可用推理内核吐 token;调engine.train()后原始 forward 恢复,engine.step()走 ZeRO 训练路径。输入输出都是同一个模型的同一份参数。
机制二:推理 workspace 的"借还"管理
一句话白话:训练时把推理缓存的显存还回去,生成前再借回来,避免两个引擎争抢一张卡。
源码定位:deepspeed/runtime/hybrid_engine.py 中的retake_inference_cache()(L179–L190)与generate()尾部(L332–L335)。
关键片段:workspace 是推理侧 KV-Cache 的载体,它的申请/释放就藏在generate()的首尾:
def retake_inference_cache(self): if self._config.hybrid_engine.release_inference_cache: retake_success = self.workspace.retake_workspace() # 先直接申请 if not retake_success: gc.collect() get_accelerator().empty_cache() # 清训练残留后重试 retake_success = self.workspace.retake_workspace() if not retake_success: raise RuntimeError("Unable to retake inference workspace.") # generate() 生成结束后的收尾: if self._config.hybrid_engine.release_inference_cache: self.workspace.release_workspace() # KV-Cache 显存归还,训练阶段独享 gc.collect() get_accelerator().empty_cache()数据流:generate()被调用 → 先retake_inference_cache()借到 workspace → 跑推理内核 → 若开了release_inference_cache就release_workspace()归还。这里还有个配套细节:ZeRO-3 下参数平时分片在 CPU/各卡上,generate()会按tp_gather_partition_size每 8 层一组做GatheredParameters收集,生成完逐层release_memory(),等于显存层面的"临时拼桌、用完拆桌"。
它们的关系是:机制一决定"什么时候"走推理路径,机制二决定走这条路时"有没有足够显存"——一个切换内核,一个管理内存,缺了后者前者在 ZeRO-3 场景下根本起不来。
4. 自定义与扩展:从"会跑"到"会改"
想给自己的 RLHF 算法挂上混合引擎,核心就是四步:initialize(enable_hybrid_engine=True)→eval()生成经验 → 换成自己的 loss →train()后反传。下面这个片段是完整可运行的单迭代模板(配第 2 章那份 JSON 配置,加一个hybrid_engine.enabled: true即可),把第 15 行换成你自己的 prompt 批、第 22 行换成你自己的 PPO/奖励损失即可:
import argparse, torch from transformers import AutoModelForCausalLM import deepspeed model = AutoModelForCausalLM.from_pretrained('facebook/opt-350M').half().cuda() parser = deepspeed.add_config_arguments(argparse.ArgumentParser()) args = parser.parse_args() # --deepspeed_config ds_config.json engine, _, _, _ = deepspeed.initialize(model=model, args=args, enable_hybrid_engine=True) prompt = torch.randint(0, 50272, (2, 16), device='cuda') # 第15行:换成你的 prompt 批 engine.eval() # 切推理模式:推理内核 + KV-Cache exp = engine.generate(input_ids=prompt, max_new_tokens=64) engine.train() # 切训练模式:还原训练内核 loss = compute_ppo_loss(exp, engine) # 第22行:换成你的 PPO/奖励损失 engine.backward(loss) engine.step()跑通后你会看到终端按迭代打印|E2E latency=... |Gather latency=... |Generate time=... |Training time=...的耗时分解(eval()里内置),这是调优时最直接的观测面。
主要扩展入口有三个:
- 🔧配置入口:deepspeed/runtime/config.py 的
HybridEngineConfig,往 JSON 里加hybrid_engine块即可改默认行为; - ⚡模型支持入口:deepspeed/runtime/hybrid_engine.py 的
populate_all_inference_policies()加 deepspeed/module_inject/replace_policy.py,新模型类型在这里注册推理策略; - 🔧CUDA Graph 入口:deepspeed/runtime/hybrid_engine_graph.py,decode 图缓存的构建与校验都在这里。
5. 性能指标与适用边界
这张表回答的是"每个模型规模在 RLHF 最重的 Step 3 要多久、整机成本大概多少"(官方 DeepSpeed-Chat 博客基准,A100 单节点/多节点):
| 模型 | 硬件 | Step 3(PPO)耗时 | 三阶段总计 | Azure 近似成本 |
|---|---|---|---|---|
| OPT-1.3B | 1× A6000-48G | 约 1.2 小时 | 约 2.2 小时 | 未标注 |
| OPT-13B | 8× A100-80G | 10.8 小时(40G 卡为 10.8h) | 13.6 小时 | 约 $290 |
| OPT-30B | 8× A100-80G | 1.85 天 | — | 约 $580 |
| OPT-66B | 64× A100-80G | 7.5 小时 | 约 9 小时 | 约 $1920 |
以上数字的前提(官方强调):135M tokens 训 1 个 epoch,其中 67.5M query tokens(131.9k 条,长 256)+ 67.5M 生成 tokens(131.9k 条回答,长 256),每步最大全局 batch 0.5M tokens(1024 组 query-answer 对)。做成本对比前务必对齐这套规格。
与其他 RLHF 方案的对比数据来自官方博客,非本文实测:单卡生成吞吐领先其他系统 10 倍以上,8 卡端到端相对 Colossal-AI 加速 6–19 倍、相对 HuggingFace DDP 加速 1.4–10.5 倍;单卡可训上限从对方的 1.3B/6.7B 提升到 6.5B/50B。
选型一句话:任务是 RLHF/PPO 且模型是 HuggingFace 格式,选它;只跑推理用 DeepSpeed-Inference 更轻;纯训练不需要生成,标准 DeepSpeed Engine 就够。
6. 选型建议与延伸阅读
- 如果你只想跑通 → 直接跑
tests/hybrid_engine下的单卡测试,10 分钟内验证双模切换链路; - 如果要上生产 → 重点压测 Step 3 的显存峰值,按需开
release_inference_cache与enable_cuda_graph; - 如果要支持新模型 → 先在
replace_policy注册推理策略,否则会自动回退原生generate()(有日志警告)。
仓库内可直接打开的路径:
- deepspeed/runtime/hybrid_engine.py — 混合引擎核心:
generate()、LoRA 融合/还原、ZeRO-3 分区 gather、workspace 借还 - deepspeed/runtime/hybrid_engine_graph.py — decode 阶段 CUDA Graph 缓存
- deepspeed/runtime/config.py —
HybridEngineConfig配置字段定义 - tests/hybrid_engine/hybrid_engine_test.py 与 tests/hybrid_engine/hybrid_engine_config.json — 最小测试与配置样例
- blogs/deepspeed-chat/README.md — RLHF 完整博客与基准数据
已知局限:未找到兼容推理策略的模型类型会自动降级为模型原生generate()路径,加速特性失效。
【免费下载链接】DeepSpeedDeepSpeed is a deep learning optimization library that makes distributed training and inference easy, efficient, and effective.项目地址: https://gitcode.com/GitHub_Trending/de/DeepSpeed
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考