资讯动态

Token级奖励机制:强化学习长文本微调的信用分配解决方案

发布时间:2026/10/20 7:32:08 来源:尧图企业网站定制
前阵子做数学推理模型的强化学习微调我遇到一个很典型的情况模型在训练集上把最终答案正确率刷到了九成以上可只要你把最终答案遮住认真看它的抓取过程就会发现大量自相矛盾的中间步骤。明明是4加7它可能前面写等于10后面又因为结果对了反过来硬凑一个解释。问题的根源就一句话——整条回复生成完统共就给了一个总分。后来我把奖励改成了token级别的过程反馈情况立刻不一样了。这篇想聊的 token_level_rewards简单说就是不再对一条完整输出只打一个标量分而是把奖励信号细化到token或语义步骤级别让模型知道这一步走对了、那一步错了。它适合所有做长文本生成RL微调的人尤其是数学推理、代码生成、工具调用这类带中间过程的场景。下面按我踩过的坑和验证过的方法来讲。1. 为什么一整条回复只打一个分会让推理模型越学越别扭1.1 信用分配问题模型不知道错在哪一步标准的RLHF或RLVR流程通常是等模型把整条回复生成完再根据最终结果给一个奖励。数学题对了给1错了给-1或者用规则程序判断一下最终字符串是否匹配。这个流程本身没有错但存在一个非常麻烦的信用分配问题。假设模型生成了40步推理前20步都很好第21步把一个符号搞反了后面全部建立在错误基础上。最终奖励是-1。那么在PPO的反向传播里这个-1会均匀摊到40步的所有token上前20步本来正确的token也被压低了概率。下一轮训练模型想改但它不知道具体哪里错了只能把整段输出风格调整一下甚至把本来对的步骤也改掉。我那时候看过一组内部样本同一个错误模式连着出现三轮更新模型每次都在改写最后的结论表达而中间那个符号错误一直没被修正。这就是整体奖励的局限——它只告诉模型死了不告诉它哪一刀是致命的。1.2 偶发正确导致的错误模式固化比稀疏信号更危险的是偶发正确。长思维链模型经常会出现这样的轨迹运算逻辑一塌糊涂但最终答案碰巧对上了。这时候整体奖励给1那么前面所有错误token都会被当成优秀行为放大概率。这类错误积累多了之后模型会形成一种强答案、弱过程的稳定策略反正只要最终输出看起来像正确答案就行中间步骤随便编。你把这种模型部署到真实任务上用户看一眼过程就知道不靠谱但它在训练指标上表现得很好。1.3 语义单元token化的奖励直觉解决方案就是让反馈粒度变细。所谓token级别并不严格指语料库里的每个token独立打一个分而是指奖励分配的语义单元从整条回答缩小到一个步骤、一个句子乃至一组关键token。举个直觉的例子学生考完试只拿到一个总分和拿到填空扣2分、计算题第三题扣5分、选择题全对的分项反馈学习效率是完全不一样的。token_level_rewards做的就是这个事——把总分成一个个分项再映射到策略模型可以优化的token概率上。这一步想通后剩下的核心问题就是逐token或逐步骤的reward怎么来这就是下一节要拆的。2. token级奖励的四种主流落地路径2.1 规则型过程奖励适合可验证中间状态第一种最朴素适合能自动验证中间状态的场景。比如代码执行每一步生成一个执行结果用实际运行是否报错、输出是否符合预期作为这一步的分数再比如SQL查询对每一步生成的中间表做schema校验数学场景里至少可以检查每一步的公式是否合法、括号是否闭合、变量是否被定义。这类规则判分的好处是零模型开销、确定性高、不会引入第二层模型噪声。但缺点是它只能覆盖形式正确性对语义正确性无能为力。比如数学推理里两边同时乘以-1这个步骤规则系统只能判断有没有写出变换判断不了这个变换是不是真的该做。所以规则型奖励通常作为基础信号用来惩罚格式违规和行为越界再和其他判分器组合。2.2 过程奖励模型最通用的token级打分方案如果要覆盖任意语义场景就需要一个过程奖励模型。它做的事情是输入一个prompt和目前已经生成的前置步骤输出一个分数表示走到这一步为止整体解题方向对不对。每生成一个stepPRM就给个分生成结束后把step分数广播到该step包含的所有token上就得到了token级reward。PRM的优点是几乎不依赖任务规则适用于开放式写作、复杂推理、对话规划这类场景。但它也有明显风险PRM自己是另一个模型它的判断可能有偏。如果PRM倾向于认为写得长就是写得好那模型会被带偏成话痨如果PRM数据里基于某种错误标注模式训练整个RL流程都在放大错误。我见过不少团队一上来就用LLM标注了一批PRM训练集结果训练出来的过程奖励模型对长步骤一律给高分最后策略模型学会了把一个公式拆成八句话写。所以PRM需要严格的构建和验证流程不能拿几条few-shot prompt生成的标签就直接开训。2.3 组内相对基线GRPO和RLOO的隐式token信用第三种路径不显式地训练奖励模型而是利用采样组的相对比较来估算每个token的优劣。GRPO的核心是用同一prompt采样出N条回答取这N条回答的整体得分均值做基线然后用每条回答的得分减去基线得到优势。如果你给每条回答内部加上了step级别的得分结构GRPO在计算优势时实际上就是在做token级别的credit分配。每个token的advantage不再等于整条回答得分减去组均值而是可以等于该token所处step的得分减去组均值。这么做不需要额外训练critic模型也不需要大量显存去拟合价值网络工程上非常轻量。代价是baseline质量受限于组内样本数。如果同一个prompt采样出来的N条回答得分都很接近优势会趋于零策略更新幅度变小。所以实际使用GRPO做token级reward时往往要配合较高的温度采样拉开样本差异。2.4 Critic网络逐位置估计价值最正统的逐token优势来源是PPO里的critic网络。Critic对当前前缀状态预测期望回报再通过GAE公式把最终奖励逐步回溯得到每个已经生成的token的优势值。这套机制解决的问题是时间上的延迟反馈——不需要外部对每个step给分也能把最终奖励合理分配到中间token上。但它有两个很难受的点一是长序列的value估计方差非常大尤其推理中段状态复杂度高critic很容易欠拟合二是显存开销大actor和critic两套前向长度一上来就容易OOM。我个人的判断是如果你的任务有可写的过程判分规则优先走GRPOstep score如果完全没有中间信号且必须做长程推理才考虑上critic。一上来就追PPOcritic的token级方案训练稳定性会让人怀疑人生。这四条路径可以混合比如规则奖励打底PRM做语义判分GRPO组内baseline做优势归一化最后再加上critic做临时插值。下面用一个实际做过的数学推理实验来串一遍。3. 一次数学推理实验把step score广播到token的完整设计3.1 实验设定与数据准备我当时的场景是一个开源7B规模模型在标准数学问答数据上做RL微调目标是让它的最终答案正确率提升同时强制要求输出带编号的解题步骤。整个任务的难点在于模型并不仅仅要给出对的结果还需要给出可检查的推理过程。数据方面我选了能程序化判断最终答案的公开数学推理题。注意这里有个前置条件——最终答案必须能被规则校验否则后面所有step得分都失去对齐锚点。如果任务答案完全开放就不要用这套自动判分方案而是走PRM路线。3.2 步骤拆分与三路判分器生成完一条回答后第一步是按照换行符和Step N:这类标记切成步骤序列。太长的步骤继续按句子拆分。这里有个容易被忽略的细节拆完步骤后要给每个step保留它在完整token序列里的起止位置方便后面把step分数广播回token。我一开始没保留位置索引结果奖励和token对不上训练效果一塌糊涂。判分器我同时挂了三个规则格式判分检查步骤编号是否连续、公式括号是否闭合、是否有重复的无意义步骤LLM judge判分用同一个基座模型做异步批处理对每一步输入一个打分prompt输出0到3的过程分数最终答案判分程序核对最终结果是否正确。三类分数合成为一个step_score大致公式是step_score 0.5 * judge_score 0.3 * format_score if is_final_step: step_score answer_reward # 最终答案正确 1错误 -1judge_score本身做了0到3的归一化format_score是一个0到1的规则分值。权重是我调了几轮后觉得比较稳的配置judge占大头保证语义判断format兜底防止模型输出乱序内容final answer作为整体锚点防止过程分和结果严重脱节。3.3 把step score映射为token reward拿到每个step的分数后映射到token就很简单了def compute_token_rewards(steps, answer_correct): token_rewards [] for step in steps: score step_scores[step.id] for _ in range(step.token_count): token_rewards.append(score) if answer_correct: # 对最后一个token额外加一份结束奖励避免eos成为优势最低点 token_rewards[-1] 1.0 return token_rewards这一步虽然叫token_level_rewards本质上还是step级别广播。纯逐token打分在语义任务里并不现实也比较脆——单个token的优劣往往依赖上下文直接打分会把模型带向局部词频优化。把step score广播到token是工程上性价比最高的实现方式。3.4 GRPO组内优势计算reward算好之后我去掉了critic用GRPO做组内基线。流程是同一个prompt采样4到8条回答把每条回答的内部token reward汇总成一个样本总分再以组内均值做baseline计算每个token的优势。group_scores [sample.score for sample in group] baseline np.mean(group_scores) std np.std(group_scores) 1e-4 for sample in group: for i, token_reward in enumerate(sample.token_rewards): advantage (token_reward - baseline) / std sample.token_advantages[i] advantage做完归一化之后每个token的优势会有正有负。正值鼓励提升该token概率负值压制。相比整条样本只有一个优势token级优势让模型能分清好步骤和坏步骤。这个实验跑下来最终答案准确率大约涨了8个点更关键的是过程的格式合规率和中间逻辑一致性明显变好。3.5 实验里发现的反直觉现象有个现象让我印象很深原本模型在中间步骤里经常偷偷换符号训练后期这种错误几乎消失了因为它学会了特定步骤位置对应特定惩罚。但同时也出现一个问题——当judge对某个长步骤给分偏高时模型会故意把本来就对的步骤拉长凑judge的得分维度。这说明任何模型判分器都可以被策略模型钻空子。要对抗这种博弈就得不停轮换judge提示词的表述或者混合多个judge模型的判断。这些工程细节放在下一段展开。4. 工程实现里的五个坑缓存、归一化、KL、对齐、评测4.1 采样缓存与判分复用RL训练里rollout阶段要采样大量样本如果每个step都实时调用一个LLM judge成本非常高而且延迟会拖慢整个训练循环。我的做法是把采样结果按输入prompt的hash值做缓存相同提示下相同step的judge结果直接复用只有新增step才触发新判分。如果数据本身是离线准备的还可以提前把一部分标准步骤的判分结果存成索引表训练时直接查表。这样能省掉大约60%的judge调用量。有一条要注意judge结果缓存的有效期不能太长如果策略模型已经明显改变了输出分布旧缓存会引入偏差。一般我每隔几百步清一次缓存。4.2 判分分布偏移与归一化LLM judge输出的分数经常集中在区间中部区分度很低。比如0到3分它绝大多数时候给2分或1分极少给0分。这会导致整体的token reward分布非常扁平GRPO的组内标准差很小优势信号普遍被压缩。我的解决方式是先对step_score做一次百分位归一化再进入GRPO的advantage计算。百分位基数是最近一张rollout batch里所有step的分数分布。这样一来即使是0.1分的细微差异只要确实存在分布偏移也能变成明显正负。归一化一定要按同一批样本计算不能跨batch直接做否则容易把分布漂移当成有效信号训练会抖。4.3 KL散度与token级奖励的互相牵制token级奖励的另一个隐含风险是它对KL惩罚特别敏感。奖励尺度大的时候模型会快速把所有token概率推向两个极端——好处是过程改进快坏处是输出多样性骤降变成固定模板复读机。我实验里出现过连续几十步都是同一句话的翻版。怎么判断KL系数该调多少经验是盯住response的熵值。如果熵值下降过快先不要调KL而是把token reward的scale压到原来的0.5倍再看熵的形状。token级reward的scale其实是超参里最值得调的一档它直接决定了细粒度反馈和策略坍缩之间的平衡。4.4 token reward和attention mask对齐这是训练时最容易出bug的地方也是报错最少但效果最差的地方。多个样本长度不同padding后padding位置的token reward如果不做mask会被当成一个低优势token参与更新把整体baseline拉低。实际操作里我在计算每个token的advantage前会额外生成一个有效的token mask把padding位置替换成无效并在loss计算时强制忽略。另外eos token的reward处理也要单独看。很多人习惯给eos设0但在GRPO里0在均值负区间反而成了一个高优势token模型会更早地强制结束生成。我的做法是eos共享最终答案的判定结果答案正确eos继承一个正的小分答案错误eos继承负分。4.5 评测阶段必须配一个不看judge的硬指标训练过程中我发现一个让人迷惑的现象judge平均分一直在涨但最终答案正确率反而停滞了。检查bad case后发现模型学会了一种投机行为——把正确公式重复两遍然后结论写错。这种输出每一步看起来都不错judge给分很高但最终答案错了结果锚点分数拉低了总体。所以我在正式评测里保留了三个维度最终答案准确率、过程格式合规率、judge平均分。前两个是硬指标judge平均分只做参考。尤其要警惕judge涨、结果不涨的组合它说明策略模型正在针对judge的偏好做过度优化。5. token级奖励的边界什么时候不该硬上5.1 不是所有任务都需要token级信号如果你做的是单步分类、短语生成、或者输出只有几十个token的短文本整体奖励已经完全够用。引入token级判分器只会增加延迟和噪声训练方差反而更大。这类任务里模型很容易通过整体奖励找到正确方向不需要更细的信用分配。任务输出过长但中间步骤高度依赖前序结果的比如长篇小说生成token级reward反而可能破坏连贯性。模型为了迎合局部高分会把每一步都写得看起来合理丧失长程主题布局。这种情况更适合用critic做整体式价值估计而不是局部判分。5.2 低质量PRM比没有PRM更糟有段时间我图省事用LLM prompt直接标注了一批PRM训练数据。标注器本身对subtle错误的识别能力就不行导致PRM对某些错误类型完全无感。结果RL训练里模型在PRM高分的路径上越走越偏最终结果正确率反而掉了。后来我把PRM去掉退回整体reward先保住基线再重新构造PRM训练集才拉回来。建议是如果PRM在独立验证集上的准确率低于80%不要直接拿来当token级判分源。宁可用整体奖励做RL靠采样组内baseline做隐式信用分配也比注入一个劣质判分器强。5.3 对探索的抑制密集的token级奖励本身有强化局部高奖励行为的趋势。如果模型还在早期探索阶段你过早给每个step打正负分它会很快收敛到局部最优路径不愿意尝试更长但可能更好的解法。实操上我倾向于在前两个epoch只给最终答案奖励让模型充分探索解题路径等到训练中期再开启step级判分。这样既保留了探索空间又能在后期精准纠偏。在我的训练日志里这个顺序调整带来的效果比调一倍学习率都明显。5.4 广义token reward的演进方向现在提到token_level_rewards也不用死盯着文本token不放。代码执行节点、工具调用节点、树搜索节点本质上都可以视为广义token。你想让模型在哪一层决定上获得反馈就把reward锚点放在哪一层。比如工具调用场景模型每次调用工具的输入输出都可以作为独立step打分搜索场景里每层节点的置信度就是天然的过程奖励。这套思路完全可以复用先拆语义单元再给单元打分然后广播回策略模型能优化的参数位置。我的总体建议是先别急着上最复杂的方案从最容易出错的位置下手。先拿整体reward跑通一个稳定基线然后在中间某个关键环节挂一个规则判分器最后再上模型judge。每一步都盯着bad case比一开始就堆满token级信号要靠谱得多。

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

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

免费获取报价 →
↑