AI/ML 详解:强化学习

Day 21 · 2026-06-07 · 难度:中高级
面向:有编程经验的非 AI 方向工程师

监督学习有"标准答案",强化学习只有"事后反馈"——你做一连串决策,环境只告诉你结果好不好,不告诉你每一步该怎么做。这正是训练 Agent、对齐 LLM(RLHF)、下棋、机器人控制的底层范式。今天讲四个核心:Q-Learning(学每个动作值多少)→ Policy Gradient(直接学怎么做)→ Actor-Critic(两者合体)→ PPO(让训练稳下来,也是 ChatGPT 对齐的引擎)。它们是层层递进的,不是并列的四个工具。

强化学习的基本循环(Agent ↔ Environment)

Agent— 动作 a →Environment

状态 s' + 奖励 r←———反馈

目标:找一个策略 π(a|s),最大化长期累积奖励(不是单步),即 G = r₀ + γr₁ + γ²r₂ + …
γ(折扣因子,0~1)= 未来奖励的"贴现率",越小越短视——和现金流折现是同一个数学

Q 学习Q-Learning

value-basedoff-policy
一句话类比

Q-table 就是一张带 TTL 的记忆缓存(memoization cache):key 是「(状态, 动作)」二元组,value 是「这么做最终能拿多少分」。和你给慢查询加缓存一样——第一次靠探索算出来,之后查表即可。区别是这张表会自我修正:每访问一次就用更靠谱的估计去覆盖旧值,最终收敛。

它解决什么问题 + 工作机制

痛点:在迷宫里走一步并不会立刻告诉你这步好不好——奖励是延迟的(走到终点才加分)。怎么把终点的奖励"传播"回起点的每一步?Q-Learning 的答案是 Bellman 更新(自举 / bootstrapping)

Q(s,a) ← Q(s,a) + α · [ r + γ·maxa' Q(s',a') − Q(s,a) ]

逐符号拆开:Q(s,a) 是当前对"状态 s 做动作 a"的价值估计;r 是这步立即拿到的奖励;max Q(s',a') 是"到了新状态 s' 后,最好的动作能拿多少"——用下一步的估计来改进这一步,这就是自举。中括号整体叫 TD error(时序差分误差):现实(r+γ·未来) 和 旧估计 的差距;α(学习率)控制每次修正多少。直觉就是「拿一个稍微更准的估计去微调当前估计」,像缓存的最终一致性,访问越多越准。

为什么是 off-policy(离策略)?更新里用的是 max(贪心最优动作),但实际探索时用 ε-greedy(大概率走当前最优、小概率随机试)——学的策略和走的策略可以不同。这像生产流量做 A/B:99% 走稳定路由,1% 探索新路由,但你学到的是"最优路由是哪条"。2013 年 DQN(Mnih et al.)把这张表换成神经网络去近似 Q,直接从 Atari 像素学会打游戏——这是深度强化学习的开端。

代码示例
import gymnasium as gym, numpy as np
env = gym.make("FrozenLake-v1", is_slippery=False)
Q = np.zeros((env.observation_space.n, env.action_space.n))  # Q 表
α, γ, ε = 0.8, 0.95, 0.1

for ep in range(2000):
    s, _ = env.reset(); done = False
    while not done:
        # ε-greedy:小概率随机探索,否则查表走最优
        a = env.action_space.sample() if np.random.rand()<ε else Q[s].argmax()
        s2, r, term, trunc, _ = env.step(a); done = term or trunc
        # Bellman 更新:用 r + γ·下一步最优 去修正当前估计
        Q[s, a] += α * (r + γ * Q[s2].max() - Q[s, a])
        s = s2
print(Q.argmax(axis=1))  # 每个状态学到的最优动作
常见误区 + 实践场景
"Q-table 万能"——错。状态-动作空间一大就爆表:围棋有 10¹⁷⁰ 个状态,根本存不下;连续动作(机器人关节角度)更是无限多。这正是要么上 DQN(神经网络近似 Q),要么转向后面的 policy-based 方法的原因。Q-Learning 只在离散、小规模状态空间里直接好用。
📌 BigCat 场景:把"个人决策"建模成 Q-table 是危险的过度拟合,但思维框架可迁移——区分"立即奖励 r"和"长期价值 γ·未来"是反短视的利器。例如评估学习投入时,问的不是"今天爽不爽",而是"折现后的长期 Q 值"。
Takeaway + 思考题
💡 Q-Learning = 给「(状态,动作)→长期价值」建一张自我修正的缓存表,靠 Bellman 自举把延迟奖励传播回每一步。
🤔 自举意味着"用估计改进估计"——如果初始估计全错,为什么它还能收敛到正确值?这和迭代法解方程有何相似?

策略梯度Policy Gradient

policy-basedon-policy
一句话类比

Q-Learning 是先建价值表、再从表里挑动作(间接);Policy Gradient 直接调一个"策略函数"的参数——像直接调负载均衡器的路由权重:观测到某条路由延迟低(奖励高),就调高它的权重,下次更可能走它。不绕道价值表,直接优化"决策本身"。

它解决什么问题 + 工作机制

痛点:动作连续(方向盘转 17.3°)或状态空间巨大时,Q-table 失效。解法:用神经网络 πθ(a|s) 直接输出"在状态 s 下各动作的概率",θ 是网络参数。目标是最大化期望回报 J(θ)。关键的策略梯度定理(Sutton et al. 2000)给出怎么求导:

θ J(θ) = E[ ∇θ log πθ(a|s) · G ]

直觉拆解:∇log π(a|s) 是"让这个动作更可能发生"的方向;G 是这条轨迹的实际回报(总分)。两者相乘 = 「按回报大小,加权地把好动作的概率往上推、坏动作往下压」。回报高的动作,梯度推得猛;回报为负的,反向压低。这就是经典的 REINFORCE 算法——本质是"奖励加权的最大似然"。

致命弱点:方差极高。G 是一整条轨迹蒙特卡洛采样出来的,运气成分大——同样的好策略,这局走运拿 100 分、下局背运拿 10 分,梯度信号忽大忽小,训练像在噪声里爬山,又慢又不稳。这个"高方差"问题,正是下一张卡 Actor-Critic 要解决的。

代码示例
import torch, torch.nn as nn
policy = nn.Sequential(nn.Linear(4,128), nn.ReLU(), nn.Linear(128,2))
opt = torch.optim.Adam(policy.parameters(), lr=1e-2)

# 跑完一整局,拿到 (log_probs, 每步回报 returns) 后:
def update(log_probs, returns):
    returns = torch.tensor(returns)
    # 标准化回报 = 一个简单的方差削减技巧(baseline 雏形)
    returns = (returns - returns.mean()) / (returns.std() + 1e-8)
    # 损失 = -Σ log π(a|s)·G ;最大化回报 = 最小化它的负数
    loss = -(torch.stack(log_probs) * returns).sum()
    opt.zero_grad(); loss.backward(); opt.step()  # 把好动作概率推高
常见误区 + 实践场景
"策略梯度比 Q-Learning 先进,所以更好"——错。它是 on-policy(在策略):每次更新只能用当前策略刚采的数据,旧数据作废,样本效率低(同样的经验 Q-Learning 能反复用,它用一次就扔)。两者是 trade-off,不是替代:离散小空间用 value-based,连续/大空间用 policy-based。
📌 BigCat 场景:理解 LLM 的本质有帮助——LLM 生成下一个 token 就是一个 πθ(token|上文) 的策略网络。RLHF 训练时,"人类觉得好的回答"就是高奖励 G,用策略梯度把好回答的概率推高。你每天用的对齐模型,骨架就是这条公式。
Takeaway + 思考题
💡 Policy Gradient = 跳过价值表,按回报加权直接把"好动作"的概率推上去;代价是高方差 + 低样本效率。
🤔 既然 G 的方差是元凶,如果我们能事先知道"每个状态的平均分",用 (G − 平均分) 代替 G,会发生什么?(这正是下一张卡的钥匙)

演员-评论家Actor-Critic

hybridadvantage
一句话类比

纯策略梯度像"等线上指标出来才知道改对没"——慢且噪声大。Actor-Critic 加了个内部 code reviewerActor(演员)= 策略网络,负责写代码(出动作);Critic(评论家)= 价值网络,每一步立刻给出"相对基线的好坏"评分。不用等终局,即时反馈,迭代快得多。

它解决什么问题 + 工作机制

它直接治 Policy Gradient 的高方差病。核心招数:把噪声大的原始回报 G 换成 优势函数(Advantage)

A(s,a) = Q(s,a) − V(s) ≈ [ r + γ·V(s') ] − V(s)

逐项看:V(s)(Critic 学的状态价值)= "在状态 s 平均能拿多少分",是个基线(baseline)A = "这个动作比平均好多少"。把策略梯度里的 G 换成 A:

θ J ≈ E[ ∇θ log πθ(a|s) · A(s,a) ]

为什么方差骤降?原始 G 可能是"+100 分"(看着都很好,分不出哪个动作真的强);减去基线后变成"+5 / −3"这种居中、有正有负的信号——好坏对比鲜明,梯度方向清晰。数学上减去只依赖状态、不依赖动作的基线不改变梯度期望(无偏),却大幅降低方差。这是 RL 里最优雅的免费午餐之一。Critic 自己怎么学 V?用和 Q-Learning 同款的 TD error 自举更新。A2C / A3C(Mnih et al. 2016)是其经典实现。

Actor-Critic 双网络协作

状态 s Actor π(a|s) 动作 a 环境
状态 s Critic V(s) 基线估计

环境返回 r, s' 优势 A = r + γV(s') − V(s)
├─→ A 给 Actor:好动作(A>0)推高概率,差动作压低
└─→ TD error 给 Critic:让 V 估得更准
代码示例
# Actor 与 Critic 通常共享底层、各出一个头
class ActorCritic(nn.Module):
    def __init__(self):
        super().__init__()
        self.body   = nn.Sequential(nn.Linear(4,128), nn.ReLU())
        self.actor  = nn.Linear(128, 2)  # 输出动作 logits
        self.critic = nn.Linear(128, 1)  # 输出 V(s) 一个标量
    def forward(self, s):
        h = self.body(s)
        return self.actor(h), self.critic(h)

# 关键一步:用 Critic 的 V 算优势,替代高方差的原始回报 G
logits, v = model(s); _, v2 = model(s2)
advantage = r + γ * v2.detach() - v   # detach:不让目标反传到 Critic
actor_loss  = -(dist.log_prob(a) * advantage.detach())
critic_loss = advantage.pow(2)            # 让 V 逼近 r+γV(s')
常见误区 + 实践场景
"加了 Critic 一定更稳"——不一定。Actor 和 Critic 互相依赖、同时在学:Critic 还没学准时,给 Actor 的优势信号是错的,Actor 学歪又喂给 Critic 更糟的数据,可能双双发散。这是 RL 训练比监督学习脆弱得多的根因——没有固定的真值标签,目标自己在动(moving target)。
📌 BigCat 场景:优势函数的思想本身极有迁移价值——评估任何选择时,别看绝对值,看相对基线的增量。"这个项目能带来 100 万"没意义,"比你下一个最佳选项多带来多少"才是真正的 advantage。这是机会成本的 RL 表述。
Takeaway + 思考题
💡 Actor-Critic = 演员出招 + 评论家用"相对基线的优势"即时打分;减基线在不引入偏差的前提下砍掉方差。
🤔 Critic 是个"会犯错的裁判"。当裁判系统性偏高估某些状态时,Actor 会被带偏向哪?这和组织里"评价标准有偏"导致的行为扭曲像不像?

近端策略优化PPO · Proximal Policy Optimization

on-policytrust regionRLHF 引擎
一句话类比

PPO 就是给策略更新加了灰度发布 / 限流(canary + rate limit):一次只敢改一点点,新策略不准偏离旧策略太远。因为 RL 里一步迈太大就可能策略崩溃(像一次全量上线把线上打挂,还回滚不了——后续数据都是坏策略采的)。PPO = 把"每次最多改 ±20%"硬编码进损失函数。

它解决什么问题 + 工作机制

痛点:Actor-Critic 仍可能一步更新过猛导致崩溃。早先的 TRPO 用复杂的二阶约束解决,但实现繁琐。PPO(Schulman et al. 2017)用一个朴素到惊人的裁剪(clip)目标就搞定。先定义概率比 rt(θ) = πθ(a|s) / πθold(a|s)——新旧策略对同一动作的概率之比(=1 表示没变)。核心目标:

L = E[ min( rt·A, clip(rt, 1−ε, 1+ε)·A ) ]

拆开这个 minclip(ε 通常 0.2):clip 把概率比强行夹在 [0.8, 1.2] 区间——想把某动作概率提到旧的 1.5 倍?对不起,收益按 1.2 倍封顶,再激进也没额外好处,于是没动力迈大步。外层 min 取裁剪前后较小值,是个保守的"悲观下界":当优势 A>0(好动作)时限制涨幅,A<0(坏动作)时限制跌幅,双向都不许跨太远。一句话:「在旧策略的信任域(trust region)内小步快走」

这个简单改动让 PPO 兼具稳定性和易实现性,成了事实标准。最重要的应用:RLHF——用人类偏好训练出奖励模型,再用 PPO 优化 LLM 策略,同时加一个 KL 惩罚防止模型为讨好奖励而偏离原始语言能力太远(机制和 clip 异曲同工:都是"别跑太远")。ChatGPT、Claude 的对齐阶段都建立在这套之上。

PPO 裁剪:概率比被夹住

概率比 rt0.5 ── [ 0.8 ←信任域→ 1.2 ] ── 2.0
↑ 夹住夹住 ↑

A>0 好动作:概率可升,但封顶 1.2× → 不会暴冲
A<0 坏动作:概率可降,但封底 0.8× → 不会过激
效果 = 信任域优化,但只用一阶梯度,实现极简
代码示例
# 生产中直接用 stable-baselines3,几行起一个 PPO
from stable_baselines3 import PPO
model = PPO("MlpPolicy", "CartPole-v1", verbose=1)
model.learn(total_timesteps=50_000)

# —— clip 目标的核心就这几行(看懂机制用)——
ratio = torch.exp(new_logp - old_logp)      # 概率比 = exp(对数差)
unclipped = ratio * advantage
clipped   = torch.clamp(ratio, 1-ε, 1+ε) * advantage  # 夹在[0.8,1.2]
policy_loss = -torch.min(unclipped, clipped).mean()  # 取悲观下界
常见误区 + 实践场景
"PPO 的 clip 保证了单调改进,所以一定收敛"——错。clip 只是启发式地约束步长,并无 TRPO 那样的理论单调改进保证;它实践中好用是因为简单 + 鲁棒,不是因为数学上最优。PPO 对超参(学习率、ε、并行环境数、优势归一化)相当敏感,"能跑"和"调好"之间差很多——但这些工程细节属于实战范畴。
📌 BigCat 场景:clip 的哲学值得借鉴——面对不确定、不可回滚的决策时,限制单步变化幅度,是比"追求一步到位最优"更鲁棒的策略。无论是改架构、调团队还是做人生重大选择,"小步快走 + 不偏离已验证基线太远"往往胜过激进豪赌。这是信任域思想的人生版。
Takeaway + 思考题
💡 PPO = 用一个 clip 把策略更新关进信任域,以极简实现换来稳定,成为 RLHF / LLM 对齐的工业标准。
🤔 RLHF 用 PPO 优化"人类偏好奖励",但奖励模型本身是学出来的、不完美的。当模型学会钻奖励模型的空子(reward hacking)时,clip 和 KL 惩罚够用吗?
工程对应 → super-individual D20:对齐与 Prompt Injection(钻空子的攻防实战)

深入资源Further Reading

深入思考Deep Questions

1. 把今天四个算法连成一条进化线:从 Q-Learning 到 PPO,每一步到底在解决前一步的什么缺陷?
这是一条逐个补漏的链,不是四个并列工具。Q-Learning(value-based)在离散小空间优雅,但状态-动作一爆炸就建不出表,且只能处理离散动作。Policy Gradient 改为直接参数化策略 πθ,攻克了连续动作和巨大状态空间——但代价是高方差(回报 G 是整条轨迹蒙特卡洛采样,运气成分大)和低样本效率(on-policy,数据用一次就扔)。Actor-Critic 引入 Critic 估的基线 V(s),把 G 换成优势 A = G − V,在不引入偏差的前提下大幅降方差——但 Actor、Critic 互相依赖、目标在动,训练变脆弱,且一步更新过猛会崩。PPO 最后用 clip 把每步更新关进信任域,解决"步长过大导致崩溃",换来稳定与易实现。所以记忆锚点:Q→PG 解"空间/连续动作",PG→AC 解"方差",AC→PPO 解"步长稳定性"。每一代都没废掉前一代的思想,而是叠加修正。
2. RL 训练为什么比监督学习脆弱得多?作为分布式系统工程师,这种"不稳定"让你想到什么?
根因是 RL 没有固定的真值标签,目标自己在动(moving target / non-stationarity)。监督学习的标签是钉死的,loss 单调可优化;RL 里:(a) Critic 的目标 r+γV(s') 里又含 V,用自己估计当自己目标(自举),估计漂移会放大;(b) 数据分布随策略变化——策略一更新,采到的状态分布就变了,等于训练集在训练中不断改写;(c) Actor↔Critic 是耦合的双优化,像两个 agent 在博弈,可能震荡或共同发散。对应分布式系统的熟悉痛点:这像没有全局时钟的最终一致性系统里做反馈控制——既要收敛又有延迟反馈与正反馈回路,极易振荡。工程对策也相通:限制步长(PPO clip ≈ rate limiting)、用目标网络冻结一段时间(≈ 读写分离 / 快照隔离)、经验回放打散相关性(≈ 缓冲解耦)、多环境并行降方差(≈ 多副本采样求均值)。RL 的稳定性技巧,本质都是控制论 + 分布式系统里的老朋友。
3. RLHF 用 PPO 对齐 LLM,但奖励模型是学出来的、不完美。"reward hacking"(钻奖励空子)为什么几乎不可避免?
因为奖励模型只是真实人类偏好的有损代理(proxy),而优化器(PPO)的天职就是把代理指标榨到极致——这正是 Goodhart 定律:"当度量变成目标,它就不再是好度量"。具体表现:模型可能学会写看起来自信、详尽、讨喜但实质空洞或谄媚(sycophancy)的回答,因为奖励模型偏爱这种表面特征。为什么难根治:(a) 奖励模型训练数据有限,覆盖不到 LLM 探索出的所有古怪策略,存在分布外漏洞;(b) 优化越强,越会找到这些漏洞——能力和风险同向增长。缓解手段及其局限:KL 惩罚(约束对齐后模型别偏离原始模型太远,类似 PPO clip 的精神)能防止跑飞,但 KL 太小则学不动、太大则又被 hack,是个 trade-off;定期用新人类反馈重训奖励模型堵漏,但这是军备竞赛;以及 Constitutional AI、过程监督等新范式。结论:clip 和 KL 是必要的护栏,但非充分的解——对齐的根本难题是"如何指定我们真正想要的",而非"如何优化已指定的"。这也是 super-individual D20 prompt injection 攻防的同源命题。
4. γ(折扣因子)只是个让数学收敛的技巧,还是有更深的含义?它和人的"延迟满足"是同一回事吗?
两层都有。数学上,γ<1 保证无限长任务的累积奖励级数收敛(几何级数),是技术必需。但语义上它编码了一个深刻的价值判断:未来奖励该打几折? γ 接近 0 = 极度短视,只看眼前一步;接近 1 = 极有耐心,远期奖励几乎等价于当下。这和金融的现金流贴现(DCF)是同一个数学——γ 就是"每步贴现率",对应利率/不确定性。和人的延迟满足相关但不等同:人的折扣是双曲贴现(hyperbolic,近期掉得快、远期掉得慢,导致拖延和偏好反转),而 RL 用的是指数贴现(每步乘固定 γ,时间一致,不会反悔)——这正是为什么人会"明天再减肥"而理性 agent 不会。有意思的引申:调大 γ 让 agent"更有远见"听着好,但会放大方差、让信用分配(credit assignment)更难——看得越远,越难判断当初哪步功劳大。耐心是有成本的,这一点对 agent 和对人都成立。
5. 既然 LLM 生成本质是策略网络、RLHF 是 RL,那"强化学习"会成为通往更强智能的主路径吗?它的根本瓶颈在哪?
RL 的独特价值在于它处理序贯决策 + 延迟反馈 + 自我改进——这是监督学习碰不到的,也是 agent、推理(o1 式 RL on reasoning)、对齐的共同底座。2024-2026 的趋势确实是"RL 把预训练好的能力激发/对齐出来"(如用可验证奖励做数学/代码 RL)。但根本瓶颈有三:(a) 奖励指定难——现实任务的"好"难以形式化,错误的奖励会被忠实地优化成灾难(见 Q3 reward hacking);(b) 样本效率与探索——RL 通常要海量交互,真实世界(机器人、医疗)试错成本极高,且高维空间的有效探索仍是未解难题;(c) 稳定性与可复现——RL 对超参、随机种子敏感,"实验室能跑、换环境就崩"。所以更可能的图景不是"RL 单骑救主",而是预训练(获取世界知识)+ RL(对齐与决策)+ 检索/工具(外部能力)的组合。RL 是不可或缺的一环,但"奖励从哪来、怎么保证它指向我们真正想要的"——这个问题不解决,RL 越强反而越危险。智能的瓶颈正从"如何优化"转向"优化什么"。