表格 Q-Learning 需要为状态和动作保存数值,面对图像输入时难以直接扩展。用神经网络近似 Q 函数后,又会遇到相关样本和不断变化的目标。本文从这些训练困难解释 DQN 的经验回放与目标网络,再比较 PPO 的策略更新思路,不把某一种算法当作所有任务的默认答案。
为什么「Q-Learning + 神经网络」直接训会崩
把 Q 表换成网络 ,用 TD 误差做回归:
这个式子暗藏三个致命问题,每一个都足以让训练爆炸:
- 样本相关:连续的游戏帧高度相似(第 100 帧和第 101 帧几乎一样),违反 SGD 要求的「独立同分布」,梯度方向被局部相关性带偏。
- 移动靶:监督学习里标签是固定的,这里目标 也来自同一个网络——你在追一个自己投射的影子,网络一动目标也动,极易震荡发散。
- 自举放大误差:Q 值的微小高估会通过 max 操作不断自我强化,最后 Q 值膨胀到天文数字。
DQN 的两根救命稻草
经验回放(Experience Replay):把每一步 存进一个几十万容量的回放缓冲区,训练时随机抽 batch。相关性被随机抽样打散,老数据还能反复利用——一举两得。
目标网络(Target Network):复制一份参数冻结的网络 专门计算目标值,每隔几千步才把在线网络的参数同步过去。靶子变成「慢速移动的靶」,回归有了稳定的锚点:
就是这两个工程 trick,让 DQN 在 49 款 Atari 游戏上达到了人类水平。深度 RL 的历史证明了一个朴素道理:这个领域的突破往往不来自更漂亮的理论,而来自驯服不稳定性的工程手段。
两个重要改进:Double 与 Dueling
Double DQN:max 操作系统性高估 Q 值(噪声最大者被选中)。解法很优雅——用在线网络选动作,用目标网络评估动作,把「选择」和「评估」解耦:
Dueling DQN:把网络拆成两股——状态价值 和动作优势 ,最后合成 。直觉:很多状态下「选哪个动作无所谓」(比如敌人很远时向左向右都行),单独学 让网络不必为每个动作凑出准确 Q 值,学习效率大增。
从价值方法到策略方法:PPO
DQN 家族处理离散动作(上下左右按钮),但机器人控制、大模型 RLHF 的动作空间是连续的或巨大的,这类问题更适合策略梯度:直接参数化策略 并优化它。
朴素策略梯度的问题是步长敏感:一次更新太猛,策略突变,采样的数据分布剧变,训练崩溃。TRPO 用复杂的二阶优化约束更新幅度,PPO(Proximal Policy Optimization)用一行截断达到了几乎相同的效果:
其中 是新旧策略的概率比, 是优势估计(这个动作比平均好多少), 通常 0.2。
设计直觉:概率比超出 后梯度被截断清零——单步更新幅度被硬性限制在一个「信赖区间」内。简单、稳定、通用,PPO 因此成为从游戏 AI 到 RLHF 的默认算法。
Stable-Baselines3 实战
今天没人手写 PPO 训练循环,Stable-Baselines3(SB3)是标准工具:
import gymnasium as gym
from stable_baselines3 import PPO, DQN
from stable_baselines3.common.evaluation import evaluate_policy
# DQN:离散动作(CartPole)
env = gym.make("CartPole-v1")
model = DQN("MlpPolicy", env,
buffer_size=50_000, # 回放缓冲区
target_update_interval=500, # 目标网络同步频率
exploration_fraction=0.2, # epsilon 退火
verbose=1)
model.learn(total_timesteps=50_000)
# PPO:连续控制(Pendulum)
env2 = gym.make("Pendulum-v1")
model2 = PPO("MlpPolicy", env2,
n_steps=2048, # 每次更新采样的步数
batch_size=64,
clip_range=0.2, # 就是那个 epsilon
verbose=1)
model2.learn(total_timesteps=100_000)
mean_r, std_r = evaluate_policy(model2, env2, n_eval_episodes=10)
print(f"平均回报: {mean_r:.1f} ± {std_r:.1f}")
消融应该怎样比较
保留完整 DQN 作为基线,分别修改经验回放和目标网络,比较多个随机种子的回报曲线、Q 值范围和失败比例。这些组件旨在缓解训练不稳定,但去掉组件并不保证每次都会崩溃,单个种子的曲线也不能证明普遍结论。
PPO 的 clip_range 同样应在固定采样预算下比较,而不是预设某个值必然发散。这里给出实验设计,不提供未运行的回报数字。记录环境版本、终止条件和评估策略,才能解释配置变化带来的影响。
踩坑与排查
| 症状 | 可能原因 | 排查方法 |
|---|---|---|
| 回报升到一半突然崩溃 | 目标网络同步太频繁/无回放 | 检查 buffer_size、target_update_interval |
| Q 值指数膨胀 | 自举高估无约束 | 换 Double DQN;降学习率;加梯度裁剪 |
| PPO 完全不学 | 奖励尺度太离谱 | 奖励归一化;检查优势估计是否正常 |
| 训练慢但 GPU 闲着 | 环境交互是瓶颈(CPU 跑仿真) | 向量化环境 VecEnv 多开实例 |
| 策略学到「躺平」 | 奖励函数有漏洞 | 回放 episode 视频,看智能体在干什么 |
| 同配置结果不可复现 | 环境/种子未固定 | 固定所有 seed;记录 SB3 版本 |
奖励塑形(reward shaping)是另一个深坑:你设计的奖励和「你真正想要的行为」之间永远有缝隙,智能体以钻漏洞为毕生使命。经典案例:赛艇游戏智能体发现不跑完赛道、在原地刷分点转圈得分更高。调 RL 的第一课是先看视频再看曲线。
动手练习
- 用 SB3 在 CartPole 上分别训练 DQN 和 PPO,对比样本效率(达到 475 回报所需步数)。
- 对 DQN 做消融:去掉经验回放、去掉目标网络,画出三条学习曲线并解释形态差异。
- 把 PPO 的 clip_range 分别设为 0.1、0.2、0.5,观察训练稳定性变化。
面试常问
Q:经验回放和目标网络分别解决什么问题? 经验回放解决样本相关性:连续帧高度相关违反 i.i.d. 假设,随机抽样打散相关性,还提高了数据利用率。目标网络解决移动靶问题:TD 目标由同一网络计算,参数更新导致目标同步漂移,冻结的目标网络提供稳定锚点,周期性同步平衡了稳定性与新鲜感。
Q:PPO 的 clip 机制为什么有效? 策略梯度中,新旧策略差异大时重要性采样比率 偏离 1,梯度估计方差爆炸且更新方向不可靠。clip 把 超出 的部分梯度清零,等效于单步策略更新幅度受限,近似实现了 TRPO 的信赖域约束,但只需一阶优化,实现简单、效果相当。
Q:DQN 和 PPO 怎么选? 看动作空间和场景:离散小动作空间(游戏按钮)DQN 样本效率更高;连续控制(机器人关节)或巨大动作空间(LLM 词表)只能 PPO 等策略方法;需要离线训练/复用历史数据选 DQN(off-policy),在线交互便宜选 PPO(on-policy 但稳定)。
深度强化学习的工程观:算法只是入场券,稳定性技巧和奖励工程才是日常。
相关阅读:强化学习基础、LLM 对齐:SFT 与 RLHF、训练稳定性实战。
💬 评论
这套方案在哪一步卡住了?也欢迎补充你的实现方式或踩坑经验。