
简介这是一份面向强化学习初学者的可直接运行实例围绕自动打乒乓球模拟环境展开帮助读者在无需复杂配置的前提下直观理解智能体如何通过与环境交互优化击球策略。资源包共10个文件以6个pth模型权重、3个py脚本和1个pyc缓存文件为主压缩包约35.78MB其中py脚本承担环境封装、DQN算法与训练测试逻辑pth文件保存不同训练阶段的网络参数替换后即可对比智能体在各阶段的表现。已有4371人学习下载说明其上手门槛与实用性得到较多验证。读者可借此掌握状态、动作、奖励与策略等核心概念观察得分反馈如何驱动策略迭代并在此基础上尝试替换算法或调整参数探索Q-learning与DQN等方法的差异适合作为强化学习入门练手与课程实验的参考素材。1. 强化学习实例为什么“直接能跑”比“讲得深”更稀缺如果你搜过强化学习 直接能用的资源大概率经历过这种落差论文里的公式推导看懂了开源仓库的 README 也读了结果pip install完一跑就报错环境版本对不上、依赖冲突、GPU 显存不够折腾一下午连第一个 episode 都没跑起来。这份强化学习实例包解决的就是这个问题——它把环境依赖、训练循环、结果可视化全部打包好拿到手直接运行就能看到智能体从随机乱走到稳定收敛的完整过程。它适合三类人刚学完 Q-learning 理论想动手验证的初学者、需要快速搭一个 baseline 做对比实验的算法工程师、以及想替换核心方法做二次开发的研究者。代码结构刻意保持扁平没有过度封装每个算法模块独立成文件改起来不需要翻五层继承关系。下面我从实际拆包和运行的角度把这份资源的结构、跑法、参数调优和常见坑讲清楚。2. 拆开实例包目录结构、算法模块与运行入口2.1 目录布局与模块职责拿到压缩包解压后根目录下通常是这样的结构不同版本可能略有差异但核心模块一致rl-examples/ ├── envs/ # 环境封装 │ ├── gridworld.py # 网格世界环境 │ └── cartpole_wrapper.py ├── agents/ # 算法实现 │ ├── q_learning.py # 表格型 Q-learning │ ├── dqn.py # 深度 Q 网络 │ └── reinforce.py # 策略梯度 ├── configs/ # 超参数配置 │ ├── q_learning.yaml │ └── dqn.yaml ├── train.py # 统一训练入口 ├── evaluate.py # 模型评估与可视化 └── requirements.txt这个布局的好处是职责清晰envs/只管环境交互接口agents/只管算法逻辑configs/把超参数从代码里抽出来。你替换算法时只需要在agents/下新增文件然后在train.py里注册一下即可不用动环境代码。train.py是统一入口通过命令行参数指定用哪个算法、哪个环境、哪个配置文件。常见用法是python train.py --agent q_learning --env gridworld --config configs/q_learning.yaml这行命令的含义是用 Q-learning 算法在 GridWorld 环境上训练超参数从configs/q_learning.yaml读取。--agent和--env是必填项--config不指定时会加载同名默认配置。2.2 环境接口为什么统一成 Gym 风格所有环境都遵循 Gym 风格的接口reset()返回初始观测step(action)返回(next_obs, reward, done, info)。这不是为了好看而是为了算法代码能跨环境复用。Q-learning 的更新逻辑不关心状态是离散的网格坐标还是连续的 CartPole 四元组它只关心step返回了什么。以 GridWorld 为例核心逻辑大概是这样class GridWorld: def __init__(self, size5, goal(4, 4), trap[(1, 1), (2, 3)]): self.size size # 网格边长 self.goal goal # 目标位置 self.trap trap # 陷阱位置列表 self.state (0, 0) # 智能体初始位置 def reset(self): self.state (0, 0) return self.state def step(self, action): # action: 0上, 1下, 2左, 3右 x, y self.state if action 0: x max(0, x - 1) elif action 1: x min(self.size - 1, x 1) elif action 2: y max(0, y - 1) elif action 3: y min(self.size - 1, y 1) self.state (x, y) if self.state self.goal: return self.state, 1.0, True, {} # 到达目标奖励 1 if self.state in self.trap: return self.state, -1.0, True, {} # 踩到陷阱奖励 -1 return self.state, -0.01, False, {} # 每步小惩罚鼓励最短路径这里有几个参数值得注意size控制网格大小默认 5×5 对初学者足够直观trap是陷阱坐标列表你可以改成任意位置来测试算法的避障能力每步的-0.01惩罚是“步数惩罚”目的是让智能体学会走最短路而不是绕圈子。如果把步数惩罚设成 0Q-learning 在 GridWorld 上可能学出绕远路但同样能到终点的策略——这不是 bug是奖励设计的问题。2.3 训练循环episode、step 与探索率衰减训练主循环的逻辑不复杂但几个关键变量的含义必须搞清楚for episode in range(num_episodes): state env.reset() total_reward 0 while True: # epsilon-greedy 策略选择动作 if random.random() epsilon: action env.action_space.sample() # 随机探索 else: action agent.choose_action(state) # 利用当前策略 next_state, reward, done, _ env.step(action) agent.update(state, action, reward, next_state, done) state next_state total_reward reward if done: break # 探索率衰减 epsilon max(epsilon_min, epsilon * epsilon_decay)num_episodes是训练轮数GridWorld 上一般 5001000 轮就能收敛epsilon是探索率初始通常设 1.0完全随机随着训练衰减到epsilon_min一般 0.010.05epsilon_decay控制衰减速度常见取值 0.995。这三个参数配合不好会出现两种典型问题衰减太快智能体还没探索完所有状态就开始“利用”容易卡在局部最优衰减太慢训练后期还在大量随机动作收敛曲线会一直震荡。提示如果你把epsilon_decay设成 0.99 以下1000 轮训练可能不够收敛需要同步增加num_episodes。3. 跑通第一个实例从安装依赖到看到收敛曲线3.1 环境准备与依赖安装先确认 Python 版本。这份实例包通常要求 Python 3.8 以上推荐 3.9 或 3.10太新的版本3.12可能遇到部分依赖还没适配的问题。创建虚拟环境是标准操作python -m venv rl_env source rl_env/bin/activate # Linux / macOS # rl_env\Scripts\activate # Windows pip install -r requirements.txtrequirements.txt里一般包含numpy、gymnasium或旧版gym、matplotlib、pyyaml。如果安装过程中遇到gym和gymnasium冲突说明你的环境里之前装过旧版先pip uninstall gym再装。常见做法是锁定版本号比如gymnasium0.29.1避免自动升级到不兼容的新版本。安装完成后跑一个快速自检python -c import gymnasium; print(gymnasium.__version__)能打印出版本号就说明基础环境没问题。如果报ModuleNotFoundError检查虚拟环境是否激活、pip 是否指向正确的 Python 版本。3.2 运行 Q-learning观察奖励曲线的三个阶段用默认配置跑 Q-learningpython train.py --agent q_learning --env gridworld --config configs/q_learning.yaml训练结束后会在logs/下生成奖励曲线图。一条典型的收敛曲线分三个阶段前 100 轮左右奖励在 -1 到 -0.5 之间波动这是随机探索阶段100 到 300 轮奖励快速上升说明 Q 表开始学到有效策略300 轮之后曲线趋于平稳接近每 episode 的最大奖励。如果你的曲线在第二阶段就平了但奖励值不高大概率是陷入了局部最优可以调大epsilon_min或增加num_episodes。configs/q_learning.yaml里的关键参数learning_rate: 0.1 # Q 值更新步长 gamma: 0.99 # 折扣因子 epsilon: 1.0 # 初始探索率 epsilon_min: 0.05 # 最小探索率 epsilon_decay: 0.995 # 每 episode 衰减系数 num_episodes: 800 # 训练总轮数learning_rate设 0.1 是表格型 Q-learning 的常见值设太大如 0.5会导致 Q 值震荡不收敛设太小如 0.01学习速度慢gamma设 0.99 表示智能体比较“短视”更看重近期奖励GridWorld 这种短路径任务用 0.90.99 都合理。3.3 切换到 DQN验证方法可替换性同一个环境换 DQN 算法只需要改--agent参数python train.py --agent dqn --env gridworld --config configs/dqn.yamlDQN 的配置文件多了几个神经网络相关的参数hidden_dim: 64 # 隐藏层维度 buffer_size: 10000 # 经验回放池容量 batch_size: 64 # 每次采样批次大小 target_update: 100 # 目标网络更新间隔步数buffer_size设 10000 对 GridWorld 足够但如果换到状态空间更大的环境如 CartPole可能需要 50000 以上target_update控制目标网络同步频率设太小会导致训练不稳定设太大则学习滞后常见范围 1001000 步。DQN 在 GridWorld 上不一定比 Q-learning 收敛快因为状态空间小表格方法本身就够用。但换成 CartPole 这种连续状态环境Q-learning 就无能为力了必须用 DQN 做函数逼近。注意从 Q-learning 切到 DQN 时如果报显存不足把batch_size降到 32 或 16GridWorld 的观测维度很低不需要大 batch。4. 避坑与排查五个真实翻车记录4.1 奖励曲线不收敛一直震荡现象训练 500 轮后奖励仍在 -1 到 0 之间大幅波动没有上升趋势。原因最常见的是epsilon_decay设得太大如 0.999探索率衰减太慢训练后期仍在大量随机动作其次是learning_rate设得过大Q 值更新步长太大导致震荡。解决先把epsilon_decay调到 0.990.995 区间观察 300 轮内是否有上升趋势如果仍然震荡把learning_rate从 0.1 降到 0.05 或 0.01。改完参数后建议固定随机种子跑三次排除单次运行的偶然性。4.2 训练速度异常慢一个 episode 要好几秒现象GridWorld 这种小环境一个 episode 理论上毫秒级完成但实际跑起来每轮要等好几秒。原因大概率是train.py里每步都调用了env.render()渲染操作会强制同步刷新界面严重拖慢训练速度。另一个可能是日志写入太频繁每个 step 都往磁盘写一条记录。解决检查train.py中是否有render调用训练时关掉渲染只在evaluate.py里开启。日志改成每 10 个 episode 记录一次而不是每步记录。如果用的是 Jupyter Notebook把训练循环放在.py文件里跑Notebook 的交互式输出也会拖慢速度。4.3 换了环境后算法完全学不动现象在 GridWorld 上跑得好好的 Q-learning换到 CartPole 后奖励一直是 20 左右随机水平完全不学习。原因Q-learning 是表格型方法它用字典或数组存储每个状态-动作对的 Q 值。CartPole 的状态是连续的四维浮点数理论上无限多种状态表格方法根本存不下。这不是参数问题是方法选型错误。解决连续状态环境必须用函数逼近方法把--agent换成dqn或reinforce。如果坚持用表格方法需要先对环境做离散化把连续状态分桶但这会损失精度不推荐。4.4 依赖安装报错提示版本冲突现象pip install -r requirements.txt时报Cannot install gymnasium and gym because these package versions have conflicting dependencies。原因系统里之前装过旧版gym而实例包依赖gymnasium两者在包名和 API 上有重叠但不兼容。解决先pip uninstall gym gymnasium -y清理干净再重新pip install -r requirements.txt。如果仍然冲突手动指定版本pip install gymnasium0.29.1 numpy1.24.0。建议始终在虚拟环境里操作避免污染系统 Python。4.5 评估时表现远差于训练时的奖励现象训练曲线显示奖励已经收敛到 0.9 以上但用evaluate.py跑评估时智能体表现很差经常撞陷阱或绕远路。原因评估时如果仍然开着探索epsilon 0智能体会以一定概率随机选动作表现自然下降。另一个可能是评估环境和训练环境的初始状态分布不一致。解决评估时把epsilon强制设为 0完全利用策略在evaluate.py里加一行agent.epsilon 0。同时确认评估用的环境参数如size、trap位置和训练时一致。如果评估环境是随机初始状态多跑几个 episode 取平均单次评估的方差可能很大。5. 进阶技巧替换核心方法与验证收敛性5.1 替换算法模块的标准化流程这份实例包的设计意图就是让你改。替换算法的标准流程是三步在agents/下新建算法文件实现choose_action和update两个方法在train.py的算法注册表里加一行映射新建对应的 YAML 配置文件。以替换成 SARSA 为例# agents/sarsa.py class SARSA: def __init__(self, n_states, n_actions, lr0.1, gamma0.99): self.Q np.zeros((n_states, n_actions)) self.lr lr self.gamma gamma def choose_action(self, state, epsilon0.1): if random.random() epsilon: return random.randint(0, self.Q.shape[1] - 1) return np.argmax(self.Q[state]) def update(self, state, action, reward, next_state, next_action, done): # SARSA 用实际选择的 next_action 更新而非 max target reward self.gamma * self.Q[next_state][next_action] * (1 - done) self.Q[state][action] self.lr * (target - self.Q[state][action])注意 SARSA 的update比 Q-learning 多一个next_action参数因为它是 on-policy 方法用实际执行的下一个动作来更新而不是用最大 Q 值。这个差异在悬崖行走Cliff Walking类环境里会体现得很明显Q-learning 学出贴着悬崖走的最优路径SARSA 学出绕远的保守路径。两者没有优劣之分取决于你的任务是否允许冒险。5.2 收敛性验证不要只看一条曲线单次训练的奖励曲线受随机种子影响很大我一般会固定三个不同种子各跑一次把三条曲线画在同一张图上。如果三条曲线都在相近的 episode 数收敛到相近的奖励水平说明算法稳定如果某条曲线明显偏离检查那次运行的epsilon衰减轨迹和 Q 值分布。另一个验证手段是看 Q 表的数值分布。收敛后目标状态附近的 Q 值应该明显高于其他区域且相邻状态的 Q 值差异平滑。如果 Q 值出现大面积相同或剧烈跳变说明学习率或折扣因子需要调整。# 可视化 Q 表GridWorld 场景 import seaborn as sns q_table agent.Q.reshape(env.size, env.size, -1) sns.heatmap(q_table.max(axis2), annotTrue, cmapYlOrRd)这张热力图能直观看出智能体是否学到了“越靠近目标 Q 值越高”的梯度。如果热力图一片均匀说明训练不充分或奖励设计有问题。5.3 从实例到项目的迁移习惯这份实例包的价值不在于它本身能解决多复杂的问题而在于它提供了一个可运行的最小闭环。我自己的习惯是每学一个新算法先在这个框架里跑通 GridWorld确认理解无误后再把envs/下的环境替换成目标任务的封装算法代码基本不动。这样能把“算法逻辑”和“环境适配”两个问题解耦出问题时定位更快。从那以后我每次拿到新的强化学习代码都强制先跑一遍它的默认配置确认能复现 README 里的结果再动手改。这个习惯帮我省掉了大量“以为是算法问题、其实是环境配置问题”的排查时间。希望帮到你。本文还有配套的精品资源点击获取