共计 2643 个字符,预计需要花费 7 分钟才能阅读完成。
为什么深度强化学习让新手头疼?
刚接触深度强化学习 (DRL) 时,我踩过的坑简直能写本百科全书。最让人崩溃的是:

- 算法选择困难:DQN、PPO、SAC… 光看名字就头晕,更别说理解它们的区别了
- 环境配置噩梦:装完 PyTorch 发现 CUDA 版本不对,配好 CUDA 又报错 Gym 版本冲突
- 玄学般的训练:同样的代码昨天还能训练,今天突然不收敛了,奖励曲线比心电图还刺激
技术选型:新手友好组合
经过反复试验,我推荐这个 ” 黄金组合 ”:
- 算法选择:从经典的 DQN 开始
- 优势:结构简单,适合离散动作空间(比如游戏按键)
-
对比:PPO 适合连续控制(如机器人行走),SAC 更复杂但样本效率高
-
工具链:
- PyTorch:比 TensorFlow 更 pythonic,调试方便
- OpenAI Gym:提供标准化的游戏环境(我们选用 Atari 的 Pong)
- WandB:实时监控训练过程(免费版就够用)
手把手实现 DQN
第一步:搭建 Q 网络
import torch
import torch.nn as nn
class QNetwork(nn.Module):
"""
处理 84x84 灰度图像的 CNN 网络
输出维度对应游戏中的 6 个可能动作
"""
def __init__(self, action_dim):
super().__init__()
self.conv = nn.Sequential(nn.Conv2d(4, 32, 8, stride=4), # 输入是 4 帧堆叠的灰度图
nn.ReLU(),
nn.Conv2d(32, 64, 4, stride=2),
nn.ReLU(),
nn.Conv2d(64, 64, 3, stride=1),
nn.ReLU())
self.fc = nn.Sequential(nn.Linear(64 * 7 * 7, 512),
nn.ReLU(),
nn.Linear(512, action_dim)
)
def forward(self, x):
x = self.conv(x)
return self.fc(x.flatten(1))
关键点说明:
– 输入是连续的 4 帧图像(帮助网络感知运动)
– 卷积层逐步提取空间特征
– 最终输出每个动作的 Q 值
第二步:实现经验回放
from collections import deque
import random
class ReplayBuffer:
def __init__(self, capacity):
self.buffer = deque(maxlen=capacity) # 自动淘汰旧数据
def push(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)
为什么需要经验回放?
– 打破数据间的相关性
– 提高样本利用率
– 建议 buffer 大小至少 1e5
完整训练流程
# 超参数设置
BATCH_SIZE = 32
GAMMA = 0.99
EPS_START = 1.0
EPS_END = 0.01
EPS_DECAY = 1000000
# 初始化
env = gym.make('PongNoFrameskip-v4')
model = QNetwork(action_dim=env.action_space.n)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)
memory = ReplayBuffer(100000)
for episode in range(1000):
state = preprocess(env.reset()) # 图像预处理函数
episode_reward = 0
while True:
# ε-greedy 策略选择动作
epsilon = EPS_END + (EPS_START - EPS_END) * \
math.exp(-1. * steps_done / EPS_DECAY)
if random.random() < epsilon:
action = env.action_space.sample()
else:
with torch.no_grad():
action = model(state).argmax().item()
# 执行动作
next_state, reward, done, _ = env.step(action)
next_state = preprocess(next_state)
memory.push(state, action, reward, next_state, done)
# 训练阶段
if len(memory) > BATCH_SIZE:
batch = memory.sample(BATCH_SIZE)
# 此处省略具体训练代码...
state = next_state
episode_reward += reward
if done:
print(f"Episode {episode}, Reward: {episode_reward}")
break
避坑指南(血泪经验)
奖励设计技巧
- 奖励塑形:Pong 原始奖励只有±1,可以给球拍接近球时加小奖励
- 帧跳过:每 4 帧执行一次动作,中间重复,加速训练
- 裁剪画面:只保留游戏区域,去除记分牌等干扰
超参数调优
这些参数最敏感:
1. 学习率:从 3e- 4 开始尝试
2. Batch Size:32-256 之间
3. γ (折扣因子):0.9-0.99
训练监控
推荐使用 Weights & Biases:
import wandb
wandb.init(project="pong-dqn")
# 在训练循环中添加
wandb.log({
"episode_reward": episode_reward,
"epsilon": epsilon
})
进阶路线图
当你的智能体学会打 Pong 后:
- 挑战更复杂游戏:
- 试试 Breakout 或 SpaceInvaders
-
需要调整网络结构和奖励函数
-
升级算法:
- Double DQN:解决 Q 值过估计
-
Dueling DQN:更好评估状态价值
-
分布式训练:
- 使用 Ray 框架实现并行采样
- Ape- X 架构提升数据多样性
思考题
- 为什么 DQN 的输入需要堆叠多帧图像?如果只用单帧会有什么问题?
- 当发现训练时 loss 震荡剧烈,可能是什么原因?该如何调整?
- 如果要让同一个智能体能玩多种 Atari 游戏,网络结构需要如何改进?
希望这篇指南能帮你跨过 DRL 的第一道门槛。记住,第一个智能体表现再差也值得庆祝——我的第一个 DQN 连球都接不到呢!
正文完
发表至: 人工智能
近一天内
