7.0 KiB
DQN 走迷宫:原理与推导
一、为什么需要 DQN
Q-Learning 把 Q 值存在一张表里,需要为每个 (状态, 动作) 单独维护一个数。状态空间一大(比如用整张地图做状态),表就装不下、也学不到没见过的格子。
DQN 的思路:用神经网络把 Q 值"拟合"出来,而不是逐格记录
Q(s,a)\;\approx\;Q(s,a;\theta)
其中 \theta 是网络参数。输入状态 $s$,输出各动作的价值。见过 (s,a) 后学到的规律会被网络泛化到相近的状态(虽然本迷宫是离散格,但组件完全通用)。
迷宫的问题设定(状态、动作、奖励、转移)与 Q-Learning 完全相同,见 q-learning.md 第一、二节;下面直接讲 DQN 自己的部分。
二、损失函数:把 TD 更新变成监督学习
Q-Learning 的更新是 $Q(s,a)\leftarrow Q(s,a)+\alpha,\delta$,其中 TD 误差
\delta = r+\gamma\max_{a'}Q(s',a')-Q(s,a)
DQN 把它改写成最小化均方误差的目标:
L(\theta)=\mathbb{E}_{(s,a,r,s')}\Big[\big(Y-Q(s,a;\theta)\big)^{2}\Big]
其中目标值 Y 用"冻结"的参数 $\theta^{-}$(目标网络)计算:
Y=r+\gamma\max_{a'}Q(s',a';\theta^{-})
对 \theta 求梯度(Y 视为常数,不做梯度):
\nabla_{\theta}L=\mathbb{E}\Big[-2\big(Y-Q(s,a;\theta)\big)\,\nabla_{\theta}Q(s,a;\theta)\Big]
这正是把贝尔曼最优方程的样本近似当成监督学习的"标签":Y 当标签、Q(s,a;\theta) 当预测,用梯度下降让预测逼近标签。Y 本身又依赖旧参数 $\theta^{-}$,所以这是"自举式"(bootstrap)的监督——目标会随学习移动。
符号说明:
| 符号 | 含义 |
|---|---|
\theta |
主网络参数(全部权重矩阵),每步梯度更新 |
\theta^{-} |
目标网络参数,\theta 的滞后副本,每 C 步同步一次;算 Y 时冻结、不求梯度 |
Q(s,a;\theta) |
输入状态 $s$,网络输出的动作 a 的 Q 值("预测") |
Y |
TD 目标("标签"):本步实际奖励 r 加 \gamma 倍下一状态的最优价值估计 |
r,\ s',\ a' |
本步实际观测到的值:拿到的奖励、到达的新状态、新状态下设想尝试的动作 |
\max_{a'} |
对 s' 处全部 4 个动作的 Q 值取最大,即"假设下一步走最优" |
\gamma |
折扣因子,本实现取 0.99 |
\mathbb{E}_{(s,a,r,s')} |
对经验转移的分布求期望;实现上就是回放池小批量上的平均 |
L(\theta) |
MSE 损失:预测与标签之差的平方的期望 |
\nabla_{\theta}L |
损失对 \theta 的梯度;Adam 沿其反方向更新参数 |
三、两个关键技巧
3.1 经验回放(Replay Buffer)
把每条经历 (s,a,r,s',terminated) 存入一个容量有限的缓冲池,训练时随机抽一个小批量更新。注意存的是 terminated(到达终点)而非 truncated(超时截断):截断只是回合被强制叫停,未来价值依然存在,TD 目标不应因此丢掉 bootstrap 项。
为什么必须这么做:TD 目标依赖下一个状态,而连续几步的状态高度相关。若直接用当前轨迹在线更新,梯度在时间上强相关,损失会剧烈震荡甚至发散;随机抽样把这些样本打乱成近似独立同分布,相当于稳定的"数据集",梯度才像普通监督学习那样平稳下降。此外一条经验可被多次复用,样本效率更高。
3.2 目标网络(Target Network)
若 Y 也用同一个网络 \theta 计算,则"标签"和"预测"同时变化,更新目标一直在追着自己跑(自举放大),训练极易振荡。
解决办法:维护一份滞后副本 $\theta^{-}$,用 \theta^{-} 算 $Y$,而 \theta 只负责预测并求梯度;每隔固定步数 C 把 \theta 复制给 $\theta^{-}$。这保证了一个更新周期内标签固定,等价于把损失函数稳定下来再优化。
四、探索与状态表示
- 探索:与 Q-Learning 相同,用 ε-greedy:以概率
\varepsilon均匀随机、否则取 $\arg\max_{a}Q(s,a;\theta)$;ε 按\varepsilon_k=\max(\varepsilon_{\min},\varepsilon_0\lambda^{k})指数衰减。 - 状态表示:网络输入需要数字向量。本实现把格子坐标编码成 one-hot 向量(长度 $n\times n$,当前位置为 1),可扩展到"把整张地图作为网格图像 + 卷积"的标准 DQN 形式。
五、与 Q-Learning 的对比
| Q-Learning | DQN | |
|---|---|---|
| Q 的载体 | 表格 Q[r,c,a] |
神经网络 Q(s,a;\theta) |
| 更新目标 | $r+\gamma\max_{a'}Q(s',a')$(查表) | 同上,但用目标网络 \theta^{-} |
| 优化手段 | 直接加法修正 | 对 MSE 损失做梯度下降 |
| 额外组件 | 无 | 经验回放、目标网络 |
| 泛化 | 无(没见过的格子没值) | 有(近似相近状态) |
| 收敛性 | 有理论保证(Watkins) | 无严格保证,靠工程技巧稳定 |
两者本质相同:都在做贝尔曼最优方程的随机近似,只是函数逼近方式不同。
六、超参数(默认值)
| 超参数 | 默认 | 说明 |
|---|---|---|
| 网络宽度 hidden | 128 | 两层隐层 |
| 学习率 lr | 1e-3 | Adam 优化器 |
| 批量大小 batch | 64 | 每次从回放池抽样数 |
| 回放容量 | 20000 | 过大滞后、过小样本相关 |
| γ | 0.99 | 折扣因子 |
| ε 衰减 | 1.0 → 0.02 | 每局 ×0.995 |
| 目标更新 C | 200 步 | 同步频率 |
七、运行与观测
在仓库根目录运行:python -m dqn.dqn_maze --plot;参数:--rows/--cols(默认 9,须奇数)、--seed、--episodes(默认 800)、--braid、--hidden、--plot。输出:迷宫、策略箭头、贪心路径、成功率统计;--plot 生成两张图。
| 图 | 判读 |
|---|---|
| 逐局回报 | 总体上升(噪声大,看滑动平均) |
| 滑动成功率 | 趋近 1 |
| 单局步数 vs BFS 红线 | 降到红线 = 学会最短路 |
| TD 损失(对数轴) | 下降即 Q(s,a;\theta) 逼近 $Q^{*}$;平台或回升提示超参/技巧问题 |
| V(s) 快照 | 价值从终点回传的过程 |
常见问题:
| 现象 | 原因 | 调整 |
|---|---|---|
| 损失震荡不降 | 无目标网络或回放太小 | 检查目标网络、增大回放容量 |
| 学不到终点 | 探索不足或 γ 太小 | 提高 ε 下限、γ 取 0.99 |
| 前期好后期崩 | 回放池被旧数据稀释、学习率过大 | 降 lr、增容量 |
| 结果不稳定 | DQN 本身随机性强 | 固定 seed、多看几次 |
八、小结
DQN = Q-Learning 的公式 + 神经网络的表达力 + 两条工程稳定技巧。核心仍是那条损失
L(\theta)=\mathbb{E}\big[(r+\gamma\max_{a'}Q(s',a';\theta^{-})-Q(s,a;\theta))^{2}\big]
吃透这条式子,再理解回放和目标网络各自解决的"相关性与自举"两个病,DQN 的骨架就齐了——换成连续动作空间(DDPG)或图像输入(CNN)只是改编码器和输出头。
配套阅读:q_learning/q-learning.md(表格法原理与推导)。