用 100 个 GRPO 步骤,让 3.5 亿参数模型更可靠地产生结构化输出

2026-09-03 26 预计阅读时间: 1 分钟
来源: huggingface.co AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:10 分钟

结构化输出的难点通常不在“能不能生成 JSON”,而在于模型能否持续遵守约束:字段完整、类型正确、枚举值合法,并且在输入稍微变化后仍然稳定。这个实验的核心思路,是围绕一个 3.5 亿参数模型设计专门的结构化输出奖励,再用 100 个 GRPO 步骤快速验证微调是否有效。

这里的 100 步更像一个小规模实验预算,而不是适用于所有任务的固定结论。它的价值在于缩短反馈周期:先用明确的奖励函数验证方向,再决定是否扩大数据量、训练步数或模型规模。

为什么 GRPO 适合这个任务

GRPO(Group Relative Policy Optimization)会针对同一个输入采样一组回答,然后根据组内相对奖励更新模型。对于结构化输出任务,这种机制很实用,因为一次采样可以同时暴露多种失败模式:

  • 有的回答是合法 JSON,但缺少必需字段。
  • 有的字段存在,但类型错误,例如把数字写成字符串。
  • 有的格式正确,却违反业务规则。
  • 有的回答内容合理,只是混入了 Markdown 代码围栏或额外解释。

奖励函数不必一开始就复杂。可以把“可解析”“字段完整”“类型正确”和“业务语义正确”拆成多个可观察信号,再按优先级组合。这样做比只用一个模糊的文本相似度分数更适合约束输出。

对于 350M 级别模型,训练成本相对可控,但容量也有限。因此训练目标应当收窄到一个清晰的输出协议,例如“根据工单文本返回固定 JSON”,而不是同时要求模型掌握复杂推理、长上下文和多种工具调用格式。

把结构化输出写成奖励函数

下面的示例不依赖深度学习框架,可以直接运行。它模拟一个训练前的奖励评估器:输入模型候选回答,检查 JSON、字段、类型、枚举值以及额外文本。实际接入 GRPO 框架时,可以把 structured_reward 作为每个 rollout 的奖励函数。

运行前只需要 Python 3.9 或更高版本:

python reward_demo.py

将下面内容保存为 reward_demo.py

import json
from typing import Any, Dict

REQUIRED_FIELDS = {
    "category": str,
    "priority": int,
    "action": str,
}
VALID_CATEGORIES = {"billing", "technical", "account"}
VALID_ACTIONS = {"reply", "escalate", "close"}


def structured_reward(text: str) -> float:
    """Return a 0..1 reward for a strict support-ticket JSON protocol."""
    text = text.strip()

    # Penalize common behavior that breaks JSON parsers downstream.
    if text.startswith("```") or text.endswith("```"):
        return 0.0

    try:
        value: Any = json.loads(text)
    except json.JSONDecodeError:
        return 0.0

    if not isinstance(value, dict):
        return 0.1

    score = 0.2  # Valid JSON object.

    if set(value) == set(REQUIRED_FIELDS):
        score += 0.2
    else:
        return score

    for field, expected_type in REQUIRED_FIELDS.items():
        # bool is an int subclass, but is not a valid priority here.
        if field == "priority":
            type_ok = type(value[field]) is int
        else:
            type_ok = isinstance(value[field], expected_type)
        if not type_ok:
            return score
    score += 0.25

    if value["category"] in VALID_CATEGORIES:
        score += 0.15
    if value["action"] in VALID_ACTIONS:
        score += 0.15
    if 1 <= value["priority"] <= 5:
        score += 0.05

    return round(score, 2)


if __name__ == "__main__":
    candidates = [
        '{"category":"technical","priority":2,"action":"reply"}',
        '```json\n{"category":"technical","priority":2,"action":"reply"}\n```',
        '{"category":"other","priority":"high","action":"reply"}',
    ]

    for candidate in candidates:
        print(f"{structured_reward(candidate):.2f}  {candidate}")

这个奖励函数体现了一个重要顺序:先保证机器可解析,再检查协议完整性,最后奖励业务约束。不要让“内容看起来合理”覆盖格式错误,否则模型可能学会输出一段人类可读但无法被服务消费的文本。

在真实训练中,可以为不同失败级别设置更平滑的分数。例如,合法 JSON 但缺字段的回答得到 0.2,字段完整但枚举值非法得到 0.65,完全符合协议的回答得到 1.0。组内相对比较会让模型更容易识别哪些候选更好。

100 步实验应该观察什么

短训练最适合回答三个问题,而不是证明模型已经完成生产级优化。

1. 格式错误是否快速下降

记录每个步骤中以下指标的变化:

  • JSON 解析成功率。
  • 必需字段完整率。
  • 字段类型正确率。
  • 枚举值合法率。
  • 是否出现额外解释或 Markdown 围栏。

如果 100 步后只有训练集格式正确率上升,而稍有改写的输入仍然失败,说明模型可能只是记住了模板。此时应增加输入多样性,而不是继续盲目增加训练步数。

2. 奖励是否被投机利用

奖励函数存在漏洞时,模型会找到评分最高但业务无效的输出。例如,奖励只检查字段存在,模型就可能输出空字符串;只检查 JSON 合法,模型就可能使用无意义的默认值。

因此,验证集必须包含边界输入:缺少信息、冲突信息、未知类别、超范围优先级,以及很长或很短的文本。奖励函数也要加入业务有效性检查,必要时对空值、默认值和重复内容扣分。

3. 训练是否损害原有能力

350M 模型的参数容量有限,窄任务微调可能影响通用指令跟随能力。除了目标格式测试,还应保留一组未参与训练的通用指令、拒答和长文本样例,观察训练后是否出现明显退化。

可以把实验配置记录成简单的 YAML,便于复现和比较:

model: your-350m-instruction-model
algorithm: grpo
steps: 100
rollouts_per_prompt: 8
max_new_tokens: 160
learning_rate: 1.0e-6
reward:
  parser: json
  required_fields: [category, priority, action]
  reject_markdown_fence: true
validation:
  - json_parse_rate
  - schema_valid_rate
  - enum_valid_rate
  - extra_text_rate

这里的字段名是实验配置示例,具体参数需要按所使用的训练库调整。rollouts_per_prompt 越大,组内比较通常越稳定,但显存和生成成本也会增加;max_new_tokens 则应贴合协议长度,避免模型通过无关长文本获得训练机会。

采用前的检查清单

短步数 GRPO 适合做方向验证:成本低、反馈快,能够迅速判断奖励设计是否真的推动了结构化输出。它并不能替代更大规模的验证,也不能保证模型永远生成合法 JSON。

落地时建议保留以下边界:

  • 推理服务仍使用 JSON Schema、类型校验或解析重试做最终防线。
  • 训练奖励和离线评估使用同一套严格协议,但不要只用训练奖励衡量效果。
  • 验证集加入未见过的字段组合、输入改写和异常样例。
  • 同时监控结构化输出成功率与原有指令能力,避免局部指标变好、整体体验变差。
  • 先运行 100 步小实验,确认奖励没有漏洞,再扩大训练规模。

真正值得优化的不是“让模型看起来更像 JSON”,而是让模型在清晰协议、可测奖励和服务端校验共同作用下,稳定地产生下游系统能够信任的结构化结果。


相关推荐