ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Pong游戏AI强化学习源码包:DQN训练与避坑指南

Pong游戏AI强化学习源码包:DQN训练与避坑指南 简介基于Python强化学习与深度强化学习的游戏AI训练项目面向具备Python编程基础、希望入门强化学习或完成毕业设计的学生。项目整合深度Q网络与经典Q学习等算法并配有Pong游戏与迷宫Plus两类实验场景方便从原理推导到代码实现完整理解。该压缩包一共包含49个文件大小约2.4MB主要文件有Python脚本、编译缓存、PNG训练图片、Markdown文本说明和3份PDF论文报告Python脚本用于智能体训练和模型加载图片可查看训练动态文档有助于研读算法背景与报告写作要点目录还划分了code、log、报告等模块结构清晰。目前已有267人学习下载可作为毕业设计、课程项目或自学者动手实验的完整素材。除源码与项目说明外还提供可复现Pong等游戏AI效果的支持文件便于读者搭建环境、快速运行并在此基础上开展算法改进与实验总结。1. 跑通 Pong 游戏 AI这份 Python 强化学习源码包能让你少走多少弯路一份能跑通的 Python 强化学习源码和一份从论文手搓的强化学习代码差距比你想象的大。深度强化学习DQN在 Atari Pong 上的训练看起来只是「环境交互 梯度更新」实际落地时遇到的环境版本、奖励稀疏、探索率衰减、模型保存路径每一个都能让训练一夜白跑。这个压缩包把游戏 AI 训练需要的训练代码、参考论文、Q-learning 迷宫实验、课程报告打包在一起适合做毕业设计、刚入强化学习坑、以及想快速复现 DQN 完整流程的开发者。它不是零散 demo而是一套能直接跑通、能改参数、能出日志的 Python 工程项目。这份资源最值得下载的地方就是省去了从零搭环境、写训练循环、补报告模板的时间。2. 资源拆解与训练管线从参考论文到可复现代码的落地路径拿到压缩包先别急着跑代码。这套资源里真正值钱的不只是 dqn.py还包括参考论文、迷宫实验和报告模板四块合起来才构成完整的学习闭环。我把解压后的结构整理成表格对照着看会更清楚。路径里面有什么干什么用参考论文/1910.09986.pdf、Playing atari with DRL.pdfDQN 的设计依据调参和写报告时对照Pong_for_demo/runs/env、dqn.py、pong_load_model.py、requirements.txt、pong_runs/、log/Pong 的 DQN 训练与推理主模块迷宫Plus/Q-learning 代码、README、报告/表格型 Q-learning 最小实验理解 Q 表迭代根目录 README.md项目说明、大作业提交内容和要求复现步骤与报告格式说明2.1 四个模块各管哪一段参考论文目录放了两篇 PDF其中 Playing Atari with DRL 就是 DQN 的原始论文另一篇以 arXiv 编号 1910.09986 命名。这两个 PDF 具体内容的差异不用纠结重点是它们都是 DQN 体系的参考材料训练代码里的经验回放、目标网络、CNN 预处理都能从论文里找到设计动机。写课程报告或者毕业设计说明书的时候这两篇可以直接作为参考文献来源省去找论文的功夫。Pong_for_demo 是整套资源的核心。dqn.py 负责训练pong_load_model.py 负责加载训练好的权重做推理requirements.txt 是环境依赖清单pong_runs 目录存放训练过程中保存的模型权重和相关记录log 目录放训练日志。runs/env 是 gym 环境相关的配置目录这里提醒一句目录结构别乱改代码里读取路径基本是相对路径挪了位置容易加载不到模型。迷宫Plus 是单独的一个 Q-learning 实验和 Pong 的深度强化学习形成对比。Pong 用 CNN 拟合 Q 值迷宫用表格直接存 Q 值两个放在一起看正好理解「为什么状态空间一大表格方法就撑不住必须上函数近似」。报告目录下的大作业提交内容和要求 pdf明确写了报告该包含哪些章节、图表和结果照着写就行。2.2 DQN 的三个关键机制经验回放、目标网络、奖励裁剪Pong 的原始状态是 210×160×3 的一帧画面如果用 Q-learning 表格存所有状态状态空间是天文数字。DQN 的思路是用卷积神经网络把帧映射成动作的 Q 值网络结构就是对论文那张 CNN 图的直接实现。但光有网络还不够训练要稳定必须解决三个问题。第一个是经验回放。游戏产生的相邻样本高度相关直接按顺序训练网络会在这几个样本上反复震荡。DQN 的做法是把交互数据放进一个固定大小的 buffer训练时随机采样一小批打断样本间的相关性。第二个是目标网络。更新 Q 值时如果目标还是自己算的容易原地发散所以代码里维护一份延迟同步的权重用来计算目标值。第三个是奖励裁剪。Pong 的奖励天然只有 -1、0、1代码通常还会把 reward clip 到 [-1, 1]这样 loss 的波动不会因为一次大奖励而失控。这三个机制在 dqn.py 里都有对应实现。如果你在跑的过程中发现训练发散、loss 出现 nan优先怀疑目标网络同步周期和奖励裁剪是不是被动过。2.3 训练管线总览整个训练流程是一条固定管线每一步都有对应代码环境重置gym 创建 Pong 环境返回初始帧做灰度化、裁剪、缩放到 84×84并堆叠最近 4 帧作为状态选择动作以 epsilon 概率随机探索否则让网络输出当前状态下各动作的 Q 值取最大者执行动作env.step(action) 得到下一帧、奖励和结束标志reward 做裁剪存储经验把 (state, action, reward, next_state, done) 存进 replay buffer采样训练buffer 足够大之后随机采样一个 batch计算 TD 误差更新网络权重同步目标网络每 N 步把训练网络的权重复制给目标网络周期评估每隔固定步数跑若干局无探索的评估记录平均 reward 并保存模型这条管线里的第 2 步和第 5 步是调参高发区。epsilon 衰减太快探索不足reward 卡在 -21 不动学习率调太大loss 直接发散。后面避坑章节会具体展开。3. 跑通 DQN 训练环境依赖、dqn.py 参数与模型加载3.1 装完 requirements.txt 先做三件事requirements.txt 里一般固定了 gym、numpy、opencv-python 这类核心依赖网络构建部分常见的是 tensorflow 或 keras。装依赖本身不难难的是版本匹配。老项目在 Python 3.10 上经常暴露版本问题所以我一般建议先在一个干净的虚拟环境里装。pip install -r requirements.txt装完不要直接开训先做三个冒烟检查。第一确认 gym 的 Atari 环境能创建python -c import gym; envgym.make(PongNoFrameskip-v4); print(env.observation_space, env.action_space)这一步能同时验证 gym 和 atari_py 装没装对。第二确认 numpy 版本不要过新部分老代码对 numpy 1.24 的 API 变更敏感遇到 np.float 报错就降级到 numpy 1.23。第三确认代码里的 import 路径和你实际的 Python 环境一致尤其是用了 conda 虚拟环境时python 指向不对会装错环境。这三个检查做完再跑 dqn.py 心理就有底了。如果环境创建报错绝大多数情况是 gym 版本和 atari_py 不匹配解法在避坑章节里。注意Pong 的 reward 基线是 -21训练早期 reward 不动是正常的先看趋势而不是单点值。3.2 dqn.py 网络结构与参数dqn.py 的网络结构是这个项目最值得抄作业的部分。以下的构建代码是 DQN 在 Atari 场景下的标准结构和论文里 CNN 图一一对应# dqn.py 中 DQN 网络的常见结构Keras 实现 from tensorflow import keras from tensorflow.keras import layers def build_dqn(action_size): # 输入是 84x84 灰度帧堆叠 4 帧, 通道维度放最后 inputs keras.Input(shape(84, 84, 4)) x layers.Conv2D(32, 8, strides4, activationrelu)(inputs) x layers.Conv2D(64, 4, strides2, activationrelu)(x) x layers.Conv2D(64, 3, strides1, activationrelu)(x) x layers.Flatten()(x) x layers.Dense(512, activationrelu)(x) # 输出是每个动作的 Q 值, 线性激活, 不是 softmax outputs layers.Dense(action_size, activationlinear)(x) return keras.Model(inputs, outputs)这段代码有几个细节值得说。输入 84×84×4 不是随便定的论文里明确做了灰度化、裁剪和 4 帧堆叠目的是给网络提供运动信息比如球的运动方向。网络最后用线性激活而不是 softmax因为 Q 值是动作价值的回归目标不是分类概率。Pong 的动作空间是 6 个离散动作对应 outputs 的维度就是 6。训练循环的核心逻辑如下每一步先和环境交互再攒够 batch 后训练# dqn.py 训练循环核心部分 for step in range(total_steps): action agent.act(state, epsilon) # epsilon-greedy 探索 next_state, reward, done, _ env.step(action) replay_buffer.add(state, action, clip_reward(reward), next_state, done) state preprocess(next_state) if len(replay_buffer) batch_size: batch replay_buffer.sample(batch_size) # 随机采样打断相关性 loss agent.train_on_batch(batch) # 计算 TD 误差并更新权重 if step % target_update 0: agent.update_target() # 目标网络权重同步 if step % eval_interval 0: agent.evaluate_and_log() # 评估并记录日志训练循环里最容易被忽视的是 clip_reward 这一步。Atari Pong 的原始奖励数值本身不大但保险起见还是裁剪到 [-1, 1]否则个别大奖励会让 batch 的 loss 异常波动。target_update 的周期要适度太频繁等于没有目标网络太久则目标值滞后导致训练慢。3.3 超参数怎么看、怎么改这是一个面向 Pong 的常用参数表数值是 DQN 在 Atari 上的常见区间不是绝对标准但可以作为起步值参数常见取值调整方向learning_rate0.0001~0.00025发散就调小太慢就调大gamma0.99越大越看重远期收益epsilon 初始1.0前期大量随机探索epsilon 最小0.1保留少量探索epsilon 衰减每步 1e-6 量级衰减过快会导致 reward 不动batch_size32显卡内存够可以试 64replay buffer100000越大越稳定但更吃内存target_update1000 步同步频率论文常用值调参的核心原则是一条一次只动一个参数。我见过不少人把 lr、epsilon 衰减、buffer 大小一起改训练崩了根本不知道是哪一项造成的。正确的做法是保持其他参数不变只动一个跑一段固定步数对比 log 里的 reward 和 loss 变化。3.4 加载模型做推理训练完的权重通常在 pong_runs 目录下dqn.py 训练结束后会自动保存。pong_load_model.py 负责把权重加载回来跑一局看效果# pong_load_model.py 核心逻辑 model build_dqn(action_size) model.load_weights(pong_runs/weights_final.h5) # 训练时保存的权重, 以实际文件名为准 epsilon 0.05 # 评估时探索率降到极低, 否则动作会随机 state env.reset() done False while not done: q_values model.predict(state[np.newaxis, ...], verbose0) action np.argmax(q_values[0]) # 贪心选择 Q 值最大的动作 state, _, done, _ env.step(action)加载推理这个环节有两个高频翻车点我复现时就踩过。第一是 epsilon 忘记降下来模型虽然加载了但动作一半都是随机的看起来跟没训练一样。第二是保存和加载的路径不一致训练时用的是相对路径推理脚本目录换了就加载不到。建议在代码里把模型路径定义成常量训练和推理共用同一个路径来源。4. Q-learning 迷宫从 Q 表迭代到路径可视化的最小闭环4.1 迷宫建模状态、动作、奖励三要素Pong 是深度强化学习的代表那 Q-learning 迷宫就是理解强化学习最小闭环的最佳入口。迷宫的环境极小Q 表可以直接存状态、动作、奖励这三要素看得清清楚楚。迷宫地图通常是一个二维数组0 表示通路1 表示墙# 迷宫地图: 0 通路, 1 墙, (1,1)为起点, (4,4)为终点 maze [ [1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 1], [1, 0, 1, 1, 0, 1], [1, 0, 1, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1], ] states [(r, c) for r in range(6) for c in range(6) if maze[r][c] 0] actions [(0, -1), (0, 1), (-1, 0), (1, 0)] # 左右上下这里的建模要诀是奖励设计。走到终点给 10这是主目标每走一步给 -0.1这是为了惩罚绕路让智能体学最短路径撞墙时留在原地并给 -1这是为了快速教它避开墙壁。三档奖励缺一不可只有终点奖励的话智能体会在迷宫里乱逛很久才收敛。4.2 Q 表迭代与超参数选择Q-learning 的核心更新公式是 TD(0)在代码里表现为一行# Q-learning 核心更新公式 q_table[state][action] lr * ( reward gamma * np.max(q_table[next_state]) - q_table[state][action] )这行的含义值得拆开讲。reward gamma * np.max(q_table[next_state])是 TD target表示当前这一步的即时奖励加上未来能拿到的最优价值gamma 越高越看重远期。减去q_table[state][action]得到 TD 误差再乘以学习率 lr 更新原来的估计。整个过程就是让 Q 表逐步逼近真实的价值函数。迷宫场景的超参数和 Pong 很不一样因为状态空间小更新快参数可以更激进参数迷宫建议值说明lr0.1~0.5状态少, 学习率可以大gamma0.9~0.99终点价值要能往回传epsilon 初始1.0前期自由探索epsilon 最小0.01保留少量随机episodes500~2000小地图 500 足够训练循环和 Pong 类似只是把网络换成了 Q 表。每一步先用 epsilon-greedy 选动作执行后按上面那行公式更新表格然后移动到下一个状态。一个完整的 episode 从起点到终点或达到最大步数结束。4.3 可视化路径与收敛判断Q-learning 迷宫最直观的验证方式是把学到的路径画出来以及把 Q 值的空间分布画成热力图# 用 matplotlib 画最优路径和 Q 值热力图 import matplotlib.pyplot as plt import numpy as np policy np.argmax(q_table, axis2) # 每个格子选 Q 值最大的动作 heatmap np.max(q_table, axis2) # 每个格子的最大 Q 值 plt.imshow(heatmap, cmaphot) plt.show()判断收敛的标准很简单把每一 episode 的步数画出来从最初的上百步随机游走逐渐降到十几步的稳定最优路径说明学到位了。如果步数曲线一直居高不下或热力图里终点附近的 Q 值没有明显突起优先检查奖励设计和 epsilon 衰减这两个是迷宫实验里最常出问题的地方。5. 避坑记录Pong 不收敛、版本报错与迷宫绕路5.1 环境与依赖相关的两个高频报错现象gym.make(PongNoFrameskip-v4) 直接报 ModuleNotFoundError: No module named gym.envs.atari原因gym 的 Atari 环境不是内置的依赖 atari_py 或 autorom 自动下载 ROMrequirements.txt 里如果漏了这一项就会报错。解决先执行pip install atari_py或pip install gym[atari]装完重新确认 ROM 已就位。如果网络拉不到 ROM常见做法是手动下载 ATARI 的 ROM 文件并放到 gym 的 ROM 目录下。老代码建议锁定 gym 版本到 0.21 系列这个版本对 atari_py 的兼容最稳定后面 0.26 之后接口改动很大。现象AttributeError: TimeLimit object has no attribute ale原因新版 gym 把 env.ale 的访问路径改了老代码通过 env.ale 拿原始帧或执行动作会崩。这个报错在 Python 3.10 新版 gym 的组合下很常见。解决改用env.unwrapped.ale访问底层 ALE 接口或者直接锁回gym0.21.0。项目里的 dqn.py 走的是标准 gym API但预处理部分如果用了ale.lives()这类接口就必须加 unwrapped。5.2 训练效果相关的三个隐蔽坑现象Pong 训练了几千步reward 纹丝不动地停在 -21原因-21 意味着 AI 一局球一个都没赢处于完全随机水平。最常导致这个结果的是 epsilon 衰减太快探索在很早就结束了AI 困在「永远朝一个方向打球」的局部策略里另一个可能是 reward 没做裁剪个别 reward 异常值把 loss 推爆。解决把 epsilon 衰减周期拉长从 1.0 衰减到 0.1 的过程至少覆盖 10 万步确保 AI 有足够时间试到「接球、回球」这个正反馈序列。同时检查 reward clip 是否生效loss 曲线如果出现尖刺优先降学习率。现象加载训练好的模型跑推理AI 看起来还是乱走原因第一嫌疑是 epsilon 没在推理脚本里降为接近 0动作仍然随机第二是模型路径不一致加载了初始权重而不是训练后的权重第三是训练时保存权重的地方和推理时读取的地方不是同一个文件。解决把推理脚本里的 epsilon 直接写死为 0.05加载后用同一个权重文件跑一遍训练时的评估逻辑做对比。我一般会在推理前打印一次预测 Q 值的分布如果六个动作的 Q 值几乎相等说明权重没加载进来。现象Q-learning 迷宫 2000 回合还在绕远路原因epsilon 衰减到 0 之后策略完全确定一旦早期学了个绕路策略就固化下来或者每步 -0.1 的惩罚太小绕远路的代价不明显。解决epsilon 下限保留 0.01~0.05让 AI 始终有概率重新发现更短路。惩罚和奖励同比例放大比如每步 -0.5、终点 50这样路径长度的差距在 Q 值上体现更明显。6. 验证与进阶从 reward 曲线到改出你自己的游戏 AI6.1 用日志和 reward 曲线验证训练有效判断训练不是看着玩的有效方式是把 log 目录的 reward 和 loss 拉出来画图。Pong 的随机策略 baseline 是 -21训练有效时 reward 曲线会慢慢从 -21 上升到 -10 再到 0 附近有的会继续突破到正值。原始曲线抖动很大我习惯先做滑动平均再判断趋势。如果 reward 纹丝不动先查日志里的 epsilon 是不是在按预期衰减再查 loss 有没有出现尖刺。比曲线更直观的验证是加载模型让 AI 跟内置对手打一局看它能不能接住几次球并完成回球这是最直观的能力证明。6.2 下一步能改什么Double DQN、优先经验回放、场景迁移这个资源包只做到 DQN 基线但训练管线的结构天然支持升级。Double DQN 只需要改目标 Q 值的计算方式把「目标网络直接输出 max Q」换成「用训练网络选动作、用目标网络给值」Pong 上通常能改善过估计问题。优先经验回放改动大一点但收敛速度的提升肉眼可见。更简单的玩法是把环境换成其他 Atari 游戏比如 Breakout 的 action_size 只有 4把 dqn.py 里的环境名和输出维度改掉预处理逻辑不用动就能跑。改完之后用 6.1 的方法验证训练是否有效一目了然。写过这个包之后我形成了一个固定习惯每次改网络或超参数先只跑 2000 步看趋势绝不直接通宵跑全量。训练曲线不动就先查 epsilonreward 一直是 -21 就先调探索率和学习率而不是加网络深度。这个排查顺序帮我拦住了至少三次白跑一夜的无效训练。希望帮到你。本文还有配套的精品资源点击获取
返回列表