显存与计算优化
💾 详细分析 PPO 训练中四个模型的显存占用,并给出一个估算实例(例如 7B 模型,Adam 优化器)。¶
PPO 训练需要同时驻留 Actor、Critic、Reference、Reward Model 四个模型。我们以 7B 参数,FP16 混合精度,Adam 优化器 为例,列出显存账单。
单个模型的理论显存占用
PPO 中四个模型的角色与显存需求
-
Actor:需要训练,存权重 + 梯度 + 优化器状态。若有 LoRA,仅 LoRA 参数可训练,底座权重冻结(无梯度、无优化器状态),大幅降低。
-
Critic:同 Actor,通常需要训练,全参数或部分参数。
-
Reference:纯推理,冻结,仅需 FP16 权重。
-
Reward Model:纯推理,冻结,仅需 FP16 权重。
最小显存消耗方案(LoRA + 共享底座)
假设:
-
Actor 和 Critic 共享底座,使用 LoRA(r=16),可训练参数仅 ~0.2% 总参数,约 14M。
-
底座权重(7B)冻结,FP16 常驻,不需梯度/优化器状态。
-
Ref 和 RM 也共享同一底座,或分别加载(若兼容)。
显存估算:
若不用 LoRA,全参数训练 Actor+Critic,则 Actor 和 Critic 各自需 84 GB,完全不现实。因此实际大规模 RLHF 必须用 LoRA 或 ZeRO 分片。
🖥️ 如何使用 CPU Offload 将 Ref 和 Reward 模型卸载到系统内存,以减少 GPU 显存压力?¶
CPU Offload 是将不参与梯度更新的模型(Ref、RM)的权重和计算迁移到 CPU 内存,只在需要时临时拷贝到 GPU,用完后立即释放。
实现方式:
-
手动管理:用 PyTorch 的
.to('cpu')/.to('cuda')切换模型位置。 -
框架集成:DeepSpeed Inference、Accelerate 的
cpu_offload,可自动将推理模型权重放在 CPU,并在前向时逐层加载到 GPU 执行,然后卸载下一层。
优势:
-
Ref 和 RM 完全冻结,只需推理。它们占用 14 GB × 2 = 28 GB GPU 显存,卸载后这笔显存被释放,可用于增大 Actor/Critic 的 batch size 或序列长度。
-
与训练流水线重叠:Ref/RM 的计算可与 GPU 上的训练异步,进一步隐藏延迟。
开销:
- CPU→GPU 数据传输受 PCIe 带宽限制(~16-32 GB/s),对于大模型会有明显延迟。优化手段:逐层加载,每次只传一层权重,隐藏在前向计算中;或对 Ref/RM 使用量化,减少传输量。
实践技巧:将 Ref 和 RM 部署在单独的 CPU 推理服务(如 llama.cpp),通过网络将 prompt/response 发送过去获取奖励,完全隔离 GPU 资源。
📦 在 RLHF 中,通常对 Actor/Critic 使用 ZeRO 的哪个阶段?为什么对 Ref/Reward 不用 ZeRO?¶
ZeRO 各阶段:
-
ZeRO-1:分片优化器状态。
-
ZeRO-2:分片梯度 + 优化器状态。
-
ZeRO-3:分片参数 + 梯度 + 优化器状态。
典型选择:
-
Actor 和 Critic:通常使用 ZeRO-2(若可全参数微调)或 ZeRO-3(若模型极大)。ZeRO-2 可以显著降低显存(优化器状态与梯度被分片),且通信开销适中。如果使用 LoRA,则无需 ZeRO,因为可训练参数少。
-
Ref 和 RM:不需要 ZeRO,因为它们是冻结的,没有梯度,更没有优化器状态。ZeRO 主要目的是消除训练状态(梯度、优化器)的冗余,不适用于纯推理。对它们直接使用 CPU Offload 或量化更有意义。
为什么不用 ZeRO-3 对 Ref/RM? ZeRO-3 会在前向时收集参数,增加通信开销,对于不需要梯度更新的模型来说得不偿失。
🧩 LoRA 在 RLHF 中的显存节省原理:如果只训练 Adapter,其他模型权重可以 half-precision 并冻结,优化器状态也大幅减少。¶

RLHF 中的实践:Actor 和 Critic 都使用 LoRA。Critic 的价值头(一个线性层)通常全参数训练,因为它参数量小,不影响大局。整体训练所需 GPU 显存可以轻松控制在单张 80GB 卡内,甚至 48GB 卡也可训练 13B 模型。
⚡ 混合精度训练(FP16/BF16)在 RLHF 中如何配置?不同模型是否可以使用不同精度?¶
混合精度训练:前向和反向计算使用 FP16/BF16,主权重副本和优化器状态使用 FP32。
RLHF 中的配置:
-
Actor 和 Critic:使用标准的 PyTorch AMP(
torch.cuda.amp)。对 FP16,需开启损失缩放(GradScaler);对 BF16,动态范围与 FP32 相同,通常无需缩放。 -
Ref 和 RM:可完全以 FP16/BF16 进行推理,不需要 FP32 主副本,因为权重固定。
-
优化器:Adam 的动量、方差状态以 FP32 存储,这与 AMP 的设定一致。
不同模型使用不同精度:
-
完全可以,且推荐。例如 Actor 使用 BF16 训练(避免缩放),Ref 使用 FP16 推理(省显存),RM 甚至可以使用 INT8 量化推理。
-
需注意:如果 Actor 和 Ref 权重在计算 KL 时共享,需保证两者精度一致,否则 KL 计算会产生系统性偏差。通常 Actor 和 Ref 都应基于同一 FP16/BF16 底座。
数值稳定性:对 FP16 训练,需监控梯度是否下溢;BF16 则几乎不出现此问题。对于大型模型,BF16 是首选。
🔁 梯度检查点(Gradient Checkpointing)可以应用于 Actor/Critic,对训练速度和显存的影响?¶
梯度检查点:在前向时不保留部分中间激活,在反向传播时重新计算它们,以时间换空间。
在 Actor/Critic 中的应用:
-
可对 Transformer 的每一层或每隔几层设置检查点。通常对整个底座启用,LoRA 层通常不启用(参数量极小)。
-
显存节省:激活内存通常占总显存的 30–50%,启用检查点可将其降至 10% 左右,显存节省显著,允许使用更大的 batch size 或更长序列。
-
速度影响:需要重新计算部分前向,通常增加约 20–30% 的训练时间。对于 RLHF 这种本身采样慢的任务,这点额外计算是可接受的。
推荐策略:在 Actor 和 Critic 上均启用梯度检查点,尤其是长序列生成时。如果显存仍不足,可配合 CPU Offload 和 ZeRO。
🗄️ 在经验缓冲区中存储什么数据?为什么通常只存储 token id、log prob、value、reward、mask 等,而不保留完整计算图?¶
经验缓冲区 存储一次 rollout 批次的数据,用于后续 PPO 更新。内容包括:
-
input_ids:prompt tokens -
response_ids:Actor 生成的 token 序列 -
log_probs:旧策略下每个 token 的对数概率 -
values:Critic 预测的状态价值(若当时已计算) -
rewards:每个 token 的即时奖励(包含 KL 惩罚和最终 RM 奖励) -
masks:指示哪些 token 是生成的(非 padding,非 prompt),用于损失计算。
为什么不存完整计算图?
-
显存爆炸:计算图保存了从输入到输出的所有中间张量和函数,一个 batch 的计算图可达数十 GB,无法扩展。
-
数据与训练分离:经验数据会被重复使用多个 epoch,且在分布式系统中可能跨节点传输。存贮轻量级的 token 和概率数据极大降低了 I/O 和网络开销。
-
重计算策略:在 PPO 更新时,我们用
response_ids重新输入 Actor 和 Critic,重新计算所需的new_log_probs和values。这增加了一点计算,但换取了巨大的存储和传输灵活性。
实践:缓冲区数据通常序列化存储(如 PyTorch 的 save 或 numpy 数组),使用高效的压缩格式(如 Zstandard)。
📈 分析 RLHF 训练中的显存高峰通常出现在哪个阶段?(往往是 PPO 更新时的反向传播)¶
显存高峰 通常出现在 PPO 更新的反向传播阶段。原因:
- 此时需要同时存在:
- Actor 和 Critic 的模型参数、梯度、优化器状态
- 前向时保存的激活(用于反向重计算)
- 如果共享底座,Actor 和 Critic 的梯度会叠加
-
若使用 GAE 计算,Critic 价值网络的前向激活也会被保留
-
序列长度和 batch size 直接影响激活内存大小。
相比之下,采样阶段 虽然 Actor 推理需要 KV Cache,但因为是自回归生成,激活在每步后被释放,且没有梯度和优化器状态,显存压力小得多。奖励计算阶段同样仅需推理。
监控与缓解:
-
使用
torch.cuda.max_memory_allocated()记录峰值。 -
若高峰超出显存,启用 梯度检查点、ZeRO-3、减小 batch size 或 缩短序列长度,或使用 CPU Offload 将部分优化器状态移至内存。
📏 如何通过减少 PPO 的 mini-batch 大小或序列长度来缓解显存压力?¶
减小 mini-batch 大小:
-
每次训练步的 mini-batch 是处理的总 token 数。减半可近似减半激活内存和梯度同步开销。
-
代价:梯度噪声增加,训练可能震荡。可使用梯度累积来模拟大 batch 更新,保持优化稳定性。
缩短最大生成长度:
-
在线采样时限制
max_new_tokens。序列长度是激活内存的主要贡献者(二次关系)。将上限从 2048 降至 1024,激活可减少约 50%。 -
代价:可能截断完整的回答,导致奖励信号失真(被截断的回答通常质量低)。可对截断的回答给予罚分,并鼓励模型生成更短的回答(通过长度惩罚加入奖励)。
实践策略:在训练初期使用较短的序列和较小的 batch,快速迭代找到稳定超参数;后期放宽限制以提升质量。同时动态监控 KL 散度,防止截断带来的偏好漂移。
🧠 如果 Critic 模型过大,是否可以使用更小的独立初始化 Critic?有什么代价?¶
完全可以。Critic 的任务是预测状态价值,属于回归任务,理论上无需与 Actor 同等的容量。我们可以:
-
使用一个比 Actor 小得多的预训练模型(例如 Actor 7B,Critic 1.5B)作为 Critic。
-
或者使用与 Actor 同架构但仅保留前几层,后接价值头。
优点:
-
显存占用大幅减少,可容纳更大 Actor 或更长序列。
-
训练速度更快。
代价:
-
价值估计准确度可能下降:小模型表达能力弱,可能无法精细区分好坏状态,导致优势函数噪声增大,策略梯度方差升高。
-
对分布偏移更敏感:小 Critic 可能难以快速适应策略变化,加剧 Critic 欠拟合。
实践建议:若选择独立小 Critic,将其初始学习率设得比 Actor 稍高,并考虑更频繁地更新 Critic。也可使用与 Actor 共享底座的较小头,例如底座的前 N 层共享,后几层分叉给 Actor 和 Critic,平衡显存与表达能力。
🤝 比较使用共享底座(Actor/Critic 共享部分层) vs. 独立模型在显存和训练效果上的差异。¶
共享底座:
-
显存节省:底座权重只存一份,显著节省显存(例如节省 14 GB)。
-
但训练时两个任务的梯度会通过共享底座相互干扰。可能出现 Actor 追求高奖励而 Critic 追求精确拟合,导致底座特征漂移,反而降低两者表现。
-
需要精心平衡两个损失的权重,或使用梯度分离策略(如仅允许 Actor 更新底座)。
独立模型:
-
显存翻倍,但优化更稳定。Actor 和 Critic 各自独立学习,不会相互拖累。
-
训练效果通常更好,尤其是当对齐任务复杂、需要强判别能力时。
选择建议:
-
资源极度紧张:共享底座 + LoRA,并对 Critic 价值头用较大学习率。
-
追求最佳效果:独立模型,但 Critic 可适当缩小。
🔄 奖励归一化和优势白化等操作是否会带来额外的显存或计算开销?如何高效实现?¶
奖励归一化(Reward Normalization):
-
对每个 batch 的奖励计算均值和标准差,然后标准化。
-
开销:极小。只需在 CPU 或 GPU 上对一维张量做
mean()和std(),时间复杂度 O(B)O(B),显存开销忽略不计。
优势白化(Advantage Whitening):
- 在计算 GAE 优势后,对整个 batch 的优势减去均值除以标准差(Z-score 标准化)。同样 O(BT)O(BT) 复杂度,无显存压力。
高效实现:
-
使用
torch.std_mean等向量化操作,避免 Python 循环。 -
在分布式环境中,每个 rank 计算局部统计量后通过 AllReduce 得到全局统计量(需要小量通信),然后标准化。通信量极小。
🌐 在多节点训练中,如何利用 NVLink 和 InfiniBand 的带宽特性优化模型并行效率?¶
NVLink(GPU 间直连,高带宽):
-
适用于张量并行(TP):将单层权重切分到同一节点内的多张 GPU,每次前向需要 all-reduce 中间结果。NVLink 的高带宽(900 GB/s)能有效支持这种密集通信。
-
优化:将 TP 组限制在同一个 NVSwitch 域内(通常一个节点内),避免跨节点 TP。
InfiniBand(节点间网络,低延迟):
-
适用于数据并行(DP) 和 ZeRO:节点间需要梯度 AllReduce 或参数收集。InfiniBand RDMA 可提供高带宽(200–400 Gbps)和低延迟。
-
优化:
- 使用梯度累积减少 AllReduce 频率。
- 对 ZeRO-3,适当增大通信桶大小,减少小数据包开销。
- 利用 NCCL 的多树拓扑进行高效 AllReduce。
混合策略:
-
节点内:TP 或 ZeRO 的分片内通信用 NVLink。
-
节点间:DP 梯度同步用 InfiniBand。
-
这种层次化并行能最大化通信效率。
🧬 对于 MoE 架构的模型,RLHF 训练有什么特殊的显存和通信挑战?¶
MoE(Mixture of Experts) 将 FFN 层替换为多个专家,通过门控路由激活一部分专家。
挑战:
-
显存:总参数量极大(如 Mixtral 8×7B 拥有 ~46B 参数),但每次激活仅约 14B。然而所有专家权重必须常驻显存,导致显存需求与总参数量成正比。RLHF 四个模型如果都用 MoE,显存压力巨大。
-
通信:MoE 需要将 token 路由到不同专家,如果专家分布在不同 GPU,会引入大量 all-to-all 通信。在 RLHF 的采样和训练阶段,这个通信量都很大。
-
负载均衡:门控可能偏爱某些专家,导致负载不均,需要辅助负载均衡损失,调参复杂。
缓解:
-
对 RLHF 的 Actor 使用 MoE,而 Ref、RM、Critic 使用稠密小模型。
-
将 MoE 的专家分布限制在同一节点内,利用 NVLink 减轻通信压力。
-
考虑使用专家参数的分片(类似 ZeRO)来降低显存。
📊 当模型规模扩展到 70B 以上,RLHF 训练的最小硬件配置大概需要多少 GPU 显存?¶
典型配置:训练 70B 模型,全参数 PPO 几乎不可行,必须用 LoRA。假设:
-
底座:70B,FP16 权重 ≈ 140 GB。
-
若底座冻结,LoRA 适配器参数量约为 0.1% ≈ 140M,可训练参数微小。
-
Ref 和 RM 可共享同一 70B 底座(推理),需另加 ~140 GB。
-
Critic 可用独立的小模型,或用 LoRA。
最小显存估算:
-
一个 70B 权重:~140 GB
-
Ref/RM 共享底座:不额外加,或部署在 CPU。
-
训练时激活、优化器状态等:~60–100 GB。
-
总计约 200–240 GB。
因此最少需要 4×A100-80G 或 3×H100-80G。若使用 QLoRA(4-bit 底座),甚至可以在 2×A100 上训练 70B 的 RLHF。
🔢 是否可以使用 8-bit 优化器(如 bitsandbytes AdamW)来训练 Actor/Critic?会影响对齐质量吗?¶
可以,但需小心。8-bit Adam 将 FP32 的优化器状态(动量和方差)量化为 8-bit,同时保留 FP16 的主权重,显著降低显存(优化器状态从 8 字节/参数降至 2 字节/参数)。
对对齐质量的影响:
-
在多数微调场景下,8-bit 优化器与 FP32 的表现几乎持平,精度损失很小。
-
然而 RLHF 的奖励信号可能非常微妙,对权重更新精度敏感。8-bit 量化可能引入微小的梯度累积误差,导致最终对齐分数略低于 FP32 优化器(通常在 1% 以内)。
-
推荐:对 Critic 使用 FP32 优化器(因为价值函数对精度更敏感),Actor 可使用 8-bit Adam。如果追求极致显存压缩,两者均可 8-bit,并通过更频繁的评估监控效果。
🚀 推理阶段,如何对 Actor 应用量化(如 GPTQ/AWQ)以加速采样?量化模型是否适合用于 RLHF 训练?¶
加速采样:在采样阶段,使用 GPTQ(4-bit 量化)或 AWQ 压缩 Actor,可以大幅提高推理吞吐,降低显存,从而允许更大的采样 batch 和更长的序列。量化后的模型可直接用 vLLM 或 TensorRT-LLM 部署。
量化模型能否用于 RLHF 训练?
-
不能直接训练:量化模型(如 GPTQ)权重被离散化,不适合直接接收梯度更新。它们更适合作为冻结的推理引擎。
-
变通方案:训练仍使用 FP16/BF16 的 Actor 底座+LoRA。在采样时,将底座权重导出为量化格式,结合 LoRA 适配器(FP16)的融合进行推理。这样既保留了训练精度,又获得了推理加速。部分框架(如 TensorRT-LLM)支持在线加载 LoRA 并进行量化推理。
注意:量化会引入一些噪声,可能略微降低生成质量,需要在速度和精度间权衡。
🔁 如何实现“离线生成、在线训练”的异步 RLHF?将采样后的数据持久化,训练时读取,这样可以解耦资源。¶
实现方案:
-
采样阶段:使用当前 Actor 权重,一次性对大量 prompt 进行采样,生成经验数据(token ids, log probs, rewards 等),将这批数据序列化并保存到共享存储(如 S3、NAS、HDFS)。
-
训练阶段:独立启动训练任务,从存储读取经验数据,进行 PPO 更新。训练完成后,保存新 Actor 权重。
-
循环:再次启动采样任务,使用新权重,覆盖或补充数据池。
优点:
-
采样和训练可以在不同集群、不同时间运行,资源完全解耦。
-
可利用便宜的低优先级 GPU 进行采样,高峰时用高性能 GPU 训练。
挑战:
-
策略滞后:训练时使用的数据是旧策略产生的,需要重要性采样修正(见下一题)。
-
存储开销:经验数据通常需 TB 级别存储,且需设计版本管理。
🔄 异步 RLHF 中,由于采样策略与当前策略不同步,需要做重要性采样修正吗?如何做?¶

额外措施:
-
监控新旧策略的 KL 散度,若过大(如 >0.02),应丢弃该批数据,重新采样。
-
使用 早期停止:如果采样数据过时,其重要性权重方差极大,裁剪可以防止梯度爆炸,但可能损失样本效率。通常设定 KL 阈值触发重采样。
高效实践:在采样数据中记录策略版本号。训练时检查版本,若版本差异超过预设阈值,自动丢弃并通知调度器启动新采样。
💡 除了常规优化,还有哪些“极端”显存压缩技术可以用于 RLHF?如参数分片、重计算等。¶
极端压缩技术列表:
-
QLoRA:底座 4-bit 量化,LoRA 适配器 FP16。可将 65B 模型压缩至 ~40 GB 显存,支持单卡微调。在 RLHF 中 Actor 和 Critic 都可应用,但需评估精度损失。
-
ZeRO-Infinity:将模型参数、梯度、优化器状态卸载到 CPU 甚至 NVMe 固态硬盘,突破 GPU 显存墙。适合训练超大模型,但会牺牲速度。
-
激活检查点(Gradient Checkpointing)++:选择性对更深层激活进行重计算,或使用类似 FlashAttention 的内存高效注意力,大幅降低激活内存。
-
CPU 推理 Ref/RM:将 Reference 和 Reward Model 完全移至 CPU(使用 llama.cpp 等),GPU 仅负责 Actor/Critic 训练和采样。
-
模型剪枝与蒸馏:对 Critic 或 Actor 进行结构化剪枝,或蒸馏出更小的 Critic,降低模型尺寸。
-
激活序列并行:将长序列切分到多个 GPU,每个 GPU 只存储部分激活,减少单卡激活内存。
-
混合精度深度压缩:使用 8-bit 优化器 + 4-bit 底座 + FP16 适配器,多种精度混合,最大化显存利用率。
组合使用:例如,QLoRA + CPU Offload Ref/RM + 激活检查点 + ZeRO-2,可在一张 A100-80G 上训练 70B 模型的 RLHF。这是目前“平民化”大模型对齐的最强武器。