深度强化学习新手入门:从零构建你的第一个awesome DRL智能体

1次阅读
没有评论

共计 2643 个字符,预计需要花费 7 分钟才能阅读完成。

image.webp

为什么深度强化学习让新手头疼?

刚接触深度强化学习 (DRL) 时,我踩过的坑简直能写本百科全书。最让人崩溃的是:

深度强化学习新手入门:从零构建你的第一个 awesome DRL 智能体

  • 算法选择困难:DQN、PPO、SAC… 光看名字就头晕,更别说理解它们的区别了
  • 环境配置噩梦:装完 PyTorch 发现 CUDA 版本不对,配好 CUDA 又报错 Gym 版本冲突
  • 玄学般的训练:同样的代码昨天还能训练,今天突然不收敛了,奖励曲线比心电图还刺激

技术选型:新手友好组合

经过反复试验,我推荐这个 ” 黄金组合 ”:

  1. 算法选择:从经典的 DQN 开始
  2. 优势:结构简单,适合离散动作空间(比如游戏按键)
  3. 对比:PPO 适合连续控制(如机器人行走),SAC 更复杂但样本效率高

  4. 工具链

  5. PyTorch:比 TensorFlow 更 pythonic,调试方便
  6. OpenAI Gym:提供标准化的游戏环境(我们选用 Atari 的 Pong)
  7. 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 后:

  1. 挑战更复杂游戏
  2. 试试 Breakout 或 SpaceInvaders
  3. 需要调整网络结构和奖励函数

  4. 升级算法

  5. Double DQN:解决 Q 值过估计
  6. Dueling DQN:更好评估状态价值

  7. 分布式训练

  8. 使用 Ray 框架实现并行采样
  9. Ape- X 架构提升数据多样性

思考题

  1. 为什么 DQN 的输入需要堆叠多帧图像?如果只用单帧会有什么问题?
  2. 当发现训练时 loss 震荡剧烈,可能是什么原因?该如何调整?
  3. 如果要让同一个智能体能玩多种 Atari 游戏,网络结构需要如何改进?

希望这篇指南能帮你跨过 DRL 的第一道门槛。记住,第一个智能体表现再差也值得庆祝——我的第一个 DQN 连球都接不到呢!

正文完
 0
评论(没有评论)