欢迎关注我的CSDN:https://spike.blog.csdn.net/
本文地址:https://spike.blog.csdn.net/article/details/145640762
GPRO,即 Group Relative Policy Optimization,分组相对的策略优化,是 PPO(Proximal Policy Optimization, 近端策略优化) 的优化版本,省略优化 评论家模型(Critic Model),用于估计价值(Value Function Model),降低模型训练的资源消耗。
GRPO 目标的工作原理如下:
- 为查询生成一组响应。
- 根据预定义的标准(例如准确性、格式),计算每个响应的奖励。
- 比较组内的反应以计算他们的相对优势。
- 更新策略以支持具有更高优势的响应,剪裁(clip)确保的稳定性。
- 规范更新以防止模型偏离基线太远。
GRPO 有效的原因:
- 无需评论:GRPO 依靠群体比较,避免对于单独评估者的需求,从而降低了计算成本。
- 稳定学习:剪裁(clip) 和 KL 正则化确保模型稳步改进,不会出现剧烈波动。
- 高效训练:通过关注相对性能,GRPO 非常适合推理等绝对评分困难的任务。
在 DeepSeekMath (2024.4) 中,使用 GPRO 代替 PPO。
回顾一下 PPO 模型的公式与框架,PPO 是先训练 奖励模型(RM),通过强化学习策略,将奖励模型的能力,学习到大语言模型中,同时,注意模型的输出符合之前的预期,不要偏离过远(KL Divergence)。即:
- RM(Reward Model, 奖励模型): m a x r ϕ { E ( x , y w i n , y l o s s ) ∼ D [ l o g σ ( r ϕ ( x , y w i n ) − r ϕ ( x , y l o s s ) ) ] } \underset{r_{\phi}}{max} \{ {E_{(x,y_{win},y_{loss}) \sim D}}[log \ \sigma(r_{\phi}(x,y_{win}) - r_{\phi}(x,y_{loss}))] \} rϕmax{E(x,ywin,yloss)∼D[log σ(rϕ(x,ywin)−rϕ(x,yloss))]}
- PPO(Proximal Policy Optimization, 近端策略优化): m a x π θ { E x ∼ D , y ∼ π θ ( y ∣ x ) [ r ϕ ( x , y ) ] − β D K L [ π θ ( y ∣ x ) ∣ ∣ π r e f ( y ∣ x ) ] } \underset{\pi_{\theta}}{max} \{ E_{x \sim D,y \sim \pi_{\theta}(y|x)}[r_{\phi}(x,y)] - \beta D_{KL}[\pi_{\theta}(y|x) || \pi_{ref}(y|x)] \} πθmax{Ex∼D,y∼πθ(y∣x)[rϕ(x,y)]−βDKL[πθ(y∣x)∣∣πref(y∣x)]}
- KL 散度(KL Divergence):
D K L [ π θ ( y ∣ x ) ∣ ∣ π r e f ( y ∣ x ) ] = π r e f ( y ∣ x ) l o g π r e f ( y ∣ x ) π θ ( y ∣ x ) = π r e f ( y ∣ x ) ( l o g π r e f ( y ∣ x ) − l o g π θ ( y ∣ x ) ) \begin{align} D_{KL}[\pi_{\theta}(y|x) || \pi_{ref}(y|x)] &= \pi_{ref}(y|x) \ log{\frac{\pi_{ref}(y|x)}{\pi_{\theta}(y|x)}} \\ &= \pi_{ref}(y|x) (log \pi_{ref}(y|x) - log\pi_{\theta}(y|x)) \end{align} DKL[πθ(y∣x)∣∣πref(y∣x)]=πref(y∣x) logπθ(y∣x)πref(y∣x)=πref(y∣x)(logπref(y∣x)−logπθ(y∣x))
其中,Actor 和 Critic 损失函数如下:
a
[
i
,
j
]
=
r
e
t
u
r
n
s
[
i
,
j
]
−
v
a
l
u
e
s
[
i
,
j
]
L
o
s
s
a
c
t
o
r
=
−
1
M
N
∑
i
=
1
M
∑
j
=
1
N
a
[
i
,
j
]
×
e
x
p
(
l
o
g
_
p
r
o
b
[
i
,
j
]
−
o
l
d
_
l
o
g
_
p
r
o
b
[
i
,
j
]
)
L
o
s
s
c
r
i
t
i
c
=
1
2
M
N
∑
i
=
1
M
∑
j
=
1
N
(
v
a
l
u
e
s
[
i
,
j
]
−
r
e
t
u
r
n
s
[
i
,
j
]
)
2
L
o
s
s
=
L
o
s
s
a
c
t
o
r
+
0.1
∗
L
o
s
s
c
r
i
t
i
c
\begin{align} a[i,j] &= returns[i,j] - values[i,j] \\ Loss_{actor} &= -\frac{1}{MN} \sum_{i=1}^{M} \sum_{j=1}^{N} a[i,j] \times exp(log\_prob[i,j]-old\_log\_prob[i,j]) \\ Loss_{critic} &= \frac{1}{2MN} \sum_{i=1}^{M} \sum_{j=1}^{N} (values[i,j] - returns[i,j])^{2} \\ Loss & = Loss_{actor} + 0.1*Loss_{critic} \end{align}
a[i,j]LossactorLosscriticLoss=returns[i,j]−values[i,j]=−MN1i=1∑Mj=1∑Na[i,j]×exp(log_prob[i,j]−old_log_prob[i,j])=2MN1i=1∑Mj=1∑N(values[i,j]−returns[i,j])2=Lossactor+0.1∗Losscritic
PPO 的奖励(Reward 计算),一般而言,超参数
β
=
0.1
\beta=0.1
β=0.1:
r
t
=
r
ψ
(
q
,
o
≤
t
)
−
β
l
o
g
(
π
θ
(
o
t
∣
q
,
o
<
t
)
π
r
e
f
(
o
t
∣
q
,
o
<
t
)
)
r_{t} = r_{\psi}(q,o_{\leq t}) - \beta log(\frac{\pi_{\theta}(o_{t}|q,o_{<t})}{\pi_{ref}(o_{t}|q,o_{<t})})
rt=rψ(q,o≤t)−βlog(πref(ot∣q,o<t)πθ(ot∣q,o<t))
在 PPO 中使用的价值函数(Critic Model),通常与策略模型(Policy Model)大小相当,带来内存和计算负担。在强化学习训练中,价值函数作为基线,以减少优势函数计算中的方差。然而,在 大语言模型(LLM) 的场景中,只有最后一个 Token 被奖励模型赋予奖励分数,使训练一个在每个标记处都准确的价值函数,变得复杂。GRPO 无需像 PPO 那样,使用额外的近似价值函数,而是使用同一问题产生的多个采样输出的平均奖励,作为基线。
GRPO 使用基于 组相对(Group Relative) 的优势计算方式,与奖励模型比较特性一致,因为奖励模型通常是在同一问题上不同输出之间的比较数据集上进行训练的。同时,GRPO 没有在 奖励(Reward) 中加入 KL 惩罚,而是直接将训练策略与参考策略之间的KL散度添加到损失函数中,从而避免了在计算优势时增加复杂性。
GPRO 的公式,
Q
Q
Q 表示 Query,即输入的问题,采样出问题
q
q
q,推理大模型,输出
G
G
G 个输出
o
i
o_{i}
oi:
J
G
R
P
O
(
θ
)
=
E
[
q
∼
P
(
Q
)
,
{
o
i
}
i
=
1
G
∼
π
θ
o
l
d
(
O
∣
q
)
]
L
o
s
s
=
1
G
∑
i
=
1
G
(
m
i
n
(
(
π
θ
(
o
i
∣
q
)
π
θ
o
l
d
(
o
i
∣
q
)
)
A
i
,
c
l
i
p
(
π
θ
(
o
i
∣
q
)
π
θ
o
l
d
(
o
i
∣
q
)
,
1
−
ϵ
,
1
+
ϵ
)
A
i
−
β
D
K
L
(
π
θ
∣
∣
π
r
e
f
)
)
D
K
L
(
π
θ
∣
∣
π
r
e
f
)
=
π
r
e
f
(
o
i
∣
q
)
π
θ
(
o
i
∣
q
)
−
l
o
g
π
r
e
f
(
o
i
∣
q
)
π
θ
(
o
i
∣
q
)
−
1
A
i
=
r
i
−
m
e
a
n
(
{
r
1
,
r
2
,
…
,
r
G
}
)
s
t
d
(
{
r
1
,
r
2
,
…
,
r
G
}
)
\begin{align} J_{GRPO}(\theta) &= \mathbb{E}[q \sim P(Q), \{{o_{i}}\}_{i=1}^{G} \sim \pi_{\theta_{old}}(O|q)] \\ Loss &= \frac{1}{G}\sum_{i=1}^{G}(min((\frac{\pi_{\theta}(o_{i}|q)}{\pi_{\theta_{old}}(o_{i}|q)})A_{i}, clip(\frac{\pi_{\theta}(o_{i}|q)}{\pi_{\theta_{old}}(o_{i}|q)},1-\epsilon,1+\epsilon)A_{i}-\beta \mathbb{D}_{KL}(\pi_{\theta}||\pi_{ref})) \\ \mathbb{D}_{KL}(\pi_{\theta}||\pi_{ref}) &= \frac{\pi_{ref}(o_{i}|q)}{\pi_{\theta}(o_{i}|q)} - log\frac{\pi_{ref}(o_{i}|q)}{\pi_{\theta}(o_{i}|q)} - 1 \\ A_{i} &= \frac{r_{i}-mean(\{r_{1},r_{2},\ldots,r_{G}\})}{std(\{r_{1},r_{2},\ldots,r_{G}\})} \end{align}
JGRPO(θ)LossDKL(πθ∣∣πref)Ai=E[q∼P(Q),{oi}i=1G∼πθold(O∣q)]=G1i=1∑G(min((πθold(oi∣q)πθ(oi∣q))Ai,clip(πθold(oi∣q)πθ(oi∣q),1−ϵ,1+ϵ)Ai−βDKL(πθ∣∣πref))=πθ(oi∣q)πref(oi∣q)−logπθ(oi∣q)πref(oi∣q)−1=std({r1,r2,…,rG})ri−mean({r1,r2,…,rG})
GRPO 的 KL 散度,使用蒙特卡洛(Monte-Carlo) 近似计算 KL散度(Kullback-Leibler Divergence),结果始终为正数。
参考源码,TRL - GRPO:
# Advantages
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)
# KL 散度
per_token_kl = torch.exp(ref_per_token_logps - per_token_logps) - (ref_per_token_logps - per_token_logps) - 1
# 期望
per_token_loss = torch.exp(per_token_logps - per_token_logps.detach()) * advantages.unsqueeze(1)
# 联合 loss
per_token_loss = -(per_token_loss - self.beta * per_token_kl)
# mask loss
loss = ((per_token_loss * completion_mask).sum(dim=1) / completion_mask.sum(dim=1)).mean()
GRPO 的 训练源码:trl/trainer/grpo_trainer.py
PPO 的伪码流程:
policy_model = load_model()
ref_model = policy_model.copy() # 不更新
critic_model = load_reward_model(only_last=False)
reward_model = critic_mode.copy() # 不更新
for i in steps:
# 1. 采样阶段
prompts = sample_prompt()
# old_log_probs[i][j](from policy_model), old_values[i][j](from critic_model)
responses, old_log_probs, old_values = respond(policy_model, critic_model, prompts)
# 2. 反馈阶段
scores = reward_model(prompts, responses)
# ref_log_probs[i][j](from ref_model)
ref_log_probs = analyze_responses(ref_model, prompts, responses) # ref logps
# rewards[i][j] = scores[i] - (old_log_probs[i][j] - ref_log_prob[i][j])
rewards = reward_func(scores, old_log_probs, ref_log_probs) # 奖励计算
# advantages[i][j] = rewards[i][j] - old_values[i][j]
advantages = advantage_func(rewards, old_values) # 奖励(r)-价值(v)=优势(a)
# 3. 学习阶段
for j in ppo_epochs: # 多次更新学习,逐渐靠近奖励
log_probs = analyze_responses(policy_model, prompts, responses)
values = analyze_responses(critic_model, prompts, responses)
# 更新 actor(policy) 模型,学习更新的差异,advantages[i][j]越大,强化动作
actor_loss = actor_loss_func(advantages, old_log_probs, log_probs)
critic_loss = critic_loss_func(rewards, values) # 更新 critic 模型
loss = actor_loss + 0.1 * critic_loss # 更新
train(loss, policy_model.parameters(), critic_model.parameters()) # 参数
参考 知乎 - 图解大模型RLHF系列之:人人都能看懂的PPO原理与源码解读
KL 散度的实现,如下:
import torch
import torch.nn.functional as F
# 假设我们有两个概率分布 P 和 Q
P = torch.tensor([0.1, 0.2, 0.7]) # 参考的、真实的
Q = torch.tensor([0.2, 0.3, 0.5]) # 模型生成的
# 计算 Q 的对数概率
log_Q = torch.log(Q)
# 使用 PyTorch 的 kl_div 函数计算 KL 散度
kl_divergence = F.kl_div(log_Q, P, reduction='sum') # 注意先Q后P
print(f"KL Div (PyTorch): {kl_divergence}")
log_P = torch.log(P)
kl_elementwise = P * (log_P - log_Q)
# 对所有元素求和,得到 KL 散度
kl_divergence = torch.sum(kl_elementwise)
参考:
- 知乎 - GRPO: Group Relative Policy Optimization
- GitHub - GRPO Trainer
- Medium - The Math Behind DeepSeek: A Deep Dive into Group Relative Policy Optimization (GRPO)