极市导读
文章详细分析了NeMo-Aligner在PPO流程中的优化策略,包括推理加速、显存优化以及不同引擎之间的协同工作,同时探讨了其在训练效率和可扩展性方面的表现。 >>加入极市CV技术交流群,走在计算机视觉的最前沿
今年 10 月在费城开 COLM 的时候,我有幸见到了 NVDA 两篇工作的 post,一篇是 RULER,现在已经是 long context 几乎必测的 benchmark;另一篇便是今天要讨论的工作——NeMo-Aligner。
https://github.com/NVIDIA/RULER
https://github.com/NVIDIA/NeMo-Aligner
Aligner 这个名字自然是非常恰当,毕竟“Aligner 并不试图贡献新的 Alignment 算法,而是专注于如何集成更多的 Alignment 算法”。所以,我姑且就用 Aligner 这一名字称呼这一系列的工作:
-
DeepSpeed-Chat——微软;
-
NeMo-Aligner——NVDA;
-
OpenRLHF——开源社区;
-
veRL——字节;
https://github.com/microsoft/DeepSpeedExamples/blob/master/applications/DeepSpeed-Chat/README.md
https://github.com/NVIDIA/NeMo-Aligner
https://github.com/OpenRLHF/OpenRLHF
https://github.com/volcengine/veRL
虽然 Aligner 是为了各类基于 RL 的 Alignment training 而生的,然而这些算法会有相当一部分时间用于推理(rollout),所以推理引擎社区也乐于关注 Aligner 工作的进展和需求。在下文中,我也会尽力从我的认知水平出发,讨论 Aligner 对于 Inference Engine 的需求。
Introduction
-
Nemo-Aligner 是一套集成了主流对齐算法的 toolkit,涵盖 RLHF / DPO / SteerLM 和 SPIN。能够有效调度千卡规模的计算资源,完成 Llama 4.1 405B 这种规模模型的训练。此外,toolkit 也支持 PEFT。作者希望这一框架足够 extenable,不过考虑到 RLHF / DPO 的差距已经非常大了,能复用的部分有限。个人感觉想要 support 一个新的算法还是比较困难。
-
当前的 Alignment 算法基本还是地主的赛场,需要显著的计算资源。譬如 PPO 算法的计算流中,需要同时有四个模型进行复杂的交互。如果用 405B 的模型同时充当这四个模型的 base,在不进行优化的情况下,动用的资源可以简单算一算。
70B 模型用 FP16:140GB
4 个彼此交互的模型:140GB * 4 = 560GB
Adam:每个参数需要 8 个字节(两个动量):70B * 8bytes = 560GB
Critic 和 Policy 同时都需要训练,所以需要两个优化器 = 1120GB
激活值和梯度:保守估计是模型大小的 1.5 倍,大概 210GB
KL 散度和其他各类开销:保守估计 200GB
大概 2100GB,也即 3 台 80G A100 ???
-
基于我不严谨的计算,可以发现运行这样的 aligner 框架的开销非常恐怖。据此,Nemo-Aligner 需要在系统上下大功夫。首先,在 Megatron-LM 上 continue,支持 distributed 3D (data, tensor, pipeline) parallelism training。再者,在 rollout(可以理解为 sampling 阶段)使用 TensorRT-LLM 来做 inference,毕竟目前 trt-LLM 的推理效率确实是 SOTA。(而且也是英伟达自家的产品 )
Model Alignment Background
SFT
pre-trained / base model 通过 Supervised Fine-Tuning 的方式来迫使模型基于 cross-entropy mimic 人类期望的回答。SFT 对于 DPO / RLHF 是必须的,因为不做 SFT,模型几乎无法 follow 人类指令。这一过程也被称为 behavior cloning。
RLHF
-
这是今天的主角。RLHF 避免了显式为 RL 定义 reward function,转而通过 reward model 给出的 judgement 作为 reward。reward 模型从一组 pairwise dataset 中训练得到,一个 instruct 给出两个 response,其中一个标为 chosen,另一个标为 rejected。reward model 的 loss 基于 Bradley-Terry 模型得到,尝试让 Reward(chosen) > Reward(reject) 的概率尽可能大。RLHF 有两大主流方法,一种是 REINFOCE(1992),相对古老;而 NeMo-Aligner 更倾向于 PPO(2022)。
-
下图一定程度表示了 PPO 的过程,这里尤其需要注意到模型参数是否会被训练。事实上,如前文所述,SFT Model、Policy / Actor、Reward Model、Value / Critic 这四个模型都会在计算流中被使用。其中 SFT 和 Reward 是 inference only 的,而 Policy 和 Value 会被更新。
-
SFT 已经论述过了,简单提一下 Reward Model Training。一般而言,reward model 会在 SFT model 基础上加上一层 linear layer,然后连同这个 linear layer 训练,将 linear layer 的 final project value 作为 reward。(在 SGLang 中体现为 classify 接口)
https://sgl-project.github.io/backend/native_api.html#Classify-(reward-model)
-
计算流可以如下概括:
1. 初始化:SFT model 初始化 Actor,Reward Model 初始化 Critic;
2. Rollout / 生成输出:Actor 对输入的 Prompts 生成相应 / responses。
3. Reward Compute / 计算奖励:Reward Model 对生成的 responses 进行评分,提供 reward 得分。
4. 价值评估与更新:Critic 计算当前 responses 的价值(注意是 value 而非 reward),然后基于 value
和 reward 计算优势函数(advantage function),最终确定 Actor 的更新方向和幅度。此外,Critic
也会在这一步进行更新,减少其预测的 value 和实际奖励之间的误差,从而提高下个状态 value 的精度。
5. Advantage funciton 的值最红会用于优化 Actor,同时这步更新需要基于 SFT Model 的 logits 计算
KL 散度并且加以惩戒,避免 Actor 相较于 SFT model 偏离太远,走到了 Reward Model 的盲点 / blind
spots 上。
DPO
-
DPO 是和 PPO 同样声名大噪的对齐方法,这是一种 offline, off-policy 的对齐方法。试图直接将模型对齐成为符合偏好的最佳 policy 而避开了显式需要一个 reward model。这也是其名字的由来:Your Language Model is Secretly a Reward Model。(PS:最近发现有好几个给论文起名的新 pattern,譬如 XXX is All You Need,XXX is Secretly a YYY,XXX Empowers XXX,最后还有 Make XXX Great Again )
-
相较于使用隐式的 reward model,DPO 更为激进地选择使用 reference policy 通过 Bradley-Terry Mdel 隐式推导出 chosen / reject pair 之间的 reward。具体来说,通过计算 chosen/ reject pair 之间的对数概率差值来得到 reward,而该差值又由 optimal and reference policies 共同计算。这样的差值又通过 scaling 并且经过 sigmoid 得到最终的 loss。训练过程中,reference policy 固定不动,仅用于构造 chosen / rejected responses。
SteerLM
-
SteerLM 和 DPO 类似,避免复杂的 RL 方式,仅仅是 supervised finetuning。
-
计算流如下:
1. 训练属性属性预测模型 / Attribute Prediction Model(APM),用于给定一个输入 prompt,给出多个语义评分。
譬如对 correctness、toxicity 分别打 0 ~ 4 分。
2. 利用 APM 标标注 prompt-response pair,得到属性分。
3. 做 SFT:输入是 prompt + 属性分字符串,模型学习对给定 prompt 和属性分目标生成相应的 response。
4. 推理的时候,在 prompt 结尾加上需要的属性分即可。
-
听上去真的是比其他方法简单多了,避免了 Reward Model 和 PPO 训练的复杂性,将属性对齐问题转为了显式的条件对齐。不过,猜测效果不是那么好
Self-Play Fine-Tuning
这是我们组的工作 ,不由得说影响力真不错。SPIN 和 DPO 类似,绕开了显式使用 reward model(注意到 reward model 的意义在于绕开了显式使用 reward function )。SPIN 方法中,strong model 会从一个 weak model 的自我博弈中迭代而来。具体而言,给定一个 prompt / response pairs,让 weak model 对 prompt 进行一次新的标注,得到 prompt / (response, generated response) 这样的 preference data。然后,policy 在这样自我合成了一般数据的 preference pair 上训练,使其对于给定的 prompt,其偏向 response 的概率高于偏向先前自我构造的 generated response。这里需要用到 preference loss function,而这样的 preference loss function 和 DPO 中使用的 loss function 一模一样。
Online / Offline vs On-policy / Off-policy
作为强化学习白纸,这里记录下自己理解的概念。
1.On-Policy vs Off-Policy:用于收集经验的策略(behavior policy)和被优化的策略(target policy)是否是同一个。 On-Policy 使用当前正在学习的 policy 收集数据,数据只能使用一次,用后即弃,更加稳定容易收敛;但是数据利用率低,构造数据成本高昂,典型算法有 PPO 和 SARSA。而 Off-Policy 可以使用任意的 policy 来构造数据,可以复用历史数据,可以使用外部策略(如人类)的数据,数据利用率高,但是训练可能不稳定,需要复杂的重要性采样,典型算法如 Q-Learning,DQN,SAC。
2.Online vs Offline:算法是否需要在训练过程中与环境交互。 Online 算法在训练过程中不断与环境交互,实时收集数据,动态调整策略,可以更适应环境变化,然而实时访问的风险和成本较高。典型场景有实时控制、在线决策。Offline 算法又称为 Batch RL,只使用预先收集的数据集,训练过程不与环境交互,类似于监督学习。安全,不需要实时环境,但是受限于数据质量,难以处理分布外的情况。
3.PPO 是 online on-policy 的;但是对于 Online 而言,我和组里的同学讨论,这个定义可能没那么严格。Online 的特点可能是小批量大轮次,比如 SPIN 中一轮 1w 个 preference data 然后 train 3 个 epoch,而典型的 PPO 可能是 64 个 preference data train 成百上千轮,所以 SPIN 可能不是典型的 Online。
4.【from 我们组的学长】从 RL 的角度来看,是否从当前 policy 采样决定是否是 on policy;而是否和环境交互决定是否是 on line。这种界限并不清晰,没必要纠结,知道在描述什么即可。
NeMo-Aigner For RLHF / PPO
-
讨论了非常久的 alignment background,现在我们回到 NeMo-Aligner 上。之后的解析(对 OpenRLHF 和 veRL)应该不会涉及 RL 背景了。RLHF 的流程如本文第一张图所示,而主要的开销还是在 PPO 流程中,具体而言是 rollout。
-
如前文所述,PPO 过程需要 4 个模型组件参与整个流程。
a. PPO Actor:训练且推理,由 SFT Model 初始化而来,是 PPO 最终希望得到的微调结果。
b. Reference Policy:仅推理,一般设置为 SFT Model 本身,不进行任何修改。在 PPO 过程中,Actor 会
Reference Policy 同时计算 logits【这里会在后文论述】,然后计算 KL 散度,防止模型过度偏移。
c. PPO Critic:训练且推理,从 reward model 初始化而来,在 PPO 中计算 value【并非 reward】。
d. Reward Model:仅推理,对 Actor 产生的 rollout data 提供 rewards。
-
上述四种 model 都可以任意大小,因此 NeMo-Aligner 实现了分布式训练,通过 PyTriton 启动 server 和 client,从而不再要求 critic 和 actor 在同一个节点上。
-
直观上,可以启动四个模型 server 来同时 host 四个组件。然而,注意到 PPO Actor 和 Reference Policy 实际上是同一架构同一参数量的模型,不过参数会有所更新。因此,NeMo-Aligner 将二者组合在同一个 job 上。二者不同时使用,所以可以将其中不用的一个 offload 到 DRAM 上,推理时再异步 swap 回来。【这里也有很深的文章可以做,后文分析】这种 offload and swap 的策略在 PPO Critic 和 Reward Model 上也同样适用。
-
Response generation 占据了 PPO 全流程的主要时间。显然,我们可以采用 inference engine 来加速这样的 generation。为什么不可以用 training engine 的 forward 来进行 generation 呢?这个问题问的看似愚蠢,仔细想想,为什么我们没有用 training engine 来做 inference,比如直接拿着 deepspeed 的 forward 来做推理,反而要单独设计推理引擎呢?其实还是推理的计算特性所致。
https://zhuanlan.zhihu.com/p/4148050391
-
在之前的文章(上方链接到的文章)中有提到,inference(或者说占据主要时间的 decode)是 autoregressvie 的,每个 token 依赖于前序所有 token,所以无法单个 sequence 的 decode 是无法并行的(能组起的 batch 都是多个 sequence 的 continuous batching),从而计算密度并不高。另一方面,decode 阶段需要进行大量的内存读取和数据传输,对通讯要求较高。总归,inference engine 是主要被 memory bounded 的。 而 training engine 则不然,training engine 的 batch size 可以开的非常大,直到打满 engine 的计算能力,从而使 compute bounded 的。 因此,用 training engine 来做 inference 并不科学。(尽管我的本科写过的作业从不考虑这个问题 )
-
如上所述,我们已经发现,对于 PPO 中的四个 components,我们分别需要 inference engine 和 trainning engine,彼此优化各自的计算目标。现在考虑这个问题:为了计算 KL 散度需要得到 reference policy 和 actor 分别的 logits,这个 logits 该由什么 engine 得到?显然,inference engine 得到 logits 快很多,而且现代引擎都支持这个请求。然而,目前 inference engine 得到的 logits 精度是更低的,不应该来计算 KL 散度。更科学的方法是用 trainning engine 得到二者的 logits 来计算 KL 散度。
-
至于为什么 inference engine 的 logits 精度更低,这也是为了 inference 速度做出的牺牲。直观上来说,continuous batching 组的 batch 越大,精度”飘“的更厉害。即便 batch size 写死为 1,也不够准确。
-
考虑到 engine 启动的开销很大,PPO 过程中任意 engine 都不应该被关掉。这无疑带来了更大的显存压力,为此 NeMo-Aligner 在反向传播时重新计算 training 阶段的激活值,减少了峰值显存压力。此外,inference 需要的显存小于 training,所以在单个节点的显存允许时,NeMo-Aligner 在 inference 时只采用 tensor parallelism,避免了 pipeline parallelism 带来的跨节点通讯开销。
-
在随后的迭代训练过程中,inference 的 engine 需要与 training engine 更新过的参数进行同步。【这里有很大文章】为了实现这样的同步,inference engine 使用了 Tensor RT Refitter 来进行 in-place update,而不是关掉 engine 重启新的(我在开会的时候就觉得这个优化非常重要,尽管当时我并不理解其重要性,但是最近越发理解这个接口几乎是 dominate 的)。最后,rollout 阶段不同 inference engine 的 latency 也不尽相同,所以 NeMo-Aigner 又为此设置了一个 router,真是工程量可怕。
训练实际表现与 Scalability
-
这张图很有意思。首先,纵向比较,显然我们发现 Rollout 阶段的 response generation 占据了过半数的时间。另一方面,横向观察第二行,我们注意到当计算资源线性增长时,不同阶段的效率增长不尽然相同。Response generation 近乎是超线性的 ,而 logits 计算显然是亚线性的。
-
具体而言,training 开销是亚线性的,因为随着节点增加,每个 data parallel rank 的 micro-batch size 减少,计算利用率降低。而在 pipeline parallelism 中,流水段必须在 optimizer 调用之前完成,由此带来了填充和清空流水线的开销,且这一开销和 mirco batch 大小无关。所以 mirco batch 减小,流水线的计算用时减小,填充和清空开销增大,所以计算利用率降低,增长亚线性。计算 logits 也是类似的。(原文这里其实也暗含了 logits 是 trainning engine 得到的,然而没有明示 )
-
generation 的开销是线性的,一方面是 router 做的足够好,response 近乎被等分给了每个 engine,这是线性的。此外,inference 的开销可能会稍微增大,毕竟 KV Cache 被复用的次数少了,然而整体上影响很小【感觉自己有点扯淡了 】。0generation 随着更大计算资源的投入,几乎是线性增长的。
-
当然,engine 的 weight update 开销并不能降低,原因显然。至于 actor 和 critic 之间,由于是异步通讯的,actor 几乎不需要等待 critic 的上一轮相应,也不存在开销了。
-
如下图所示,为了进一步说明每个 feature 带来的加速,作者做了分 component 的 ablation。去除 Trt-LLM 集成后,PPO 每一步的开销增加了 7 倍。接着,在推理阶段使用 pipeline parallelism 导致开销增长 4 倍,inference engine 不断重启带来了 3 倍开销,而不使用异步请求会导致 1.5 倍开销。反过来,router 看上去并不重要,可能这和 PPO rollout 的样本量有关。一轮就 128 个 sample,router 的优化似乎弥补不了其开销 【但我相信对于 SPIN 而言不是这样的,因为 PPO 是小批量多批次,而 SPIN 是大批量小批次】
-
剩下的部分就快速写过了。作者在 trainning SteerLM 的时候用了 LoRA,惊奇发现 LoRA 带来的损失几乎没有超过 benchmark 的误差范围。
-
作者在 SPIN 中,仅进行了一个 epoch 的训练,并且不将先前轮次的 sample 使用到下一轮,避免了 datset size 每次都翻倍。
Remain Question
之所以来学习以 NeMo-Aligner 为代表的 Aligner 框架,是因为有收到 OpenRLHF 团队的反馈,希望能够将 SGLang 集成到他们的框架中去。前文已经提到了,有一些地方可以做文章的。
-
如何从 actor 和 reference policy 拿到更准的 logits?目前需要通过 training engine 而非 inference engine。不过,对于 inference engine 而言,有什么是我们能做的呢?
-
如何提供 Reward Model 的通用接口?直接在 SFT model 的 last layer 加上一个 linear head 再训练一番即可作为 reward model。推理引擎能为此提供更好的接口么?
-
如何做更好的 weight update?四个模型需要两套 engine,如何从 Training Engine 上调用鲁棒的 Inference Engine Weigtht Update 接口呢?目前 OpenRLHF 集成 deepspeed 和 vllm,经常因为二者的 depedency 和更新带来很大的问题,我们能做些什么?
希望接下来几周的工作可以让这些问题更明朗。
公众号后台回复“数据集”获取100+深度学习各方向资源整理
极市干货
点击阅读原文进入CV社区
收获更多技术干货

