CarRacing DQN/DoubleDQN:训练改进、实验结果与可视化分析
项目概述
本项目围绕 Gymnasium CarRacing-v2 视觉控制任务,搭建并改进 DQN/DoubleDQN 训练框架,目标是把训练从“能跑通”提升到“可复现、可对比、可解释”,并以实验日志与可视化结果支撑算法迭代。
项目输出包括:
- 可配置训练入口(DQN/DoubleDQN),支持固定随机种子与关键开关;
- 标准化日志与对比图(reward、loss、SPS、UPS 等);
- 模型权重与评估流程,便于复用与复现;
- 面向后续扩展(PER、NoisyNet、n-step return 等)的工程底座。
项目目标
- 可复现:训练参数配置化,固定
seed,日志与产物可追溯,保证同配置可复跑。 - 可对比:将关键改进点做成开关,支持 baseline vs improved、消融对照与多组实验对比绘图。
- 可解释:不仅观察 reward,还记录 loss、回合长度与吞吐指标(SPS/UPS),让训练现象可分析。
- 可扩展:在不破坏训练主流程的前提下,能快速加入更多 DQN 变体或训练技巧。
- 可落地:在 GPU 环境下支持 AMP 等加速手段,能进行长时间稳定训练并输出可汇报结果。
核心算法原理(DQN 系列)
1. DQN:用深度网络逼近动作价值函数
强化学习中,动作价值函数 Q(s, a) 表示在状态 s 下选择动作 a 后,未来累计回报的期望。DQN 使用神经网络 Q(s, a; θ) 来逼近该函数,并通过最小化 TD 误差训练网络。
核心计算包含:
- ε-greedy 探索:以概率
ε随机探索,以1-ε选择argmax_a Q(s, a; θ)。 - 目标值(TD Target):
y = r + γ max_{a'} Q(s', a'; θ⁻)。 - 损失(TD Loss):常见为 Huber/MSE:
L = E[(Q(s, a; θ) - y)^2]。
为提升稳定性,DQN 通常配合两项关键机制:
- 经验回放(Replay Buffer):从历史样本中随机采样训练,打破样本相关性,提高数据利用率。
- 目标网络(Target Network):使用参数较慢更新的
θ⁻计算目标值,降低“目标漂移”带来的不稳定。
2. Double DQN:缓解 Q 值过估计
标准 DQN 的 max 操作容易引入过估计。Double DQN 将“选动作”和“评估动作”拆开:
- 先用在线网络选动作:
a* = argmax_{a'} Q(s', a'; θ) - 再用目标网络评估:
y = r + γ Q(s', a*; θ⁻)
这通常能让学习目标更稳,减少价值函数发散风险。
3. Dueling Network:分解状态价值与动作优势
Dueling 结构将 Q(s, a) 分解为:
- 状态价值
V(s):状态本身的价值 - 动作优势
A(s, a):相对平均动作的优势
常用聚合方式:
Q(s, a) = V(s) + (A(s, a) - mean_a A(s, a))
这样能让网络在“动作差异不大”的状态下更高效地学习,有助于视觉任务的稳定与样本效率。
4. 本项目中的稳定性增强手段(与算法配套)
- 观测归一化:将
uint8(0~255)缩放到float(0~1),改善 CNN 输入尺度。 - 梯度裁剪:限制梯度范数,缓解训练后期梯度爆炸与震荡。
- AMP:在 GPU 上启用混合精度,在尽量不损失稳定性的前提下提高吞吐。
项目介绍
这个项目面向一个具体问题:如何让 CarRacing-v2(图像输入的驾驶控制任务)上的 DQN/DoubleDQN 训练,从“能跑”变成“可复现、可对比、可解释”,并且能支撑后续持续迭代。
项目具体做了什么
- 把训练做成一条标准管线:环境预处理 → 采样与回放 → 更新网络 → 记录日志 → 画图对比 → 保存模型/评估。
- 把关键算法点模块化为开关:
double_q、dueling、normalize_obs、max_grad_norm、amp等都可以通过参数控制,方便做对照实验。 - 把“实验结果”落到文件与指标上:训练过程自动输出 CSV 日志,并对 reward、loss、SPS、UPS 做可视化与表格统计,确保结论可追溯。
项目范围(从输入到输出)
- 任务与环境:Gymnasium
CarRacing-v2,图像观测输入,离散动作空间(continuous=False)。 - 输入状态:经过灰度化、缩放与帧堆叠后的图像序列(典型形状为
(4, 84, 84))。 - 输出策略:离散动作(转向/加速/刹车/空动作),通过估计
Q(s, a)选择动作。 - 训练产物:
- 训练日志(CSV):reward、loss、epsilon、SPS、UPS、episode length;
- 模型权重(
.pt); - 对比曲线图(PNG)。
代码结构与关键文件(可在演讲中直接指路)
- 训练入口
- DQN:
nn/src/car_racing/car_racing_ros/dqn_model/training_dqn.py - DoubleDQN:
nn/src/car_racing/car_racing_ros/doubledqn_model/training_double_dqn.py - 核心智能体与网络
- DQN Agent:
nn/src/car_racing/car_racing_ros/dqn_model/dqn_agent.py - Dueling 网络结构:
nn/src/car_racing/car_racing_ros/dqn_model/base_agent.py - DoubleDQN Agent:
nn/src/car_racing/car_racing_ros/doubledqn_model/doubledqn_agent.py - 配置文件(复现与对比的关键)
nn/src/car_racing/car_racing_ros/configs/dqn.yamlnn/src/car_racing/car_racing_ros/configs/double_dqn.yaml- 实验产物目录
- 日志:
nn/src/car_racing/car_racing_ros/training/logs/ - 权重:
nn/src/car_racing/car_racing_ros/training/saved_models/ - 图像:
nn/src/car_racing/car_racing_ros/training/*.png - 对比绘图工具
nn/src/car_racing/car_racing_ros/plot_comparison.py
项目核心亮点
- 不只是论文复现:把 Double DQN、Dueling Network、梯度裁剪、观测归一化等技术从“零散在代码里”变成“可开关、可对比的模块”;
- 不只有单条 reward 曲线:补充 SPS、UPS 等工程指标与完整实验结果表格,让训练过程透明可分析;
- 不只有数字结果:对关键术语、表格指标逐个解释,让结果可理解、可验证;
- 是一个可迭代的工程底座:为后续集成 PER、NoisyNet、n-step 等更多 DQN 变体预留了统一接口。
汇报大纲
- 项目背景与研究动机
- 系统性改进方案(算法 + 工程)
- 实验设计与结果来源
- 量化结果与效果分析
- 关键术语与表格解读
- 总结与未来展望
1. 项目背景与研究动机
CarRacing-v2 是一个典型的强化学习视觉控制任务。智能体从图像观测中学习驾驶策略,目标是在赛道上尽可能稳定地前进并获得更高累计奖励。
典型难点:
1. 输入是像素图像:状态维度高,特征提取依赖 CNN,训练开销明显高于低维状态任务。
2. 奖励稀疏且波动较大:智能体在训练初期经常无法形成有效驾驶行为,reward 曲线会剧烈震荡。
3. 强化学习训练稳定性差:经验回放、目标网络、探索率衰减等细节都会显著影响结果。
4. 实验复现成本高:如果参数散落在代码中,每次做对比实验都容易引入额外变量。
因此,本项目的核心目标并不是只追求一个“更高的 reward”,而是构建一个:
- 能固定随机种子、重复实验的训练框架;
- 能记录 reward、loss、SPS、UPS 等关键指标的监控系统;
- 能对比 baseline 与改进方案的实验平台;
- 能为后续扩展 PER、NoisyNet、n-step return 等方法提供统一接口的工程底座。
2. 系统性改进方案
2.1 算法层改进
本项目并非简单替换算法名称,而是把多个稳定训练的重要机制做成了可开关、可对比的模块。
1. Double DQN 机制
Double DQN 的核心作用是缓解标准 DQN 中常见的 Q 值过估计(overestimation) 问题。
- 标准 DQN 往往使用同一个目标值过程同时“选动作”和“评估动作”,容易把某些动作价值估得过高。
- Double DQN 将这两个过程分开:
- 用在线网络(policy net)选择下一个动作;
- 用目标网络(target/frozen net)评估该动作对应的 Q 值。
这样做的好处是: - 学习目标更稳; - reward 曲线通常更容易上升; - 在长时间训练中更不容易出现价值函数发散。
2. Dueling Network 结构
Dueling Network 将 Q 值函数分解为两部分:
- 状态价值 V(s):当前状态本身有多“好”;
- 动作优势 A(s, a):在该状态下,某个动作比平均动作“好多少”。
这样做的意义在于:
- 网络能先学会“这个状态值不值得继续”,再学“这个状态下哪个动作更优”;
- 在很多状态下,不同动作差异不大,Dueling 结构通常能提升特征利用效率;
- 对图像输入任务尤其有帮助,因为它能更好地区分“环境状态信息”和“动作偏好信息”。
3. 梯度裁剪(Gradient Clipping)
max_grad_norm 用于限制梯度范数,防止训练中出现梯度爆炸。
通俗理解:
- 如果一次反向传播得到的梯度过大,参数更新会非常剧烈;
- 过剧烈的更新会导致 loss 突增、Q 值震荡甚至训练崩掉;
- 梯度裁剪会在梯度过大时将其缩放回安全范围。
这类机制对强化学习尤其重要,因为强化学习的目标值本身就在不断变化,比监督学习更容易不稳定。
4. 观测归一化(Observation Normalization)
环境原始图像通常是 uint8 类型,像素值范围是 0~255。
normalize_obs 会把它转换为 float32 并缩放到 0.0~1.0。
这样做的好处是:
- CNN 输入尺度更稳定;
- 梯度传播更平滑;
- 不同 batch 之间的数值范围更一致;
- 通常能加快收敛并减少训练前期震荡。
5. AMP 混合精度训练
AMP 是 Automatic Mixed Precision 的缩写,即自动混合精度训练。
它的作用是:
- 一部分计算使用半精度浮点数,降低显存压力;
- 关键位置保留高精度,避免数值不稳定;
- 在 GPU 环境下通常可以提升吞吐。
对于需要长时间训练的 CarRacing 图像任务,AMP 可以显著改善训练效率。
2.2 工程层改进
1. 参数化训练入口
训练脚本 training_dqn.py 已支持大量命令行参数,例如:
- --episodes
- --max-timesteps
- --dueling
- --double-q
- --amp
- --normalize-obs
- --seed
这意味着实验不再依赖“手改代码”,而是可以通过命令行固定配置、直接复现。
2. 性能监控指标
训练日志中额外记录了两类工程指标: - SPS(Steps Per Second):每秒环境交互步数; - UPS(Updates Per Second):每秒网络更新次数。 它们可以帮助回答两个关键问题: - 训练慢是因为环境模拟慢,还是因为网络更新慢? - 新增算法模块后,性能收益是否值得吞吐开销?
3. 张量处理与设备搬运优化
代码中对经验回放采样后的 batch 做了更合理的设备搬运与形状整理,减少了细粒度张量操作带来的额外开销,也降低了训练过程中的维度错误风险。
4. 可视化脚本统一化
plot_comparison.py 支持:
- 多个日志同时绘图;
- reward、loss、SPS、UPS 等多个指标对比;
- --smooth N 滑动平均平滑;
- 服务器/无头环境下通过 MPLCONFIGDIR 规避 Matplotlib 缓存问题。
2.3 环境预处理设计
训练入口中对 CarRacing 做了标准视觉强化学习预处理: 1. GrayScaleObservation:彩色图像转灰度,降低输入复杂度。 2. ResizeObservation(84, 84):将图像统一到经典 Atari 风格尺寸。 3. FrameStack(4):堆叠连续 4 帧,让网络获得速度与运动方向信息。 4. SkipFrame(4):每 4 步执行一次同动作,减少冗余计算并提升训练效率。
这些预处理对于视觉控制任务非常关键,因为单帧图像通常不足以让智能体推断速度和方向变化。
3. 实验设计与结果来源
3.1 实验日志来源
本文中的量化结果主要来自以下日志文件:
- nn/src/car_racing/car_racing_ros/training/logs/DQN_baseline_long.csv
- nn/src/car_racing/car_racing_ros/training/logs/DQN_improved_long.csv
这两份日志都包含 200 个 episode 的训练记录,适合作为本次主实验对比数据。
这些文件可以作为开发阶段验证使用,但在“正式对比结论”上,*_long.csv 更有代表性。
3.2 对比组定义
为便于理解,这里将实验分为两组:
| 组别 | 配置说明 | 主要特点 |
| :--- | :--- | :--- |
| Baseline | 较原始的 DQN 训练配置 | 作为对照组,观察未充分优化时的训练表现 |
| Improved | 启用 dueling、double_q、normalize_obs、amp、max_grad_norm 等改进 | 目标是提升稳定性、样本效率和最终策略性能 |
3.3 图像结果
下面给出项目中已经生成的对比图,便于从趋势上观察 reward 和 loss 的变化。

4. 量化结果与效果分析
4.1 主实验结果表(200 Episodes)
基于 DQN_baseline_long.csv 和 DQN_improved_long.csv 统计得到的主结果如下:
| 指标 | Baseline | Improved | 变化趋势 | 解读 |
|---|---|---|---|---|
| 平均奖励(Mean Reward) | -41.99 | 101.72 | 明显提升 | 从整体负回报转为正回报,说明改进配置已经学到更有效的驾驶策略 |
| 末回合奖励(Last Episode Reward) | 64.06 | 723.53 | 大幅提升 | 训练结束时 Improved 的策略质量显著更高 |
| 最近 10 回合平均奖励 | 6.48 | 597.17 | 大幅提升 | 说明 Improved 不只是偶然跑出高分,而是在后期形成了稳定优势 |
| 最佳单回合奖励(Best Reward) | 219.81 | 916.20 | 大幅提升 | 改进配置具备更高的性能上限 |
| 平均回合长度(Mean Episode Length) | 205.38 | 221.40 | 小幅提升 | 智能体平均存活更久、能覆盖更多赛道步骤 |
| 平均 SPS | 9.20 | 9.30 | 基本持平 | 改进后的训练并未明显拖慢环境交互速度 |
| 平均 UPS | 2.30 | 2.32 | 基本持平 | 加入增强模块后,网络更新吞吐仍然保持稳定 |
| 平均 Loss | 0.2504 | 0.3509 | 数值上升 | 在强化学习中 loss 不能单独等价为“效果变差”,需结合 reward 与训练阶段一起分析 |
4.2 如何理解这张表
上表中的每一个指标都有明确含义,下面逐项解释。
1. 平均奖励(Mean Reward)
这是整个训练过程中,所有 episode 奖励的平均值。
- 如果平均奖励长期为负,通常说明智能体大部分时间仍然在“乱开”或频繁偏离赛道。
- 如果平均奖励转正,说明整体策略已经从“经常失败”走向“经常能有效完成驾驶”。
本实验中,平均奖励从 -41.99 提升到 101.72,说明改进配置在整体训练质量上显著优于 baseline。
2. 末回合奖励(Last Episode Reward)
这是最后一个训练回合的 reward,用于观察训练结束时模型大致处于什么水平。
- Baseline 末回合奖励为 64.06;
- Improved 末回合奖励为 723.53。
这表示在训练末期,改进配置已经学到更成熟的驾驶行为。
3. 最近 10 回合平均奖励
这个指标比“单个末回合奖励”更可靠。
原因是单次 episode 可能有偶然性,而最近 10 回合平均值更能反映训练后期是否稳定地好。
在本实验中:
- Baseline 最近 10 回合平均奖励仅为 6.48;
- Improved 最近 10 回合平均奖励达到 597.17。
这说明 Improved 后期性能提升并不是偶然尖峰,而是稳定趋势。
4. 最佳单回合奖励(Best Reward)
表示整个训练过程中跑出的最高单回合 reward。 它反映模型的性能上限,但不代表模型每次都能稳定达到这个水平,因此需要结合“最近 10 回合平均奖励”一起看。
5. 平均回合长度(Episode Length)
这里的 length 表示一个 episode 持续了多少步。
- 在 CarRacing 中,回合越长通常意味着智能体越不容易早早失控;
- 但“回合长”并不一定等于“得分高”,仍需与 reward 一起看。
本实验中 Improved 的平均回合长度略高,说明它一般能在赛道上坚持更久。
6. SPS 与 UPS
这两个指标是工程性能指标:
- SPS(Steps Per Second):每秒完成多少个环境交互步;
- UPS(Updates Per Second):每秒完成多少次网络参数更新。
如果一个改进方案让 reward 提升很多,但 SPS、UPS 下降非常明显,就要重新评估“性能收益是否值得工程成本”。
本实验中这两个指标几乎持平,说明改进配置在不牺牲吞吐的前提下获得了更好的学习效果。
7. Loss 为什么没有直接下降?
这是强化学习文档里最容易误解的地方。
在监督学习中,loss 降低通常意味着模型更好;但在强化学习里,loss 是 TD 误差相关的训练信号,它会受到以下因素影响:
- 当前策略是否进入了更高回报、但也更复杂的状态分布;
- 目标网络是否在持续变化;
- Q 值估计范围是否扩大;
- 后期 episode 是否更长,包含更多高价值状态。
因此:
- reward 是衡量策略表现的核心指标;
- loss 更适合作为训练稳定性的辅助参考;
- 不能只因为 loss 数值更大,就断言模型效果更差。
在本实验中,虽然 Improved 的平均 loss 更高,但 reward、后期均值和最佳表现都明显更好,因此应综合判断为“策略性能更优”。
4.3 一句话总结主实验
在 200 回合主实验中,改进配置实现了: - 从整体负回报到整体正回报的跃迁; - 训练末期奖励的显著提升; - 最近 10 回合稳定高分; - 几乎不增加吞吐损耗的前提下获得明显性能收益。
5. 名词解释
下面对文中核心术语做一个集中解释,方便在汇报或答辩时直接引用。
| 名词 | 解释 |
| :--- | :--- |
| DQN | Deep Q-Network,使用神经网络逼近动作价值函数 Q(s, a) 的经典值函数方法 |
| Double DQN | 对 DQN 的改进,通过“选动作”和“评估动作”分离来降低 Q 值过估计 |
| Dueling Network | 将 Q 值拆成状态价值 V(s) 和动作优势 A(s, a) 两部分建模的网络结构 |
| Q 值 | 在状态 s 下执行动作 a 后,未来累计回报的估计值 |
| 目标网络(Target/Frozen Net) | 用于生成训练目标的网络,参数更新较慢,用于稳定训练 |
| 经验回放(Replay Buffer) | 存储历史交互样本,从中随机采样训练,打破数据相关性 |
| Reward | 每个 episode 的累计奖励,是评价策略好坏的最直接指标 |
| Loss | 训练时 Q 值预测与目标值之间的误差,更多反映学习过程信号,而非最终任务表现 |
| Episode | 从环境重置到终止的一次完整交互过程 |
| SPS | Steps Per Second,每秒环境交互步数,反映采样吞吐 |
| UPS | Updates Per Second,每秒参数更新次数,反映训练吞吐 |
| AMP | 自动混合精度训练,在 GPU 上可降低显存占用并提升速度 |
| Gradient Clipping | 梯度裁剪,把过大的梯度限制在安全范围内,避免训练发散 |
| Observation Normalization | 将输入观测从 0~255 缩放到 0~1,让网络更容易学习 |
| Frame Stack | 将连续多帧图像拼接为一个状态,使模型能感知运动趋势 |
| Skip Frame | 连续多步重复同一动作,减少计算量并提升训练效率 |
6. 实验结果结论
结合日志、曲线和量化表格,可以得到以下结论:
1. 改进方案有效:Improved 组在 200 回合主实验中显著优于 baseline,尤其体现在后期 reward 的稳定提升。
2. 性能提升具有持续性:最近 10 回合平均奖励大幅领先,说明不是偶然波动,而是训练后期已形成稳定优势。
3. 工程开销可接受:SPS 与 UPS 基本持平,说明启用 Dueling、Double DQN、归一化与裁剪后,并未带来明显吞吐损失。
4. 评价模型不能只看 loss:强化学习中的 loss 只是过程信号,必须与 reward、回合长度和后期趋势结合分析。
7. 未来工作展望
基于当前已经完成的框架,后续可以继续扩展:
1. 更强的 DQN 变体:如 PER(Prioritized Experience Replay,优先经验回放)、Noisy Networks、n-step return。
2. 更系统的超参数搜索:利用现有参数化脚本,对 lr、batch_size、target_update、epsilon_decay 做网格搜索或贝叶斯优化。
3. 评估体系完善:增加固定模型检查点、独立评估脚本、均值与方差统计,让结果更适合论文或课程报告使用。
4. 迁移到更多环境:将当前框架迁移到 Atari 或机器人控制任务,验证方法的通用性。