dmrookie's Studio.

强化学习2.Value-based 方法

字数统计: 7k阅读时长: 28 min
2026/07/31
loading

上一篇强化学习基础的结尾留了一个悬念:那个 20 行的表格 Q-learning 在 4 格走廊上完美收敛,但如果把 Q 表格换成神经网络,"一切都开始不收敛了"。

这篇文章就来讲这条路线:Value-based 方法。它的核心信念极其简洁——

如果我能准确知道"在每个状态下、每个动作的长期价值",那最优策略就是免费的副产品:π∗(s)=arg⁡max⁡aQ∗(s,a)\pi^*(s) = \arg\max_a Q^*(s,a)。

问题在于,这个 QQ 表格在 Atari 里有大约 1010010^{100} 行,在围棋里更多。于是整个故事就变成了:如何用一个神经网络稳定地逼近 Q∗Q^*。DQN 之后那一长串改进(Double、Dueling、优先回放、多步、分布式……)看起来像一堆零散的 trick,但每一个都在修一个具体的 bug。这篇文章的写法就是:问题 → 方法 → 公式 → 效果。

从 Q-learning 说起

时序差分:用估计去更新估计

上一篇讲了,如果模型 T,RT, R 已知,直接用价值迭代就能解。现在模型未知,我们只有一条条采样出来的经验 (s,a,r,s′)(s, a, r, s')。怎么办?

最朴素的想法是蒙特卡洛(MC):跑完一整条轨迹,把实际拿到的回报 GtG_t 当作 Q(st,at)Q(s_t,a_t) 的样本,做平均。这样做无偏,但必须等到回合结束,而且方差很大(一条轨迹里所有的随机性都累加进了 GtG_t)。

时序差分(Temporal Difference, TD) 的想法要聪明得多:不用等到最后,走一步就能学。因为贝尔曼方程告诉我们

Q∗(s,a)=E[r+γmax⁡a′Q∗(s′,a′)]Q^*(s,a) = \mathbb{E}\left[ r + \gamma \max_{a'} Q^*(s',a') \right]

那就用括号里的东西当作"标签",去更新 Q(s,a)Q(s,a)。定义 TD error:

δ=r+γmax⁡a′Q(s′,a′)⏟TD target(新估计)−Q(s,a)⏟旧估计\delta = \underbrace{r + \gamma \max_{a'} Q(s', a')}_{\text{TD target(新估计)}} - \underbrace{Q(s,a)}_{\text{旧估计}}

更新规则就是朝着减小 TD error 的方向挪一小步:

Q(s,a)←Q(s,a)+αδQ(s,a) \leftarrow Q(s,a) + \alpha \delta

这就是 Q-learning。它最迷人也最危险的地方在于:用自己的估计来更新自己的估计,这叫 自举(bootstrapping)。好处是不用等回合结束、方差小;坏处是如果估计本身错了,错误会自我强化。

MC、TD、DP 三者对比

维度 动态规划(DP) 蒙特卡洛(MC) 时序差分(TD)
需要模型 T,RT,R 需要 不需要 不需要
需要完整轨迹 不需要 需要 不需要(走一步就学)
是否自举 是 否 是
偏差 无(模型准确时) 无偏 有偏(初期估计不准)
方差 无 高 低
适用性 小规模、模型已知 回合制任务 通用,最主流

一句话记忆:MC 用真实回报换低偏差,TD 用自举换低方差。中间还有连续的谱系(nn-step return、TD(λ\lambda)),后面会讲到。

表格 Q-learning 为什么能收敛

在表格情形下,Q-learning 的收敛有严格保证,条件是:

  1. 每个状态-动作对被访问无限多次(充分探索)
  2. 学习率满足 Robbins-Monro 条件:∑tαt=∞\sum_t \alpha_t = \infty,∑tαt2<∞\sum_t \alpha_t^2 < \infty

有意思的是,条件里完全不要求数据由什么策略产生。这就是 Q-learning 是 off-policy 算法的根源:TD target 里用的是 max⁡a′\max_{a'},它评估的永远是"贪心策略",而不是"实际采样时用的策略"。所以你可以用 ϵ\epsilon-greedy 去探索、甚至用完全随机的策略采数据,学到的仍然是最优 Q∗Q^*。这个性质极其宝贵——它意味着数据可以复用、可以来自别处,是后面经验回放能成立的前提。

对照组:SARSA 与悬崖行走

把 TD target 里的 max⁡a′Q(s′,a′)\max_{a'} Q(s',a') 换成"实际采取的下一个动作"Q(s′,a′)Q(s',a'),就得到 SARSA(名字来自 s,a,r,s′,a′s,a,r,s',a'):

Q(s,a)←Q(s,a)+α[r+γQ(s′,a′)−Q(s,a)]Q(s,a) \leftarrow Q(s,a) + \alpha \left[ r + \gamma Q(s',a') - Q(s,a) \right]

这一字之差让它变成了 on-policy 算法:它评估的是"包含探索行为的当前策略"。经典的悬崖行走(cliff walking) 实验把两者的差异展示得很清楚:

悬崖行走:Q-learning 与 SARSA 学到的路径

  • Q-learning 学到的是最优路径:紧贴悬崖边走,路程最短。但训练过程中因为 ϵ\epsilon-greedy 会随机走动,贴着悬崖时偶尔会掉下去,所以训练期间的实际回报更差。
  • SARSA 学到的是绕远的安全路径。因为它的 target 里包含了"下一步可能会随机乱走"这件事,于是主动远离悬崖,训练期间表现更好。

结论:如果你在乎的是"最终部署的最优策略",选 off-policy;如果你在乎的是"学习过程中不能出事故"(真机、线上系统),on-policy 的保守性反而是优点。


从表格到函数逼近

为什么必须逼近

表格法有两个致命限制:

  1. 存不下:Atari 的状态是 84×84×484 \times 84 \times 4 的像素,状态数天文级
  2. 不泛化:表格里每个状态各自为政,见过 100 万个局面之后,遇到第 100 万零一个仍然一无所知

我们希望的是:Q(s,a;θ)≈Q∗(s,a)Q(s,a;\theta) \approx Q^*(s,a),其中 θ\theta 是一个神经网络的参数。相似的状态自然会得到相似的 QQ 值,这就是泛化。

Fitted Q Iteration:把 RL 变成一串监督回归

最直接的做法是 Fitted Q Iteration(FQI):既然 TD target 可以算出来,那就把它当标签,做一次标准的监督回归。收集一批数据 D={(s,a,r,s′)}\mathcal{D} = \{(s, a, r, s')\},然后反复:

  1. 用当前网络 QkQ_k 给每条数据算标签:y=r+γmax⁡a′Qk(s′,a′)y = r + \gamma \max_{a'} Q_k(s', a')
  2. 训练一个新网络 Qk+1Q_{k+1} 去拟合 {((s,a),y)}\{((s,a), y)\},即最小化 ∑(Qk+1(s,a)−y)2\sum (Q_{k+1}(s,a) - y)^2

用神经网络实现就是 Neural Fitted Q (NFQ)。这个视角很重要:它揭示了深度 RL 的本质就是"一串不断变化标签的监督学习问题"。

为什么它会崩

但直接把这个思路做成在线的、随机梯度的版本,训练经常发散。原因就是上一篇提到的致命三角,这里具体展开:

问题 具体表现
标签非平稳 标签 y=r+γmax⁡Q(s′;θ)y = r + \gamma \max Q(s';\theta) 里的 θ\theta 就是我们正在更新的参数。参数一动,标签就跟着动,等于"追一个自己在动的目标"
数据强相关 在线采样得到的连续状态几乎一样,一个 mini-batch 内样本高度相关,梯度估计有严重偏差,网络会对最近的经验过拟合
自举放大误差 某个状态的 QQ 被高估后,会通过 max⁡\max 传播到它的所有前驱状态,正反馈循环
参数共享的干扰 更新一个状态的 QQ 会不可避免地改变其他状态的 QQ,可能把已经学好的地方破坏掉(灾难性干扰)

DQN 的贡献,就是用两个非常朴素的工程手段,把前两个问题压住了。


DQN:让深度网络 + Q-learning 真的能跑

2015 年的 DQN 用同一套网络结构和超参数,在 49 个 Atari 游戏上达到甚至超过人类水平。它的损失函数是:

L(θ)=E(s,a,r,s′)∼D[(r+γmax⁡a′Q(s′,a′;θ−)−Q(s,a;θ))2]\mathcal{L}(\theta) = \mathbb{E}_{(s,a,r,s') \sim \mathcal{D}} \left[ \left( r + \gamma \max_{a'} Q(s', a'; \theta^{-}) - Q(s,a;\theta) \right)^2 \right]

和 NFQ 相比,公式上只多了两个记号:D\mathcal{D} 和 θ−\theta^-。但正是这两个东西起了决定性作用。

关键设计一:经验回放(Experience Replay)

把每一条交互经验 (s,a,r,s′,done)(s,a,r,s',\text{done}) 存进一个容量固定的缓冲池 D\mathcal{D}(通常 105∼10610^5 \sim 10^6 条),训练时从池中随机采样 mini-batch。

DQN 全貌

它同时解决了两个问题:

  • 打破时间相关性:随机采样让 mini-batch 内的样本近似独立,梯度估计更接近真实梯度
  • 提高样本效率:一条经验可以被反复使用几十次,而不是用一次就扔(这正是 off-policy 性质带来的红利)

顺带还有一个好处:它把训练数据的分布平滑了,避免了策略突变导致的训练震荡。

关键设计二:目标网络(Target Network)

再复制一份网络参数 θ−\theta^- 专门用来算标签,每隔 CC 步(比如 10000 步)才同步一次 θ−←θ\theta^- \leftarrow \theta。

这相当于把"追自己动的目标"变成了"追一个每隔一段时间才跳一次的目标",在每个区间内标签是固定的,退化成了标准监督回归。代价是引入了一点滞后(标签用的是旧参数,稍微过时),换来的是稳定性。后来常见的变体是软更新:

θ−←τθ+(1−τ)θ−,τ≪1\theta^{-} \leftarrow \tau \theta + (1-\tau)\theta^{-}, \quad \tau \ll 1

平滑地跟随,DDPG/TD3/SAC 都用这个(第三篇会见到)。

网络结构与工程细节

DQN 的网络输入是状态,输出是所有动作的 QQ 值(一次前向就能拿到 max⁡\max,不必对每个动作各跑一次):

1
2
3
4
5
6
7
状态 s (84×84×4)
↓
CNN 卷积层 ×3
↓
全连接层
↓
[Q(s,a1), Q(s,a2), ..., Q(s,a_n)] ← 输出维度 = 动作数

那些论文里不太起眼、但少了就跑不起来的细节:

细节 做法 为什么
帧堆叠 最近 4 帧拼成一个状态 单帧不满足马尔可夫性(看不出速度)
跳帧(frame skip) 每 4 帧决策一次,动作重复 省算力,且相邻帧信息冗余
奖励裁剪 奖励 clip 到 [−1,1][-1, 1] 不同游戏分数量级差异巨大,统一超参
Huber loss 误差大时退化为线性 TD error 偶尔很大,平方损失梯度会爆
ϵ\epsilon 退火 从 1.0 线性降到 0.1 早期多探索,后期多利用
梯度裁剪 按范数裁剪 防止个别异常样本破坏网络

核心代码

用 PyTorch 写出来,DQN 的核心其实只有十几行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import torch, torch.nn.functional as F

def dqn_loss(q_net, target_net, batch, gamma=0.99):
s, a, r, s_next, done = batch # a: LongTensor [B]

# 当前 Q(s,a):网络输出所有动作的 Q,再按实际动作取值
q_sa = q_net(s).gather(1, a.unsqueeze(1)).squeeze(1)

with torch.no_grad(): # 标签不回传梯度
q_next_max = target_net(s_next).max(dim=1).values
target = r + gamma * q_next_max * (1.0 - done) # 终止状态不再自举

return F.smooth_l1_loss(q_sa, target) # Huber loss

# 训练循环骨架
for step in range(total_steps):
a = epsilon_greedy(q_net, s, eps=schedule(step))
s_next, r, done = env.step(a)
buffer.push(s, a, r, s_next, done) # 经验回放
s = env.reset() if done else s_next

if step > learn_start and step % train_freq == 0:
loss = dqn_loss(q_net, target_net, buffer.sample(batch_size))
optimizer.zero_grad(); loss.backward()
torch.nn.utils.clip_grad_norm_(q_net.parameters(), 10.0)
optimizer.step()

if step % target_update_freq == 0: # 目标网络同步
target_net.load_state_dict(q_net.state_dict())

注意 (1.0 - done) 这一项:终止状态之后没有未来,不能再自举。这是新手最常见的 bug 之一,写错了会导致价值被系统性高估。


DQN 家族:每个改进修哪个 bug

DQN 能跑,但远不完美。接下来这一串改进,建议按"它在修什么"来记,而不是按名字记。

Double DQN:修 max 带来的过估计

问题。TD target 里的 max⁡a′Q(s′,a′;θ−)\max_{a'} Q(s',a';\theta^-) 有一个统计学上的系统性偏差。因为对于任何有噪声的估计,

E[max⁡aQ^(s,a)]≥max⁡aE[Q^(s,a)]\mathbb{E}\left[\max_{a} \hat{Q}(s,a)\right] \ge \max_{a} \mathbb{E}\left[\hat{Q}(s,a)\right]

也就是说,max⁡\max 会优先挑中那些"恰好被高估"的动作。噪声本身是零均值的,但经过 max⁡\max 筛选后就变成了正偏差。而且这个偏差会通过自举一层层传播、累积,导致 QQ 值整体虚高,策略被误导。

max 操作带来的过估计偏差

方法。把"选哪个动作"和"评估这个动作值多少"用两套参数分开:

YDoubleDQN=r+γ Q ⁣(s′,  arg⁡max⁡a′Q(s′,a′;θ)⏟用在线网络选动作;  θ−⏟用目标网络评估)Y^{\text{DoubleDQN}} = r + \gamma \, Q\!\left(s', \; \underbrace{\arg\max_{a'} Q(s',a';\theta)}_{\text{用在线网络选动作}}; \; \underbrace{\theta^{-}}_{\text{用目标网络评估}}\right)

在线网络说"我觉得 a3a_3 最好",目标网络说"那我来给 a3a_3 打分"。两者的噪声不相关,被同时高估的概率就小得多。

效果。实现代价近乎为零(只改一行),Atari 上的 QQ 值估计明显更接近真实值,得分显著提升。这是性价比最高的改进之一。

1
2
3
4
5
# Double DQN:只改标签的计算方式
with torch.no_grad():
a_star = q_net(s_next).argmax(dim=1, keepdim=True) # 在线网络选
q_next = target_net(s_next).gather(1, a_star).squeeze(1) # 目标网络评
target = r + gamma * q_next * (1.0 - done)

Dueling Network:修"很多状态下动作无关紧要"

问题。在很多状态下,选哪个动作根本不重要(比如赛车游戏里前方空旷的直道,任何动作差别都很小)。但标准 DQN 必须为每个动作独立学一个 QQ 值,样本被浪费在学习"这些动作其实都差不多"这件事上。

方法。把网络拆成两个头:一个估状态价值 V(s)V(s),一个估优势 A(s,a)A(s,a),再合起来:

Q(s,a)=V(s;θV)+(A(s,a;θA)−1∣A∣∑a′A(s,a′;θA))Q(s,a) = V(s;\theta_V) + \left( A(s,a;\theta_A) - \frac{1}{|\mathcal{A}|}\sum_{a'} A(s,a';\theta_A) \right)

1
2
3
        ┌──→ 全连接 ──→ V(s)         标量
CNN ────┤ ├──→ Q(s,a)
└──→ 全连接 ──→ A(s,a) 向量

减去优势均值这一步是必需的:否则 VV 和 AA 之间存在一个常数的自由度(V+cV+c、A−cA-c 给出同样的 QQ),无法唯一确定,训练会漂移。

效果。V(s)V(s) 的更新会受益于所有动作的样本(而不是只有被选中那个动作的样本),学习效率更高,在动作数多的游戏上提升明显。

Prioritized Experience Replay:修"均匀采样浪费算力"

问题。均匀采样意味着那些已经学得很好、TD error 接近 0 的经验,和那些"意外的、还没学明白"的经验被同等对待。而在稀疏奖励任务里,含有奖励的那几条经验可能只占回放池的万分之一。

方法。按 TD error 的大小分配采样概率——越"意外"的经验越值得学:

pi=∣δi∣+ϵ,P(i)=piα∑jpjαp_i = |\delta_i| + \epsilon, \qquad P(i) = \frac{p_i^{\alpha}}{\sum_j p_j^{\alpha}}

α\alpha 控制优先级的强度(α=0\alpha=0 退化为均匀采样)。但这样采样引入了偏差(改变了期望所对应的分布),需要用重要性采样权重纠正:

wi=(1N⋅P(i))β,再除以 max⁡jwj 归一化w_i = \left( \frac{1}{N \cdot P(i)} \right)^{\beta}, \quad \text{再除以 } \max_j w_j \text{ 归一化}

β\beta 从一个较小值退火到 1:训练早期允许有偏(快速学习),后期逐渐无偏(保证收敛)。

效果。在 Rainbow 的消融实验里,它是贡献最大的两个改进之一,在稀疏奖励任务上尤其明显。代价是实现复杂(需要 sum-tree 数据结构做 O(log⁡N)O(\log N) 采样)和额外的超参。

Multi-step Returns:调节偏差-方差的旋钮

问题。单步 TD 的自举太"近视":奖励信号每次只能往前传播一格(回忆上一篇价值迭代那张表,传播 3 格要 3 轮)。在长视野任务里,信息扩散得太慢。

方法。用 nn 步真实奖励 + 第 nn 步的自举:

Y(n)=∑k=0n−1γkrt+k+γnmax⁡a′Q(st+n,a′;θ−)Y^{(n)} = \sum_{k=0}^{n-1} \gamma^k r_{t+k} + \gamma^n \max_{a'} Q(s_{t+n}, a'; \theta^{-})

n=1n=1 是 DQN,n→∞n \to \infty 是蒙特卡洛。nn 就是偏差-方差的旋钮:nn 越大,用的真实信息越多(偏差小),但方差越大。实践中 n=3∼5n=3 \sim 5 通常最好。

n-step 的偏差-方差权衡

顺便提一句理论上的瑕疵:Q-learning 是 off-policy 的,严格来说多步回报需要重要性采样修正(Retrace、Tree-backup 等算法就是干这个的)。但实践中当 nn 较小、策略变化不快时,直接忽略修正效果也很好——这是深度 RL 里典型的"理论上不严谨但工程上有效"的例子。

Distributional RL:学分布而不是期望

问题。Q(s,a)Q(s,a) 是回报的期望,但期望丢掉了大量信息。"稳定拿 10 分"和"一半概率 0 分、一半概率 20 分"的期望相同,但风险截然不同。而且用一个标量去拟合一个多峰分布,网络内部的表示会很别扭。

方法(C51)。让网络直接输出回报的分布。把可能的回报值离散成固定的 51 个原子 {z1,…,z51}\{z_1, \ldots, z_{51}\}(比如均匀分布在 [−10,10][-10, 10]),网络对每个动作输出这 51 个原子的概率。分布版的贝尔曼算子是:

Z(s,a)  ←D  r+γZ(s′,a∗),a∗=arg⁡max⁡a′E[Z(s′,a′)]Z(s,a) \;\overset{D}{\leftarrow}\; r + \gamma Z(s', a^*), \quad a^* = \arg\max_{a'} \mathbb{E}[Z(s',a')]

即:把下一状态的分布整体乘以 γ\gamma 再平移 rr,然后投影回固定的原子网格上(因为缩放平移后原子位置会错开),最后用交叉熵损失训练。

QR-DQN 反过来做:固定概率(均匀的分位数),让网络输出分位数的位置,用分位数回归损失训练,避免了投影这一步,效果更好。

效果。得分提升明显,而且训练更稳定。一个反直觉的点:即使我们最终只用期望去做决策(arg⁡max⁡E[Z]\arg\max \mathbb{E}[Z]),学分布这件事本身也提升了性能——因为它给网络提供了更丰富的训练信号(一种辅助任务式的正则化效果)。

Noisy Nets:让探索可学习

问题。ϵ\epsilon-greedy 的探索是盲目的:它以固定概率完全随机地乱按,不管当前状态。在需要"连续做出十几个特定动作才能拿到第一个奖励"的游戏(如 Montezuma's Revenge)里,随机乱按几乎不可能碰对。

方法。把网络权重变成带噪声的参数:

w=μw+σw⊙εw,εw∼N(0,I)w = \mu^w + \sigma^w \odot \varepsilon^w, \qquad \varepsilon^w \sim \mathcal{N}(0, I)

其中 μw,σw\mu^w, \sigma^w 都是可学习的。每次前向采样一次噪声,于是网络的输出本身带随机性,产生的探索是状态相关的、且有时间一致性的(一次采样在整个前向过程中固定)。更妙的是 σw\sigma^w 由梯度自己学:网络会在还不确定的地方保持大噪声,在已经确定的地方自动把噪声降下来。

效果。取代了 ϵ\epsilon-greedy,在需要深度探索的游戏上提升显著,且少了一个需要手调的退火超参。

Rainbow:把它们全部装上

Rainbow 把上面六项(Double + Dueling + PER + Multi-step + Distributional + Noisy)组合到一起,在 Atari 上大幅超过各个单项。它的消融实验结论比算法本身更有价值:

移除的组件 性能损失 结论
Prioritized Replay 很大 关键
Multi-step 很大 关键
Distributional 较大 重要
Noisy Nets 中等 有用
Dueling 小 视任务而定
Double 小(在分布式框架下) 分布式 RL 已部分缓解过估计

Rainbow 消融实验

值得注意的是 Double 的边际贡献变小了——因为 distributional RL 本身就抑制了过估计。这提示我们:这些 trick 之间不是简单叠加的关系,有些在修同一个 bug。

一张汇总表

改进 修的问题 核心改动 代价
经验回放 数据相关、样本浪费 缓冲池 + 随机采样 内存
目标网络 标签非平稳 冻结一份参数算标签 更新滞后
Double DQN max⁡\max 过估计 选动作与评估解耦 几乎为零
Dueling 动作无关状态浪费样本 Q=V+(A−Aˉ)Q = V + (A - \bar{A}) 结构略复杂
优先回放 均匀采样低效 按 TD error 采样 + IS 纠偏 实现复杂、多超参
Multi-step 信息传播慢 nn 步回报 方差上升
Distributional 期望丢信息 学回报分布 计算量、输出维度
Noisy Nets 探索盲目 可学习的参数噪声 参数量翻倍

再往后

绕不过去的墙:连续动作空间

Value-based 方法的所有操作都依赖 max⁡aQ(s,a)\max_a Q(s,a) 和 arg⁡max⁡aQ(s,a)\arg\max_a Q(s,a)。动作离散时,一次前向输出所有动作的 QQ 值,取最大即可。但机械臂的动作是 7 个连续的关节力矩,arg⁡max⁡\arg\max 变成了一个在每一步决策时都要解一遍的非凸优化问题,完全不可行。

离散化行不行?7 个维度每维切 10 档就是 10710^7 个动作,且丢掉了动作空间的连续结构。这就是 value-based 路线的天花板,也是下一篇 policy gradient 存在的根本理由:既然 arg⁡max⁡\arg\max 难算,那就训练一个网络专门输出这个 arg⁡max⁡\arg\max。

规模化:分布式与长记忆

  • Ape-X / R2D2:把数据采集和学习分离——几百个 actor 并行在环境里跑、往共享的优先回放池里灌数据,一个 learner 专心更新参数。回放池的吞吐量成了新的性能来源。R2D2 进一步在网络里加入 LSTM 处理部分可观测,并解决了"回放里存储 RNN 隐状态"的一系列工程问题。
  • Agent57:结合内在动机的探索、元控制器自适应调节 γ\gamma 与探索强度,第一次在全部 57 个 Atari 游戏上超过人类基准。

离线设定:外推误差

如果只给一个固定数据集、不允许交互(Offline RL),DQN 类方法会失败得很惨。原因在 max⁡a′Q(s′,a′)\max_{a'} Q(s',a') 这一项:max⁡\max 会主动去挑那些数据集中从未出现过的动作,而网络在这些动作上的输出纯属外推,往往严重偏高;这个虚高的值又通过自举传播开来,最终整个 QQ 函数被污染。这叫 外推误差(extrapolation error)。

主流的应对思路是"保守":

  • BCQ:限制 max⁡\max 只在"数据集中可能出现的动作"里取
  • CQL:在损失里加一项,主动压低那些没在数据里出现过的动作的 QQ 值
  • IQL:用期望分位数回归隐式地做 max⁡\max,完全避免对未见动作求值

什么时候该选 value-based

适合 不适合
动作空间离散且不大 连续或超高维动作空间
交互成本高,需要复用数据 需要显式随机策略(博弈、部分可观测)
有大量历史数据可灌进回放池 奖励极稀疏且需要长程探索(需额外机制)
需要评估"每个动作值多少" 需要严格的策略改进保证

实践踩坑清单

按我踩过的频率排序:

  1. 忘了 (1 - done):终止状态继续自举,QQ 值持续膨胀,是最常见的静默 bug
  2. 目标网络同步太频繁:等于没有目标网络,训练发散;太慢则学习停滞。Atari 上常用 C≈104C \approx 10^4 步
  3. γ\gamma 过大:任务实际视野只有几十步却用 γ=0.999\gamma = 0.999,方差和自举误差都被放大
  4. 回放池太小:退化成在线学习,样本相关性回来了;太大则含有太多过时策略的数据
  5. learn_start 太早:池子里只有几百条高度相关的数据就开始训练,网络先过拟合到一个错误的解上
  6. 没做奖励缩放:奖励量级 10310^3 时 TD error 巨大,配合平方损失直接梯度爆炸
  7. 只看 loss 不看 QQ 值:DQN 的 loss 不单调下降是正常的(标签在变)。真正该监控的是平均 QQ 值——如果它持续上升不收敛,几乎肯定是过估计发散了
  8. ϵ\epsilon 退火太快:过早停止探索,锁死在局部最优

小结

  • Value-based 方法的信条是"估准 Q∗Q^*,策略免费"。TD 学习用自举把贝尔曼方程变成可采样的更新规则;Q-learning 因为 target 里用了 max⁡\max 而天然 off-policy,这使得数据复用成为可能。
  • 表格换成神经网络后,标签非平稳和数据强相关两个问题让训练发散。DQN 用目标网络和经验回放分别压住它们,这是深度 RL 的起点。
  • 后续改进各修一个具体 bug:Double 修 max⁡\max 的过估计、Dueling 修样本利用率、优先回放修采样效率、多步回报调节偏差-方差、分布式 RL 保留回报的完整信息、Noisy Nets 让探索可学习。Rainbow 的消融告诉我们优先回放和多步回报最关键,且这些 trick 之间存在功能重叠。
  • 它的天花板是连续动作空间:arg⁡max⁡aQ\arg\max_a Q 不可解。这直接催生了下一篇的主题。

下一篇进入 Policy gradient methods:不再绕道价值函数,而是直接把策略参数化,对期望回报求梯度。我们会从 REINFORCE 的高方差问题出发,看基线和优势函数如何降方差,为什么 TRPO/PPO 要限制更新步长,以及 DDPG/SAC 如何攻克连续控制——最后连到当下 LLM 训练里的 RLHF 与 GRPO。

CATALOG
  1. 1. 从 Q-learning 说起
    1. 1.1. 时序差分:用估计去更新估计
    2. 1.2. MC、TD、DP 三者对比
    3. 1.3. 表格 Q-learning 为什么能收敛
    4. 1.4. 对照组:SARSA 与悬崖行走
  2. 2. 从表格到函数逼近
    1. 2.1. 为什么必须逼近
    2. 2.2. Fitted Q Iteration:把 RL 变成一串监督回归
    3. 2.3. 为什么它会崩
  3. 3. DQN:让深度网络 + Q-learning 真的能跑
    1. 3.1. 关键设计一:经验回放(Experience Replay)
    2. 3.2. 关键设计二:目标网络(Target Network)
    3. 3.3. 网络结构与工程细节
    4. 3.4. 核心代码
  4. 4. DQN 家族:每个改进修哪个 bug
    1. 4.1. Double DQN:修 max 带来的过估计
    2. 4.2. Dueling Network:修"很多状态下动作无关紧要"
    3. 4.3. Prioritized Experience Replay:修"均匀采样浪费算力"
    4. 4.4. Multi-step Returns:调节偏差-方差的旋钮
    5. 4.5. Distributional RL:学分布而不是期望
    6. 4.6. Noisy Nets:让探索可学习
    7. 4.7. Rainbow:把它们全部装上
    8. 4.8. 一张汇总表
  5. 5. 再往后
    1. 5.1. 绕不过去的墙:连续动作空间
    2. 5.2. 规模化:分布式与长记忆
    3. 5.3. 离线设定:外推误差
    4. 5.4. 什么时候该选 value-based
  6. 6. 实践踩坑清单
  7. 7. 小结