GRPO的数据集,告别“标准答案”

发布于 2025-09-23 分类: AI

第二步:构建数据集——GRPO的“原料”有何不同

在上一章中,我们为项目绘制了详细的“蓝图”,精确定义了我们的任务。现在,是时候准备“建筑材料”了——也就是我们的训练数据集。

如果你有监督微调(SFT)的经验,你可能会下意识地开始思考:“我该去哪里找成千上万个由DBA专家手写的、针对各种问题的最优SQL查询呢?”

请停下来。这正是GRPO训练的魅力所在:它将我们从对“完美标准答案”的无尽追求中解放出来。 GRPO的数据准备工作比SFT要简单得多,也更能体现我们作为AI训练专家的智慧。

1. 与监督微调(SFT)的根本区别

让我们通过一个直观的对比,来理解这两种方法在数据需求上的本质差异。

  • SFT 数据集格式:

    一个典型的SFT数据点,是一个严格的“提示-完成”对 (Prompt-Completion Pair)。

    {
      "prompt": "问题:查询用户表中年龄最大的前3位用户的姓名。 Schema: CREATE TABLE users(id INT, name VARCHAR, age INT);",
      "completion": "SELECT name FROM users ORDER BY age DESC LIMIT 3;"
    }
    

    这里的 "completion" 必须是一个高质量的、甚至是“黄金标准”的答案。模型的学习目标就是像素级地模仿这个答案。

  • GRPO 数据集格式:

    而一个GRPO数据点,则是一个“提示-验证元数据”对 (Prompt-Verification Metadata Pair)。

    {
      "prompt": {
        "question": "查询用户表中年龄最大的前3位用户的姓名。",
        "schema": "CREATE TABLE users(id INT, name VARCHAR, age INT);",
        "constraints": ["禁止使用 OFFSET"]
      },
      "expected_result": "[{\"name\": \"Alice\"}, {\"name\": \"Bob\"}, {\"name\": \"Charlie\"}]"
    }
    

    看到了吗? 最大的区别在于,我们不再提供那个“完美的SQL”。取而代之的是一个名为 expected_result 的字段。这个字段存储的是当一个正确的SQL被执行后,应该返回的真实结果

    在训练过程中,模型是看不到 expected_result 的。它只会接收 prompt,然后生成自己的SQL。我们的“自动裁判”(奖励函数)会执行模型生成的SQL,并将结果与 expected_result 进行比对,以此来判断模型的回答是否“语义正确”。

    这才是整个流程中最精妙的地方。我们把对模型的评价标准,从**“你写得跟我给的范例像不像?”** 转变为了 “你写的代码,能不能解决实际问题?”

%%{
  init: {
    'theme': 'base',
    'themeVariables': {
      'primaryColor': '#ffffff',      
      'primaryTextColor': '#111827',   
      'nodeBorder': '#6b7280',         
      'lineColor': '#4b5563',          
      'fontSize': '14px'
    }
  }
}%%
graph TD
    subgraph "SFT 训练流程"
        A[Prompt] --> B{"SFT 模型"};
        B --> C["生成的 SQL"];
        D["黄金标准 SQL"]
        C -- 计算损失 (Loss) --> E{比较差异};
        D -- 对比 --> E;
        E --> F[更新模型];
    end

    subgraph GRPO 训练流程
        G[Prompt] --> H{"GRPO 模型"};
        H -- 生成 --> I["候选 SQL"];
        J[预期结果集]
        I -- 执行 --> K[实际结果集];
        K -- 比较 --> L{奖励函数};
        J -- 对比 --> L;
        L -- 生成奖励信号 --> M[更新模型];
    end

    %% 为高对比度和移动端可读性进行样式设计
    classDef gold std fill:#fefce8,stroke:#facc15,stroke-width:2px;
    classDef reward fn fill:#fff1f2,stroke:#fb7185,stroke-width:2px;
    classDef model fill:#f0f9ff,stroke:#38bdf8,stroke-width:2px;
    classDef final_step fill:#f0fdf4,stroke:#4ade80,stroke-width:2px;

    class D,J gold;     
    class L reward;      
    class B,H model;     
    class F,M final_step;

上图清晰地展示了两种模式的差异。SFT是静态的模仿,而GRPO是动态的、基于结果的验证与学习

2. 数据集生成实践

理解了原理,我们就可以动手构建自己的GRPO数据集了。好消息是,我们不必从零开始。我们可以巧妙地利用现有的、为SFT准备的Text-to-SQL数据集(如 Spider, WikiSQL 等),通过一个自动化流程将其转化为我们需要的格式。

下面是具体的生成逻辑,我们将一步步实现它:

  1. 第一步:获取源数据
    从现有的数据集中,我们可以轻松获得成千上万的 (自然语言问题, Schema, 黄金SQL) 三元组。这里的“黄金SQL”虽然我们不直接用它来训练模型模仿,但它将作为我们生成 expected_result 的关键工具。

  2. 第二步:执行黄金SQL,生成预期结果
    这是最核心的转换步骤。我们需要搭建一个本地的数据库环境(例如,使用SQLite或Docker化的PostgreSQL)。然后,编写一个脚本,遍历所有的三元组:

    • 在数据库中根据 Schema 创建相应的表结构。
    • (如果数据集提供)向表中填充样本数据。
    • 执行 黄金SQL
    • 获取 查询返回的结果。
  3. 第三步:序列化并存储结果
    数据库查询返回的结果通常是表格形式的。为了方便后续进行精确的、自动化的比较,我们需要将其序列化为一个标准格式,比如JSON字符串。

    • 重要提示:SQL查询结果的行顺序通常是不保证的,除非使用了 ORDER BY。为了确保比较的确定性,一个最佳实践是在序列化为JSON之前,对结果集(比如,一个字典列表)按照所有键进行排序,确保无论原始行顺序如何,最终的JSON字符串都是唯一的。
    • 处理完后,expected_result 字段的内容可能看起来像这样(一个排序后的JSON字符串):"[{\"name\": \"Alice\"}, {\"name\": \"Bob\"}, {\"name\": \"Charlie\"}]"
  4. 第四步:(可选)丰富约束条件
    为了让我们的模型训练任务更具挑战性,也更贴近真实世界的复杂需求,我们可以编写一个脚本,为每个问题自动、随机地生成一些额外的约束。

    • 例如
      • 随机挑选一个SQL关键字(如 JOIN, GROUP BY, WHERE)并加入到约束列表 ["禁止使用 'JOIN'"]
      • 随机设置一个执行时间上限 ["执行时间不能超过 2 秒"]
      • 要求必须使用某种语法结构 ["必须使用 CTE"]
        通过这种方式,我们可以极大地扩充训练数据的多样性,迫使模型学习在不同限制下灵活构建查询的能力。
  5. 第五步:组合成最终格式
    最后,我们将所有处理好的部分组合成最终的数据格式,并保存为一个JSONL文件。

    {
      "prompt": {
        "question": "找出上个月所有‘电子产品’类别中,销售额超过 $1000 且利润率最高的前五种商品。",
        "schema": "CREATE TABLE products(...); CREATE TABLE sales(...);",
        "constraints": ["禁止使用子查询"]
      },
      "expected_result": "[{\"product_name\": \"SuperPhone X\"}, {\"product_name\": \"UltraLaptop Pro\"}, ...]"
    }
    

通过以上步骤,我们就能高效地、大规模地构建出适用于GRPO训练的数据集。我们没有花费任何精力去手写或校验“最优SQL”,而是建立了一套生成可验证事实的自动化流水线。这不仅效率更高,也让我们能将更多精力投入到更有创造性的工作中去,比如设计一个能够精确评估模型能力的奖励函数。

在下一章,我们将进入整个GRPO流程中最核心、最考验智慧的部分:设计奖励函数。它将是模型的“导航系统”,直接决定了我们最终能训练出一个怎样的“SQL专家”。


-- 感谢阅读 --