资讯动态

大语言模型强化学习中的KL散度惩罚:原理、实现与调参实战

发布时间:2026/10/26 15:49:55 来源:尧图企业网站定制
强化学习做语言模型对齐KL散度惩罚这一项几乎是绕不开的。但我在实际调参和读代码的过程中发现很多人对它的理解停留在加个正则项防止跑偏这个层面真到了要调系数、要换算法、要排查训练崩溃的时候就说不清楚它到底在约束什么、约束的是谁和谁的距离、为什么系数稍微大一点模型就变得又怂又笨。这篇就围绕大语言模型强化学习里的KL散度惩罚把它的数学本质、工程实现、调参经验和常见坑一次讲透适合正在做PPO或GRPO对齐训练、被KL项折磨过的同学参考。1. 为什么语言模型的强化学习非要加一个KL惩罚1.1 从奖励 hacking 说起语言模型做强化学习和打游戏、控制机器人那套强化学习有个根本区别游戏里的奖励是环境给的规则固定你没法篡改而语言模型的奖励通常来自一个奖励模型Reward Model这个奖励模型本身是个神经网络是对人类偏好的近似拟合。只要它是拟合出来的就一定有漏洞。策略模型在优化过程中会拼命找奖励模型的高分区域而这些高分区域未必真的对应高质量回答。典型表现就是输出变得极端疯狂堆砌当然可以这是个非常好的问题这类讨喜句式或者把回答写得又长又绕因为奖励模型可能对长度、对某些措辞有偏好。这就是奖励 hacking。KL散度惩罚的作用就是给策略模型拴一根绳子让它别跑得离初始模型通常是SFT模型太远。初始模型虽然不完美但至少是个正常的语言模型输出分布是合理的。你允许策略在它附近微调但不允许它为了刷分彻底变成一个奖励模型专用拍马屁机器。1.2 KL约束的两种理解角度第一种角度是分布约束。KL散度衡量的是两个概率分布之间的差异我们把策略模型 π_θ 和参考模型 π_ref 之间的KL散度作为惩罚项加进目标函数本质上是要求策略的输出分布不要偏离参考分布太远。第二种角度是信任域。这其实是TRPOTrust Region Policy Optimization那一脉的思想每次策略更新只允许在一个信任区域内变动超出这个区域就惩罚。KL散度就是这个区域大小的度量。PPO之所以叫近端Proximal就是因为它用各种近似手段把策略限制在旧策略附近而语言模型对齐里额外加的这项KL是限制在参考模型附近两者约束的对象不一样这点后面会专门讲。理解这两层含义很重要因为很多人在调参时把这两个KL搞混了以为PPO自带的clip就等价于KL惩罚结果发现模型还是跑偏。1.3 没有KL惩罚会发生什么我做过一组对照实验同一个SFT模型、同一个奖励模型一组加KL惩罚一组不加。不加KL的那组训练到大概几百步之后奖励分数确实一路飙升但人工看输出质量断崖式下跌回答开始重复、开始出现无意义的高频短语、开始答非所问但语气极其自信。这就是典型的策略崩溃。加KL的那组奖励涨得慢一些但输出质量稳定。这个对比非常直观地说明KL惩罚不是可有可无的正则它是维持训练不发散的核心稳定器。奖励模型给的是代理目标KL惩罚守的是真实目标的底线。2. KL散度惩罚项的数学形式与两种估计方式2.1 目标函数里那一项到底长什么样语言模型RLHF里PPO的完整目标大致是这样objective E[ r(x, y) ] - β * KL(π_θ || π_ref)其中 r 是奖励模型的打分β 是KL惩罚系数KL项衡量策略分布和参考分布的差异。最大化这个目标就是在拿高分和别跑偏之间找平衡。β 越大越保守β 越小越激进。这里有个容易忽略的点KL项是逐token累加的。一条回答有多个token每个token位置都有一个KL贡献最后求和或求平均。这直接影响到不同长度回答的惩罚力度后面讲实现时会展开。2.2 两种KL估计k1 和 k3工程实现里KL散度不是精确计算的而是用采样估计。常见的有两种估计量业界习惯叫 k1 和 k3。k1 估计量kl_k1 log(π_θ(y|x) / π_ref(y|x))这是最直接的形式用对数概率比作为单样本估计。它的期望确实是KL散度但方差比较大而且它可以是负数当策略概率低于参考概率时单步看会有点反直觉。k3 估计量kl_k3 (π_ref(y|x) / π_θ(y|x)) - log(π_ref(y|x) / π_θ(y|x)) - 1这个形式看起来复杂但它有个非常好的性质恒大于等于零。因为对任意正数 uu - log(u) - 1 ≥ 0等号只在 u1 时成立。这意味着k3估计出来的KL永远非负不会出现单步负值训练信号更稳定。估计量表达式是否非负方差常见使用场景k1log(π_θ/π_ref)否较大早期实现、简单场景k20.5 * (log(π_θ/π_ref))²是中部分框架默认k3π_ref/π_θ - log(π_ref/π_θ) - 1是较小主流推荐我个人的经验是如果你的训练经常出现KL项忽正忽负、loss抖动厉害优先换成k3。它在数值上更稳尤其是策略和参考模型差异较大的时候k3的估计更接近真实KL。2.3 逐token KL与序列级KL的区别这里有个实现细节值得单独说。有些实现是把整条序列的log概率比加起来算一个序列级KL有些是逐token算KL再求和。数学上序列的联合概率比等于各token条件概率比的乘积取对数后就是各token对数概率比之和所以逐token求和和序列级在k1下是等价的。但在k3下就不完全等价了因为k3是非线性的。逐token算k3再求和和先算序列概率比再套k3公式结果不一样。主流实现比如很多开源PPO框架是逐token算再求和这样每个token都有独立的惩罚信号梯度更细。你要复现别人的结果一定要先确认对方用的是哪种不然KL数值对不上调参就无从谈起。3. PPO里的KL和参考模型KL两个容易混淆的东西3.1 PPO的clip约束的是新旧策略PPO的核心是重要性采样加clip。它维护一个旧策略 π_old每次更新时用 π_θ/π_old 这个比率做重要性加权并把比率clip在 [1-ε, 1ε] 区间内。这个clip约束的是当前策略和上一次更新时的策略之间的距离目的是让每次更新步子别太大。注意这个约束和参考模型没关系。π_old 是训练过程中不断更新的它越来越远离初始的SFT模型。所以PPO的clip本身并不能防止策略跑离参考模型它只保证训练过程平滑。3.2 参考模型KL约束的是和SFT的距离参考模型 π_ref 是固定的就是那个SFT模型训练全程不变。KL惩罚项约束的是 π_θ 和 π_ref 的距离。这个约束才是真正防止奖励hacking的那道防线。所以一个完整的PPO训练里其实同时存在两个距离约束clip管新旧策略KL惩罚管策略和参考模型。两者作用不同缺一不可。我见过有人为了简化把KL惩罚去掉只留clip结果就是前面说的策略崩溃也见过有人把clip调得很小、KL系数也调得很大结果模型几乎不更新训练半天没进展。3.3 自适应KL系数是怎么回事固定β有个问题训练初期策略和参考模型差不多KL很小β固定的话惩罚几乎不起作用训练后期策略跑远了KL变大同样的β可能惩罚过重。所以很多实现用自适应KL系数。逻辑大概是设定一个目标KL值 kl_target每隔若干步检查当前实际KL。如果实际KL大于目标的一定倍数比如1.5倍说明跑太远了把β调大如果实际KL小于目标的一定比例比如0.5倍说明太保守把β调小。if current_kl 1.5 * kl_target: beta * 2 elif current_kl 0.5 * kl_target: beta / 2这个策略在早期RLHF实现里很常见。它的好处是自动平衡不用手工死调β。但要注意β的调整频率不能太高否则会引入震荡。一般每几十到几百步调一次比较合适。提示自适应KL虽然省心但目标KL值 kl_target 本身还是要调的。设太小模型学不动设太大等于没约束。经验上从0.01到0.1之间试具体看任务。4. GRPO里KL惩罚的处理差异4.1 GRPO去掉了价值网络GRPOGroup Relative Policy Optimization这两年被讨论得很多它最大的改动是去掉了PPO里的价值网络Critic。PPO需要一个价值模型来估计优势函数这个价值模型本身也要训练占显存、增加复杂度、还容易估计不准。GRPO的做法是对同一个问题采样一组回答比如8个或16个用这组回答的奖励均值作为基线每个回答的奖励减去均值就是它的相对优势。这样就不需要价值网络了显存省一大块训练也简单。优势估计变成A_i (r_i - mean(r)) / std(r)其中 r 是组内各回答的奖励。这个相对优势直接反映了这个回答比同组平均好多少。4.2 GRPO的KL项位置GRPO虽然去掉了价值网络但KL惩罚项依然保留而且位置和PPO类似还是加在奖励里或者作为独立惩罚项。具体来说很多GRPO实现是把逐token的KL惩罚直接从奖励里扣掉per_token_reward -beta * kl_per_token然后序列奖励加上这些逐token惩罚。这样KL惩罚就融进了优势计算里。这里有个细节GRPO因为用组内相对优势奖励的绝对尺度被归一化了所以KL惩罚的系数β和PPO里的β不能直接照搬。PPO里β0.01可能合适GRPO里同样的任务可能要重新试。我一般会先在GRPO上从更小的β开始试因为组内归一化会让KL惩罚的相对影响被放大。4.3 两种算法KL惩罚的实操对比维度PPOGRPO价值网络需要不需要优势估计GAE Critic组内相对奖励KL惩罚位置奖励中扣除或独立项逐token从奖励扣除β典型范围0.01 ~ 0.10.001 ~ 0.05显存占用高较低KL调参敏感度中较高从表里能看出来GRPO虽然结构简单了但KL系数反而更敏感。原因是组内归一化把奖励的动态范围压缩了KL惩罚在这个压缩后的尺度上占比变大。所以从PPO迁移到GRPO时β一定要重新调不能直接复制。5. KL系数调参的实战经验5.1 β太大和太小的典型症状β太大模型输出变得极其保守几乎和SFT模型没区别奖励涨不上去训练loss平得像一条直线。你去看输出会发现它不敢做任何冒险的表达回答四平八稳但没有提升。这时候KL值会一直很低因为策略根本不敢动。β太小训练前期奖励涨得飞快但很快出现重复、啰嗦、格式崩坏。KL值会持续攀升最后可能飙到一个很大的数。人工评估质量明显下降。这时候你去看KL曲线会发现它单调上升没有回落这就是失控的信号。合适的β应该让KL值在一个合理区间内波动既不持续攀升也不一直趴在地上。奖励稳步上升输出质量肉眼可见地变好。5.2 怎么根据KL曲线判断训练状态KL曲线是训练健康度的核心指标我一般会盯这几个特征平稳波动KL在一个区间内上下波动说明策略在参考模型附近探索健康。持续上升KL单调攀升说明惩罚不够策略在跑远需要调大β或检查奖励模型。骤降后趴平KL突然掉到接近零并保持说明惩罚过重或学习率太低策略不敢动。剧烈震荡KL大幅上下跳可能是batch太小、学习率太高或者β自适应调整太频繁。我习惯把KL曲线和奖励曲线叠在一起看。理想状态是奖励上升的同时KL温和上升然后趋于平稳两条线不要出现奖励暴涨、KL也暴涨的同步失控。5.3 一个可复用的调参流程第一步固定一个中等偏大的β比如0.05先跑一小段确认训练能起来、KL不爆炸。这一步是确认流程通。第二步逐步减小β观察奖励和KL的变化。每次减半跑够步数看趋势。找到那个奖励涨得动、KL不失控的区间。第三步在这个区间里微调配合自适应KL让训练后期自动收紧。第四步用人工评估或留出的评测集验证别只看奖励分数。奖励高不代表质量好这是RLHF的铁律。注意调β的时候一次只动一个变量。同时改学习率、batch size、β你根本不知道是哪个起的作用。6. 实现KL惩罚时踩过的坑6.1 log概率的计算精度问题KL惩罚依赖log概率而log概率的计算在混合精度训练下很容易出问题。我遇到过一次用fp16算log_softmaxKL项出现大量inf和nan。原因是某些token的概率极小log之后是很大的负数fp16的动态范围不够。解决办法是KL相关的计算强制用fp32或者至少把log_softmax那一步转成fp32再算。这个坑很隐蔽因为训练前期可能没事跑到中途才爆。建议一开始就把KL计算路径设成fp32别省这点显存。6.2 参考模型的推理开销KL惩罚需要参考模型的log概率这意味着每个batch你要跑两次前向一次策略模型一次参考模型。参考模型虽然不训练但前向开销实打实存在显存也要多占一份模型权重。优化手段有几个一是参考模型用更小的精度比如int8量化做推理只要log概率够准就行二是把参考模型的log概率预先算好缓存起来如果同一批数据会被多次使用的话三是用LoRA之类的参数高效方法让策略模型和参考模型共享底座只维护一份增量。我实测下来参考模型前向大概占整个训练step耗时的30%到40%不是小数目。如果显存紧张优先考虑量化参考模型。6.3 KL惩罚和长度偏置前面提过KL是逐token累加的这带来一个隐蔽的偏置长回答的KL惩罚天然比短回答大。如果奖励模型对长度没有明显偏好这个偏置会让模型倾向于生成短回答因为短回答KL惩罚小。这个偏置在有些任务里是好事抑制啰嗦在有些任务里是坏事该详细的地方也变短了。如果你发现模型输出普遍变短且质量下降可以检查是不是KL惩罚的长度偏置在作祟。缓解办法是对KL做长度归一化或者调整奖励里对长度的处理。6.4 参考模型选错导致的全盘皆输参考模型一般用SFT模型但如果你用的SFT模型本身质量就不行KL惩罚就是在把一个差模型当锚点策略再怎么优化也被拽着。我见过有人图省事用一个没充分训练的checkpoint当参考模型结果训练出来的模型上限很低。参考模型的选择标准它应该是你当前能拿到的、质量最好的、输出风格符合预期的模型。宁可多花时间训好SFT也别在参考模型上将就。7. 几个高频疑问的直给回答7.1 KL惩罚能不能完全去掉能但不建议。去掉KL惩罚在短时间、小规模、奖励模型非常可靠的情况下可能work但绝大多数场景下会奖励hacking。如果你非要去至少要保留PPO的clip并且密切监控输出质量。我的建议是保留KL哪怕系数很小它是个廉价的安全网。7.2 β设成负数会怎样β为负意味着你在奖励跑偏鼓励策略远离参考模型。这在正常对齐里没有任何意义只会加速崩溃。除非你在做某种对抗性实验否则β必须为正。7.3 为什么我的KL一直是零KL恒为零通常意味着策略模型和参考模型的输出概率完全一样。可能原因策略模型根本没更新学习率是零或者梯度没传进去、参考模型和策略模型指向了同一个对象配置写错两个变量引用同一份权重、或者KL计算被错误地detach了。逐个排查最常见的是配置里参考模型路径写成了策略模型路径。7.4 训练中途能不能换参考模型技术上可以但不推荐。参考模型一换KL的基准就变了之前累积的训练动态全部失效相当于重新开始。如果非要换建议把β也重新调并且做好训练会有一段震荡期的准备。7.5 小模型和大模型的KL调参有区别吗有。小模型容量有限策略能探索的空间小KL一般不会涨得太离谱β可以设小一点。大模型表达能力强稍微一优化就可能跑很远KL更容易失控β往往要设大一些或者更依赖自适应KL。我调7B模型和70B模型同样的任务β能差一个数量级。8. 把KL惩罚用对的几个关键认知绕了一圈回到最核心的认知KL惩罚不是为了让训练更稳这么简单它是在代理目标和真实目标之间架的一道桥。奖励模型是代理人类偏好是真实KL惩罚通过约束策略不偏离参考模型间接守住了真实目标的下限。调参的时候别把β当成一个孤立的超参数去死磕。它和奖励模型的可靠性、SFT模型的质量、任务对输出多样性的要求都相关。奖励模型越可靠β可以越小SFT模型越好参考锚点越稳β的容忍范围越大任务越需要多样性β越要小心别设太大。我自己的习惯是每次开新任务先花时间把SFT和奖励模型的质量确认好再动KL系数。因为KL惩罚再精妙也救不了一个烂的奖励模型。反过来奖励模型靠谱了KL系数在一个不小的范围内都能work调参反而轻松。最后分享一个排查小技巧如果训练效果不达预期先把β设成一个明显偏大的值跑一小段看模型能不能稳定输出正常文本。如果能说明流程和参考模型没问题问题在β太小或奖励模型如果不能说明是更底层的问题比如参考模型选错、log概率算错。这个二分法能帮你快速定位问题层级省下大量瞎调参的时间。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑