ML-For-Beginners 强化学习入门实战:用 Q-Learning 训练彼得穿越迷宫吃苹果 ML-For-Beginners 强化学习入门实战用 Q-Learning 训练彼得穿越迷宫吃苹果【免费下载链接】ML-For-Beginners12 weeks, 26 lessons, 52 quizzes, classic Machine Learning for all项目地址: https://gitcode.com/GitHub_Trending/ml/ML-For-Beginners本节课程出自开源机器学习课程 ML-For-Beginners 第 8 章强化学习Reinforcement Learning。课程以俄罗斯作曲家普罗科菲耶夫的交响童话《彼得与狼》为背景用一个8x8的方格棋盘模拟彼得的世界他要学会在避开狼与水域的前提下找到苹果。本文将完整复现这套从随机游走基线到Q-Learning 表格训练再到策略评估的完整流程并结合仓库源码深入剖析环境实现、奖励函数、贝尔曼方程与超参数的作用读完即可亲手在 Jupyter Notebook 中跑通并理解第一个强化学习算法。课程背景与前置准备强化学习RL与监督学习、无监督学习并称为三大机器学习范式监督学习依赖带标签数据集如本课程前面的 分类 与 回归无监督学习的代表是 聚类而强化学习则不依赖任何预标注数据——智能体agent在某个环境environment中反复做实验通过执行动作、观察奖励逐渐学习到最优行为策略。本课需要 Python 环境可直接在本地或云端运行 8-Reinforcement/1-QLearning/notebook.ipynb。注意如果从云端打开还需要把 rlboard.py 下载到与 notebook 相同的目录因为棋盘环境代码都在这个独立模块中。强化学习涉及三个核心概念智能体agent需要学习决策的主体本课中就是彼得状态states环境在某一时刻的全部信息本课中即棋盘布局 彼得当前位置每个状态下的动作集合a set of actions per state在指定状态下执行动作后智能体会获得对应的奖励reward。想象超级马里奥马里奥站在悬崖边上、头顶有一枚金币——这就是一个状态向右走一步动作会掉下悬崖得到很低的分数而按下跳跃键动作则能得分并存活这是一个正面的结果会带来正的数值奖励。借助强化学习和一个模拟器游戏我们可以教会智能体活得更久、得更多分的策略。环境彼得与狼的 8x8 棋盘为简化问题把彼得的世界看成一块width x height的方格棋盘。棋盘中的每个格子可以是五种类型之一地面ground彼得和其他生物可以行走水域water显然不能行走树或草地tree/grass可以休息的地方苹果apple彼得想找到用来充饥的东西狼wolf危险应当避开。上述环境逻辑封装在 rlboard.py 的Board类中其中Cell内部类用整数常量定义了五种格子empty 0、water 1、wolf 2、tree 3、apple 4见 rlboard.py 第 44-49 行。Board还提供了随机生成棋盘的方法randomize(seed...)rlboard.py 第 67-97 行以及判断格子内容at()、校验坐标合法性is_valid()、计算移动后的位置move_pos()等底层工具方法。虽然理解这些底层实现并不影响掌握强化学习概念但为了让代码可复现课程先把Board导入并生成一张种子为 13 的随机棋盘代码块 1from rlboard import * width, height 8,8 m Board(width,height) m.randomize(seed13) m.plot()m.plot()会输出类似上面插图的棋盘画面蓝色水域、绿色树丛、红色苹果、狼头图标以及代表彼得的小人。动作与策略Actions and Policy彼得的任务是找到苹果同时避开狼和其他障碍。为此他在任意位置都可以从四个动作中选一个上U、下D、左L、右R。把动作定义成字典映射到对应的坐标增量——例如向右移动R对应坐标变化对(1,0)代码块 2actions { U : (0,-1), D : (0,1), L : (-1,0), R : (1,0) } action_idx { a : i for i,a in enumerate(actions.keys()) }由此可总结本场景的策略与目标策略policy定义智能体行为的函数输入任意状态返回一个动作。本例中状态由棋盘及玩家当前位置共同表示目标goal强化学习的最终目标是学到一个足够好的策略来高效解决问题。但在动手之前先实现一个最简单的基线策略——随机游走random walk。基线策略随机游走随机游走策略在每一步都从合法动作中随机挑一个一直走到苹果为止代码块 3def random_policy(m): return random.choice(list(actions)) def walk(m,policy,start_positionNone): n 0 # number of steps # set initial position if start_position: m.human start_position else: m.random_start() while True: if m.at() Board.Cell.apple: return n # success! if m.at() in [Board.Cell.wolf, Board.Cell.water]: return -1 # eaten by wolf or drowned while True: a actions[policy(m)] new_pos m.move_pos(m.human,a) if m.is_valid(new_pos) and m.at(new_pos)!Board.Cell.water: m.move(a) # do the actual move break n1 walk(m,random_policy)每次调用walk返回路径长度不同运行之间结果波动很大。把随机游走重复 100 次并统计代码块 4def print_statistics(policy): s,w,n 0,0,0 for _ in range(100): z walk(m,policy) if z0: w1 else: s z n 1 print(fAverage path length {s/n}, eaten by wolf: {w} times) print_statistics(random_policy)观察输出会发现平均路径长度约 30–40 步——这相当多因为棋盘上到最近苹果的平均距离其实只有 5–6 步。也就是说纯随机乱走不仅低效还频繁被狼吃掉或掉进水里。奖励函数Reward Function要让策略更聪明必须先量化哪些动作比别的动作更好这正是奖励函数的职责它对每个状态返回一个分数数值越高代表越优代码块 5move_reward -0.1 goal_reward 10 end_reward -10 def reward(m,posNone): pos pos or m.human if not m.is_valid(pos): return end_reward x m.at(pos) if xBoard.Cell.water or x Board.Cell.wolf: return end_reward if xBoard.Cell.apple: return goal_reward return move_reward奖励设计的三档语义非常清晰普通移动代价-0.1用来催促彼得少绕路到达苹果给大额正奖励10而走出棋盘、踩水或撞上狼则给-10的终止性惩罚。这套奖励函数与 rlboard.py 中move的允许越界逻辑配合——训练时彼得走出棋盘会被当作一次 episode 终止后面训练代码中m.move(dpos, check_correctnessFalse)即利用了这一设计见 rlboard.py 第 111-114 行。奖励函数有一个值得强调的特点大多数情况下只有在游戏结束时我们才获得实质性的大额奖励。因此算法必须记住那些最终导向正奖励的中间步骤并提升其重要性同时抑制导向坏结果的步骤。Q-Table 与 Q-Learning 的直观理解本课要讲的算法叫Q-Learning。在该算法中策略由一个函数或数据结构定义称为Q-Table它记录在给定状态下执行每个动作的好坏程度。之所以叫表是因为它常被表示为表格或多维数组。由于棋盘尺寸是width x heightQ-Table 可以用形状为width x height x len(actions)的 numpy 数组表示代码块 6Q np.ones((width,height,len(actions)),dtypenp.float)*1.0/len(actions)注意我们用相等值本例为1/4 0.25初始化所有 Q 值——这正好对应随机游走策略每个状态下的所有动作同等好。把 Q-Table 传给plot函数即可在棋盘上可视化m.plot(Q)。棋盘每个格子中央的箭头表示该格偏好的移动方向因为初始所有方向等概率格子里显示的是一个圆点。接下来要做的就是运行大量模拟去探索环境、让 Q-Table 学会更好的数值分布从而让彼得更快找到苹果。Q-Learning 的本质贝尔曼方程一旦开始移动每个动作都对应一个即时奖励理论上可以总是选择即时奖励最高的动作。然而在绝大多数状态里走一步并不会直接抵达苹果因此我们无法仅凭当下就判断哪个方向更好。请记住重要的不是即时结果而是整场模拟结束时获得的最终结果。为了处理这种延迟奖励需要借助动态规划的思想把问题递归地思考假设当前处于状态s希望移动到下一个状态s。执行动作后立刻得到由奖励函数定义的即时奖励r(s,a)外加某种未来奖励。如果 Q-Table 已能正确反映每个动作的吸引力那么到达s后我们会选择使Q(s,a)最大的动作a。因此在状态s能拿到的最佳未来奖励就是maxaQ(s,a)对所有s处的可能动作取最大。于是得到在状态s下执行动作a的 Q-Table 更新公式——贝尔曼方程Q(s,a) ← (1-α)Q(s,a) α(r γ·max_{a}Q(s,a))其中 γ 称为折扣因子discount factor它决定了你更偏好当前奖励还是未来奖励。学习算法的伪代码基于贝尔曼方程可以把整个学习算法写成伪代码用相同数值初始化所有状态-动作对的 Q-Table设置学习率 α ← 1大量重复模拟从随机位置出发循环执行在状态s选择动作a执行动作移动到新状态s若触发游戏结束条件或累计奖励过小退出模拟计算新状态下的奖励r按贝尔曼方程更新 Q 函数Q(s,a)←(1-α)Q(s,a) α(r γ·maxaQ(s,a))s←s更新累计奖励并减小 α。探索与利用Exploit vs. Explore上面伪代码的第 2.1 步没有说明具体如何选择动作。如果完全随机选就是在**探索explore环境——彼得会频繁死亡也会走到平时不会去的地方另一种极端是利用exploit**已知的 Q-Table在状态s总选 Q 值最高的动作——但这样会错过对其他状态的探索很可能找不到最优解。因此最佳方案是在探索与利用之间取得平衡按与 Q-Table 中数值成比例的概率选择动作。一开始所有 Q 值相同等价于随机选择随着对环境了解加深彼得会越来越倾向于沿最优路径前进同时仍有小概率偶尔选一条未探索的岔路。Python 实现与 5000 轮训练动手实现学习算法前还需要一个把 Q-Table 中的任意数值转换为对应动作概率向量的工具函数probs()代码块 7def probs(v,eps1e-4): v v-v.min()eps v v/v.sum() return v这里往向量里加了一个很小的eps是为了避免初始状态下各分量完全相等、做归一化时除以 0。接下来运行 5000 次实验也叫epochs完成学习代码块 8lpath [] for epoch in range(5000): # Pick initial point m.random_start() # Start travelling n0 cum_reward 0 while True: x,y m.human v probs(Q[x,y]) a random.choices(list(actions),weightsv)[0] dpos actions[a] m.move(dpos,check_correctnessFalse) # we allow player to move outside the board, which terminates episode r reward(m) cum_reward r if rend_reward or cum_reward -1000: lpath.append(n) break alpha np.exp(-n / 10e5) gamma 0.5 ai action_idx[a] Q[x,y,ai] (1 - alpha) * Q[x,y,ai] alpha * (r gamma * Q[xdpos[0], ydpos[1]].max()) n1这段实现有几个关键设计点值得结合源码与参数逐一说明动作选择遵循探索-利用平衡random.choices(list(actions), weightsv)让动作按probs(Q[x,y])生成的概率被选中而不是取最大值的贪心策略允许越界以终止 episodem.move(dpos, check_correctnessFalse)允许彼得走出棋盘走出后reward返回end_reward从而结束这一局——这是利用 rlboard.py 中move的check_correctness参数rlboard.py 第 111-114 行学习率 α 随步数衰减alpha np.exp(-n / 10e5)随 episode 内步数 n 增大而缓慢减小让后期更新更稳定折扣因子gamma 0.5在本课示例中表示对当前奖励与未来奖励各取一半的折中贝尔曼更新Q[x,y,ai] (1-alpha)*Q[x,y,ai] alpha*(r gamma*Q[xdpos[0], ydpos[1]].max())正是前面公式的直接落地其中Q[xdpos[0], ydpos[1]].max()即max_{a}Q(s,a)终止条件进入终局r end_reward或累计奖励过低cum_reward -1000说明长期无效游荡都会结束当前 episode并把步数n记入lpath供后续分析学习过程使用。算法执行完毕后Q-Table 已被更新为能反映各状态下每个动作吸引力的数值。可以把它画到棋盘上在每个格子画一个指向期望移动方向的矢量为简化用圆点代替箭头头部——此时格子中的箭头已经不再是均匀的圆点而是清晰指向通往苹果的路径说明彼得已学到一张有效的导航地图。检查策略严格贪心策略可能卡死由于 Q-Table 记录了每个状态-动作对的吸引力用它定义高效导航非常容易。最简单的方式是直接选择 Q-Table 中值最高的动作代码块 9def qpolicy_strict(m): x,y m.human v probs(Q[x,y]) a list(actions)[np.argmax(v)] return a walk(m,qpolicy_strict)如果把上面代码多运行几次可能会发现它有时挂起需要在 notebook 里按 STOP 中断。原因在于可能出现两个状态在最优 Q 值上互相指向对方的死循环智能体便在这两个状态之间无限往返。针对这个现象课程留了两个挑战任务任务 1修改walk函数把路径的最大长度限制在某个步数比如 100观察上面代码是否会时不时返回这个上限值。任务 2修改walk函数使其不再返回之前到过的位置。这能避免walk无限循环但智能体仍可能被困在一个走不出去的角落。更好的导航策略带随机性的概率策略训练时用的导航策略比严格贪心更好因为它同时兼顾了利用与探索。该策略按与 Q-Table 值成比例的概率选择每个动作代码块 10def qpolicy(m): x,y m.human v probs(Q[x,y]) a random.choices(list(actions),weightsv)[0] return a print_statistics(qpolicy)这种策略仍可能让彼得回到已探索过的位置但从运行结果看print_statistics会跑 100 次模拟它得到的平均路径非常短——大约在3–6 步之间远优于随机游走的 30–40 步。这说明经过 5000 轮学习彼得已经能高效抵达苹果。观察学习过程平均路径长度的三个阶段学习过程本质上是探索与利用的持续博弈。除了最终效果变好更值得观察的是平均路径长度在学习过程中的演变。把每个 epoch 结束时的路径长度lpath画成曲线可以总结出三条规律平均路径长度先上升一开始对环境一无所知彼得容易陷进水域或撞上狼等糟糕状态。随着学到更多知识并开始利用他能更久地探索环境但此时仍不很清楚苹果在哪里随着学习深入路径变短一旦学得足够多彼得更容易达成目标路径长度开始下降。但因为始终保留探索空间他常常偏离最佳路径去尝试新选项导致路径仍长于最优解路径长度会突然跳增曲线上能看到某些点长度突然增大。这体现了过程的随机性——某些时刻 Q-Table 系数被新值覆盖而被搞坏。理想情况下应通过降低学习率来抑制例如训练后期只对 Q 值做小幅调整。总体而言学习过程的成功与质量高度依赖这样一些参数学习率learning rate、学习率衰减learning rate decay与折扣因子discount factor。这些参数通常称为超参数hyperparameters以区别于训练中真正优化的参数例如 Q-Table 的系数。寻找最优超参数取值的过程叫超参数优化hyperparameter optimization值得作为独立专题深入学习。课后实战让世界更真实本课作业 8-Reinforcement/1-QLearning/assignment.md 提出一个更真实的世界原场景中彼得几乎不会累、不会饿这显然不现实。请在 notebook.ipynb 的基础上实现下列规则重新定义奖励函数并重新训练每次移动都会让彼得损失能量energy、增加疲劳fatigue吃苹果可以补充能量走到树或草地绿色格子上休息可以消除疲劳彼得需要找到并战胜狼战胜狼需要能量与疲劳达到一定水平否则会输掉战斗。提示新世界中状态更复杂——除了位置还包括疲劳与能量水平。可以把状态表示为元组(Board, energy, fatigue)也可以定义一个可能继承自Board的状态类甚至可以修改 rlboard.py 中的Board类本身。请注意保留随机游走相关代码以便最后把新算法的胜负统计与随机游走基线做对比。由于战胜狼是小概率事件你可能需要显著增加 epoch 数量并调整超参数训练时间会比本课示例长得多。评分标准关注三点是否完整给出新世界规则定义、Q-Learning 实现与文字说明相比随机游走是否显著提升胜率以及代码结构与文档是否清晰。可以参考同目录下 solution 中的参考答案做对照也可以继续学习本模块第 2 课 2-Gym在 Gym 仿真环境中使用更通用的强化学习接口。小结通过彼得与狼的棋盘世界你已经亲手完成了一个最小但完整的 Q-Learning 闭环从环境建模rlboard.py 中的Board与Cell出发依次构建奖励函数、初始化 Q-Table、运行 5000 轮基于贝尔曼方程的迭代更新最终用概率式策略把平均路径从随机游走的 30–40 步压缩到 3–6 步。与此同时你也理解了探索 vs. 利用延迟奖励学习率衰减折扣因子等强化学习的核心思想——它们不仅是理解 Q-Learning 的关键也是后续学习深度强化学习算法的基础。【免费下载链接】ML-For-Beginners12 weeks, 26 lessons, 52 quizzes, classic Machine Learning for all项目地址: https://gitcode.com/GitHub_Trending/ml/ML-For-Beginners创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考