项目源码:https://github.com/NocoldBob/RL
第一篇:速通贪吃蛇游戏
第二篇:手撕 GPT(零基础保姆级教学)
第三篇:让贪吃蛇训练更稳定、更容易复现
第四篇:DQN 实战:四种策略同场对照
第五篇:PPO 实战:从单步更新到稳定策略优化
第六篇:Double DQN 实战:降低 Q 值,成绩就会更好吗
前言:先看清局面,还是直接给动作打分
普通 DQN 接收一个状态,直接输出三个动作的 Q 值:左转、右转、直行各值多少。
但有些局面存在一个共同特点:无论选择哪个动作,整体都很好或都很危险。例如蛇头被身体和
边界包围时,三个动作可能都不理想;面对开阔区域时,多个动作可能都可以接受。
Dueling Network 的想法是把判断拆成两部分:
- 这个局面整体有多好,记为状态价值
V(s); - 在这个局面中,各动作相对有多好,记为动作优势
A(s,a)。
然后再把它们合成 Q 值。直觉上,它让网络先回答“当前局面怎么样”,再回答“在这里哪个动作
更值得选”。
不过这一篇不会直接假设 Dueling 一定更强。我们做一个完整的2×2配对实验:
| 网络结构 | 普通 DQN 目标 | Double DQN 目标 |
|---|---|---|
| 普通 Q 网络 | DQN | Double DQN |
| Dueling 网络 | Dueling DQN | Dueling Double DQN |
这样既能观察 Dueling 的单独作用,也能观察它与 Double DQN 是否存在组合效果。
一、普通 Q 网络混合了两种信息
普通 DQN 的最后一层直接输出:
Q(s, 左转), Q(s, 右转), Q(s, 直行)这些数字同时承担两项工作:
- 描述状态本身的质量;
- 描述动作之间的相对差异。
如果某个状态下三个动作差别很小,普通网络仍然要分别学习三个 Q 值。三个输出中会重复包含
大量“这个状态整体不错”的信息。
Dueling Network 希望共享这部分信息,让状态价值只学一次,把剩余容量用于学习动作差异。
二、Dueling Network 怎样拆分 Q 值
网络先使用同一个卷积编码器提取状态特征,然后分成两个输出头:
Value Head: V(s) → 1 个值 Advantage Head: A(s, a) → 3 个值最直观的合并方式似乎是:
Q(s, a) = V(s) + A(s, a)但这个写法存在不可辨识问题。例如给V加 1,同时给每个A减 1,最终 Q 值完全不变。
网络无法确定哪一部分应该由 V 表示,哪一部分应该由 A 表示。
本项目采用论文中常见的均值归一化写法:
Q(s, a) = V(s) + A(s, a) - mean_a A(s, a)减去动作优势均值后,每个状态下的优势贡献均值为 0,V(s)就对应这一组 Q 值的中心。
三、代码实现
核心网络只需要一个共享编码器和两个线性输出头:
classDuelingQNetwork(nn.Module):def__init__(self,input_channels,action_count,grid_size):super().__init__()feature_dim=16*grid_size*grid_size self.encoder=nn.Sequential(nn.Conv2d(input_channels,16,kernel_size=3,padding=1),nn.ReLU(),nn.Flatten(),nn.Linear(feature_dim,64),nn.ReLU(),)self.value_head=nn.Linear(64,1)self.advantage_head=nn.Linear(64,action_count)defforward(self,observation):features=self.encoder(observation)value=self.value_head(features)advantage=self.advantage_head(features)returnvalue+advantage-advantage.mean(dim=1,keepdim=True)原来的普通 Q 网络没有被删除。DQNAgent根据dueling开关选择网络类型,因此旧命令、旧
检查点和原有教学流程保持兼容。
在默认6×6环境中:
普通 Q 网络: 38,147 个参数 Dueling 网络: 38,212 个参数 新增: 65 个参数,约 0.17%增加的 65 个参数正是Value Head的64个权重和1个偏置。因此这次性能差异不太可能
只是由模型规模大幅增加造成。
四、Dueling 与 Double 解决的不是同一件事
这两个名字经常一起出现,但它们修改的是两个不同位置:
- Dueling Network修改网络结构,把
Q分解为状态价值和动作优势; - Double DQN修改 TD 目标,让在线网络选动作、目标网络评价动作。
所以二者可以独立开关:
DQN 普通网络 + 普通目标 Double DQN 普通网络 + Double 目标 Dueling DQN Dueling 网络 + 普通目标 Dueling Double DQN Dueling 网络 + Double 目标正因为它们作用不同,不能只比较 DQN 和 Dueling Double DQN,然后把全部差异都归功于 Dueling。
四组全因子对照可以把两个组件的作用拆得更清楚。
五、如何运行四种模型
1. 获取项目和安装依赖
git clone https://github.com/NocoldBob/RL.git cd RL py-3.12-m venv.venv.\.venv\Scripts\Activate.ps1 python-m pip install-r requirements.txt2. 分别训练
普通 DQN:
python.\贪吃蛇\train_dqn.py--output-dirruns\dqnDouble DQN:
python.\贪吃蛇\train_dqn.py--double-dqn--output-dirruns\double-dqnDueling DQN:
python.\贪吃蛇\train_dqn.py--dueling--output-dirruns\dueling-dqnDueling Double DQN:
python.\贪吃蛇\train_dqn.py--dueling--double-dqn `--output-dirruns\dueling-double-dqn播放程序会从检查点配置中识别普通或 Dueling 网络:
python.\贪吃蛇\play_dqn.py `.\runs\dueling-double-dqn\checkpoints\best.pt--fps 15六、正式实验规则
本次使用新脚本benchmark_dueling_dqn.py统一训练和评估四种模型:
python.\贪吃蛇\benchmark_dueling_dqn.py `--seeds 7 42 2026--episodes 1000--eval-episodes 100 `--device cpu--torch-threads 1实验规则如下:
- 地图大小为
6×6; - 蛇长度达到 4 视为通关;
- 每局最多 100 步;
- 每个模型训练 1000 局;
- 训练种子固定为
7、42、2026; - 每个检查点使用同一组 100 张地图评估;
- 评估时使用零探索贪心动作;
- 四组使用相同回放池、batch、学习率、探索曲线和目标同步间隔;
- 除网络结构和 TD 目标两个因子外,其他条件保持一致。
脚本输出results.json和results.csv。若训练检查点已经存在,可以使用--reuse只重新
评估和汇总。
七、总体成绩:组合模型领先
三训练种子的聚合结果如下,±后为种子之间的总体标准差:
| 算法 | 平均奖励 | 平均得分 | 平均通关率 | 平均训练时间 |
|---|---|---|---|---|
| DQN | 20.10 ± 14.03 | 0.98 ± 0.20 | 12.3% ± 3.4% | 15.0 ± 1.8 秒 |
| Double DQN | 13.76 ± 1.57 | 0.68 ± 0.18 | 10.0% ± 4.2% | 13.7 ± 0.2 秒 |
| Dueling DQN | 13.05 ± 14.06 | 0.75 ± 0.28 | 8.7% ± 3.8% | 15.5 ± 0.7 秒 |
| Dueling Double DQN | 29.03 ± 5.76 | 1.18 ± 0.20 | 15.3% ± 3.3% | 16.0 ± 0.4 秒 |
结果不是“加入 Dueling 就稳定提升”:
- Dueling DQN 的平均奖励比普通 DQN 低
7.05; - Double DQN 的平均奖励也比普通 DQN 低
6.34; - 两者组合后,平均奖励反而比普通 DQN 高
8.93; - 组合模型同时取得最高平均得分和通关率。
这提示两种改进在当前任务和预算下可能存在协同,而不是各自独立带来稳定增益。
八、为什么还要计算交互效应
全因子实验可以估算三个量:Dueling 主效应、Double 主效应,以及二者交互效应。
以奖励为例,按每个种子的配对差值计算后再取平均:
Dueling 主效应: +4.11 Double 主效应: +4.82 交互效应: +22.33这里的主效应会同时考虑另一个开关打开和关闭时的差值。例如 Dueling 主效应不是简单用Dueling DQN - DQN,而是还包含组合模型与 Double DQN 的差值。
交互效应定义为:
(Dueling Double - Dueling) - (Double - DQN)如果两个组件只是简单相加,交互应接近 0。本次得到较大的正交互,说明 Double 目标在
Dueling 结构下的作用,与它在普通结构下的作用明显不同。
不过这里只使用三个训练种子。交互效应是这次实验的现象,不是已经得到统计学充分验证的普遍
规律。
九、逐种子看,故事没有均值那么整齐
| 种子 | DQN | Double DQN | Dueling DQN | Dueling Double DQN |
|---|---|---|---|---|
| 7 | 1.80 | 15.18 | 14.20 | 28.76 |
| 42 | 22.63 | 11.57 | -4.72 | 36.22 |
| 2026 | 35.87 | 14.52 | 29.65 | 22.11 |
组合模型在 seed 7 和 seed 42 中第一,但在 seed 2026 中低于 DQN 和 Dueling DQN。
如果只展示 seed 42,我们会觉得 Dueling Double DQN 是压倒性升级;如果只展示 seed 2026,
又会发现普通 DQN 才是冠军。三个种子的结果提醒我们:强化学习中的初始化、早期探索轨迹和
回放数据分布,足以改变最终排名。
十、动作区分度变大,等于决策更好吗
本次额外记录了两个动作区分指标:
动作极差 = max Q(s,a) - min Q(s,a) 动作间隔 = 最大 Q(s,a) - 第二大 Q(s,a)动作间隔越大,表示模型对第一选择相对第二选择更坚定。三种子平均结果为:
| 算法 | Q 动作极差 | 第一、第二动作间隔 |
|---|---|---|
| DQN | 4.909 ± 1.400 | 0.632 ± 0.182 |
| Double DQN | 4.647 ± 0.867 | 0.564 ± 0.168 |
| Dueling DQN | 4.373 ± 1.132 | 0.620 ± 0.150 |
| Dueling Double DQN | 5.315 ± 0.722 | 0.677 ± 0.132 |
组合模型的动作极差和第一、第二动作间隔都最大,而且种子波动相对较小。但 Dueling DQN 单独
使用时并没有让这两个均值全面超过普通 DQN。
更重要的是:动作间隔大只代表更坚定,不代表方向正确。seed 42 的 Dueling DQN 平均
奖励为-4.72,但它仍可能对错误动作给出很明确的排序。价值网络的“自信”不能替代环境
回报验证。
十一、V 和 A 内部到底学到了什么
普通网络没有可单独解释的 V、A 输出。Dueling 两组模型在评估轨迹上的内部统计为:
| 指标 | Dueling DQN | Dueling Double DQN |
|---|---|---|
V(s)均值 | 8.77 | 6.44 |
V(s)标准差 | 2.61 | 2.34 |
| ` | A(s,a) | ` 均值 |
| 动作优势极差 | 4.37 | 5.31 |
组合模型学到的状态价值中心更保守,但动作优势的绝对规模和极差更大。可以把它理解为:它对
局面的基础估值较低,同时更依赖动作之间的相对差异做选择。
这是一种可观察的内部表征,不应被写成因果证明。两组模型访问的轨迹不同,统计中的状态分布
也会随策略改变。
十二、这次实验能说明什么
可以说明:
- Dueling 网络已在不破坏旧 DQN 流程的前提下实现;
- 四种算法在相同训练预算和配对种子下完成了全因子对照;
- Dueling 只增加 65 个参数,计算代价很小;
- Dueling 或 Double 单独使用都没有稳定超过普通 DQN;
- 二者组合取得最高三种子平均成绩,并表现出较大的正交互;
- 动作优势分解可以提供普通 Q 网络没有的内部诊断。
不能说明:
- Dueling Double DQN 在所有环境中都优于 DQN;
- 三个随机种子已经足以证明统计显著性;
- 动作 Q 间隔越大,策略一定越好;
V(s)或A(s,a)可以脱离策略访问分布直接比较;- 当前超参数分别是四种算法的最优配置。
十三、留给读者的实验
实验 A:增加训练种子
python.\贪吃蛇\benchmark_dueling_dqn.py `--seeds 1 2 3 4 5 6 7 8 9 10--reuse新种子没有现成检查点时仍会自动训练。更多种子可以判断正交互是否稳定存在。
实验 B:增加训练预算
python.\贪吃蛇\benchmark_dueling_dqn.py--episodes 3000 `--output-dirruns\dueling-dqn-3000观察 Dueling DQN 单独使用时是否只是学习速度较慢。
实验 C:只比较网络结构
训练 DQN 与 Dueling DQN,保持--double-dqn都关闭。这个对照最适合观察结构本身的作用。
实验 D:观察单张地图的 V 与 A
从同一条轨迹连续记录V(s)和三个A(s,a),观察接近墙壁、吃到食物和进入开阔区域时,
状态价值与动作优势分别怎样变化。
项目地址:https://github.com/NocoldBob/RL