知乎:https://zhuanlan.zhihu.com/p/1925691663654850602
不知不觉,deepseek r1 的那篇技术报告过去半年了,“遇事不决上强化”的风气也没有年初那么夸张了。或多或少,大家都陆续回归到 sft 和 rl 混合使用的传统路线了。
既然强化的浮夸风已经过去大半,我们不妨平心静气的看下小作坊的强化之路 —— Skywork or1 的技术报告。
前言
并非所有团队都有 dpsk、seed、qwen 的豪华配置,包括但不限于:infra 基建,人才密度,gpu,市面上所有好模型的请求额度,标注团队 …… 因此,不同团队做强化学习的目标是不同的:
- dpsk 和各大厂的核心团队:追赶 OpenAI 和 Google,提高模型泛化能力,尽早建立技术壁垒;
- 普通从业者和小作坊:培养强化学习的认知,掌握相关技术栈,助力于日常工作或是保证自己能跟得上一线技术团队;
- 普通爱好者:发论文。
当大家都把目光聚焦在御三家的研究成果时,我个人反倒是觉着,小作坊的经验分享和技术报告更有价值。一方面内容更加接地气(消耗的资源量并不浮夸,有复现的可能性),另一方面也足够的大方(qwen3 的技术报告,关于强化细节那是一个字都没提)。
skywork 的这篇技术报告最起码在以下几点上来说是聪明的:
- 使用了字节的 verl 和 qwen 的底座模型,复现起来非常容易;
- 找了一个强有力的的 baseline:DeepScaleR,模仿它,分析它,超越它;
- 卡的数量很友好,各种消融实验都是 32卡、64卡、128卡;
- 选定了一个核心技术方向来分析:Mitigating Policy Entropy Collapse;
- 不搞花里胡哨的,就做所有人都在做的 math、code,吃透就好;
- 消融实验足够多,可能不具有普适性,但至少告诉了我们强化的哪些细节是需要做消融的。
训练策略
这篇文章的 rl 用的是最基础的 GRPO 算法,唯一的改动点就就是移除了 response 的长度归一化项
来减轻长度偏差,这个技巧以及下文会提到的 clip_higher 技巧都在 DAPO 论文里有讲。
基础知识我也就不再多赘述了,有疑惑的话可以再翻翻 PPO、GRPO、DAPO 的论文。
多阶段训练
llm 自从进入 long cot 阶段之后,4k、8k 基本就是过家家了,32k 已经是 post training 的入门级长度了 —— 这极大的增加了 rl 的算力需求。文章的作者别出心裁,提出可以把 rl 的训练分为多阶段进行:先训 8k,等模型的效果快收敛了,再训 16k …… 以此类推。
先训 8k,再训 16k 是一种听上去就不太对的做法。毕竟超长的 response 会在训练中被视为负例,训 8k 的时候很可能让模型学的不再出更长的 response,失去解决复杂问题的能力,等到再训 16k 的时候就啥也学不到了。
rl 涉及到的一个特殊概念是“模型的提升空间”,举个例子:学生 A 完全掌握了“反证法”且只会使用该方法,在低年级阶段成绩很好,但在高年级阶段遇到更复杂的问题的时候,却完全学不会;学生 B 则是同时学习了“反证法”、“归纳法”、“构造法”等,由于学的不深,在低年级的的时候成绩不是很理想,但高年级接触更多知识的时候,往往能学的更快。所以衡量一个策略好坏的标准,一定不仅仅是当前 step 的得分,而是要看远点,看看更多 step 下的趋势变化。
文章里并没有在原理上解释多阶段训练为什么具有可行性,只是默默的把消融实验的结果抬了出来:一直训 16k 的蓝线和先训 8k 再训 16k 的黄线,最终会在测试集合取得相同的效果。那么在这种情况下,多阶段训练的优点就彰显出来了:
- 节省算力,具体看下图的左图,相同 gpu 卡时的情况下,多阶段训练的模型效果始终处于领先地位;
- 提高 token efficiency,也就是让模型少说废话。这一点是很有意义的,现在的 long cot 模型(以 R1 为代表),无意义的废话确实很多。初始阶段不要用过长的输出,说不定真能让模型少说一些“让我好好想一下”这种无意义的 token。(不要因为现在流行 long cot 就忘了大模型是按照 token 数量收费的, 如果效果一样,输出短的模型一定是更好的模型 )
- Preserve Scaling Potential,也就是模型还能训回去,response 在后续训练中依然能稳定提升,模型的上限没有被削减。(感觉实验不充分,个人持小小的怀疑态度)
多阶段训练既能省算力,又不会限制模型的上限
说句题外话,就我的视角而言,post training 最关键的并不是 sft 和 rl 谁更重要,恰恰就是 multi_stage 的设计 。既可以是多阶段的 sft,也可以是多阶段的 rl,亦或者是相互交叉的多阶段。不同 topic 的多阶段,不同任务难度的多阶段,不同训练方法的多阶段,不同 seq_len 的多阶段等等,都需要做实验去设计,也往往都能让模型的效果有所提升。
pretrain 的技术方法正在全面入侵 post training(退火、课程学习、多阶段训练 ……)。
针对截断的 advantage mask
承接上文,文章继续讨论了“response 截断的数据到底该不该作为 rl 阶段的负例”。
在 8k 的训练设置下,模型初始截断比例非常高。如下图所示,训练初期(0-100 步),准确率的升主要是由于截断比例的急剧下降。在 100 步之后,算法开始提高非截断 response 的准确率。
截断比例,模型准确率,非阶段数据的模型准确率
从数学的视角出发,模型的优化目标有两个变量:
,分别代表非截断的概率,和非截断时的期望 reward。很显然,我们不是很在乎截断率,我们希望模型在训练中主要聚焦在提高非截断时的期望 reward。那么,这就需要设计一些 advantage mask 策略,来修正模型的优化目标。
针对 advantage mask,一共有三种策略:
- no mask:无任何 mask
- advantage_mask_before:截断 response 不参与 group 内的 advantage 计算,这些截断 response 的 advantage 设为零。公式中
是非截断的 response,
- advantage_mask_after:截断 response 参与 group 内的 advantage 计算,这些截断 response 的 advantage 设为零 。公式中
是所有的 response,
三种策略的消融实验从下图可以看到具体情况:
- 图 a 是截断比例,能发现 advantage_mask_before 策略变化明显;
- 图 b 是训练准确率,advantage_mask_before 策略准确率下降,no mask 和 advantage_mask_after 两个策略都能稳定上升;
- 图 c 是非截断数据的准确率,advantage_mask_before 最高,no mask 最低,但是和 advantage_mask_after 的趋势一样,都是先降再升。
advantage_mask_before 策略的表现非常特殊:非截断 response 的准确率有所提升,整体的训练准确率却持续下降,且截断比例稳步上升。这种现象应该是一种典型的 reward hacking —— 我不知道这道题会不会写,我只要告诉老师说题目“超纲”了就不会被批评。
最终,advantage_mask_before 策略因为试图作弊,率先出局。
不同 mask 策略下的截断比例、模型准确率、非截断数据的模型准确率
进一步的,我们来看下不同策略训出来的模型在更长的 response_len 下的表现(此时基本上不存在截断问题了)。下图的曲线很明显的说明了,黄线 no mask ≈ 红线 advantage_mask_after > 绿线 advantage_mask_before。
注意下图中的紫线,应该是在 no mask stage1 基础上做了 stage2 (更长 response_len)训练的模型,说明第一阶段学到的较短的 response 完全没影响模型的上限,第二阶段的 response_len 和 accuracy 仍然能稳步提升。
各种 advantage mask 策略在不同长度下的表现
在更大的推理 response_len下,advantage mask 技巧并不能带来更优秀的性能。第一阶段不使用 advantage mask 的模型,在第二阶段扩大 response_len 之后,依然能提高 accuracy,并且显著优化了 token efficiency。如无必要,勿增实体 ,作者选择不做 advantage_mask。
高温采样
GRPO 的分组特性意味着 response 的采样过程会直接影响每个 group 的质量和多样性,进而影响模型的训练效果。如果采样的温度过高,随机性过大,可能会导致“全错 group”出现的概率,进而影响训练效率;采样温度过低,则会降低 group 内的多样性,增加“全对 group”出现的概率。
针对采样温度,文章做了一个消融实验,基本结论是:高温采样的模型效果更好。较高的温度显著增强了早期阶段的学习信号,并为后续训练保留了更大的潜力 。同时,高温采样初始的熵也更大, 衰减也更慢。(图中的熵之所以降到 0.2 就不再降低了,是因为作者加了熵 loss 来阻止进一步下降)
高温采样的熵更大,模型效果也更好
自适应熵控制
文章指出,熵坍缩会严重影响模型的效果。所以希望在 rl 的过程中抑制熵的快速下降。提出的做法就是引入一个熵 loss,来把熵控制在某个值附近。
这个熵 loss 的系数
是动态调控的,为此需要引入两个额外的超参数:
- :期待模型的熵稳定在这个值附近;
- :熵损失系数的调整步长。
在第
步更新的时候,actor model 的熵记为
,如果它小于
,就把熵 loss 的系数
加上
,大于则减去
,这个策略只在
时生效。具体的公式为:
关于熵的讨论是这篇文章最精彩的地方,下文会详细解读,这里就不多提了。
KL Loss
训到一定阶段后,KL_loss 会将 actor model 的策略拉到和 reference model 一致,导致 KL_loss 下降至接近零。同时,模型在测试集合上的性能也基本不会再提升了。所以文章里的实验也都不再加 KL_loss 了。
如图,KL_loss 初期基本没影响,训得久了之后(750 step 往后)就让模型训不动了。KL_loss 一直以来被诟病的就是“reference_model 为什么不定期更新一下”。都过了几千、几万step 了,还要被原始的模型来约束,非常不合理,现在很多强化工作确实也都选择不用 KL_loss 了。
KL loss 会影响模型进一步的学习
缓解策略熵坍缩的探索
名词解释
- ,即 rollout batch size:每次使用的 prompt 数量
- ,即 mini batch size:每次模型进行参数更时新使用的 response 数量
- ,即数据复用:每次 rollout 产生的 response 的使用次数
- ,即 group size:每条 prompt 生成的 response 数量
- :response 的最大长度
- :temperature
- :在一次 rollout 数据的情况下,对模型的参数进行多少次更新。该数值等于 1 即为 on-policy,大于 1 即为 off-policy。特别的,
- on-policy:所有参与训练的 response 都是模型自己生成出来的
- off-policy:参与训练的 response 可能是较早时期的模型生成出来的
on-policy 和 off-policy 的区别,主要体现在对数据的使用上,off-policy 的训练效率会明显更高一些。一方面,off-policy 可以不等所有的 response 生成完毕,就启动模型训练;另一方面,off-policy 可以多次使用同一条数据,提高数据利用率,on-policy 则不可以。
左图为 on-policy,右图为 off-policy
核心论点:过早的熵坍缩会导致模型效果差
“过早的熵坍缩会导致模型效果差,强化训练中引入熵 loss 能明显缓解熵坍缩的速度”属于文章的核心观点,关于熵的所有讨论和实验也都是围绕这一观点进行的。文章里通过一个对比实验来支撑该观点:
- 蓝线引入了 1e-3 的 熵 loss,熵坍缩的比较快,模型的测试效果差;
- 黄线引入了 5e-3 的 熵 loss,熵坍缩的比较慢,模型的测试效果好。
个人觉着,文章里对该论点的证明有点单薄了,baseline 缺少一个不加熵 loss 的曲线,实验也不是那么充分。虽然目前大部分人都认可 “熵低 = 模型输出多样性差 = 强化的探索空间小 = 强化效果差 ” 这一观点,但其实还是缺少一些系统性的分析和证明的,毕竟 entropy 和 diversity 并不能完全划等号,也有很多强化的工作完全无视对熵的控制。
蓝线的熵过早坍缩,模型的效果变差
rollout 多样性对熵的影响
既然熵对模型的效果影响很大,那哪些因素能影响熵呢?首先是 rollout 的采样阶段,文章分析了这几个超参数:
,
,temperature。实验结论如下图所示,在 on-policy 的情况下,
和
怎么调整都不会出现快速熵坍缩。
至于 Temperature,上文提到过:使用适当选择的较高温度,在训练初期会导致较差的测试性能,但最终能带来更大的性能提升。
rollout 超参数的消融实验,只要 on-policy 就不会熵坍缩
对熵的影响
对
的分析,其实就是对 off-policy 和 on-policy 的分析。文章通过三组消融实验给出了一个结论:on-policy 在任何情况下都好于 off-policy 。
实验 1:相同的 rollout 配置下,off-policy 容易熵坍缩,on-policy 不易熵坍缩 。下图中,只有蓝线的 on-policy 没有快速熵坍缩,自然而然效果也更好了。
rollout 固定为 64,on-policy 不会快速熵坍缩,且模型效果最好
实验 2 :on-policy 可以用更少的训练数据,达到比 off-policy 更好的效果 。重点观察下图的红线和黄线,红线每一步训练使用了 64 条 prompt,而黄线是 32 条 prompt。模型效果上,红线初期是明显压着黄线的,但在 1500 step 左右,红线训不动了,黄线还在上继续涨。
训得越久,on-policy 的效果越好
实验 3 :即使增大 rollout 的数据量,也无法阻止 off-policy 的模型快速熵坍缩 。黄线和绿线的趋势完全一致,即使黄线的 rollout 数量有 256 条之多。
增大 rollout 数量,off-policy 依然熵坍缩
如何防止熵坍缩
总结下前文关于熵的讨论,大概可以概括为下面这几个观点:
- 熵坍缩就会导致模型效果差;
- 只要不是 on-policy,模型必然会快速熵坍缩,调整任何其他参数都没用;
- off-policy 能大幅度提高训练效率以及数据的利用率,不可能完全舍弃,尤其是“异步训练框架”的使用就离不开 off-policy 的训练策略;
- 即使是 on-policy,也只是缓慢熵坍缩,并不能从根本上防止熵坍缩。
气氛都铺垫到这儿了,那肯定要讨论下如何防止熵坍缩呗。文章给出了两个方法来防止熵坍缩:加入熵 loss 和 clip higher 技巧 。
先说熵 loss,这个非常直接,让模型直接朝着熵增的方向学习呗。此处必须提一句的是,语言模型的训练是朝着熵减的方向去优化的,所以 熵 loss 和语言模型 loss 是两个截然相反的优化目标,把它们揉杂在一起是一个很反直觉的行为。
有没有感觉强化学习很拧巴,既希望模型朝着某个分布去学习,又怕模型过于拟合某个分布导致没有训练空间。我是感觉这也能从侧面说明, pretrain 阶段是在学习人类语言分布,而 posttrain 阶段只是在调整模型学到的人类语言分布
言归正传,下图充分说明了熵 loss 不是随便加的,加大了模型直接崩溃,加小了又没作用。
熵 loss 系数需要保守一些,不能太大
除此之外,熵 loss 对训练数据也很敏感 ,相同参数配置下使用不同的数据,会得到完全不同的趋势。(这里其实可以更细致的探究一下,两份数据的 response 多样性到底差别多大,比较一下 response 的熵的平均值,也许能给出一个近似的拟合公式:“熵在多大的区间内适合多大的 loss 系数”)
熵 loss 系数对数据十分敏感
文章读到这里我觉着作者的意图已经很明显了,深度学习的惯用伎俩:当一个参数同时兼具 significant 和 sensitive 两个属性的时候,那不妨就给它补充一个 dynamic 属性。 也就是文章前面介绍过的动态调整熵 loss 系数的方法。
需要注意的是,当
较大的时候,自适应系数也控制不了熵。文章推测是因为“熵损失是基于整个词汇表计算的,这可能会增加许多非预期 token 出现的概率”。因此,只推荐
的情况下使用自适应的熵控制。
引入熵 loss 后,模型效果明显变好
再说另一个技巧,DAPO 论文里已经讲过的 clip higher,认真读下 DPAO 论文里的分析过程就好啦,这已经快属于强化学习的常识之一了。
如下图所示,clip_higher 之后,熵坍缩显著放缓,模型在测试集表现更好。但是不能设置的过高,比如图中的红线 0.28。有意思的是,红线的 0.28 恰恰是 DAPO 原始论文中的推荐值,所以这说明 clip higher ratio 也是严重依赖于数据的(既然熵 loss 都动态调控系数了,干脆 clip higher ratio 也动态调算了)
clip higher 有效缓解了熵坍缩
训练资源分配的探索
训练资源分配问题是 qwen、seed 不太会探讨的一个话题,因为他们并不缺卡,但对普通团队来说资源分配确实是搞强化实验的命门所在。
首先明确下 rl 的训练时间:
,分别代表 data rollout 的时间,policy update 的时间,以及其他操作的时间(reward computation,experience generation),更具体地说:
- 的主要影响因素是 rollout batch size (
)和 group size(
)
- 的主要影响因素是 随机梯度下降(SGD)的步数(
)
rl 的耗时分布,rollout 是绝对的大头
policy update 的耗时
毋庸置疑,
是
的大头,因此可以合理做出判断:适当增加每次 rollout 时的
,可以在几乎不增加
的情况下提高训练效率(这里很好理解,一次 rollout,多次 policy update 自然是能让模型学的更多)。下表也证明了这个假设,一次 rollout,更新四次模和更新一次模型相比,仅仅增加了 3% 的耗时。
增加 policy update 的次数,总耗时几不变
但是前面的实验提到过,off-policy 会加剧熵坍缩。因此,不建议仅仅为提高训练效率而增加
,除非有适当的机制来缓解熵坍缩。
rollout 的耗时
现在考虑如何减少生成时间
。如下表所示,
主要由 batch size 和生成最长的那条 response 所需要的时间来确定。在计算资源充足的时候,增加生成阶段的 gpu 数量并不能明显降低最长的那条 response 所需要的时间,也就不能大幅度减少生成阶段的耗时 。
gpu 资源翻倍,但 generation time 并不能减小一倍
也就是说,当有了更多的训练资源用来做生成阶段的推理,完全不调整超参数带来的收益是比较小的,这就是个木桶效应,更多的卡也没办法解决最长那条 response 对应的耗时。最好的办法是,增加卡数量的同时,也增大 rollout batch size 或者 group size,通过利用更大的 rollout buffer,来获得更精确的梯度估计 。
长话短说,论文做了一些简单的消融实验,结论大概是:rollout batch size 越大,模型在测试集上的表现越好;group size 越大,模型在测试集上的表现越好。 具体效果详见下面两张图。
rollout batch size 越大,测试集表现越好
group size 越大,测试集表现越好
其他
文章还有一些内容,诸如:训练数据如何清洗,math 任务的 verifier 怎么设计,code 任务的 sandbox 怎么搞等等。感兴趣的同学自己去读读吧(显然我不感兴趣哈哈),我就不再解读了。
最后总结一句,rl 的实验结果和底座模型能力、训练数据、训练 topic 息息相关。大家都知道 qwen 底座模型训过很多的题,那它就很容易在学习数学题的时候熵减过快,就会依赖文章里讲的控制熵的技巧。也许 llama 模型的做题能力较差,也就完全不需要考虑文章对熵的各种分析。
我们学习的是文章作者在尝试稳定 rl 训练的思考过程和实验设计(尤其是消融实验的动机和分析),而不是一昧相信文章里给出的实验结论。
PS:都看到这里,来个点赞 、在看 、关注 吧。 您的支持是我坚持的最大动力!
欢迎多多关注公众号「刘聪NLP」,加入交流群,交个朋友吧,一起学习,一起进步!