返回 AICAP-180
B11 · Day 107GRPO/后训练机理 + 首跑

GRPO 训练循环搭建

B11 这一批是「自己跑通一次后训练」的实操段,处在 B1→B18 能力曲线上「从会评判到会训练」的转折点。昨天 Day 106 把数据落成了 64 行 prompt+verifier 的 grpo_train.jsonl;今天 Day 107 把数据、reward 函数、超参三者装进一个真正的训练循环,并跑 50 步冒烟(smoke run)确认管线不崩。明天 Day 108 才上 ≥300 步

阶段: B11 · GRPO/后训练机理 + 首跑(Day 101-110) 标签: #GRPO #training-loop #hyperparameters #KL

今日导引(由浅入深)

B11 这一批是「自己跑通一次后训练」的实操段,处在 B1→B18 能力曲线上「从会评判到会训练」的转折点。昨天 Day 106 把数据落成了 64 行 prompt+verifier 的 grpo_train.jsonl;今天 Day 107 把数据、reward 函数、超参三者装进一个真正的训练循环,并跑 50 步冒烟(smoke run)确认管线不崩。明天 Day 108 才上 ≥300 步全量训练看曲线,Day 109 评测,Day 110 复盘。今天最小可判定产出:在本仓侧,离线验证奖励计算(grpoScore/groupRelativeAdvantage)正确;在 GPU 侧(待 GPU),产出 step-0 的起始 reward 基线与训练日志首条。今天的核心知识点是四个超参——lr / KL 系数 beta / group size G / max_steps——尤其 beta 的「太大学不动、太小崩溃」张力。

1. 机理精读

GRPO 目标的形状(不写完整公式,讲清结构)。 GRPO 每步对一个 prompt 采样一组 G 个 rollout,对组内每个 rollout 算优势 A_i(组内标准化奖励),然后最大化「优势加权的 token 对数概率」——优势为正的 rollout 提升其 token 概率,优势为负的压低。同时减去 KL 惩罚 β·KL(π_θ ‖ π_ref)。直觉:「组里比平均好的多生成一点,差的少生成一点,但整体别偏离参考太远」。

GRPO 的损失里为什么有 KL 项。 GRPO 在最大化组内优势加权的 token 概率的同时,对「当前策略 vs 参考策略」加一个 KL 惩罚 β·KL(π_θ ‖ π_ref)。参考策略通常是训练起点的冻结副本(或 SFT 模型)。KL 项是缰绳:防止策略为了刷奖励跑得离原模型太远而崩成胡言乱语。没有这根缰绳,RL 极易把模型推向「奖励高但输出退化」的角落(典型 reward hacking 失败模式)。

beta 的双向失效。 beta 太大 → KL 缰绳太紧,策略几乎动不了,reward 曲线早早平台;beta 太小 → 缰绳松,策略可能为追奖励而坍缩(输出退化、KL 爆炸)。因此实践常配 KL 系数调度(如先小后大或自适应),而不是固定一个 beta 从头跑到尾。这是今天配超参时最容易拍错的一个。

lr / G / max_steps 的角色。 lr 控制每步更新幅度(RL 后训练通常比 SFT 更小,避免一步打崩);G 是每个 prompt 的 rollout 数,决定组内优势估计的方差(Day 106 已述,常取 8-16);max_steps 决定总训练量——冒烟用 50 步只为验证管线,全量在 Day 108 上 ≥300 步。Unsloth 的 trainer 用 GRPOConfig 统一配置这些项,并挂一个 reward 函数列表(可同时挂格式奖励 + 正确性奖励,组合成多目标 reward)。

四超参一图速记。

超参控制什么太大太小
lr每步更新幅度一步打崩策略学得太慢
beta(KL系数)与参考策略偏离度缰绳太紧,学不动、早平台缰绳太松,策略坍缩、KL 爆
G(group size)组内优势估计方差算力线性涨、边际收益递减advantage 噪声大、梯度不稳
max_steps总训练量过拟合奖励 / 浪费算力没学够

记法:lr 和 beta 决定"稳不稳",G 决定"准不准",max_steps 决定"够不够"。冒烟阶段 max_steps=50 只验「稳」与管线连通,不验「准」与「够」。

为什么用参考策略而不是绝对约束。 KL 把策略锚在一个会动的相对基准(参考模型)上,而非给输出加硬约束。好处是:模型可以在参考的「邻域」里自由探索更高奖励的策略,只要不跑太远。这比硬约束更灵活,也是为什么 beta 调度(先松后紧或自适应)比固定 beta 常更稳——训练早期允许多探索,后期收紧防坍缩。

与相邻概念的边界。 与 PPO 的边界(Day 110 会做 TCO 对比):GRPO 用组内基线代替 PPO 的 value/critic 网络,所以省掉一个网络与其训练成本,但保留 KL 项。与昨天数据的边界:今天不改数据格式,只读 grpo_train.jsonl + 挂 Day 105 的可验证 reward。资源:Unsloth GRPO guide (2026-01) + GRPO 论文(arXiv:2402.03300)。

2. 推导 / 手算 / 代码走读

本日 reward 计算与 grpoScore 逻辑落在 src/agent/train/grpo.ts(已建 + 单测,可离线验证)。走读要点:

  • groupRelativeAdvantage(rewards, eps=1e-8):对一组 rewards 做标准化——求 meanvariancestd,返回 rewards.map(r => (r - mean) / (std + eps))。这正是 GRPO「无 critic」的核心:用组内统计量当基线,不需要一个学习型 value 网络。eps 防止 std=0(全组奖励相同)时除零。
  • grpoScore(group: GrpoSample[]):对一组 {output, reward}groupRelativeAdvantage 算优势,返回每条附 advantagereinforce: adv > 0reinforce=true 表示该 rollout 优于组均值、梯度方向应强化它。这把「优势>0 即强化」的 GRPO 直觉变成可单测的确定性函数。
  • composeRewards(parts):把多个 {weight, reward} 组件做加权平均(如 correctness 0.7 + format 0.3),totalW===0 时返回 0。这就是「挂 reward 函数列表」在纯数学层的等价物——多目标 reward 的合成。
  • exactMatchReward(output, target) / formatReward(output, pattern):两个确定性 verifier。前者 output.trim()===target.trim() ? 1 : 0;后者 pattern.test(output) ? 1 : 0。它们就是 RLVR 里「V」的最小实现,与 Day 106 数据里 verifier 字段一一对应。
  • GrpoSample 接口({output, reward})与返回类型grpoScore 接收 GrpoSample[],返回 Array<GrpoSample & {advantage, reinforce}>。类型层就把「输入是 rollout+奖励、输出附优势与强化标记」固定下来,让单测能对每条 rollout 的 advantage 符号做 deep-equal 断言——这是「无 GPU 也能保证奖励/优势逻辑正确」的工程支点。
  • 诚实边界:文件头注释明确——这是「纯数学、无 GPU、无 API key」的可信讲解工件,不是一次训练运行。它让我们能在没有 GPU 时也把组内优势/奖励合成讲对、测对;真正的 50 步训练仍待 GPU。

手算一组优势(验证 groupRelativeAdvantage 直觉,非新增测量):组奖励 [1, 0, 0, 1] → mean=0.5,variance=0.25,std=0.5 → 优势 [(1−.5)/.5, (0−.5)/.5, ...] ≈ [1, −1, −1, 1](eps 可忽略)。命中 reward 的两条 advantage>0 → reinforce,未命中的两条 advantage<0 → 抑制。这正是组内归一化的作用:绝对奖励噪声被组均值吸收,只看「相对组里其它人好不好」。

再验「组内归一化对绝对噪声鲁棒」:若整组奖励被系统性抬高 0.5 → [1.5, 0.5, 0.5, 1.5],mean=1.0、std=0.5 不变 → 优势仍是 [1,−1,−1,1]。绝对偏移被 mean 完全吸收,advantage 不变——这就是 GRPO 相对 PPO「对奖励绝对尺度不敏感」的来源。但注意:若噪声是系统性偏置(如所有长输出都多得 0.1),组均值吸收不掉它(因为它与某个特征相关而非常数偏移),仍需显式去偏(Day 105 的长度惩罚就是干这个的)。

composeRewards 多目标合成手算(非新增测量):correctness=1、format=0,权重 0.7/0.3 → (0.7×1 + 0.3×0)/(0.7+0.3) = 0.7。若 format 也满足(=1)→ (0.7+0.3)/1 = 1.0。这说明:内容对但格式坏只能拿 0.7,逼模型同时学会「答对」+「格式正确」——单挂 correctness 会让模型为内容牺牲结构。

3. 今日实战

  1. 配置 Qwen3-4B 的 GRPO 微调:在 GRPOConfig 里设:
    • learning_rate:取 RL 量级(远小于 SFT,避免一步打崩)。
    • beta(KL 系数):起点偏小 + 配 KL 调度,别一上来给大 beta。
    • num_generations(即 G):8-16,冒烟阶段可先取 8 省算力。
    • max_steps=50:仅冒烟,验管线不验能力。
  2. 挂上 Day 105 的可验证 reward(amlReward / composeRewards 组合的多目标奖励),读 Day 106 的 grpo_train.jsonl;确认 reward 函数列表里同时含「正确性」与「格式」两类。
  3. 启动 50 步短跑冒烟:逐项确认整条管线无报错——
    • 数据加载(grpo_train.jsonl 64 行被正确解析为 prompt+verifier);
    • 采样(每 prompt 出 G 个 rollout,温度中等);
    • reward 计算(verifier 对每个 rollout 返回 0/1,composeRewards 合成多目标分);
    • 组内优势(groupRelativeAdvantage 标准化);
    • KL 项与梯度更新(无 NaN/Inf)。
  4. 记录首条 reward 日志与 起始 reward 数值(step-0 基线)——这是 Day 110 算「相对 step-0 提升」的分母。
  5. 本仓侧(离线、无 GPU):跑 src/agent/train/grpo.ts 的单测,验证 grpoScore/groupRelativeAdvantage/composeRewards 计算正确,确保「奖励计算逻辑」与 GPU 端一致。
  6. 交叉引用 docs/aipa/day107-train-loop.md 记录超参选择与理由。

4. 今日实测 / 产出

  • docs/aipa/day107-train-loop.md + 50 步 train log + 起始 reward 数值。
  • 待 GPU:真实 50 步训练需 GPU;将产出 step-0 reward 基线与训练日志首条。
  • reward 函数与 grpoScore 逻辑(src/agent/train/grpo.ts已建 + 单测,可离线验证奖励计算正确——这部分不依赖 GPU。
  • 离线已就绪(不依赖 GPU):
    • groupRelativeAdvantage:组内标准化优势,单测验证。
    • grpoScore:附 advantage + reinforce 标记,单测验证。
    • composeRewards:多目标加权合成,单测验证。
    • exactMatchReward / formatReward:确定性 verifier,单测验证。
  • 待 GPU 才能产出:GRPOConfig 实跑、step-0 reward、KL/loss 首条日志。
  • 状态诚实标注:奖励计算正确性已验证(离线);实际 50 步训练待 GPU,未升级为已完成。

5. 常见误区 / 陷阱

  1. beta 沿用过大默认:直接套一个偏大的 KL 系数会让 reward 早早平台、误判「模型学不动是任务难」,实则是缰绳太紧。这正是 SOTA 检查里点名要避开的旧默认。
  2. lr 照搬 SFT:RL 后训练对 lr 更敏感,SFT 量级的 lr 容易一步打崩策略。
  3. 50 步冒烟当成果跑:冒烟只验管线不崩,不验能力提升;50 步 reward 上不去是正常的,别据此下结论。能力提升要等 Day 108 的 ≥300 步全量。
  4. 只挂正确性 reward 不挂格式:会让模型答对内容却破坏输出结构;多目标 reward(composeRewards)要同时覆盖正确性与格式。
  5. G 设太小图省算力:G=2-4 时组内 std 几乎纯噪声,advantage 方向不稳,冒烟阶段看似省钱实则得不到可信信号;至少 G=8。
  6. 冒烟阶段就上 KL 调度的复杂曲线:50 步太短,复杂调度看不出效果反增调试面;冒烟先固定一个小 beta 验通,调度留到全量。

beta 的直觉级取值实验(思路,非新增测量):固定 lr/G,只扫 beta:

  • beta 偏大 → KL 始终很小(策略几乎不动),reward 在前几十步就走平 → 判定「缰绳太紧」,调小 beta。
  • beta 偏小 → KL 单调爆涨,reward 短暂飙升后崩 → 判定「缰绳太松」,调大 beta 或上调度。
  • beta 合适 → KL 稳在一个有界区间,reward 平滑上升 → 这就是 Day 108 三曲线诊断要看到的健康形态。

这把抽象的「beta 太大/太小」落成可观察的 KL 曲线行为,是从冒烟(107)走向全量诊断(108)的桥。

6. 学习资源(每条带 YYYY-MM)

  • Unsloth GRPO guide(2026-01)——GRPOConfig 超参约定、reward 函数列表挂载、单卡 LoRA+GRPO 流水线。
  • GRPO 原论文 / DeepSeekMath(arXiv:2402.03300, 2024-02)——KL 项、组内基线、无 critic 推导。
  • DeepSeek-R1(arXiv:2501.12948, 2025-01)——可验证奖励 + GRPO 的推理训练范式。
  • RLVR 谱系综述(arXiv:2604.15149, 2026)——KL 调度与可验证奖励组合的工程实践。
  • 本仓 src/agent/train/grpo.ts(2026-06)——groupRelativeAdvantage/grpoScore/composeRewards 的可单测、无 GPU 奖励逻辑实现。

SOTA检查 (2026-06 更新)

  • 当前主线GRPOConfig 超参约定(lr/beta/G/max_steps + KL 调度)在 2026 稳定,GRPO 仍是现役无 critic 算法。
  • 是否仍 SOTA:是。GRPO 是 2026 低成本 RL 后训练首选,超参范式未被替代;变体(Dr.GRPO/DAPO)在调归一化与裁剪上微调,但 lr/beta/G/max_steps 四参框架不变。
  • 过时黑名单
    • 避免沿用过大 beta 导致 reward 平台期的旧默认。
    • 避免 trl 旧 GRPOTrainer 早期签名(API 已变)。
    • 避免把 G 设到 2-4 这种低方差估计区间。
  • 下次复查点:再验 Unsloth 是否引入 Dr.GRPO 的 std 归一化开关(影响 groupRelativeAdvantage 的 std 项是否要去掉,规避长度/难度偏置);复查 Qwen3-4B 与当周 Unsloth 版本的兼容矩阵。

衔接

  • 昨天:Day 106 — 数据集与 prompt 构造(64 行 prompt+verifier 的 grpo_train.jsonl)。
  • 今天:四超参 lr/beta/G/max_steps 装进训练循环,50 步冒烟出 step-0 reward 基线;离线验证奖励计算正确。
  • 明天:Day 108 — 训练监控与诊断(≥300 步全量,看 reward/KL/loss 三曲线)。