强化学习基础:行业黑话
想象你正在和一个刚训练好的语言模型聊天。你问:“今天过得怎么样?”
模型可能回:“还行。” 也可能回:“我是个 AI,没有感情。”
人类觉得前者更自然、更友好——这就是偏好反馈。强化学习(RL)在 LLM 中的核心任务,就是让模型学会生成“人类更喜欢”的回复。
为了做到这一点,我们需要一套语言来描述这个过程。下面我们以 LLM 场景为基础介绍几个 RL 的“行业黑话”。
1.1 基本概念
时刻
t
:就是对话的第几步。比如:
t
=
0
:用户输入 “今天过得怎么样?” → 这是初始状态
s
0
t
=
1
:模型输出第一个词 “今” → 动作
a
0
=
“今”
t
=
2
:模型输出第二个词 “天” → 动作
a
1
=
“天”
… 直到生成完整回复,比如 “今天过得不错!”
在 LLM 中,状态
s
t
通常就是到第
t
步为止已生成的 token 序列(包括用户输入和模型已输出的部分)
动作
a
t
就是模型在第
t
步选择的下一个 token。
奖励
r
t
:这是人类(或奖励模型)对模型行为的真实反馈信号。比如:
如果模型最终生成了“今天过得不错!”,人类觉得回答的不错,打 5 分 → 这个分数会折算成一个最终奖励
r
T
(通常只在序列结束时给,即最后一个 token)
中间步骤一般没有即时奖励(
r
t
=
0
for
t
<
T
)
价值
v
:奖励
r
是真实的、来自外部的信号(比如人类打分),相对应的,价值(value)是对未来奖励的估计——因为模型不能预知未来,只能靠猜。
1.2. 价值(Value):对未来奖励的“预判”
既然模型不能看到未来,它就需要一个“预判能力”:我现在处在某个状态,未来大概能拿多少分?
这就引出了两个核心函数:
1) 状态价值函数
V
(
s
t
)
V
(
s
t
)
=
∑
a
∈
A
π
(
a
|
s
t
)
Q
(
s
t
,
a
)
它表达的是:在当前已生成的对话上下文
s
t
(比如用户刚问完 “今天过得怎么样?”,而模型还没开始回答,或已输出“今”),模型按照当前策略继续生成后续内容,平均能获得多少人类打分。
π
(
a
|
s
t
)
是模型在状态
s
t
下选择下一个词
a
的概率(例如在“今天过得怎么样?”之后,选“今”还是“还”);
Q
(
s
t
,
a
)
表示如果此刻选了某个具体词
a
,最终能拿到的预期总分;
把所有可能的下一个词按模型当前的偏好加权平均,就得到了该状态的整体“预期得分”——也就是
V
(
s
t
)
。
举个例子:当模型已经输出 “今天过得”,它会评估:“按我现在的风格继续回答,人类大概率会觉得自然,可能打 4 分”,于是
V
(
s
t
)
≈
4
。
2) 动作价值函数
Q
(
s
t
,
a
)
Q
(
s
t
,
a
)
=
r
t
+
γ
V
(
s
t
+
1
)
+
γ
2
V
(
s
t
+
2
)
+
⋯
它表达的是:如果我现在处于状态
s
t
(比如上下文是“今天过得”),并选择动作
a
(比如生成“不”),那么我能获得当前的真实奖励
r
t
(通常是 0,因为回复还没结束),再加上未来所有状态价值的折扣和。
对应到 LLM 应用场景就表示:
“如果我现在在‘今天过得’后面接‘不’,形成‘今天过得不’,那接下来我大概率会说‘错!’,组成一句完整、积极的回复,最终人类可能会打 5 分。”
其中:
r
t
是真实发生的奖励,但在 LLM 生成过程中,只有完整回复结束后才有非零值(例如人类打分
r
T
=
5
);在中间步骤(如生成“今”“天”时),
r
t
=
0
;
V
(
s
t
+
1
)
,
V
(
s
t
+
2
)
,
…
是模型自己估计的未来价值(比如生成“不”之后,预估“今天过得不错!”能拿 4.9 分);
γ
∈
[
0
,
1
]
是折扣因子(如 0.95),表示“未来的分不如现在的分值钱”——越靠后的 token 对当前决策的影响越小。
虽然中间每一步的
r
t
=
0
,但
Q
(
s
t
,
a
)
依然非常关键:它通过
V
(
s
t
+
1
)
等未来价值,把对最终人类反馈的预判传递回当前决策。这正是 LLM 在生成每个词时具备“前瞻能力”的来源——它不是随机选词,而是基于“这样说人类会不会喜欢”的长期预期来做选择。
为什么估计的价值函数 Q 里包含真实的
r
t
?
因为 RL 的目标是用真实奖励来校准价值估计。模型通过不断对比“预测的未来得分”和“实际拿到的奖励”,来修正自己的
V
和
Q
函数。
2. PPO:RLHF 的“老大哥”
PPO(Proximal Policy Optimization)是传统 RLHF(基于人类反馈的强化学习)流程中的核心算法,是 openai 在 2016年左右提出来的。原来 closeAI 的成功在那个时候就开始蓄力了。PPO的目标很直接:让语言模型生成更受人类欢迎的回复。
PPO 中的几个关键角色
模型 是否训练 输入 输出 输出维度说明
Policy Model
π
θ
✅ 是 prompt
x
(token IDs,长度
L
x
) 生成回复
y
=
(
a
1
,
…
,
a
T
)
,以及每个 token 的 log-prob
log
π
θ
(
a
t
|
s
t
)
y
:
[
T
]
logprobs:
[
T
]
Reference Model
π
ref
❌ 冻结 同上
x
同上 log-prob
log
π
ref
(
a
t
|
s
t
)
[
T
]
Critic Model
V
ψ
✅ 是 状态序列
s
t
=
x
⊕
y
≤
t
(token IDs,长度
L
x
+
t
) 价值估计
V
ψ
(
s
t
)
标量(或
[
1
]
),对每个
t
=
0
,
…
,
T
输出一个值 → 总输出
[
T
+
1
]
Reward Model
r
ϕ
❌ 冻结
(
x
,
y
)
(完整 prompt + response) 标量奖励
R
=
r
ϕ
(
x
,
y
)
标量(或
[
1
]
)
注:
⊕
表示 token 拼接;
T
是生成回复的长度(可变,但训练时通常 padding 到固定长度)。
PPO 的两阶段训练流程
PPO通过分阶段解耦“数据生成”和“策略学习”,在保证训练稳定性的同时,让模型逐步学会生成更符合人类偏好的回复。整个流程分为如下两个阶段:
阶段 1:采样与反馈(Sample + Label)
✅ 目标
用当前策略模型生成一批回复,并利用冻结的奖励模型打分,再结合当前评论家模型估计价值,最终为每个 token 动作计算出优势(Advantage) 和回报(Return),作为后续训练的监督信号。
📌 关键点:此阶段不更新任何模型参数,只是“收集数据”。Policy 和 Critic 在采样时使用的是当前最新参数,但输出会被 detach(视为常数),作为“旧策略”和“旧评论家”的快照。
🧩 参与模型与接口
模型 是否更新 输入 输出 输出维度
Policy Model
π
θ
❌(采样时不更新) prompt
x
∈
Z
L
x
生成回复
y
∈
Z
T
及每个 token 的 log-prob
log
π
θ
(
a
t
|
s
t
)
y
:
[
T
]
logprobs:
[
T
]
Critic Model
V
ψ
❌(采样时不更新) 状态
s
t
=
x
⊕
y
≤
t
∈
Z
L
x
+
t
价值估计
V
ψ
(
s
t
)
∈
R
对
t
=
0
,
…
,
T
输出
[
T
+
1
]
个标量
Reward Model
r
ϕ
❌(始终冻结)
(
x
,
y
)
标量奖励
R
=
r
ϕ
(
x
,
y
)
[
1
]
注:
L
x
是 prompt 长度,
T
是生成回复长度(实际中常 padding 到固定 max_len)。
🔢 核心计算逻辑
生成轨迹:对每个 prompt
x
,用当前策略生成完整回复
y
=
(
a
1
,
.
.
.
,
a
T
)
,形成状态序列:
s
0
=
x
,
s
1
=
x
⊕
a
1
,
…
,
s
T
=
x
⊕
y
获取最终奖励:调用冻结的 Reward Model:
R
=
r
ϕ
(
x
,
y
)
(中间步骤无奖励,即
r
t
=
0
for
t
<
T
)
计算回报(Return):
^
R
t
=
T
∑
k
=
t
γ
k
−
t
r
k
=
γ
T
−
t
R
因为只有最后一步有奖励。回报序列
^
R
0
,
^
R
1
,
.
.
.
,
^
R
T
构成目标值。
计算优势(Advantage):
A
t
=
^
R
t
−
V
ψ
(
s
t
)
,
t
=
0
,
1
,
.
.
.
,
T
−
1
表示:在状态
s
t
下执行动作
a
t
,比“平均水平”好多少。
保存“旧”值:将当前策略的 log-prob 和评论家的 value detach,作为阶段 2 的基准(即“old policy”和“old critic”)。
💻 伪代码(阶段 1)
trajectories = []
for x in prompts: # x: [L_x]
# 1. 用当前策略生成回复 y 和 log-prob
y, logprobs = policy_model.generate_with_logprobs(x) # y: [T], logprobs: [T]
# 2. 构建状态序列 s_0 ... s_T
states = [torch.cat([x, y[:t]]) for t in range(len(y) + 1)] # len = T+1
# 3. 用当前评论家估计每个状态的价值
values = torch.stack([critic_model(s) for s in states]) # [T+1]
# 4. 奖励模型打分(仅最终奖励)
R = reward_model(x, y) # scalar
# 5. 计算回报:R_t = γ^{T−t} * R
T_len = len(y)
returns = torch.zeros(T_len + 1)
returns[T_len] = R
for t in reversed(range(T_len)):
returns[t] = gamma * returns[t + 1]
# 6. 计算优势:A_t = R_t - V(s_t),仅对 t=0..T-1 有效
advantages = returns[:-1] - values[:-1] # [T]
# 7. 保存“旧”值(detach 阻断梯度)
trajectories.append({
'x': x,
'y': y,
'logprobs_old': logprobs.detach(), # [T]
'values_old': values.detach(), # [T+1]
'advantages': advantages.detach(), # [T]
'returns': returns.detach() # [T+1]
})
✅ 此阶段结束时,我们得到一个固定的数据集,后续训练将在此数据上多次迭代。
阶段 2:策略与评论家更新(Policy & Critic Learning)
✅ 目标
利用阶段 1 收集的固定轨迹数据,更新策略模型(Policy)和评论家模型(Critic),使得:
策略更倾向于选择高优势的动作;
评论家更准确地预测未来回报;
同时通过 PPO-clip 和 KL 正则防止策略突变或偏离合理语言分布。
🧩 参与模型与接口
模型 是否更新 作用
Policy Model
π
θ
✅ 被优化的主模型
Critic Model
V
ψ
✅ 被优化的价值估计器
Reference Model
π
ref
❌(始终冻结) 提供 KL 正则基准(通常是 SFT 后的初始模型)
Reward Model ❌ 不参与此阶段
🔢 核心计算逻辑
策略损失(PPO-Clip)
定义概率比:
r
t
(
θ
)
=
π
θ
(
a
t
|
s
t
)
π
old
(
a
t
|
s
t
)
=
exp
(
log
π
θ
(
a
t
|
s
t
)
−
log
π
old
(
a
t
|
s
t
)
)
PPO 损失为:
L
PPO
=
E
t
[
min
(
r
t
(
θ
)
A
t
,
clip
(
r
t
(
θ
)
,
1
−
ϵ
,
1
+
ϵ
)
A
t
)
]
若
A
t
>
0
:鼓励增加动作概率,但最多增加
(
1
+
ϵ
)
倍;
若
A
t
<
0
:鼓励减少概率,但最多减少到
(
1
−
ϵ
)
倍。
KL 散度正则(防止语言退化)