RL 的最小心智模型:试、评、改
不讲马尔可夫决策过程。先用「模型自己生成一批答案,好的多学一点,坏的少学一点」把整件事说完。
学完这节你能做到
- 用一句话讲清 RL 与监督学习的根本区别:标签是自己采样出来的
- 把 state / action / reward / policy 对应到 LLM 里的具体东西
- 说出「为什么不能直接对 reward 做梯度下降」
监督学习:抄答案。强化学习:自己写,别人打分
这是唯一需要记住的区别。
监督学习给你 (问题, 标准答案)。你的模型输出和标准答案对比,差多少就往那个方向改多少。标签是给定的。
强化学习只给你问题和一个打分函数。模型自己生成答案,打分函数给个分数,然后你根据分数调整「以后更/更不倾向于这样答」。标签是自己采样出来的。
监督学习: 问题 → 模型 → 输出 ←比对→ 标准答案 → 梯度
强化学习: 问题 → 模型 → 输出 → 打分器 → 分数 → 梯度
↑ │
└────── 好的多学,坏的少学 ──────┘
这个差别带来三个后果,后面每一节课基本都在处理其中之一:
- 贵。每次更新前都要先生成一批答案,而生成很慢。
- 不稳。采样有随机性,同一批数据可能这次全对下次全错。
- 会作弊。模型优化的是打分函数,不是你心里想的那个目标。
因为很多任务没有标准答案可抄。
「把这段代码写得更好」——好在哪?没有唯一答案,但你能判断哪个更好。 「多想几步再回答」——推理过程没有标准写法,但你能验证最终答案对不对。
监督学习只能教模型模仿已有的答案。RL 能让模型探索出训练数据里没有的解法——DeepSeek-R1 那种「等一下,我重新想想」的行为,没有人在 SFT 数据里教过它。
术语对照表
RL 的术语来自机器人和游戏,套到 LLM 上要翻译一下。这张表建议记住:
| RL 术语 | 在 LLM 里是什么 | 备注 |
|---|---|---|
| policy 策略 | 就是你的模型 | 看到「策略模型」就想「那个要训的模型」 |
| state 状态 | 当前已有的 token 序列(prompt + 已生成部分) | 每生成一个 token 状态就变了 |
| action 动作 | 下一个 token | 动作空间 = 整个词表,15 万个选择 |
| trajectory 轨迹 | 一条完整的回答 | 也叫 rollout、episode、completion |
| reward 回报 | 这条回答的得分 | LLM 里通常只在结尾给一次 |
| return 累积回报 | 从某步到结尾的总分 | 单轮任务里就等于 reward |
| value 价值 | 「从这个状态出发预期能拿多少分」 | PPO 要学它,GRPO 不学 |
| advantage 优势 | 「这条比平均好多少」 | 真正乘到梯度上的东西 |
数学上,每生成一个 token 就是一次动作,一条 100 token 的回答是 100 步决策。
但 reward 通常只在最后给一次(答案对不对)。中间 99 步都没有直接反馈——这叫稀疏奖励。
实践上的处理很直接:把整条回答的优势值平摊到每个 token 上。所以代码里你会看到优势是按序列算的,然后广播到 token 维度。第一次看会觉得别扭,记住「按序列打分、按 token 求梯度」就行。
一个玩具例子:教模型只输出数字
假设我们要让模型回答算术题时只输出数字,不要废话。
监督学习的做法:准备 1000 条 ("3+4=?", "7"),微调。问题:你得先有这 1000 条数据。
RL 的做法:写一个打分函数。
def reward(response: str) -> float:
return 1.0 if response.strip().isdigit() else 0.0
然后:
- 问模型
"3+4=?",让它生成 8 条不同回答(temperature > 0,所以每条不一样) - 打分:
"7"→ 1.0;"7"→ 1.0;"答案是 7"→ 0.0;"让我算一下:3+4=7"→ 0.0 … - 得分高的那几条,提高它们出现的概率;得分低的,降低
跑几十轮之后,模型学会了只输出数字。你一条训练数据都没有标注。
isdigit() 只检查「是不是数字」,不检查「对不对」。
模型很快会发现:随便输出一个数字就能拿 1.0 分。于是它学会输出 "0",永远拿满分,而算术能力毫无长进。
这就是 reward hacking,而且是最典型的一种:你写的 reward 有一条不用真的解决问题就能拿分的捷径。修法很简单——把答案对不对也算进去。但真实任务里的捷径往往没这么显眼。
后面「reward 从哪来」那一节会系统讲怎么设计和自查。
为什么不能直接对 reward 做梯度下降
一个自然的想法是:reward 是个数,我对它求梯度不就行了?
不行,因为 reward 函数对模型参数不可导。
参数 θ → 模型 → 采样出 token → 拼成文本 → 打分函数 → reward
↑
这一步是采样(离散、随机)
梯度传不过去
isdigit() 对参数的导数是什么?没有意义。中间「采样一个 token」这一步彻底切断了梯度链。
RL 的核心技巧就是绕过这个断点:不对 reward 求梯度,而是对「采样到这条回答的概率」求梯度,用 reward 当权重。
朴素梯度 ≈ Σ reward × ∇log P(这条回答)
回答
log P(这条回答) 是模型算出来的,可导。reward 只是一个标量系数。
直觉上:
- reward 高 → 系数为正 → 提高这条回答的概率
- reward 低 → 系数为负 → 降低这条回答的概率
这就是策略梯度,「先采样,再按分数加权地提高/降低概率」。L1 后面那节会把它一步步推出来。
三个必然出现的麻烦
写在这里,好让你知道后面每节课在解决什么:
| 麻烦 | 症状 | 哪一节解决 |
|---|---|---|
| 方差大 | 同一批数据梯度方向乱跳,训练不稳 | 基线与优势(策略梯度、PPO→GRPO) |
| 会作弊 | 分数涨了但能力没涨 | reward 设计、L4 失效模式 |
| 会跑飞 | 为了刷分把说人话的能力训丢了 | KL 惩罚与 clip(PPO→GRPO) |
强化学习和监督学习最本质的区别是什么?
为什么不能直接对 reward 求梯度?
在 LLM 的 RL 里,一次「动作」对应什么?
这节课的落点
- RL 的标签是自己采样出来的,这一条派生出「贵、不稳、会作弊」三个后果
- 术语翻译:policy = 模型,action = token,trajectory = 一条回答,advantage = 比平均好多少
- 动作是 token,reward 在序列结尾给;实践上把序列优势平摊到每个 token
- reward 不可导,所以改为对
log P(回答)求梯度、用 reward 当权重 - 玩具例子里的
isdigit()就是一个现成的 reward hacking 案例