Deep Q-Network(DQN)
用神经网络近似高维状态的 Q 值,并以 Replay 与 Target Network 稳定训练。
学习目标
- 用神经网络共享状态间的动作价值估计,并明确 replay 与目标网络的作用。
- 独立复核“两个样本中终止掩码怎样影响 DQN 目标”的输入、计算和解释。
- DQN 学习离散动作 Q,不能直接枚举无限连续动作。
为什么重要
用神经网络共享状态间的动作价值估计,并明确 replay 与目标网络的作用。
学习前需要掌握
背景与问题
图像输入有大量可能状态,表格不能逐个存储 Q。DQN 用一个网络从观察直接输出有限动作的价值,允许状态间共享表示;同时神经网络和不断变化的 bootstrap 目标使训练比小表格更敏感。
概念定义
DQN 是面向离散动作的深度 Q-learning:在线网络预测 Q(s,a),延迟更新的目标网络提供后继最大 Q,训练从回放缓冲区抽取转移。行为通常用 ε-greedy 采样,因此学习贪心目标时是 off-policy。
直观理解
在线网络像正在修订的评分表,目标网络像暂时冻结的参考版本。每一步都追逐同一张正在快速变化的表,容易让目标与预测互相拉动;延迟参考可缓解这种现象。
核心原理
一个批次先对所有状态前向,按实际动作索引抽取 Q(s,a);损失只直接约束采样动作。下一状态的最大值应在无梯度路径中计算,不让目标分支和预测一起追逐当前残差。
Replay 提高样本复用并减弱相邻样本相关性,但不创造缺失覆盖。目标网络降低变化速度,也不能保证非线性学习收敛;目标同步周期过短或过长均可能影响训练。
普通 DQN 在目标网络上同时选最大动作和评价,因此仍有最大化偏差。Double DQN 将选择与评价分开;dueling 则改变网络结构,二者解决的问题不同。下方代码只做构造批次的一次更新,不是游戏成绩实验。
数学表达
DQN loss
Target 参数 θ⁻ 在一段时间内固定。
变量含义
G_t从时刻 t 开始的折扣累计回报。R_{t+1}执行动作后收到的下一步奖励。γ折扣因子,通常位于 [0,1)。
计算步骤
- 1第一样本目标为 1+0.9×3=3.7。
- 2第二样本必须屏蔽后继 100,目标仅为 2。
- 3若漏掉终止掩码,第二目标会错误地变成 92。
代码对应位置
示例 1PyTorch DQN:Online、Target 与 Replay 的最小训练步:完整展示批次张量、DQN target、Huber loss、反向传播和 Target Network 同步。
变量解释
| 变量 | 含义 |
|---|---|
G_t | 从时刻 t 开始的折扣累计回报。 |
R_{t+1} | 执行动作后收到的下一步奖励。 |
γ | 折扣因子,通常位于 [0,1)。 |
完整数值示例
两个样本中终止掩码怎样影响 DQN 目标
已知条件
- 奖励分别为 1、2,后继最大 Q 分别为 3、100。
- 第二样本真正终止,折扣 0.9。
计算步骤
- 第一样本目标为 1+0.9×3=3.7。
- 第二样本必须屏蔽后继 100,目标仅为 2。
- 若漏掉终止掩码,第二目标会错误地变成 92。
计算结果
正确目标为 3.7 和 2.0;第二后继的高值与已终止任务无关。
处理前后对比
- DQN 学习离散动作 Q,不能直接枚举无限连续动作。
- 一次神经网络批次更新只验证张量与目标计算,不代表策略已学会环境。
计算与实现步骤
- 与环境交互并写 Replay
- 采样 mini-batch
- Online 计算 Q(s,a)
- Target 计算 y
- 优化 TD loss
- 周期同步 Target
代码实现
示例 1
PyTorch DQN:Online、Target 与 Replay 的最小训练步
example_01.py用途:完整展示批次张量、DQN target、Huber loss、反向传播和 Target Network 同步。
import random
from collections import deque
import torch
from torch import nn
torch.manual_seed(7)
online = nn.Sequential(nn.Linear(4, 32), nn.ReLU(), nn.Linear(32, 2))
target = nn.Sequential(nn.Linear(4, 32), nn.ReLU(), nn.Linear(32, 2))
target.load_state_dict(online.state_dict())
target.eval()
optimizer = torch.optim.Adam(online.parameters(), lr=1e-3)
replay = deque(maxlen=1000)
for i in range(64):
state = torch.tensor([i % 4 == j for j in range(4)], dtype=torch.float32)
next_state = torch.roll(state, 1)
replay.append((state, i % 2, 1.0 if i % 7 == 0 else 0.0, next_state, False))
batch = random.Random(7).sample(list(replay), 32)
states = torch.stack([x[0] for x in batch])
actions = torch.tensor([x[1] for x in batch]).unsqueeze(1)
rewards = torch.tensor([x[2] for x in batch])
next_states = torch.stack([x[3] for x in batch])
dones = torch.tensor([x[4] for x in batch], dtype=torch.float32)
q_sa = online(states).gather(1, actions).squeeze(1)
with torch.no_grad():
next_q = target(next_states).max(1).values
td_target = rewards + 0.99 * (1 - dones) * next_q
loss = nn.SmoothL1Loss()(q_sa, td_target)
optimizer.zero_grad()
loss.backward()
nn.utils.clip_grad_norm_(online.parameters(), 10.0)
optimizer.step()
target.load_state_dict(online.state_dict())
print('loss:', round(loss.item(), 4))代码解析
解析始终位于完整代码下方,并按实际代码段逐项对应。
输入数据与任务
完整展示批次张量、DQN target、Huber loss、反向传播和 Target Network 同步。
loss- 当前预测与目标之间的损失值。
Step 1 · 1–4 行
导入当前步骤需要的数值计算、预处理、模型或评价工具。依赖集中写在代码开头,便于复现。
import random
from collections import deque
import torch
from torch import nnStep 2 · 6–12 行
Online Network 产生 Q(s,a),Target Network 产生相对稳定的 TD 目标。
torch.manual_seed(7)
online = nn.Sequential(nn.Linear(4, 32), nn.ReLU(), nn.Linear(32, 2))
target = nn.Sequential(nn.Linear(4, 32), nn.ReLU(), nn.Linear(32, 2))
target.load_state_dict(online.state_dict())
target.eval()
optimizer = torch.optim.Adam(online.parameters(), lr=1e-3)
replay = deque(maxlen=1000)Step 3 · 14–17 行
SmoothL1Loss、梯度裁剪和周期性 Target 同步共同改善稳定性。
for i in range(64):
state = torch.tensor([i % 4 == j for j in range(4)], dtype=torch.float32)
next_state = torch.roll(state, 1)
replay.append((state, i % 2, 1.0 if i % 7 == 0 else 0.0, next_state, False))Step 4 · 19–24 行
执行当前代码段,并把得到的状态传给下一步。
batch = random.Random(7).sample(list(replay), 32)
states = torch.stack([x[0] for x in batch])
actions = torch.tensor([x[1] for x in batch]).unsqueeze(1)
rewards = torch.tensor([x[2] for x in batch])
next_states = torch.stack([x[3] for x in batch])
dones = torch.tensor([x[4] for x in batch], dtype=torch.float32)Step 5 · 26–36 行
计算损失对参数的梯度,对应数学表达中的 ∇J;梯度只给出局部变化方向。
q_sa = online(states).gather(1, actions).squeeze(1)
with torch.no_grad():
next_q = target(next_states).max(1).values
td_target = rewards + 0.99 * (1 - dones) * next_q
loss = nn.SmoothL1Loss()(q_sa, td_target)
optimizer.zero_grad()
loss.backward()
nn.utils.clip_grad_norm_(online.parameters(), 10.0)
optimizer.step()
target.load_state_dict(online.state_dict())
print('loss:', round(loss.item(), 4))代码与数学原理
参数更新由当前梯度与学习率共同决定。
平方误差代码对应均方损失。
预期输出或运行结果
loss: 0.119
常见错误 · 3 条
- gather 的动作维度错误,让损失拟合了不对应的动作。
- 目标分支未停止梯度,或真正终止仍使用后继值。
- 把单次构造数据 loss 下降当成环境策略改善。
实际应用
- 序贯决策
- 控制与资源分配
常见错误
- gather 的动作维度错误,让损失拟合了不对应的动作。
- 目标分支未停止梯度,或真正终止仍使用后继值。
- 把单次构造数据 loss 下降当成环境策略改善。
扩展内容说明
本页数值均为解释机制的构造案例,不能解读为真实训练或基准性能。定义和适用条件参见 Mnih 等:Human-level control through deep reinforcement learning。
算法属性与数据边界
失败模式、风险与性能
失败模式
- gather 的动作维度错误,让损失拟合了不对应的动作。
- 目标分支未停止梯度,或真正终止仍使用后继值。
- 把单次构造数据 loss 下降当成环境策略改善。
风险
- 奖励函数与真实目标不一致会诱发奖励黑客。
- 部署策略的行动权限必须由环境和应用层约束。
性能考虑
- 样本效率、墙钟时间和显存占用需要分别报告。
- 应使用多个随机种子、置信区间和固定评测策略。
常见问题
DQN 有 replay 就能直接用于任意离线数据吗?
不能。固定数据下 max 可能选择没有覆盖的高估动作,需单独处理离线分布偏移,不能把在线 DQN 的常规经验直接视为可靠离线方法。
网页是否会训练模型?
不会。所有图表使用固定种子或解析公式在浏览器本地计算,不执行页面中的示例代码。
推荐学习资料
Hugging Face Deep Reinforcement Learning Course
从 Q-Learning、DQN、Policy Gradient 和 Actor-Critic 逐步进入 PPO 与多智能体。
Hugging Face · Hugging Face
资料笔记
Playing Atari with Deep Reinforcement Learning
提出使用深度网络、经验回放和目标网络从像素输入学习 Atari 控制策略。
Mnih et al. · arXiv
资料笔记
vwxyzjn/cleanrl
以单文件实现和实验报告呈现 DQN、PPO、SAC 等深度强化学习算法。
CleanRL Contributors · GitHub
仓库信息
阅读可复现的单文件 RL 算法实现
主要语言:Python包含数据集或实验需要额外环境