找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1857

积分

0

好友

247

主题
发表于 6 小时前 | 查看: 4| 回复: 0

在《历时两个月的GRPO 训练踩坑实录:63% -> 96% tool selection》中,我们把工具选择准确率从 63% 做到了 97%。模型上线后,我们以为可以收工了。然而,当我们拆开线上数据仔细分析时,却发现了一个严峻的问题:在多步组合场景下,模型的准确率仅为63%。 一切仿佛又回到了原点。

97% 的假象

模型在线上平稳运行了两周,整体指标看起来相当漂亮:工具选择准确率稳定在 95% 以上。但当我们按场景进行细分统计时,真相浮出了水面。对于需要多步组合完成的复杂场景(例如“如果余额够买X股,就帮我下单”),模型的准确率只有可怜的 63%。

多步场景到底难在哪?

我们抽取了数十条多步场景下的错误案例进行分析,发现了一个明显的规律:模型的推理过程(reasoning)写得逻辑清晰、头头是道,但最终选择的工具(tool)却经常是错误的。

来看一个典型错误案例:

用户: “先查一下余额,如果够的话帮我转 10 万到理财账户”

模型输出:
{
"thought": “用户想先确认余额是否充足,然后转账。需要先查余额。”,
"action": {"tool": “transfer_funds”, “params”: {“amount”: 100000}},
"plan": [“check balance”, “transfer if sufficient”]
}

你看,reasoning 写得完全正确——“需要先查余额”。但是 action 却直接跳到了 transfer_funds(转账)。thought 和 action 完全脱节了。

在单步场景下,这个问题几乎不会出现。“查余额”只对应 check_balance,“买基金”只对应 place_order,用户的意图和所需工具是一对一的映射关系。但在多步场景中,一句话同时涉及 check_balancetransfer_funds,模型不仅需要理解用户的完整意图,还必须判断出当前步骤应该执行哪个工具。这对模型推理能力的要求是完全不同的。

问题根源出在两个层面:一是原有的输出格式不足以支持复杂的多步推理表达;二是奖励函数的精度跟不上复杂场景的评判需求。

奖励函数升级:从规则匹配到 Ground Truth 比对

我们在上一版中使用的奖励函数是基于规则的:检查输出格式是否完整、工具是否在合法列表中、是否包含了 reasoning 字段。这在单步场景下基本够用——因为格式正确的输出,其工具选择大概率也是正确的。

但在多步场景下,这套规则就失效了。check_balancetransfer_funds 都是合法工具,输出格式也都完整,规则匹配给出的分数几乎一样。这种粗糙的奖励函数根本无法区分“对的第一步”和“对的第二步”。

升级方向非常明确:放弃猜测,直接比对 Ground Truth。不再依赖规则去推测好坏,而是直接将模型的输出与人工标注的标准答案进行精确匹配。

class MultistepGTReward:
    def __call__(self, prompts, completions, **kwargs):
        solutions = kwargs.get("solution", [])
        rewards = []
        for completion, solution in zip(completions, solutions):
            parsed = parse_json(completion)
            expected_tool = parse_json(solution).get(
              "action", {}).get("tool")
            actual_tool = parsed.get("action", {}).get("tool")

            # Tool correctness — 占最大权重
            if expected_tool and actual_tool == expected_tool:
                t_score = 0.5    # 选对了
            elif actual_tool in VALID_TOOLS:
                t_score = 0.1    # 选错了但合法,保留格式激励
            else:
                t_score = 0.0

            # Params bonus
            p_score = 0.2 if has_valid_params(parsed) else 0.0

            # Reasoning + Plan
            r_score = reasoning_score(parsed)  # 最高 0.3

            rewards.append(t_score + p_score + r_score)
        return rewards

这个奖励函数的设计有几个关键考量:

1. 工具正确性(Tool correctness)占 0.5 分,这是最大的单项分数。选对和选错之间的分差达到 0.4,足以产生明确、强烈的学习信号,告诉模型“选对工具至关重要”。

2. 选错但合法的工具给 0.1 分,而不是 0 分。这很关键。如果完全不给分,模型可能会发现“什么都不输出”的期望奖励比“输出一个错误但合法的工具”更高,从而导致它连基本的输出格式都不愿意保持。

3. Reasoning 和 Plan 作为辅助信号保留,鼓励模型进行思考,但其权重(最高0.3)明确低于工具选择的正确性。

设计奖励函数的核心原则就一条:你最关心什么,就把最大的权重分配给什么。对于工具选择任务,工具的正确性就是最高优先级。

多步场景下的格式强化

奖励函数的升级解决了“如何评判”的问题,但多步场景还需要解决“如何表达”的问题。原有的输出格式过于紧凑,模型没有足够的空间展开复杂的逐步推理。

我们做了以下几项调整来强化格式:

加长思考空间:将 max_completion_length 从 200 提高到 350。原来的 200 个 token 对简单单步场景够用,但多步推理需要模型把“先做 A,因为 B,然后才能做 C”这个完整的逻辑链条写清楚。

增加探索多样性:将 num_generations 从 4 提高到 8。多步场景的正确答案路径往往不那么直观,需要更多的采样才能覆盖到正确的推理序列。

奖励推理深度:我们设计了一个逻辑关键词列表,如果模型的 thought 字段中包含这些词(如 “first”、“then”、“because”、“before”),会获得额外的加分。这不是为了鼓励凑字数,而是为了激励模型将内在的推理链显式地写出来——写出来的、可追溯的推理,远比模型内部的“心算”更可靠,也便于后续调试和分析。

LOGIC_KEYWORDS = ["first", "then", "before", "because",
                  "therefore", "need to", "in order to", "since"]

# thought 中包含逻辑词 → +0.05
if any(kw in thought.lower() for kw in LOGIC_KEYWORDS):
    r_score += 0.05

Plan 字段从装饰品变为核心组件:之前 plan 只是一个可选的加分项,现在要求 plan 字段非空才能获得这部分分数。在多步场景中,plan 不是锦上添花,而是模型理解任务整体结构和步骤顺序的关键证据。

数据准备:将多步场景展开

多步训练数据需要特殊的处理方式。每一个多步用户指令,都会被展开成多条训练样本,每条样本对应执行链条中的一步:

场景: “查余额,够的话转账 10 万”

样本 1 (第一步):
  input: “查余额,够的话转账 10 万”
  output: {"thought": “先查余额...”, “action”: {“tool”: “check_balance”}, “plan”: [“check”, “transfer”]}

样本 2 (第二步):
  input: “查余额,够的话转账 10 万\n\nPrevious steps:\nStep 1: check_balance → 余额 50 万”
  output: {"thought": “余额充足,执行转账...”, “action”: {“tool”: “transfer_funds”}, “plan”: [“transfer”]}

请注意第二步的 input,它包含了前一步的执行结果(“余额 50 万”)。这意味着模型在决定当前步骤的行动时,不仅要理解用户的原始意图,还必须理解和结合当前的执行状态。这比单步场景的难度高出一个数量级。

最终,我们构建的数据集包含 454 条基线单步数据 + 823 条由多步场景展开得到的数据。

NGRPO 依然不可或缺

训练完成后,我们查看了 NGRPO 的统计信息:Zero std: 3243/5108 (63.5%)

这意味着超过六成的采样组(group)内,所有生成结果获得的 reward 是完全相同的。试想一下,如果没有 NGRPO 机制向这些零方差组中注入虚拟的满分样本,这些组产生的梯度就是零,模型从这些样本中将什么也学不到。

一个有趣的现象是,上一轮在简单场景训练时,zero-std 的比例是 67%,而在这次更复杂的多步场景训练中,这个比例反而降到了 63.5%。这可能是因为任务变难了,模型对于同一个问题的生成结果更加多样化,导致 reward 的方差自然增大了一些。

无论如何,这个数据再次证明,NGRPO 不是一次性 Tricks,而是 GRPO 这类离线强化学习训练方法的基础设施。只要你的任务中存在大量模型“已经掌握”或容易产生同质化输出的样本,NGRPO 就是保证训练信号持续流动的关键。在探索复杂任务如多步推理时,理解并善用这类技巧至关重要。

最终结果

经过上述一系列优化,我们得到了令人满意的结果:

  • 多步场景准确率:从 63% 提升至 97%
  • 整体准确率:维持在 97%,且单步场景的性能没有发生退化。

剩余的 3% 错误案例经分析全部属于同一类型:

“I need to take out 75 rupees from ACC888”  → 模型选 withdraw,标注是 check_balance
“Withdraw 50 rupees from ACC002”            → 模型选 withdraw,标注是 check_balance
“Made profit in day trading. Can I withdraw it tonight?” → 模型选 withdraw,标注是 check_balance

在这些案例中,用户明确说出了“取钱”、“withdraw”,模型选择 withdraw 工具是完全符合直觉的。而标注人员出于业务安全流程的考虑,认为应该先 check_balance 确认余额。这本质上是标注标准层面的歧义,而非模型本身的能力问题。

因此,我们判断 97% 的准确率已经接近当前数据集和任务定义下的性能天花板

经验总结

  1. 奖励函数的精度必须与任务难度同步升级。简单任务用规则匹配或许足够,但面对复杂任务(如多步推理),必须采用与 Ground Truth 直接比对的精确奖励。这不是说最初的方案错了,而是当场景发生变化时,我们的技术方案必须随之进化。
  2. 分数分配直接反映了优化优先级。Tool correctness 0.5分,Params 0.2分,Reasoning 0.3分——这个权重分配清晰地告诉模型:选对工具是最重要的。你最关心什么,就应该在奖励函数中给予最大的权重。
  3. 为模型提供足够的“思考”空间completion_length(输出长度)和 num_generations(采样数量)等超参数需要与任务复杂度匹配。多步推理需要更长的文本来展开逻辑,也需要更多的采样来覆盖正确的推理路径。
  4. 知道何时该停止优化。当剩余的错误主要由标注歧义或任务定义模糊导致时,继续在模型层面进行优化是徒劳的。此时应该转而审视数据和质量标准。

希望这篇针对复杂任务进行强化学习优化的实战记录,能为你提供一些可行的思路。如果你在实践GRPO或类似技术时遇到瓶颈,或许可以来云栈社区与更多同行交流探讨。




上一篇:OpenAI回应GitHub服务中断,传正开发自有代码托管平台
下一篇:手把手教程:在Windows本地部署OpenClaw代理网关与联通云模型
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-3-5 19:12 , Processed in 0.521865 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表