强化学习入门实战:GridWorld与Q-learning可替换实例
简介这份强化学习实例面向零基础或初学强化学习的开发者提供可直接运行的乒乓球对战模拟环境帮助理解智能体如何通过与环境交互不断优化击球策略。压缩包共10个文件以6个pth模型权重、3个py脚本和1个pyc缓存文件为主整体约35.78MB其中py脚本承担环境封装、DQN算法与训练测试逻辑pth文件保存不同训练阶段的网络参数替换后即可对比智能体各阶段表现。资源已有4371人学习下载热度较高。读者可借此观察状态、动作、奖励与策略在具体游戏中的落地方式理解Q-learning与DQN的差异并基于现成代码尝试替换算法或调整参数快速上手强化学习实验流程。1. 强化学习实例从“跑不起来”到“改一行就能换算法”很多人第一次接触强化学习卡住的地方不是数学而是环境。装完框架复制一段示例结果要么版本对不上要么训练半天奖励不涨最后连问题出在哪都不知道。这个标题想解决的就是这件事给一个能直接运行、结果直观、方法可替换、对初学者代码友好的强化学习实例。它适合两类人——刚学完 Q-learning 公式、想看到智能体真的学会走路的同学以及已经会调库、但想要一个干净骨架去替换算法做对比实验的工程师。核心诉求只有三个跑得起来、看得见变化、换得动方法。我一般会把这类实例拆成“环境 算法 训练循环 可视化”四块任何一块都能单独替换这样才叫可替换而不是把算法焊死在训练脚本里。2. 先选对环境和算法为什么用 GridWorld 配 Q-learning 起步2.1 环境选型为什么不用 Atari 或 MuJoCo 开局初学者最容易踩的坑是一上来就上图像输入或连续控制。Atari 需要卷积网络、经验回放、目标网络训练几百万步才见效果MuJoCo 涉及物理引擎和连续动作空间调参成本极高。这些环境不是不好而是不适合“直接运行、直观”这个目标。常见做法是选一个状态空间离散、动作空间小、奖励结构清晰的环境。GridWorld网格世界就是典型智能体在一个 N×N 的格子里从起点走到终点撞墙或走错扣分到达终点给正奖励。它的好处是状态可以用整数编号Q 表可以直接打印出来看训练几十秒就能看到策略从乱走变成最短路径。这种“肉眼可见的收敛”对建立信心非常关键。我一般会把环境写成独立类只暴露reset()和step(action)两个接口内部维护状态转移和奖励计算。这样后面换成 FrozenLake、CliffWalking 甚至自己定义的格子任务只要接口一致算法代码一行不用改。2.2 算法选型Q-learning 作为可替换骨架的基准Q-learning 是离策略off-policy时序差分算法更新公式是Q(s,a) ← Q(s,a) α [ r γ max Q(s,a) − Q(s,a) ]它只需要一张 Q 表不需要神经网络不需要经验回放代码量小调试直观。选它作为基准不是因为它是“最好”的而是因为它最容易验证正确性你可以手动算几步看 Q 值更新对不对。更重要的是它天然适合做“方法可替换”的骨架。把 Q 表换成神经网络就是 DQN 的雏形把 max 换成期望就是 Expected SARSA把离策略改成在策略就是 SARSA。只要训练循环和接口设计得当替换算法只需要改一个类。下面是一个最小可运行的 Q-learning 实例环境是 5×5 网格起点左上角终点右下角撞墙或越界留在原地并给 -1 奖励到达终点给 10 并结束。import numpy as np import random class GridWorld: def __init__(self, size5): self.size size self.start (0, 0) self.goal (size - 1, size - 1) self.state self.start self.actions [0, 1, 2, 3] # 上右下左 self.action_delta [(-1, 0), (0, 1), (1, 0), (0, -1)] def reset(self): self.state self.start return self._state_id(self.state) def step(self, action): dr, dc self.action_delta[action] r, c self.state nr, nc r dr, c dc # 越界或撞墙留在原地给负奖励 if nr 0 or nr self.size or nc 0 or nc self.size: return self._state_id(self.state), -1.0, False self.state (nr, nc) if self.state self.goal: return self._state_id(self.state), 10.0, True return self._state_id(self.state), -0.1, False def _state_id(self, pos): return pos[0] * self.size pos[1] def num_states(self): return self.size * self.size def num_actions(self): return len(self.actions)这段环境代码的关键点step返回三元组(next_state, reward, done)这是最通用的接口形式。奖励设计上每走一步给 -0.1 是为了鼓励最短路径撞墙给 -1 是为了让智能体学会避开边界。如果把每步奖励设成 0智能体可能学会绕圈如果撞墙惩罚太轻它可能反复撞墙。这些数值不是玄学是可以通过打印 Q 表验证的。2.3 训练循环把探索、更新、衰减拆清楚训练循环是初学者最容易写乱的地方。常见错误是把探索率衰减写在回合内或者更新 Q 值时用错下一状态。下面是一个标准写法class QLearning: def __init__(self, num_states, num_actions, alpha0.1, gamma0.95, epsilon1.0): self.Q np.zeros((num_states, num_actions)) self.alpha alpha self.gamma gamma self.epsilon epsilon def choose_action(self, state): if random.random() self.epsilon: return random.randint(0, self.Q.shape[1] - 1) return int(np.argmax(self.Q[state])) def update(self, s, a, r, s_next, done): best_next 0.0 if done else np.max(self.Q[s_next]) td_target r self.gamma * best_next td_error td_target - self.Q[s, a] self.Q[s, a] self.alpha * td_error def train(env, agent, episodes500, epsilon_decay0.995, min_epsilon0.01): rewards [] for ep in range(episodes): s env.reset() total_reward 0.0 done False while not done: a agent.choose_action(s) s_next, r, done env.step(a) agent.update(s, a, r, s_next, done) s s_next total_reward r agent.epsilon max(min_epsilon, agent.epsilon * epsilon_decay) rewards.append(total_reward) return rewards逻辑说明choose_action用 ε-贪婪策略训练初期大量随机探索后期逐渐利用。update里done为真时下一状态价值为 0这是终止状态的处理漏掉会导致 Q 值虚高。epsilon_decay每回合乘一次而不是每步乘否则探索衰减太快智能体来不及学到东西。参数上alpha0.1是学习率太大震荡太小收敛慢gamma0.95是折扣因子越接近 1 越看重长期回报epsilon从 1.0 降到 0.01保证最终策略稳定。跑完 500 回合把rewards每 50 回合取平均应该能看到从负几十涨到接近 10。如果没涨先检查奖励设计和done处理这两处出问题最多。3. 让结果直观可视化、Q 表打印和策略回放3.1 训练曲线用移动平均看趋势别被单回合噪声骗了单回合奖励波动很大直接画原始曲线往往像心电图。常见做法是每 20 或 50 回合取移动平均再画一条平滑线。下面这段代码不依赖 matplotlib 也能跑先把数据算出来def moving_average(data, window50): if len(data) window: return [] return [np.mean(data[i:iwindow]) for i in range(len(data) - window 1)] rewards train(env, agent, episodes500) ma moving_average(rewards, window50) print(前50回合平均:, np.mean(rewards[:50])) print(后50回合平均:, np.mean(rewards[-50:]))如果后 50 回合平均明显高于前 50说明学习有效。如果两者差不多可能是探索率没降下来或者学习率太小。我一般会先看这两个数再决定要不要调参而不是一上来就画图。3.2 Q 表打印把“智能体学到了什么”变成可读的表格Q 表是离散状态下的最大优势——你可以直接看。把每个状态的最优动作打印成箭头就能看到策略长什么样def print_policy(agent, env): arrows [↑, →, ↓, ←] for r in range(env.size): row [] for c in range(env.size): s r * env.size c if (r, c) env.goal: row.append( G ) else: a int(np.argmax(agent.Q[s])) row.append(f {arrows[a]} ) print(.join(row)) print_policy(agent, env)理想输出是从起点到终点的一条箭头路径中间没有反复绕圈。如果看到箭头指向墙或原地打转说明训练不够或奖励设计有问题。这个打印比任何曲线都直观初学者能立刻判断“学没学会”。3.3 策略回放用确定性策略跑一遍看步数和路径训练时用 ε-贪婪评估时要关掉探索。写一个evaluate函数固定epsilon0跑 10 次取平均步数def evaluate(env, agent, episodes10): steps_list [] for _ in range(episodes): s env.reset() done False steps 0 while not done: a int(np.argmax(agent.Q[s])) s, r, done env.step(a) steps 1 steps_list.append(steps) return np.mean(steps_list) print(平均步数:, evaluate(env, agent))5×5 网格最短路径是 8 步从 (0,0) 到 (4,4) 需要 4 下 4 右。如果平均步数在 8 到 12 之间说明策略接近最优如果超过 20说明还有绕路。这个指标比奖励更直接因为奖励受每步 -0.1 影响步数才是真实效率。4. 方法可替换从 Q 表到 SARSA、Double Q 和 DQN 的改法4.1 替换成 SARSA只改一行更新目标SARSA 是 on-policy 算法更新时用实际选择的下一动作而不是最大 Q 值。把update改成def update_sarsa(self, s, a, r, s_next, a_next, done): next_q 0.0 if done else self.Q[s_next, a_next] td_target r self.gamma * next_q self.Q[s, a] self.alpha * (td_target - self.Q[s, a])训练循环里需要先选a_next再更新。这个改动很小但行为差异明显SARSA 在悬崖行走任务里更保守因为它把探索风险算进去了。想对比两种算法只需要在训练脚本里换一个agent类环境代码不动。4.2 替换成 Double Q-learning解决最大化偏差Q-learning 的max操作会系统性高估 Q 值尤其在噪声环境下。Double Q-learning 用两张 Q 表一张选动作一张评估class DoubleQLearning: def __init__(self, num_states, num_actions, alpha0.1, gamma0.95, epsilon1.0): self.Q1 np.zeros((num_states, num_actions)) self.Q2 np.zeros((num_states, num_actions)) self.alpha alpha self.gamma gamma self.epsilon epsilon def choose_action(self, state): if random.random() self.epsilon: return random.randint(0, self.Q1.shape[1] - 1) return int(np.argmax(self.Q1[state] self.Q2[state])) def update(self, s, a, r, s_next, done): if random.random() 0.5: best_a int(np.argmax(self.Q1[s_next])) target r (0 if done else self.gamma * self.Q2[s_next, best_a]) self.Q1[s, a] self.alpha * (target - self.Q1[s, a]) else: best_a int(np.argmax(self.Q2[s_next])) target r (0 if done else self.gamma * self.Q1[s_next, best_a]) self.Q2[s, a] self.alpha * (target - self.Q2[s, a])这个类接口和QLearning完全一致训练循环不用改。这就是“方法可替换”的价值算法之间可以横向对比环境和其他代码保持稳定。4.3 替换成 DQN 的思路把 Q 表换成网络接口不变如果状态空间变大Q 表存不下就需要函数逼近。DQN 用神经网络输出每个动作的 Q 值核心改动是choose_action里用网络前向传播update里用均方误差反向传播再加经验回放和目标网络。但对外接口仍然是choose_action(state)和update(s,a,r,s_next,done)。这意味着你可以在同一个训练循环里把QLearning换成DQN环境代码一行不改。我一般会先在小 GridWorld 上验证 DQN 实现是否正确再迁移到更大环境。如果 DQN 在 5×5 网格上都学不会大概率是网络结构或超参问题而不是环境太难。5. 避坑与排查训练不收敛时先看这五处5.1 奖励一直不涨Q 值全是零现象跑了几百回合rewards始终在初始值附近打印 Q 表发现大部分是 0。原因通常是done处理错误导致终止状态的价值被反复传播但目标值不对或者学习率太小更新量被淹没。解决先检查update里done为真时best_next是否置 0再打印几步的td_error如果一直是 0说明s和s_next传错了。5.2 策略在终点附近来回走不进入终止状态现象打印策略看到箭头在终点旁边绕圈。原因通常是到达终点的奖励不够大或者每步负奖励累积起来让智能体觉得“多走几步也无所谓”。解决把终点奖励提高到 10 以上每步惩罚降到 -0.01 到 -0.1 之间确保最短路径的累计回报明显更高。5.3 探索率衰减太快后期完全不探索现象前 100 回合奖励涨得不错后面突然卡住。原因epsilon_decay设得太小比如 0.9几十回合就降到 0.01智能体还没覆盖所有状态-动作对。解决把衰减设为 0.995 或 0.999或者用线性衰减保证前 30% 回合有足够探索。我一般会打印每 50 回合的epsilon确认它降得是否合理。5.4 换算法后结果差异巨大怀疑环境有问题现象Q-learning 能学会换成 SARSA 后完全学不会。原因SARSA 是在策略算法如果choose_action的探索率很高它会把探索带来的负奖励学进去导致策略保守甚至不敢走。解决对比算法时确保评估阶段都用epsilon0训练阶段的探索率设置一致。如果 SARSA 在悬崖任务里表现更保守那是正常特性不是 bug。5.5 随机种子不同结果忽好忽坏现象同样的代码有时收敛有时不收敛。原因网格世界虽然简单但随机初始化和探索路径会导致不同局部最优。解决固定随机种子跑 5 个种子取平均再看趋势。如果 5 个种子里有 3 个以上收敛说明算法没问题只是方差大。我一般会在训练脚本开头加random.seed(42)和np.random.seed(42)方便复现。6. 进阶技巧用参数扫描和策略热力图把实例变成实验平台当你已经能跑通基础版本下一步不是换更炫的算法而是把这个实例变成可做对比实验的平台。我常用的做法是参数扫描固定环境遍历alpha、gamma、epsilon_decay的组合每个组合跑 5 个种子记录后 50 回合平均奖励。下面是一个简化版扫描脚本def run_experiment(alpha, gamma, decay, seeds5): scores [] for seed in range(seeds): random.seed(seed) np.random.seed(seed) env GridWorld(size5) agent QLearning(env.num_states(), env.num_actions(), alphaalpha, gammagamma, epsilon1.0) rewards train(env, agent, episodes300, epsilon_decaydecay) scores.append(np.mean(rewards[-50:])) return np.mean(scores), np.std(scores) for alpha in [0.05, 0.1, 0.3]: for gamma in [0.9, 0.95, 0.99]: mean, std run_experiment(alpha, gamma, 0.995) print(falpha{alpha}, gamma{gamma}, mean{mean:.2f}, std{std:.2f})这个脚本能帮你快速看出哪些参数组合稳定。比如alpha0.3往往方差大gamma0.99在 5×5 网格上可能过慢因为折扣因子太高会让智能体过度看重远期奖励。扫描结果用表格整理比单次跑更有说服力。另一个技巧是画策略热力图把每个状态的 Q 最大值当颜色深浅箭头当动作方向。这样能一眼看出哪些区域价值高、哪些区域智能体还没学好。实现上只需要把print_policy里的箭头换成颜色块或者用matplotlib.imshow画 Q 值矩阵。热力图配合移动平均曲线基本能覆盖“学没学会、哪里没学会、为什么没学会”三个问题。我自己的习惯是每换一个算法或一组参数先跑 3 个种子看趋势再用参数扫描确认稳定性最后用策略热力图检查边界情况。这套流程跑下来一个强化学习实例就不只是“能运行”而是能支撑小规模对比实验。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

语音合成 Mod 实战:拆包、接入、调参与避坑

语音合成 Mod 实战:拆包、接入、调参与避坑

简介:面向 FreeSWITCH 语音业务开发者和运维人员,这是一份基于阿里云 TTS 的扩展放音模块,可在拨号方案的播放动作中直接引用合成语音,替代传统音频文件,满足 IVR 欢迎语、通知播报等场景的动态播报需求。模块内置两种…

2026/10/16 15:30:50 阅读更多 →
Iris-3B图像修复与4倍超分教程:一步生成式修复模糊退化老照片

Iris-3B图像修复与4倍超分教程:一步生成式修复模糊退化老照片

人工智能大模型基础模型媒体生成计算机视觉 【免费下载链接】iris-3b 项目地址: https://ai.gitcode.com/hf_mirrors/speridlabs/iris-3b 点击查看 免费下载 Iris-3B 是一款 30 亿参数的像素空间生成模型,它的超分辨支模型支持一步图像修复与 4 倍超分&…

2026/10/16 15:29:49 阅读更多 →
MATLAB块匹配全景拼接:弱纹理图像配准的实用方案

MATLAB块匹配全景拼接:弱纹理图像配准的实用方案

简介:这是一套基于MATLAB平台的计算机视觉与深度学习实战代码,聚焦块匹配算法实现全景图像拼接,适合想掌握图像特征提取、几何变换与多图融合的开发者及学习者。资源共27个文件,内有11个m脚本、13幅JPEG/JPG图像、2个BMP位图和1个…

2026/10/16 15:29:49 阅读更多 →

最新新闻

零基础学漏洞挖掘:从靶场练手到报告撰写的避坑指南

零基础学漏洞挖掘:从靶场练手到报告撰写的避坑指南

零基础写“挖洞”这个词,这几年在网安圈已经快被说烂了。但说实话,我见过太多人把这个词理解偏了。大多数新手一上来就想要平台清单,想直接拿别人的业务练手,结果要么学不进去,要么在违法的边缘反复横跳。我做了十多年…

2026/10/16 16:00:29 阅读更多 →
从APK反编译到Getshell:移动端渗透测试实战链路解析

从APK反编译到Getshell:移动端渗透测试实战链路解析

护网红队碰到移动端目标,最常用的思路就是从 APK 反编译审计开始,一路摸到服务端接口,最后 Getshell。这个过程看着玄乎,其实链路非常清晰:客户端代码里藏着服务端地址、接口参数、加密逻辑、密钥信息,甚至…

2026/10/16 16:00:29 阅读更多 →
Frenet坐标系下的无人车动作规划:多项式生成与工程避坑

Frenet坐标系下的无人车动作规划:多项式生成与工程避坑

简介:这是一份基于Frenet优化轨迹的无人车动作规划实战代码包,面向自动驾驶、辅助驾驶领域的算法学习者和初中级开发者,适用于高速场景下的轨迹规划与动作决策研究。资源采用Python实现,涵盖三次样条插值、Frenet坐标系下轨迹生成…

2026/10/16 16:00:29 阅读更多 →
数据流设计:系统稳定性的隐形骨架与三层建模方法

数据流设计:系统稳定性的隐形骨架与三层建模方法

1. 项目概述:为什么“数据流”是系统设计的隐形骨架“第三章(1):数据流”这个标题乍看像教科书里的章节编号,但如果你在实际项目里反复被“数据对不上”“前端显示空白”“后端日志里找不到请求痕迹”这类问题卡住超过…

2026/10/16 16:00:29 阅读更多 →
Flume快速入门:安装部署与监控端口实例详解

Flume快速入门:安装部署与监控端口实例详解

还记得我第一次学Flume的时候,最头疼的不是概念,而是“装环境、跑起来”这一步。网上教程东一句西一句,版本选的也不对,光一个安装就折腾到半夜。等我在某公司正式接触日志采集后,才发现原来Flume的安装和第一个实战实…

2026/10/16 16:00:29 阅读更多 →
STM32F103智能门锁实战:RC522刷卡+舵机开锁+Flash存储

STM32F103智能门锁实战:RC522刷卡+舵机开锁+Flash存储

简介:这是一套面向嵌入式初学者与课程设计需求的STM32F1智能门锁完整项目资料,基于STM32F103C8T6主控,整合RFID读卡器、OLED屏幕、按键、蓝牙通信、蜂鸣器与舵机等模块,配套上位机与下位机双向交互方案,适合作为单片机…

2026/10/16 15:59:28 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →