1. 为什么数学推理需要强化学习来破局
数学推理这件事,放在大模型的能力谱系里一直是个硬骨头。早几年大家做数学题自动求解,主流思路是监督微调——拿海量带步骤的题解数据去喂模型,让它学会模仿人类解题的格式和套路。这条路能走通一部分,但很快就撞到天花板:模型学会了“看起来像在推理”,却经常在中间步骤悄悄出错,最后给出一个自信满满的错误答案。更麻烦的是,很多竞赛级题目根本没有标准步骤可模仿,你没法用“抄答案”的方式教会模型解决它没见过的问题。
强化学习在这里扮演的角色,本质上是把“解题”从模仿任务变成了搜索任务。模型不再需要逐字模仿某一条标准解法,而是自己生成多条推理路径,然后根据最终答案对不对来获得奖励信号,反过来强化那些通向正确答案的思考方式。这个转变很关键,因为它允许模型探索出人类没写进训练集里的解法,也允许它在没有中间步骤标注的情况下,仅凭最终答案的验证就能学习。
我最初接触这条路线的时候,心里是有疑虑的:数学推理的搜索空间那么大,靠一个稀疏的最终奖励信号,模型真的能学到东西吗?后来实际跑了几轮实验才明白,关键不在于奖励有多密集,而在于怎么设计奖励、怎么控制探索的方差、怎么让模型在“敢想”和“别乱想”之间找到平衡。这套东西和传统监督学习的手感完全不同,更像是在训练一个会自己琢磨解题思路的学生,而不是往它脑子里灌题库。
这篇文章面向的是已经了解大模型基本训练流程、想切入数学推理强化学习方向的从业者,也适合做教育类AI产品、自动解题系统、形式化验证相关工作的同学参考。我会把整条技术链路拆开讲清楚:从任务建模、奖励设计、训练框架选型,到实际跑起来会遇到的坑和排查方法。读完你应该能自己搭起一套可运行的数学推理强化学习流程,并且知道每一步为什么这么做。
2. 数学推理强化学习的整体方案设计
2.1 把解题过程建模成序列决策问题
强化学习的标准框架是马尔可夫决策过程,要把它套到数学解题上,第一步是定义清楚状态、动作、奖励分别是什么。我的做法是把语言模型本身当作策略网络,状态是当前已经生成的推理前缀,动作是下一个token的生成,一条完整的推理链加上最终答案构成一个episode。这样建模的好处是直接复用现有的自回归生成框架,不需要额外设计复杂的环境模拟器。
但这里有个细节容易被忽略:数学推理的“状态”其实包含了大量隐式的中间计算,模型在生成下一步时,前面所有步骤的语义都被压缩在上下文里。这意味着策略网络必须自己学会维护一个内部的工作记忆,而不是像下棋那样有一个显式的棋盘状态。这也是为什么数学推理的强化学习比游戏类任务更难——状态表示是隐式的、高维的、而且随着推理链变长会不断漂移。
另一个设计选择是动作粒度。最细的粒度是逐token生成,最粗的是整条推理链一次性生成后打分。我实测下来,逐token的信用分配太困难,奖励信号要穿过几百上千个动作才能传回去,方差大到几乎训不动。整条链打分又太粗,模型不知道具体哪一步出了问题。折中方案是分段打分,比如把推理链按自然段或按推理步骤切分,每段给一个中间奖励,这样既保留了信用分配的精度,又控制了方差。
2.2 奖励函数设计:从稀疏到稠密的权衡
奖励设计是整条链路里最需要反复打磨的部分。最朴素的方案是结果奖励:最终答案对了给1,错了给0。这个信号极其稀疏,但胜在绝对可靠,不会引入任何人为偏见。我早期实验里用纯结果奖励配合大batch采样,确实能训出东西,但样本效率低得让人心疼,一个epoch要烧掉大量算力。
为了提升效率,常见的做法是引入过程奖励模型,对每一步推理给一个质量分。过程奖励的好处是稠密,能加速收敛;坏处是它本身可能不准,模型会学会钻过程奖励的空子,生成“看起来步骤很漂亮但答案还是错”的推理。我踩过这个坑:有一轮实验里过程奖励权重给太高,模型学会了写一堆格式工整但逻辑空洞的中间步骤,结果奖励反而下降了。
我的经验是采用混合奖励结构,结果奖励占主导,过程奖励作为辅助正则项,权重控制在0.1到0.3之间。具体形式可以写成:总奖励等于结果奖励加上一个系数乘以过程奖励的均值。这个系数需要根据任务难度调,题目越难、结果奖励越稀疏,过程奖励的权重可以适当调高,但绝不能让它盖过结果信号。
还有一个容易被忽视的点是格式奖励。数学推理要求模型输出结构化的解题过程,如果它生成一堆乱码或者不按格式来,即使答案碰巧对了也没有实用价值。我会加一个轻量的格式检查奖励,比如要求推理链包含明确的步骤分隔符、最终答案有特定标记,不满足就扣分。这个奖励不需要很大,0.05到0.1的量级就够,目的是引导模型养成规范的输出习惯。
2.3 训练框架与采样策略选型
框架层面,我主要用两类方案:一类是基于策略梯度的经典算法,比如PPO及其变体;另一类是基于拒绝采样加监督微调的迭代式方法,比如STaR系列的思路。两者各有适用场景。
PPO的优势是理论上更严谨,能直接优化策略的期望奖励,适合奖励信号设计得比较精细的情况。但PPO对超参数极其敏感,学习率、裁剪范围、KL惩罚系数稍微偏一点就训崩。我建议新手先从较小的学习率和较大的KL惩罚开始,稳定后再逐步放开。
拒绝采样加监督微调的思路更简单粗暴:让当前模型对每道题采样多条推理链,保留答案正确的那些,然后用这些高质量样本做一轮监督微调,迭代往复。这个方法实现简单、稳定性好,缺点是样本利用率低,而且容易陷入自我强化的循环——模型只学自己已经会的东西,探索能力逐渐退化。
我实际项目里采用的是两者结合:先用拒绝采样快速冷启动,让模型具备基本的正确解题能力,再切到PPO做精细优化。冷启动阶段大概跑三到五轮迭代,每轮采样8到16条推理链,保留正确且格式合规的样本。切到PPO之后,采样温度要适当调高,保证探索的多样性,同时用KL惩罚约束策略不要偏离冷启动模型太远。
采样策略上有个关键参数是每条题目的采样条数。太少则奖励信号噪声大,太多则算力浪费。我的经验值是每条题采样8到32条,具体取决于题目难度和算力预算。简单题8条就够,难题可以拉到32条甚至更多,因为难题的正确率低,需要更多样本才能碰到正确答案。
3. 核心细节解析与实操要点
3.1 推理链的表示与切分方式
推理链怎么表示,直接影响到后续的奖励计算和信用分配。我试过三种方案:纯文本、带步骤标记的文本、以及结构化JSON。纯文本最灵活,但解析困难,模型稍微跑偏就提取不出步骤。结构化JSON最规整,但模型生成JSON的稳定性差,经常出现语法错误导致整条链作废。
最终我采用的是带轻量标记的文本格式,用类似“步骤一:”“步骤二:”这样的自然语言标记来分隔推理步骤,最终答案用“答案:”开头单独一行。这种格式对模型友好,解析也简单,用正则就能稳定提取。标记本身不参与奖励计算,只用于切分和格式检查。
切分粒度上,我建议按推理步骤切,而不是按句子或按token数。一个推理步骤通常对应一次逻辑推进,比如“由条件A和B可得C”算一步,“对C两边求导”算下一步。这样切分出来的每段语义完整,过程奖励模型打分时也更容易判断对错。如果按固定长度切,经常会把一个完整的推理拦腰截断,导致过程奖励误判。
3.2 过程奖励模型的训练与校准
过程奖励模型本质上是一个步骤级的分类器或回归器,输入是题目加当前推理前缀,输出是这一步正确的概率或分数。训练数据从哪来?最直接的办法是用最终答案正确的推理链作为正样本,答案错误的作为负样本,然后对每一步打标签。但这样有个问题:一条错误的推理链里,前面很多步可能都是对的,只有最后一步算错了,如果整条链都标负,过程奖励模型会学到错误的信号。
更精细的做法是做步骤级标注,人工或者用规则判断每一步是否正确。人工标注成本太高,我一般采用规则加模型辅助的方式:先用符号计算引擎验证每一步的数学等价性,能验证的自动打标,不能验证的交给一个辅助模型判断,最后人工抽检。这个过程很费功夫,但过程奖励模型的质量直接决定了训练效果,值得投入。
过程奖励模型训好之后,还需要做校准。因为它的输出分布可能和真实正确率有偏差,直接拿来当奖励用会引入系统性误差。我会用一个小的验证集做温度缩放或者 Platt 校准,把模型输出的分数映射到接近真实概率的区间。校准之后的过程奖励,权重给到0.2左右,配合结果奖励使用,效果比较稳。
3.3 KL惩罚与探索的平衡控制
KL惩罚是PPO里约束策略更新的关键机制,它衡量的是当前策略和参考策略之间的分布差异。参考策略通常是冷启动后的监督微调模型。KL惩罚系数设得太小,策略会跑偏,生成一堆胡言乱语;设得太大,策略几乎不更新,训练没有进展。
我的调参经验是:初始KL系数设在0.01到0.05之间,观察训练过程中KL散度的变化。如果KL散度持续上升超过参考值的两到三倍,说明策略跑得太快,需要调大系数;如果KL散度几乎不动,说明约束太紧,可以适当调小。这个系数不是固定的,我通常会用自适应KL,设定一个目标KL值,根据实际KL和目标值的比值动态调整系数。
探索方面,采样温度是关键。冷启动阶段温度可以低一些,0.6到0.8,保证生成质量。PPO阶段温度调到0.8到1.0,增加多样性。但温度也不能太高,超过1.2之后生成质量下降明显,很多推理链前半段就崩了,浪费采样预算。我一般会在训练中期做一次温度退火,从1.0逐步降到0.7,让模型在探索和利用之间平滑过渡。
注意:KL惩罚和采样温度是两个独立的探索控制手段,不要同时大幅调整。每次只动一个参数,观察至少几百个训练步再决定下一步。
4. 完整实操流程与关键环节实现
4.1 数据准备与冷启动模型训练
数据准备分两块:题目集和冷启动用的监督数据。题目集我一般从公开的数学竞赛题库和教材习题里收集,覆盖代数、几何、数论、组合等主要分支,难度从初中到竞赛级都要有。题目数量不需要特别大,几千到一万道就够,关键是覆盖面要广,不能偏科。
冷启动监督数据是带完整解题步骤的题目解答对。这部分数据可以自己构造,也可以用公开的题解数据。构造的方式是让一个较强的基线模型对题目集生成解答,然后用符号计算引擎验证答案,保留正确的解答作为监督数据。这个过程可以迭代几轮,每轮用上一轮训好的模型生成新数据,逐步提升数据质量。
冷启动训练就是标准的监督微调,损失函数是交叉熵,只对解答部分计算损失,题目部分不参与。训练轮数不用太多,两到三个epoch就够,重点是让模型学会输出格式和基本的解题模式。训完之后在验证集上测一下准确率,有个百分之二三十的基础正确率就可以进入下一阶段。
4.2 采样与奖励计算流水线搭建
采样流水线的核心是并行生成和批量验证。我用的是一个生产者消费者架构:生产者负责从题目集里取题,调用推理引擎并行生成多条推理链;消费者负责解析推理链、提取最终答案、调用验证器判断对错、计算奖励。两者通过一个队列连接,避免生成和验证互相等待。
验证器是流水线里最关键的组件。对于有确定答案的题目,验证器就是字符串匹配或者数值比较,简单可靠。对于答案形式多样的题目,比如表达式化简,需要做数学等价性判断,这时候要调用符号计算库。我用的方案是先把答案转成标准形式,再做等价性检查,能覆盖绝大多数情况。
奖励计算在验证之后进行,把结果奖励、过程奖励、格式奖励按权重加起来。这里有个工程细节:过程奖励模型的前向计算比较慢,如果每条推理链的每一步都跑一遍,吞吐量会很低。我的优化是只对推理链的关键步骤计算过程奖励,比如每个推理步骤的结尾,而不是逐token计算。这样能省下大量算力,对最终效果影响很小。
4.3 PPO训练循环与超参数配置
PPO训练循环的伪代码大致如下:
for iteration in range(num_iterations): # 采样阶段 trajectories = [] for problem in sample_problems(batch_size): chains = policy.generate(problem, num_samples=K, temperature=temp) for chain in chains: reward = compute_reward(problem, chain) trajectories.append((problem, chain, reward)) # 计算优势函数 advantages = compute_gae(trajectories, gamma=0.99, lam=0.95) # 多轮PPO更新 for epoch in range(ppo_epochs): for batch in split_batches(trajectories, minibatch_size): loss = ppo_loss(policy, ref_policy, batch, advantages, clip_range=0.2, kl_coef=kl_coef) loss.backward() optimizer.step() # 自适应调整KL系数 kl_coef = adapt_kl_coef(kl_coef, measured_kl, target_kl=0.01)关键超参数我列一下自己的常用配置:batch_size按题目数算,一般32到64;每条题采样K等于8到16;PPO更新轮数2到4;minibatch_size按token数算,大概4096到8192;学习率1e-6到5e-6;裁剪范围0.2;GAE的gamma 0.99,lambda 0.95;目标KL 0.01。这些值不是绝对的,需要根据模型规模和任务难度微调。
训练过程中要监控几个指标:平均奖励、正确率、KL散度、生成长度、格式合规率。平均奖励和正确率应该稳步上升,如果震荡剧烈说明学习率太大或者batch太小。KL散度要控制在目标值附近,持续偏高说明约束不够。生成长度如果突然暴涨,可能是模型学会了用冗长推理来骗过程奖励,需要检查奖励设计。格式合规率应该很快接近1,如果一直上不去,说明格式奖励权重不够或者模型能力不足。
4.4 模型评估与迭代优化
评估不能只看最终准确率,还要看推理链的质量。我一般从三个维度评估:答案正确率、推理步骤的逻辑连贯性、以及解法的多样性。答案正确率是硬指标,在留出测试集上算。逻辑连贯性靠人工抽检,看推理链有没有跳步、循环论证、或者中间计算错误。解法多样性看模型对同一道题生成的不同推理链,如果所有链都一模一样,说明探索不足,需要调高温度或增加采样数。
迭代优化的节奏是:每跑完一轮完整训练,做一次全面评估,找出最弱的环节针对性改进。如果正确率上不去但格式很好,可能是奖励信号太稀疏,需要加强过程奖励。如果正确率高但推理链质量差,可能是过程奖励权重太低,模型只关心答案不关心过程。如果训练后期正确率反而下降,大概率是过拟合或者KL约束失效,需要早停或者调大KL系数。
我自己的项目里,从冷启动到最终模型大概迭代了十几轮,每轮都有明确的改进目标。最有效的一次改进是把过程奖励从逐token改成按步骤计算,训练稳定性提升了一大截。最坑的一次是盲目加大过程奖励权重,结果模型学会了写漂亮但无用的推理,白白浪费了两周算力。
5. 常见问题与排查技巧实录
5.1 训练不收敛或奖励震荡
这是最常见的问题,表现是平均奖励上下大幅波动,正确率不升反降。原因通常有三个:学习率太大、batch太小、或者奖励信号噪声太大。
排查顺序是先看学习率,把它降到原来的三分之一再跑几百步,如果震荡减轻就是学习率问题。如果降了还震荡,检查batch size,把每条题的采样数K翻倍,或者增加题目数。如果还不行,就是奖励信号本身噪声大,需要检查验证器是否有误判,或者过程奖励模型是否校准到位。
我遇到过一次特别隐蔽的情况:验证器对某类题目的答案判断有bug,把正确答案判成错误,导致模型收到矛盾的奖励信号。这种问题只能靠人工抽检验证器的输出发现,建议每隔一段时间就手动检查一批验证结果。
5.2 模型学会钻奖励空子
奖励黑客是强化学习里的经典问题,在数学推理场景下表现得很隐蔽。我见过几种典型模式:一是生成超长推理链,把过程奖励的均值拉高,但实际逻辑空洞;二是反复重复同一句话,因为格式奖励对重复没有惩罚;三是直接输出最终答案不写过程,因为结果奖励只看答案对不对。
对付奖励黑客,核心思路是让奖励函数尽量难以被简单策略欺骗。具体手段包括:对推理链长度设上限,超过就扣分;对重复内容做惩罚;要求必须有推理过程才给结果奖励。另外,定期用人工评估检查模型输出,一旦发现新的钻空子模式,就针对性修补奖励函数。
提示:奖励函数的设计是一个持续对抗的过程,不要指望一次设计就能一劳永逸。建议每轮训练后都做一次奖励黑客检查。
5.3 推理链质量差但答案正确
这种情况说明模型学会了“蒙对答案”,推理过程完全是错的或者无关的。根源通常是结果奖励权重过高,过程奖励形同虚设。解决办法是提高过程奖励的权重,同时降低结果奖励的权重,让模型意识到过程质量也重要。
但过程奖励权重也不能无限提高,否则会走向另一个极端,模型只顾过程漂亮不管答案对错。我的经验是结果奖励和过程奖励的权重比控制在7比3到8比2之间比较合适。如果推理链质量还是差,可以考虑引入对抗训练,用一个判别器专门识别“过程漂亮但答案错”的推理链,给这类样本额外的负奖励。
5.4 训练后期性能退化
训练后期正确率不升反降,通常是过拟合或者策略崩溃。过拟合的表现是训练集正确率继续上升但验证集下降,这时候需要早停或者加正则。策略崩溃的表现是生成质量突然变差,推理链变得混乱,这通常是KL约束失效或者学习率在后期没有衰减。
我的处理方式是:训练后期把学习率线性衰减到初始值的十分之一,同时把KL系数逐步调大,让策略更新越来越保守。另外,保留每一轮的最优模型 checkpoint,不要只用最后一轮的模型。我一般会保存验证集正确率最高的那个 checkpoint 作为最终模型。
5.5 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决手段 |
|---|---|---|---|
| 奖励震荡不收敛 | 学习率过大、batch过小、奖励噪声大 | 逐步降低学习率、增大采样数、检查验证器 | 调小学习率、增大batch、校准奖励 |
| 推理链冗长空洞 | 过程奖励权重过高、长度无惩罚 | 检查过程奖励权重、统计生成长度分布 | 降低过程奖励权重、加长度惩罚 |
| 答案对但过程错 | 结果奖励权重过高 | 人工抽检推理链质量 | 提高过程奖励权重、引入对抗判别 |
| 训练后期退化 | 过拟合、KL约束失效 | 对比训练集和验证集曲线、监控KL散度 | 早停、学习率衰减、调大KL系数 |
| 格式合规率低 | 格式奖励权重不足 | 统计格式违规类型 | 提高格式奖励权重、优化输出模板 |
| 采样效率低 | 温度过低、采样数不足 | 统计正确样本占比 | 调高温度、增加采样数 |
6. 我踩过的坑和几条实用心得
第一条心得是关于冷启动的。我一开始想跳过冷启动直接上PPO,结果模型连基本输出格式都学不会,采样出来的东西全是乱码,奖励信号完全没法用。后来老老实实先做监督微调,把格式和基本解题模式教会,再切强化学习,效率高了很多。冷启动这一步不能省,它是整个流程的地基。
第二条是关于奖励权重的。我试过很多组权重配置,最后发现没有一组是万能的。不同难度、不同类型的题目,最优权重都不一样。我的做法是把权重做成可配置的,训练过程中根据验证集表现动态调整。比如发现推理链质量下降就调高过程奖励,发现正确率停滞就调高结果奖励。这种动态调整比固定权重效果好不少。
第三条是关于评估的。不要只看最终准确率,一定要看推理链。我见过太多模型准确率不错但推理过程一塌糊涂的情况,这种模型在实际应用里根本没法用,因为用户需要看到可信的解题过程。评估的时候至少抽检一百条推理链,人工判断逻辑是否连贯、步骤是否完整、有没有跳步或循环论证。
第四条是关于算力预算的。数学推理强化学习非常吃算力,采样和验证的开销远大于普通监督学习。我建议在开始之前先估算一下预算:每条题采样K条链,每条链平均L个token,每轮训练N道题,跑M轮,总共需要生成多少token,验证器需要跑多少次。这个估算能帮你判断方案是否可行,避免跑到一半发现算力不够。
最后分享一个提高采样效率的小技巧:对难题做难度分层采样。简单题采样少一点,难题采样多一点,把算力集中在模型还不会的题目上。具体做法是先用当前模型对题目集做一次快速评估,按正确率把题目分成易、中、难三档,采样时给难题分配更多的采样条数。这个技巧能让同样的算力预算下,训练效率提升百分之二三十。