共计 3458 个字符,预计需要花费 9 分钟才能阅读完成。
传统强化学习的困境与深度 Q 网络的诞生
强化学习在 2015 年前面临的最大挑战是维度灾难(Curse of Dimensionality)。传统 Q -learning 需要维护一个包含所有可能状态 - 动作对的 Q 值表,这在实际应用中完全不现实。例如 Atari 游戏《Breakout》的像素观察空间高达 128^210(约 10^453 种可能状态),传统方法根本无法处理这种高维输入。

2015 年 2 月,谷歌 DeepMind 在 Nature 发表的论文《Human-level control through deep reinforcement learning》首次提出深度 Q 网络(DQN),其核心创新在于:
- 用卷积神经网络(CNN)替代 Q 值表,直接从原始像素预测 Q 值
- 引入经验回放(Experience Replay)打破样本间的相关性
- 使用目标网络(Target Network)稳定训练过程
两大里程碑架构对比
DQN 的简约设计
DQN 采用相对简单的 CNN 架构处理 Atari 游戏:
- 输入层:84×84×4 的灰度图像(4 帧堆叠)
- 卷积层 1:32 个 8×8 滤波器,步长 4,ReLU 激活
- 卷积层 2:64 个 4×4 滤波器,步长 2,ReLU 激活
- 全连接层:256 个单元
- 输出层:对应游戏动作数量的 Q 值输出
AlphaGo 的复合架构
相比之下,AlphaGo(2015 年 10 月版本)采用更复杂的双网络设计:
- 策略网络(Policy Network):
- 输入:19×19×17 的棋盘状态表示
- 13 层卷积网络
-
输出:361 维的落子概率分布
-
价值网络(Value Network):
- 类似策略网络的卷积主干
- 最终输出单一获胜概率值
关键差异在于:
- DQN 直接端到端学习,而 AlphaGo 分离了策略和价值评估
- AlphaGo 使用蒙特卡洛树搜索(MCTS)整合网络输出
- 围棋需要长期策略规划,而 Atari 游戏更依赖即时反应
DQN 完整实现(TensorFlow 2.x)
import tensorflow as tf
from collections import deque
import random
class ReplayBuffer:
"""经验回放缓冲区"""
def __init__(self, capacity):
self.buffer = deque(maxlen=capacity)
def store(self, state, action, reward, next_state, done):
self.buffer.append((state, action, reward, next_state, done))
def sample(self, batch_size):
return random.sample(self.buffer, batch_size)
def __len__(self):
return len(self.buffer)
class DQN(tf.keras.Model):
"""深度 Q 网络实现"""
def __init__(self, num_actions):
super().__init__()
self.conv1 = tf.keras.layers.Conv2D(32, 8, strides=4, activation='relu')
self.conv2 = tf.keras.layers.Conv2D(64, 4, strides=2, activation='relu')
self.flatten = tf.keras.layers.Flatten()
self.dense = tf.keras.layers.Dense(256, activation='relu')
self.out = tf.keras.layers.Dense(num_actions)
def call(self, inputs):
x = self.conv1(inputs)
x = self.conv2(x)
x = self.flatten(x)
x = self.dense(x)
return self.out(x)
class DQNAgent:
"""DQN 智能体实现"""
def __init__(self, env):
self.env = env
self.model = DQN(env.action_space.n)
self.target_model = DQN(env.action_space.n) # 目标网络
self.optimizer = tf.keras.optimizers.Adam(learning_rate=1e-4)
self.replay_buffer = ReplayBuffer(capacity=100000)
self.epsilon = 1.0
self.gamma = 0.99
self.batch_size = 32
self.update_target_every = 1000
def train_step(self, states, actions, rewards, next_states, dones):
"""单次训练步骤"""
# 计算目标 Q 值
next_qs = self.target_model(next_states)
max_next_qs = tf.reduce_max(next_qs, axis=-1)
targets = rewards + (1 - dones) * self.gamma * max_next_qs
# 计算当前 Q 值
with tf.GradientTape() as tape:
qs = self.model(states)
action_masks = tf.one_hot(actions, self.env.action_space.n)
current_qs = tf.reduce_sum(qs * action_masks, axis=-1)
loss = tf.keras.losses.MSE(targets, current_qs)
# 反向传播
grads = tape.gradient(loss, self.model.trainable_variables)
self.optimizer.apply_gradients(zip(grads, self.model.trainable_variables))
return loss.numpy()
关键训练技巧详解
ε-greedy 策略
- 初始 ε =1(完全随机探索)
- 线性衰减到 0.1(90% 利用,10% 探索)
- 避免陷入局部最优的必备策略
奖励裁剪(Reward Clipping)
- 将所有奖励裁剪到 [-1, 1] 范围
- 防止 Q 值因奖励幅度差异过大导致不稳定
- 特别适用于 Atari 游戏(不同游戏奖励尺度差异大)
帧堆叠(Frame Stacking)
- 将连续 4 帧堆叠作为网络输入
- 解决部分观测问题(POMDP)
- 为网络提供时间维度信息(如物体运动方向)
五大常见训练问题与解决方案
- Q 值爆炸
- 现象:Q 值持续增长远超合理范围
-
解决方案:降低学习率、加强奖励裁剪、检查目标网络更新频率
-
训练不收敛
- 现象:loss 波动无下降趋势
-
解决方案:增大回放缓冲区、减小 batch size、调整探索率衰减计划
-
过拟合
- 现象:训练得分高但测试表现差
-
解决方案:在全连接层加入 Dropout、使用 L2 正则化、增加训练数据多样性
-
灾难性遗忘
- 现象:学会新技能后忘记旧技能
-
解决方案:确保回放缓冲区足够大、优先采样重要经验(Prioritized Experience Replay)
-
高方差梯度
- 现象:训练过程不稳定
- 解决方案:使用梯度裁剪(Gradient Clipping)、改用 RMSprop 优化器
性能优化进阶技巧
分布式训练
- 使用 Ape- X 架构:
- 多个 Worker 并行收集经验
- 中央 Learner 集中训练
- 经验优先级回放(Prioritized Experience Replay)
混合精度训练
policy = tf.keras.mixed_precision.Policy('mixed_float16')
tf.keras.mixed_precision.set_global_policy(policy)
# 需在模型最后层使用 float32 保证数值稳定性
动手挑战
建议尝试以下修改并观察训练效果:
- 将 CNN 架构改为 ResNet 块
- 在 DQN 基础上添加 Dueling Network 结构
- 实现 Prioritized Experience Replay
- 尝试使用 NoisyNet 替代 ε -greedy
这些突破性工作奠定了现代深度强化学习的基础。DQN 证明了神经网络可以直接从原始像素学习控制策略,而 AlphaGo 则展示了 DRL 在复杂决策问题上的惊人潜力。理解这些基础架构,是掌握当前最先进 DRL 算法(如 Rainbow、PPO、SAC 等)的必要前提。
