https://github.com/om-ai-lab/VLM-R1/tree/main
GRPO 是一种起源于 DeepSeekMath 论文的强化学习方法,用于优化语言模型或多模态模型。它特别适用于优化特定任务的性能,如数学推理或遵循指令。
GRPO 结合了两个主要思想:
每个批次的训练过程如下:
生成样本:
num_generations
(默认为8)个不同的完成(回答)评估奖励:
计算优势:
优势 = (奖励 - 平均奖励) / (标准差 + ε)
策略优化:
r = exp(新策略对数概率 - 旧策略对数概率)
min(r * 优势, clip(r, 1-ε, 1+ε) * 优势)
KL 惩罚项(可选):
β * KL(参考策略 || 当前策略)
多次迭代更新(可选):
num_iterations
参数控制)从代码中可以看出,GRPO 有几个关键参数影响其训练行为:
从代码实现中,我们可以看到一些重要的实现细节:
RepeatRandomSampler
确保每个提示生成多个回答python# 计算分组奖励统计
mean_grouped_rewards = rewards.view(-1, self.num_generations).mean(dim=1)
std_grouped_rewards = rewards.view(-1, self.num_generations).std(dim=1)
# 标准化奖励以计算优势
mean_grouped_rewards = mean_grouped_rewards.repeat_interleave(self.num_generations, dim=0)
std_grouped_rewards = std_grouped_rewards.repeat_interleave(self.num_generations, dim=0)
advantages = (rewards - mean_grouped_rewards) / (std_grouped_rewards + 1e-4)
python# 计算策略比率和裁剪版本
coef_1 = torch.exp(per_token_logps - old_per_token_logps)
coef_2 = torch.clamp(coef_1, 1 - self.epsilon, 1 + self.epsilon)
per_token_loss1 = coef_1 * advantages.unsqueeze(1)
per_token_loss2 = coef_2 * advantages.unsqueeze(1)
per_token_loss = -torch.min(per_token_loss1, per_token_loss2)
# 添加 KL 惩罚(如果 beta > 0)
if self.beta > 0:
ref_per_token_logps = inputs["ref_per_token_logps"]
per_token_kl = torch.exp(ref_per_token_logps - per_token_logps) - (ref_per_token_logps - per_token_logps) - 1
per_token_loss = per_token_loss + self.beta * per_token_kl
GRPO 是一种用于优化语言模型或多模态模型的强大方法,特别适合没有大量人类偏好数据但有明确奖励信号的场景。它通过生成多样化的回答并比较它们的相对奖励来优化模型,将标准的 PPO 与相对奖励结合,形成一种有效的自我改进机制。
该方法的核心创新在于将"群组"内的相对比较融入到策略优化框架中,从而使模型能够学习到何种回答更好,而不仅仅是尝试增加绝对奖励值。
在 GRPO 中,KL 散度惩罚的具体实现如下:
pythonper_token_kl = torch.exp(ref_per_token_logps - per_token_logps) - (ref_per_token_logps - per_token_logps) - 1
per_token_loss = per_token_loss + self.beta * per_token_kl
KL 散度(Kullback-Leibler 散度)衡量两个概率分布 P 和 Q 的差异,其标准数学公式为:
在 GRPO 中,我们计算的是参考策略(ref_model)与当前策略(policy_model)之间的 KL 散度:
其中:
代码中使用了一种更直接的计算方式,利用了对数概率:
ref_per_token_logps
是参考模型的对数概率per_token_logps
是当前模型的对数概率具体实现是:
pythonper_token_kl = torch.exp(ref_per_token_logps - per_token_logps) - (ref_per_token_logps - per_token_logps) - 1
这个公式是 KL 散度的一种数值计算形式,我们可以通过数学推导来理解:
我们想计算 ,其中 P 是参考策略,Q 是当前策略。
定义 ,那么:
将对数展开成 Taylor 级数并取期望,可以得到:
这就是代码中的实现:
计算每个 token 位置的 KL 散度:
pythonper_token_kl = torch.exp(ref_per_token_logps - per_token_logps) - (ref_per_token_logps - per_token_logps) - 1
将 KL 惩罚项添加到策略优化损失中:
pythonper_token_loss = per_token_loss + self.beta * per_token_kl
其中 beta
是控制 KL 惩罚强度的超参数。
计算平均 KL 散度用于监控:
pythonmean_kl = ((per_token_kl * completion_mask).sum(dim=1) / completion_mask.sum(dim=1)).mean()
将 KL 散度整合到完整的 GRPO 损失函数中,我们得到:
其中:
GRPO 中的 KL 散度惩罚项是通过以下公式实现的:
这个惩罚项确保当前策略不会偏离初始参考策略太远,帮助保持模型行为的稳定性,防止过度优化导致灾难性遗忘或行为偏离。KL 散度的强度由超参数 β 控制,β 越大,模型越倾向于保持接近参考策略。
本文作者:Dong
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC。本作品采用《知识共享署名-非商业性使用 4.0 国际许可协议》进行许可。您可以在非商业用途下自由转载和修改,但必须注明出处并提供原作者链接。 许可协议。转载请注明出处!