前言
上一期我们学习了大模型微调 (SFT) 的过程,这一期我们正式进入强化学习阶段,本章我们的重点是 GRPO,我们先梳理一下过去学习的内容:
我们知道大模型简单流程是:图片 + Prompt 进入 Qwen-9b 生成 Response;
对于 SFT 的训练流程是:图片 + Prompt 进入 Qwen-9b 生成预测的 Response,接着和人工标准 Response 比较,后反向传播进行学习;
而对于 GRPO 而言,图片 + Prompt 进入 Qwen-9b 生成多个预测的 Response,Reward 判断好坏,让好的概率变大,坏的概率变小;
简单串了一遍流程之后,我们可以发现,实际上后两者的区别在于是否有监督,前者是有人工标注的答案作为监督,后者是用 reward 函数评判好坏;接下来我们详细看看这个 GRPO 是什么;
GRPO
强化学习是什么?
什么是强化学习?百度百科的解释是:
我们翻译一下:强化学习就是让一个智能体在一个环境里通过不断试错来学习,它每做一个动作,环境会给它一个奖励或惩罚。智能体的目标不是只看眼前奖励,而是学会一套策略,让长期累积奖励最大化
这个过程属于一个无监督学习,也就是说并没有标签,它只能在反馈中不断的纠正学习,强化学习里最典型的例子就是 AlphaGo 的训练过程
SFT 的短板
在学习 GRPO 之前,我们先思考一下,为什么有 SFT 还要学习 GRPO?因为 SFT 的短板是我知道回答的格式,但是我不知道怎么样的回答是好的回答;(也就是学习一个模仿能力)按照之前的数据集来说,就是你知道格式,但是你的回答实际上是没有脑洞的,因此 GRPO 就是解决让模型自己思考答案的好坏;
GRPO 的过程
这个方法的第一次提出是 DeepSeek 团队
Rollout
第一步是对同一个 Input,我们输出多组的回答;为什么要多组?原因是GRPO 不仅想知道答案本身多少分,还想知道这个答案相对于同一道题其他回答表现怎么样?因为我们可以通过对比才能知道回答质量的好坏;
num_generations = 4比如这行代码,表示的就是我们要生成 4 组回答;这个过程也就是 GRPO 的 G;
Reward
第二步,接着我们需要给我们的回答打分,采用得分函数进行评判;数学公式可以这样表达:
其中 x 表示的是输入,y 表示模型生成的回答,R 表示奖励得分;
Reward 可以分成两大类,分别是 Constraint Reward 和 Objective Reward,前者保证别犯基本错误,而后者表示我们想让你变成什么样;
比如同一道题,通过得分函数,可以得到每一个的得分:
| 回答 | Reward |
|---|---|
| A | 0.42 |
| B | 0.81 |
| C | 0.53 |
| D | 0.74 |
相对于传统的得分函数,我们采用用大模型作为裁判的形式——LLM as a judge,
我们先声明,用 LLM 做 Judge 的时候并不是判断像不像“标准答案”,如果这样的话,实际上就是变成了 SFT 的过程了;因此在我们这个任务里,Judge 要判断的是这个回答本身是否符合我们定义的“优质剧情推演”;那么要怎么样才可以给高分?应该要满足以下的条件:
- 真的看懂了图片
- 剧情必须能接得上
- 平淡、正常、脑洞真的要有层次
- 脑洞要“意外但合理”
- 不能奖励“写得漂亮”,要奖励“推演得好”
指标也就是:
| 指标 | 判断什么 |
|---|---|
| Grounding | 是否基于图片 |
| Logic | 剧情是否接得上 |
| Progression | 三层是否递进 |
| Creativity | 是否有真正的新意 |
| Controlled Surprise | 是否意外但合理 |
实际上用 LLM 去评分,绝对打分是一件比较困难的事情,可能全部挤在 8 分附近;这是 LLM Judge 很常见的问题:分数压缩
为了解决这个问题,可以先排序再打分的形式,这样有一个好处天然制造了 GRPO 需要的组内相对差异;
Relative
作为 GRPO 的 R,并不是单纯看某个回答的得分,而是对比本次回答和同组答案之间的差距,通常会先计算得分的平均值:,将同组的所有得分减去平均值,小于零的我们惩罚,大于零的我们奖励;
其中概念 Advantage 表示相对于同组表现怎么样,因此可以有 Rward > 0,但是 Advantage < 0 的情况,简单来说就是和平均值的差值,当然实际公式并不是这样,下面我们会介绍;
奖励方差
假设一组得分都是一样的,那么方差等于 0,说明 Reward Function 根本无法区分这些回答,也就是说这一组基本没有 GRPO 学习信号;假设得分有明显差异,存在明显的奖励方差时,这对可以说 GRPO 才有用
但是不是说方差可以越大越好,当方差极大的时候,可能会有一下的情况:
- Reward 函数写坏了,被模型钻漏洞了;
- 某个规则权重过大,得分的存在问题;
- 有异常样本,数据存在问题了;
因此我们希望的是Reward 有稳定、合理的区分能力;
一般来说我们把 Advantage 做标准化,统一尺度,真正的公式如下:
Policy
前面 GRPO 还没有真正改模型,下面我们正式来看看是怎么修改模型的
通过上面的学习,我们知道什么样的回答应该要鼓励,什么的回答要抑制;其实 Qwen 本身就是一个 Policy,整体的概率分布可以看成是:
其中 s 表示的是我们前面的输入和已经生成的 token,a 表示的是下一个 token;因为每一个 token 都是一次 Action,而我们的一个整个 Response 其实是一串 Action;可以通过下面这样表示:
但是每一个 token 的生成概率乘起来,最后的数会变得很小,因此我们采用了对数的形式,采用对数的形式有一个好处就是可以把累成拆分成为累加的形式
通过上述的形式,整个回答可以通过每个 token 的 log probability 相加来表示;
前面我们有讲述到 advantage,这个参数是怎么去影响概率呢?可以先简单看成下面的形式:
当 A > 0 的时候,也就是好回答,这个时候为了降低 Loss。会提高好回答的概率;同理,当 A < 0 的时候,也就是差回答,这个时候为了降低 Loss,会降低差回答的概率;
Old Policy
但是可能会存在一个问题,就是可能一次性改太猛了;可能第一次的概率是 0.2,但是下一次直接变成了 0.8,方向看起来是对的,但是过于激进了;尤其 Reward 不可能百分之百完美,如果 Reward 有漏洞,模型就可能迅速把漏洞学得非常熟;
为了解决这个问题,于是出现 Old Policy;简单来说就是保存上一次的概率,和这一次的概率做比,对生成的 token 的概率做约束,如下面公式所示:
但是一般在代码中不会这么描述
因为:
因此可以写成指数的形式:
在代码中我们通常是写成:
ratio = torch.exp( new_log_probs - old_log_probs )clip
现在我们知道了,我们的概率比率是起到约束的作用,那么这个比例的范围一般是多少?一般我们会先设置一个参数,一般参数的默认值是 0.2,因此默认允许变化的范围是 0.8~1.2
Reference Model
虽然 clip 可以小范围控制每一个 token 的概率,但是问题在于当迭代多次之后,实际上变化可能会很大,因此我们要有 Reference Model,也就是在 GRPO 之前的可靠模型,主要是负责:训练很久以后,也不要整体偏离原模型太远;
KL Divergence
那 Reference Model 是怎么去控制模型的偏离呢?研究员提出可以用 KL Divergence 来控制模型的漂移,简单来说就是两个模型的两个概率分布有多不一样;假设 KL 很大,说明偏离严重,因此要惩罚;
论文公式分析
有了前面的铺垫,我们可以正式分析 GRPO 的损失函数
我们先看最下面的 Advantage 公式,和我们之前的分析是一样,与平均值做差后除于标准差,做标注化;
第一个公式,先是比率乘上 Advantage;右半部分就是做 clip 后的比率(目的是做每一步的约束),同样也是乘上 Advantage,取两者的最小值,实际上就是在正常更新和受限制更新之间,选更保守的那个;最右边就是减去惩罚的 KL,防止模型越学越不像之前的模型;
效果对比
经过 SFT + GRPO:
只有 SFT:
看完两者的回复之后,效果高低直接立判,经过强化学习后的大模型,效果确实比之前微调的好;