强化学习训练PAPO方法

先用“答对”保方向,再用“过程质量”做精修,并且两者分开处理,避免模型靠啰嗦作弊。

当然,三个缩写的全称如下:

  1. ORM = Outcome Reward Model
    (结果奖励模型)
  2. PRM = Process Reward Model
    (过程奖励模型)
  3. PAPO = Process-Aware Policy Optimization
    (过程感知策略优化)

在训练推理模型时,常见有两种打分方式:

  1. 只看结果对不对(ORM)

    • 好处:简单直接
    • 问题:只要答案对,过程乱写也一样得高分。
    • 结果:模型可能“蒙对”,但推理质量不提升。
    • 另外,当一组回答都对了,模型就不知道该向谁学习(奖励信号变弱)。
  2. 看推理过程好不好(PRM)

    • 好处:能区分“谁推理更清晰、更严谨”
    • 问题:模型会“刷分作弊”,比如写很长很啰嗦的过程来骗高分(reward hacking),最后准确率反而掉了。

论文提出的 PAPO,核心思路是:
把“结果分”和“过程分”分开计算、分开归一化,再合起来训练。

你可以把它理解成老师评分:

  • 结果分(A_out):先保证题做对(底线)
  • 过程分(A_proc):只在“已经做对的人”里比较谁解题过程更好(拔高)

这就避免了两种极端:

  • 只看结果 → 思路质量上不去
  • 只看过程 → 容易啰嗦作弊

为什么“分开归一化”这么关键?

通俗讲:
如果把全班都混在一起按过程打分,错题但写超长的人可能分还挺高,训练方向就歪了。
PAPO 改成:先看对错,再在正确答案内部比过程质量,就不会让“错误但啰嗦”占便宜。

是的,这篇论文的实现核心可以用一条“训练流水线”讲清楚。你不用懂太多公式,也能明白它怎么做。


PAPO 训练怎么实现(通俗版)

它基于 GRPO 做了一个改造:
每轮采样一组回答,然后分两路打分,再合成一个训练信号。

第 1 步:对同一个题目,生成一组回答

比如同一个数学题,模型一次生成 8 个解答(一个 group)。

第 2 步:给每个回答打“结果分”(ORM)

  • 看最终答案对不对(对=高,错=低)
  • 这是 correctness 信号(底线)

第 3 步:给回答打“过程分”(PRM)

  • 看推理步骤是否规范、清晰、符合 rubric(评分标准)
  • 这是 reasoning quality 信号(上限)

第 4 步:关键改造——“分开归一化”

这是 PAPO 的核心,不是简单把两种分数加起来。

4.1 结果优势 (A_out)

  • 用 ORM 分数算
  • 整组回答里做归一化(对+错都参与)
  • 作用:保证训练方向始终锚定“答对”

伪代码

太好了,给你一个工程视角的 PAPO 伪代码(简化版),你能直接看懂它怎么接到 GRPO 里。


# 输入:
#   q: 一个问题
#   policy: 当前模型
#   ORM: 结果打分器(只看答案对错)
#   PRM: 过程打分器(看推理质量)
#   K: 每题采样回答数(group size)

def papo_train_step(q, policy, ORM, PRM, K=8):
    # 1) 采样一组回答
    responses = [policy.generate(q) for _ in range(K)]

    # 2) 打结果分(Outcome scores)
    #    例如:答对=1,答错=0,或概率分
    out_scores = [ORM.score(q, r) for r in responses]

    # 3) 标记哪些是“正确回答”
    correct_mask = [s > 0.5 for s in out_scores]  # 示例阈值

    # 4) 打过程分(Process scores)
    #    PRM可用rubric评分:步骤完整性、逻辑性、简洁性等
    proc_scores = [PRM.score(q, r) for r in responses]

    # 5) 计算 A_out:在全组上归一化(对+错都参与)
    #    作用:锚定正确性
    A_out = normalize_over_group(out_scores)  # z-score或rank normalize

    # 6) 计算 A_proc:只在“正确回答子集”中归一化
    #    作用:比较“正确答案之间”的推理质量
    A_proc = [0.0] * K
    correct_idx = [i for i, ok in enumerate(correct_mask) if ok]

    if len(correct_idx) >= 2:
        corr_proc = [proc_scores[i] for i in correct_idx]
        corr_proc_norm = normalize_over_subset(corr_proc)
        for j, i in enumerate(correct_idx):
            A_proc[i] = corr_proc_norm[j]
    # 若正确样本太少,可退化为全0或小权重

    # 7) 合成总优势(核心)
    #    alpha, beta 控制结果信号与过程信号权重
    alpha, beta = 1.0, 0.5
    A = [alpha * A_out[i] + beta * A_proc[i] for i in range(K)]

    # 8) 用总优势A做 GRPO/PPO 风格更新
    #    (logprob ratio + clip + KL penalty 等)
    loss = grpo_objective(policy, q, responses, advantages=A)
    policy.optimize(loss)

    return loss

这段代码的关键点(你记这3句就够)

  1. A_out 全组归一化:保证“答对”永远是主目标
  2. A_proc 仅正确子集归一化:只在“答对的人里”比谁推理更好
  3. A = A_out + A_proc先保底,再拔高

如果你要工程落地,最容易踩的坑

  • PRM评分不稳:先做 rubric 规则,避免“越长分越高”
  • 正确样本太少A_proc 会稀疏,前期可降低 beta
  • 权重设置:一般先高 alpha、低 beta,后期再慢慢提高 beta
  • 长度作弊:给 PRM 增加“冗长惩罚项”或长度归一化

如果你愿意,我下一步可以给你一个更贴近真实训练框架(TRL/GRPO)的版本,把 normalizeobjective 也展开成可实现模块清单。

4.2 过程优势 (A_proc)

  • 用 PRM 分数算
  • 只在答对的回答子集里归一化
  • 作用:只比较“正确答案之间谁过程更好”,避免错误答案靠啰嗦拿高分

第 5 步:合成总优势

把两部分组合成总训练信号:

  • 一部分保“对错”
  • 一部分提“过程质量”

然后按 GRPO/PPO 风格做策略更新(更新模型参数)。


为什么这样能避免“啰嗦刷分”

如果不分开,PRM 容易被钻空子:
模型可能写特别长、特别花哨,但答案错了,也拿到高过程分。

PAPO 的限制是:

  • 错答案先在结果分上吃亏
  • 过程分主要只在正确答案里比较

所以模型会学到:

  1. 先答对
  2. 再把过程写好
    而不是“胡写长文骗分”。

你可以把它理解成“考试评分机制升级”

  • ORM:只看最后答案
  • PRM:看解题步骤
  • PAPO:先卡答案正确,再在正确答案里比步骤质量

这就是它训练上比纯 ORM 更持续提升的原因。


如果你愿意,我可以下一步给你一个“伪代码版本”(10行左右),把 PAPO 在 GRPO 里怎么接进去写成工程视角流程。

posted @ 2026-03-31 08:39  向着朝阳  阅读(2)  评论(0)    收藏  举报