简介:这是一份面向机器学习初学者与高校课程学习者的强化学习专业课件,以单份PPT形式系统梳理强化学习的基础理论框架,适合课堂讲授、自学入门与考前复盘使用。课件共25页,从强化学习概述切入,依次讲解智能体与环境的交互基本框架、试错搜索与延迟回报两大特征,以及策略、奖赏函数、值函数和环境模型四个核心组成要素,并说明各自的定义与作用。后半部分结合马尔可夫决策过程的五元组(状态集、动作集、转移概率、报酬函数与准则函数),讲清状态转移与累积折扣奖赏的期望计算,配合公式与结构图呈现,便于理解最优策略的求解思路。压缩包内共1个文件,为pptx格式,体积约179KB,轻量易传输,可直接用于课件展示与二次编辑。目前已有779人学习,适合希望用较短时间建立强化学习整体认知的读者。
1. 讲强化学习最容易翻车的地方,不是内容太少
给人讲强化学习,翻车点往往不是内容太少,而是从头到尾都在推公式。30 页课件里塞 20 页贝尔曼方程和伪代码,台下的人第 10 分钟就低头看手机;反过来全程只放演示动画,讲完大家只记得小车会动,说不清 Q 值到底在更新什么。一份能用的「强化学习PPT课件.pptx」,要同时照顾这两类人。
所以这份课件要卡在一个夹缝里:既让强化学习入门的人看懂 MDP、回报、状态价值函数这条主线,又要留出能当场跑通的 Q-learning 代码和训练曲线,让做过强化学习实战的人看到 alpha、gamma、epsilon 这几个参数究竟怎么改变结果。公式不是不能放,而是每一页公式后面必须跟一页"跑出来长什么样"。
适合三类人:要开组内技术分享的算法工程师、课程作业要交课件的高年级学生、带新人做 python 强化学习的技术负责人。后面的顺序是课件骨架怎么批量生成、Q-learning 最小案例怎么跑通并塞进页面、DQN 与基于模型强化学习以及 IQL 离线强化学习的边界怎么讲、最后是出图和校版的可复用技巧。
2. 用 python-pptx 把强化学习课件的骨架和公式页批量生成
2.1 先定叙事线:30 页课件的章节配比
强化学习课件最常见的配比错误是"算法讲解占 80%,场景和失败案例占 0%"。听众记住的往往是失败的曲线,不是收敛的曲线。下面这套配比是按 90 分钟分享、35 页左右设计的,可以直接抄。
| 章节 | 页数 | 必须出现在页面上的东西 |
|---|---|---|
| 问题定义与 MDP 五元组 | 6 | 一张状态转移示意图、一个真实场景(库存/推荐/机械臂) |
| 回报、折扣与状态价值函数 | 5 | 同一条轨迹在不同 gamma 下的回报数值 |
| 表格型方法:Q-learning | 8 | 可运行代码、收敛曲线、V(s) 热力图 |
| 深度强化学习:DQN 与改进 | 8 | 经验回放示意、目标网络更新频率对比表 |
| 基于模型、离线与场景扩展 | 6 | Dyna 流程、IQL 适用边界表 |
| 坑与调试清单 | 2 | 训练不收敛的 5 个常见原因 |
叙事线只有一条:先让人相信"不学模型也能学策略",再说明"状态变多以后为什么必须换成神经网络",最后补"没有交互机会时怎么办"。这条线断了,DQN 那几页就会变成孤立的网络结构图。
2.2 环境准备:三条命令装齐依赖
pip install python-pptx matplotlib numpy # python-pptx 负责生成 pptx;matplotlib 出图;numpy 跑 Q-learning # 不装 gym 也能完成全部示例,避免环境版本差异挡住听众用代码生成而不是手动排版,理由很实际:强化学习的课件每隔几个月就要换新算法、换新曲线,手动挪几十个文本框的成本远高于改一行列表再重跑。把章节名、页数、图片路径都放进 Python 列表里,改一处、重跑一次,整份课件同步更新。
提示:中文字体缺失时 python-pptx 生成的页面里中文会变成方框,生成前先确认机器上有可用中文字体,并在 matplotlib 里显式指定字体名。
2.3 最小可跑脚本:生成标题页和一页公式
from pptx import Presentation from pptx.util import Cm, Pt prs = Presentation() prs.slide_width = Cm(33.87) # 16:9 宽屏宽度 prs.slide_height = Cm(19.05) # 16:9 宽屏高度 slide = prs.slides.add_slide(prs.slide_layouts[1]) # 1 = 标题+内容版式 slide.shapes.title.text = "Q-learning 的更新式" tf = slide.placeholders[1].text_frame tf.text = "Q(s,a) <- Q(s,a) + alpha * [r + gamma * max Q(s',a') - Q(s,a)]" tf.add_paragraph().text = "alpha:学习率 gamma:折扣因子 r:即时奖励" for p in tf.paragraphs: for run in p.runs: run.font.size = Pt(18) # 公式页 18pt 起步,投影仪上才看得清 prs.save("rl_course.pptx")逻辑说明:slide_width、slide_height用厘米控制画布尺寸,16:9 的 33.87×19.05 是 Office 默认的宽屏值,不设这两个参数生成的是老式 4:3,投影后会留黑边。slide_layouts[1]取的是母版里第二个版式,通常带标题占位符和内容占位符,用占位符而不是手画文本框,好处是之后改母版字号能一次性生效。tf.add_paragraph()每调一次加一行,公式和参数说明分两行放,别硬挤在一个段落里。
参数怎么调:公式字体 18~20pt,正文不低于 16pt;一行超过 24 个汉字就要换行,否则右侧会被裁掉。这些数值不是审美偏好,是后排能不能看清的问题。
2.4 用循环批量生成章节页,保持标题层级一致
sections = [ ("01", "从马尔可夫决策过程说起"), ("02", "Q-learning:不学模型也能收敛"), ("03", "DQN:把 Q 表换成神经网络"), ("04", "扩展:基于模型、离线与多智能体场景"), ] for num, title in sections: s = prs.slides.add_slide(prs.slide_layouts[0]) # 0 = 只有标题的版式 s.shapes.title.text = f"{num} {title}" s.shapes.title.text_frame.paragraphs[0].runs[0].font.size = Pt(36) prs.save("rl_course.pptx")循环里用slide_layouts[0]生成过渡页,只放一个大标题。这一步的价值在于统一:如果每页都手动调字号,最后一定会出现 34pt 和 36pt 混在一起的情况,台下看不出来但投影后标题基线会跳动。把章节信息抽成sections列表后,内容顺序的调整就是列表元素的移动,重跑一次页码自动重排。
注意:python-pptx 生成的是静态页面,动画和切换效果需要手动在 Office 里补。把动画留给"轨迹逐步展开"这种必须分步的页面,其他页别加。
3. Q-learning 最小跑通案例:把代码、曲线和状态价值函数放进页面
3.1 为什么课件示例选 5×5 网格而不是 CartPole
给听众的第一个可运行例子,要满足三个条件:跑完不超过 10 秒、每一步都能手算验证、结果能画成一张有信息量的图。CartPole 更适合放在动机页当动画,它的状态是连续四维,讲不清"某个状态的价值是多少"。5×5 网格环境零依赖、可以在白板上把 Q 表抄出来逐格更新,跑完还能画出一张状态价值函数热力图,正好把前面讲的 V(s) 和 Q(s,a) 串起来。
3.2 手写一个零依赖的网格环境
import numpy as np class GridWorld: """5x5 网格:起点(0,0),终点(4,4),(1,1)-(1,3) 是障碍""" def __init__(self, n=5): self.n = n self.start = (0, 0) self.goal = (n - 1, n - 1) self.obstacles = {(1, 1), (1, 2), (1, 3)} self.state = self.start def reset(self): self.state = self.start return self.state def step(self, action): r, c = self.state dr, dc = [(-1, 0), (1, 0), (0, -1), (0, 1)][action] # 上下左右 nr = min(max(r + dr, 0), self.n - 1) nc = min(max(c + dc, 0), self.n - 1) if (nr, nc) in self.obstacles: nr, nc = r, c # 撞障碍原地不动,不额外惩罚 self.state = (nr, nc) done = self.state == self.goal reward = 1.0 if done else -0.01 # 每步小惩罚,逼出最短路径 return self.state, reward, done逻辑说明:reset()把状态拉回起点并返回,方便训练循环里反复调用。step()里的 action 用 0/1/2/3 映射到上下左右,边界用min(max(...))做截断,撞障碍时坐标回滚。奖励设计是这套环境最值得在课件上讲的一行:到达终点给 1.0,其他每步给 -0.01。如果每步给 0,智能体会绕远路,因为绕圈不扣分;把步惩罚调成 -0.1,短路径收敛更快但容易让听众误以为"惩罚越大越好",实际调大以后前期探索会被压制。
如果本地已经装了 gym/gymnasium,要注意两者的reset()返回值不同,一个是状态、一个是(状态, 信息)元组。课件示例干脆不依赖它们,避免在讲台上现场改接口。
3.3 训练循环与状态价值函数:Q 表到 V 表只差一行
def train(env, episodes=2000, alpha=0.1, gamma=0.95, eps=0.1, seed=0): rng = np.random.default_rng(seed) n = env.n Q = np.zeros((n * n, 4)) # 状态用行号*5+列号压成一维 for _ in range(episodes): s = env.reset() done = False while not done: si = s[0] * n + s[1] a = (rng.integers(4) if rng.random() < eps else int(np.argmax(Q[si]))) # epsilon-贪心 s2, r, done = env.step(a) si2 = s2[0] * n + s2[1] target = r + gamma * np.max(Q[si2]) * (0 if done else 1) Q[si, a] += alpha * (target - Q[si, a]) # 时序差分更新 s = s2 return Q Q = train(GridWorld()) V = Q.max(axis=1).reshape(5, 5) # 状态价值函数 = 该状态下最优动作的价值逻辑说明:Q的形状是 (25, 4),把二维坐标压成一维只是为了索引方便,讲课时可以还原成 5×5 的表格。target那一行的(0 if done else 1)是关键,终止状态没有后继,如果不乘这个系数,智能体会把终点之后的价值也算进来,收敛后策略会在终点附近来回打转。alpha是学习率,控制新信息覆盖旧估计的力度;gamma是折扣因子,越接近 1 越看重远期回报;eps是探索概率,固定值便于第一次讲解,实际跑通常会衰减。V = Q.max(axis=1)这一行就是状态价值函数最直白的解释:站在某个格子,选最好的那个动作值多少分。
参数建议:alpha=0.1、gamma=0.95、eps=0.1、2000 轮,在 5×5 网格上基本都能收敛到最短路径。如果 500 轮还没收敛,先检查障碍物是否把起点围死了,再看gamma是不是被写成了 0.5。
3.4 出热力图并插进 PPT 的那几行代码
import matplotlib matplotlib.use("Agg") # 无界面环境也能出图 import matplotlib.pyplot as plt fig, ax = plt.subplots(figsize=(4, 4)) im = ax.imshow(V, cmap="viridis") for i in range(5): for j in range(5): ax.text(j, i, f"{V[i, j]:.2f}", ha="center", color="w", fontsize=9) ax.set_title("state value V(s)") fig.colorbar(im, ax=ax, shrink=0.8) fig.savefig("v_heatmap.png", dpi=200, bbox_inches="tight") slide = prs.slides.add_slide(prs.slide_layouts[5]) # 只有标题的空白版式 slide.shapes.title.text = "训练 2000 轮后的 V(s)" slide.shapes.add_picture("v_heatmap.png", Cm(9), Cm(4), height=Cm(11)) prs.save("rl_course.pptx")逻辑说明:matplotlib.use("Agg")保证在没有显示器的服务器上也能出图,写脚本批量生成课件时必须加。dpi=200是投影清晰度和文件体积的折中,dpi=100放到大屏上数字会发虚。add_picture里同时给坐标和height,宽度按原图比例自动算,不要同时指定宽和高,否则图会被拉变形。每个格子上叠数值文本,是为了让听众能把热力图上的数字和刚才白板上手算的 Q 表对上。
提示:热力图别只放收敛后的结果,把第 10 轮、第 100 轮、第 2000 轮三张并排,比任何文字都能说明"时序差分是在逐步传播奖励"。
4. 从表格型到深度强化学习:DQN、基于模型与 IQL 离线强化学习的边界怎么讲
4.1 DQN 讲三个部件就够:经验回放、目标网络、探索衰减
深度强化学习的页面最容易变成网络结构图堆砌,听众看完记不住任何东西。把这一节压到三个部件,每个部件配一个参数表,效果反而更好。
| 部件 | 解决什么问题 | 常用取值 | 讲课时要强调的一句 |
|---|---|---|---|
| 经验回放缓冲区 | 样本强相关导致训练震荡 | 容量 1e5~1e6,批次 32~256 | 打散相关性,样本能重复用 |
| 目标网络 | 自举目标跟着主网络乱动 | 每 1000~10000 步同步一次 | 目标稳一点,训练才不发散 |
| epsilon 衰减 | 后期还在乱探索 | 1.0 衰减到 0.05,前 10% 步数内完成 | 先探索后利用,不是一直随机 |
三者的顺序建议按"出错现象"来讲:先给听众看一条剧烈震荡的回报曲线,再解释这是样本相关造成的,接着引入经验回放;再给一条缓慢发散的曲线,引出目标网络。用问题带出方法,比按论文顺序讲更容易被记住。
4.2 基于模型强化学习与免模型的分界线画在哪
分界线很干脆:智能体有没有一个能预测"执行动作后下一个状态是什么、奖励是多少"的模型。Q-learning 和 DQN 都是免模型方法,它们不关心环境怎么运转,只关心值函数。基于模型强化学习多了一步:用真实交互数据拟合一个环境模型,然后在模型里做规划,典型代表是 Dyna 架构。
# Dyna-Q 骨架:一次真实交互 + n 次模型模拟 for step in range(max_steps): a = epsilon_greedy(Q, s, eps) s2, r, done = env.step(a) # 真实交互,只有这一次 Q[s, a] += alpha * (r + gamma * np.max(Q[s2]) - Q[s, a]) model[(s, a)] = (r, s2) # 把结果存进模型 for _ in range(n_plan): # 用模型多刷 n 次,不接触真实环境 (ps, pa), (pr, ps2) = random.choice(list(model.items())) Q[ps, pa] += alpha * (pr + gamma * np.max(Q[ps2]) - Q[ps, pa]) s = s2逻辑说明:n_plan是课件里最该标出来的参数,取 0 就退化成普通 Q-learning,取 5~50 能明显减少达到同等回报所需的真实交互次数。代价是当模型本身有偏差时,规划会放大偏差,这一点一定要在页面上写出来,否则听众会以为基于模型的方法全面占优。模型学错方向后,规划次数越多错得越快,这是和免模型方法最本质的差别。
4.3 IQL 离线强化学习:页面上必须写清"没有交互"这件事
IQL 离线强化学习适合放在扩展页,因为它回答的是一个很现实的问题:机器人、医疗、推荐这些场景里,你没法让策略在线试错,只能拿一批已有日志来学。讲这一节时,页面顶部写一句话就够:"训练全程不与环境交互,只用固定数据集。"
需要点明的三个边界:数据分布之外的动作,值函数估计不可信,所以离线算法通常要压制对未知动作的过度乐观;数据集覆盖越窄,学出来的策略越保守;离线训练完的策略在真实环境上线前,最好先在仿真里做一次评估。这三点如果不说,听众会天然把 IQL 当成"不用调参的 DQN"。
4.4 机械臂、图结构与联邦场景:扩展页的正确写法
最后一组扩展页不要贪多,每页只讲"这个场景改变了哪个假设"。
| 场景 | 改变的假设 | 页面上放什么 |
|---|---|---|
| 机械臂强化学习实战 | 动作连续、样本获取昂贵 | 动作空间从离散变连续、示教数据的作用 |
| 图强化学习与深度强化学习 | 状态之间有显式关系结构 | 用图表示状态时消息传递的作用 |
| 联邦深度强化学习 | 数据分散在多方、不能汇总 | 多智能体各自更新、定期同步参数 |
这三页的作用是告诉听众"前面那套框架的适用边界在哪"。每页控制在 3 行文字加一张示意图,讲完立刻切走,不要在扩展页上展开公式。
5. 批量出图与校版:让这份强化学习课件下次还能改
5.1 用一组 rcParams 管住所有训练曲线
课件里通常有五六条曲线,如果每条都用默认样式,字体大小、线宽、配色会各不相同,投影时看起来像拼凑的。把出图配置写在脚本开头,所有图函数共享。
import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt plt.rcParams.update({ "font.size": 12, # 投影后最小的可读字号 "axes.titlesize": 14, "axes.grid": True, "grid.alpha": 0.3, # 网格线压淡,别抢曲线 "figure.dpi": 200, "savefig.bbox": "tight", # 去掉四周空白,插入 PPT 不浪费版面 "font.sans-serif": ["Noto Sans CJK SC", "SimHei"], # 中文字体回退链 "axes.unicode_minus": False # 负号正常显示 }) def plot_curve(xs, ys, title, out): fig, ax = plt.subplots(figsize=(5, 3)) ax.plot(xs, ys, linewidth=1.6, color="#2b6cb0") ax.set_title(title) ax.set_xlabel("episode") ax.set_ylabel("return") fig.savefig(out) plt.close(fig) # 不关图,批量出几十张会吃满内存逻辑说明:font.sans-serif给的是一个回退链,第一个字体不存在就找第二个,这比硬写一个字体名稳。savefig.bbox="tight"会把图片周围的留白裁掉,插入 PPT 时不用担心图片自带一圈白边导致对不齐。plt.close(fig)这一行在批量出图时是必须的,一次生成二十张训练曲线图不关图,内存会一路涨上去。
5.2 开场前五分钟的检查项
| 检查项 | 判断方法 | 常见后果 |
|---|---|---|
| 中文字体是否渲染成方框 | 翻到任意一页正文 | 全场看不懂标题 |
| 公式是否超出页面右边界 | 缩放视图到 100% 逐页翻 | 投影后右侧被裁 |
| 代码字号是否低于 12pt | 站在离屏幕 3 米处试读 | 后排完全看不清 |
| 图片是否为位图放大 | 放大到 400% 看边缘 | 投影后糊成一片 |
| 动画是否只留在轨迹演示页 | 播放一遍从头到尾 | 频繁点击打断节奏 |
最后一页建议不放"谢谢",而是放那张第 10 轮和第 2000 轮的 V(s) 热力图对照,讲完最后一句"这就是时序差分把奖励一格一格传回去的过程",直接进入提问。
本文还有配套的精品资源,点击获取