ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

ML-For-Beginners 强化学习实战:在 Q-Learning 中构建带能量与疲劳机制的“真实世界”并重构奖励函数

2026/9/8 23:11:04 拓冰建站 浏览量
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/1-QLearning一节的课后作业“一个更真实的世界A More Realistic World”为核心讲解如何把“彼得与狼”的简易强化学习环境改造成一个需要消耗能量、累积疲劳、进食与休息、最终击败饿狼的复杂环境。读完本文你将掌握如何把游戏规则翻译成状态与奖励函数、如何在既有 rlboard.py 环境之上扩展实现、如何调整 Q-Learning 的超参数应对“稀有成功事件”以及如何用胜负次数这一指标公平地对比随机游走与学习策略。仓库中英文原版作业位于 8-Reinforcement/1-QLearning/assignment.md本节作业的孟加拉语译本即 translations/bn/8-Reinforcement/1-QLearning/assignment.md。在原始课程环境中Peter 可以“几乎不饿不累”地四处走动目标只是找到苹果并避开狼。这个作业要求我们补上现实约束移动要消耗体能、必须进食与休息并且最终目标从“找到苹果”升级为“找到并击败饿狼”。这也是强化学习从玩具环境走向真实问题的一个典型缩影——状态更复杂、奖励更稀疏、训练更长。一、任务来源与前置知识本作业来自“强化学习与 Q-Learning 入门”一课。该课的完整讲义位于 8-Reinforcement/1-QLearning/README.md配套的可运行入门笔记本为 8-Reinforcement/1-QLearning/notebook.ipynb环境模拟代码则封装在 8-Reinforcement/1-QLearning/rlboard.py。理解作业前需要先掌握三个强化学习基本概念Agent智能体本任务中就是 Peter。State状态智能体当前所处的情况。原世界里基本等价于“Peter 在棋盘上的位置”。Action动作每个状态下可执行的动作集合此处是上、下、左、右四个方向的移动。Reward奖励执行某个动作后环境给出的数值信号它由奖励函数定义。强化学习的目标就是在给定奖励函数下学到最优策略policy让累计奖励最大。原世界是一个width × height的方形棋盘每个格子可以是空地ground、水water不可行走、树或草tree/grass可以休息、苹果applePeter 乐于获取的食物、狼wolf危险源。这些格子类型在 rlboard.py 中以Board.Cell内部类的方式用整数常量定义empty 0、water 1、wolf 2、tree 3、apple 4。运行环境代码的方式很简单先把rlboard.py与笔记本放到同一目录然后创建并随机化棋盘from rlboard import * width, height 8, 8 m Board(width, height) m.randomize(seed13) m.plot()Board.randomize见 rlboard.py支持water_size、num_water、num_wolves、num_trees、num_apples、seed等参数控制棋盘上各类格子的数量与随机种子。二、原世界的“找苹果”基线动作集与随机游走在原世界里Peter 的四个动作用字典映射到坐标增量actions { U : (0,-1), D : (0,1), L : (-1,0), R : (1,0) } action_idx { a : i for i,a in enumerate(actions.keys()) }最简单的基准策略是随机游走random walk每次从合法动作里随机挑一个直到走到苹果成功或踩到狼/水失败。原课程的walk实现大致为def random_policy(m): return random.choice(list(actions)) def walk(m, policy, start_positionNone): n 0 # 步数 if start_position: m.human start_position else: m.random_start() while True: if m.at() Board.Cell.apple: return n # 成功 if m.at() in [Board.Cell.wolf, Board.Cell.water]: return -1 # 被狼吃掉或溺水 # 反复取动作直到落在合法且非水的格子上 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) break n 1walk返回的是路径长度用print_statistics类函数跑 100 次后可以统计出“平均路径长度”与“被狼吃掉次数”。原课程讲义指出随机游走找到苹果的平均路径长度约为 30~40 步而最近苹果的平均直线距离只有 5~6 步说明纯随机策略相当低效。这就是本作业要改写的“旧世界”基线目标 找到苹果失败 遇狼或落水。三、新世界的游戏规则作业核心内容作业要求按照以下五条规则把世界改得更真实下文为对原文规则的中文完整转述与解读移动消耗体能Peter 每从一个地方移动到另一个地方都会损失一定能量energy并累积一定疲劳fatigue。吃苹果补充能量Peter 可以通过吃苹果获得更多能量。在树下或草地上休息可消除疲劳走进棋盘上包含树或草即绿色区域的格子Peter 可以摆脱疲劳。目标变为击杀狼Peter 需要找到狼并把它杀死。战斗有门槛要杀死狼Peter 的能量与疲劳必须达到一定水平否则他会在战斗中落败。与原世界相比最本质的三点变化是动作有了代价移动不再是“免费”的任何移动都会让能量下降、疲劳上升因此绕路、迷路都要付出真实代价可恢复资源苹果能量与树/草地疲劳清零成为需要主动规划去获取的“补给点”这使策略不再只是“几何上最短路径”终局条件改变成功判定从“到达苹果格”变成“以足够好的体能状态站到狼格上并赢得战斗”吃苹果、休息的最终目的都是为这一战做准备。四、为什么这会让问题变难状态膨胀与稀疏奖励在原世界中状态 ≈ Peter 的位置状态空间大小约为width × height。加入能量与疲劳后状态还包含“能量级别”和“疲劳级别”因此理论上完整的状态空间是 (棋盘位置, 能量, 疲劳) 的三元组合。作业原文也明确给出了三种可行表示用元组(Board, energy, fatigue)表示状态为状态定义一个新类可以从Board派生甚至直接修改 rlboard.py 中的原始Board类。难度上升还体现在奖励的稀疏性上原世界中“到达苹果”在随机游走下尚能在几十步内发生而新世界中“与狼战斗并获胜”属于小概率事件大多数回合可能在到达狼之前就因为能量耗尽、被水围困等原因提前结束。正如作业注释所警告的——因为游戏成功与狼战斗是稀有事件你可能需要更长的训练时间。五、从源码看Board可以复用什么能力在动手前先盘点 rlboard.py 中可直接复用的机制能力方法 / 字段源码位置作用格子类型Board.Cell.empty/water/wolf/tree/applerlboard.py#L44-L49区分空地、水、狼、树/草、苹果读取当前格Board.at(posNone)rlboard.py#L99-L103返回 Peter 当前或指定位置的格子类型越界判断Board.is_valid(pos)rlboard.py#L105-L106判断坐标是否在棋盘内坐标增量Board.move_pos(pos, dpos)rlboard.py#L108-L109计算移动后的新坐标实际移动Board.move(dpos, check_correctnessTrue)rlboard.py#L111-L114更新human位置随机起点Board.random_start()rlboard.py#L121-L126随机把 Peter 放到一个空地上Peter 位置Board.humanrlboard.py#L64记录当前坐标(x, y)棋盘数据Board.matrixrlboard.py#L54numpy数组存每格类型可以看到位置移动、越界判定、随机起点这些机制都是现成的能量与疲劳并不存在于matrix中它们更像是一回合内随时间演化的“回合变量”因此推荐用第 4 节提到的方案一或方案二——用一个封装state去同时携带board、energy、fatigue。仓库的示例解答笔记本 8-Reinforcement/1-QLearning/solution/assignment-solution.ipynb 正是采用了“封装类”路线。它定义了一个state类其关键结构如下示例来源于仓库 solution 目录class state: def __init__(self, board, energy10, fatigue0, initTrue): self.board board self.energy energy self.fatigue fatigue self.dead False if init: self.board.random_start() self.update() def at(self): return self.board.at() def update(self): if self.at() Board.Cell.water: self.dead True return if self.at() Board.Cell.tree: self.fatigue 0 # 树/草地上休息疲劳清零 if self.at() Board.Cell.apple: self.energy 10 # 吃苹果能量补满 def move(self, a): self.board.move(a) self.energy - 1 # 规则 1移动耗能 self.fatigue 1 # 规则 1移动累积疲劳 self.update() def is_winning(self): return self.energy self.fatigue # 规则 5能量压过疲劳才能赢这段代码可以看作五条新规则的“最小可运行翻译”初始能量10、初始疲劳0每移动一步能量-1、疲劳1踏上树/草地时疲劳清零吃到苹果时能量回满示例选择把苹果与休息作为“状态刷新点”用复位到满值实现补给is_winning()用energy fatigue表示“具备击杀狼的体能条件”。需要说明以上数值初始能量 10、疲劳上限的复位策略、energy fatigue的判胜条件只是仓库示例解答的一种具体取值。作业本身并未限定参数允许你自行定义“某程度的能量与疲劳”判定这正是评分标准里“世界规则重新定义”的自由度所在。六、重写奖励函数把“能量 − 疲劳”变成每步的形状奖励原课程的奖励函数见课程代码块 5只对终点给出大额奖励move_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 x Board.Cell.water or x Board.Cell.wolf: return end_reward if x Board.Cell.apple: return goal_reward return move_reward新世界里这套函数不再适用因为“终点是苹果”已不是目标。作业要求**“按照游戏规则修改上面的奖励函数”。仓库示例解答给出了一种很自然的改造用energy - fatigue作为“每一时刻的体能净额”并把它当作连续的形状奖励shaping reward**同时用大额正负分刻画终局事件def reward(s): r s.energy - s.fatigue if s.at() Board.Cell.wolf: return 100 if s.is_winning() else -100 # 打赢 100打输 -100 if s.at() Board.Cell.water: return -100 # 溺水结束 return r # 其余情况能量净额即即时回报这种设计的直觉是energy - fatigue越大说明 Peter 状态越好因此策略会自发学会“多吃苹果抬高能量、多去树/草地休息压低疲劳”所有通往狼的战斗都发生在状态评估之后若在狼格上is_winning()为真则得到极大的正奖励100否则得到极大的负奖励-100形成清晰的稀疏终局信号落水给-100把“走进水里导致提前出局”也当作失败终局。你也可以自行发挥比如用分段函数区分普通移动、吃苹果、休息的即时奖励或对能量阈值做更细的判定。关键在于——奖励函数必须严格反映第 3 节列出的五条规则否则 Q-Learning 无从学到正确的因果链这正是作业评分中“奖励函数未完全定义 → Needs Improvement”的扣分点。七、训练循环的适配与超参数调整Q-Learning 的骨架不变核心仍是贝尔曼方程驱动 Q-Table 更新Q(s,a) ← (1 − α)·Q(s,a) α·(r γ·max_a Q(s,a))其中 α 是学习率、γ 是折扣因子。原课程用 5000 个 epoch回合训练代码片段大致如下probs用于把 Q 值转成选择各动作的概率for epoch in range(5000): m.random_start() # 随机起点 n, cum_reward 0, 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) # 允许走出棋盘以终止回合 r reward(m) cum_reward r if r end_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[x dpos[0], y dpos[1]].max()) n 1在新世界中仓库示例解答solution/assignment-solution.ipynb保持了同样的 Q-Table 更新骨架但做了四处关键调整回合对象从m变为state每个 epoch 用s state(m)开始之后所有判断都基于封装了能量/疲劳的状态对象。Epoch 数量翻倍示例使用for epoch in range(10000)印证了作业“成功事件稀有、训练需更久”的提示。动作采样前先过滤非法移动示例循环内反复抽样直到board.is_valid(move_pos(human, dpos))成立避免把能量白白浪费在越界动作上。学习率衰减更激进示例采用alpha np.exp(-n / 3000)n 为当前回合内步数gamma仍为0.5。需要强调这些超参数epoch 数、α 的衰减速率、γ 的取值不是唯一正确答案。作业注释明确指出“你可能需要调整超参数尤其是 epoch 数量才能让它工作”。调参时值得留意的经验包括若回合普遍很长说明策略常在迷路兜圈可考虑提高学习率或加强休息/吃苹果的奖励信号若狼战几乎不发生比如一直落水或绕不开水需要增大 epoch、或改进动作选择策略让智能体有更多机会靠近狼折扣因子 γ 越小智能体越“短视”γ 越大越看重远期收益——杀狼属于远期事件适度偏大的 γ 更合理。probs函数的细节也值得注意——把 Q 向量转化为概率前加上极小值eps如1e-4是为了避免初始化阶段各 Q 值完全相同时除以零见课程代码。八、如何统计并对比胜负随机游走 vs Q-Learning作业还特别要求保留负责随机游走策略的代码并在最后把你的算法结果与随机游走进行对比。对比的指标是“赢的局数”与“输的局数”。统计函数可以仿照示例解答写成这样def print_statistics(policy): s, w, n 0, 0, 0 for _ in range(100): z walk(m, policy) if z 0: w 1 # 被狼杀死 elif z 0: n 1 # 溺水 else: s 1 # 赢下与狼的战斗 print(fKilled by wolf {w}, won: {s} times, drown: {n} times)需要注意示例中对回合结束的约定随机游走版本的walk内部当站在狼格上时按state.is_winning()判断胜败——胜利返回正步数n战败返回-n走进水里返回0溺水。这一约定让三种结局赢 / 被狼杀 / 溺水能够被一条统计函数区分开。仓库的solution/assignment-solution.ipynb中保留了运行时输出execution output可作为“参考结果”观察。在固定种子13的棋盘上随机游走 100 次的记录是Killed by wolf 5, won: 1 times, drown: 94 times同样的棋盘、同样 100 次统计改用训练 10000 个 epoch 之后的 Q-Learning 策略按 Q 值概率采样见下述qpolicy记录为Killed by wolf 1, won: 9 times, drown: 90 timesdef qpolicy(m): x, y m.human v probs(Q[x, y]) a random.choices(list(actions), weightsv)[0] return a print_statistics(qpolicy)解读这份参考输出时要客观Q-Learning 策略把“战胜狼”的次数从 1 次提升到 9 次、被狼击杀从 5 次降到 1 次说明学到的策略确实更倾向于“为狼战做准备”但 100 次里仍有 90 次是溺水结束。这并不奇怪——大部分回合可能根本没走到狼所在区域印证了“与狼战斗是稀有事件”的原始提示。这些数字仅代表该示例解答在某一随机种子下的单次运行结果不应理解为普适结论换棋盘种子、改超参数都会得到不同数字作业评分的重点在于 Q-Learning 是否比随机游走赢得更多。在训练完成后还可以用m.plot(Q)可视化 Q-Table 学习结果观察每个格子上箭头的偏好方向初始时所有方向等概率学习后箭头会指向更有利的移动。九、技术要点归纳一条可复现的解题路径综合上述分析完成本作业的推荐路径可归纳为五步保留基线复制随机游走策略与print_statistics统计代码作为对照基准定义状态容器用元组(board, energy, fatigue)或封装类承载棋盘 能量 疲劳按五条规则实现移动、补给、休息与死亡逻辑重写奖励函数让奖励反映每步的能量净额并给“战胜狼 / 战败 / 溺水”配置差异明显的终局大额奖励跑更长的 Q-Learning把 epoch 数量加大示例为 10000必要时调整 α 衰减与 γ让稀有事件有足够机会被采样到按胜负次数对比在同一棋盘上用同一统计函数跑随机游走与 Q-Learning比较赢/输/溺水三类次数。期间有两条自查线索可以随时核对随机游走应“偶尔”能赢——如果 100 次里一次战斗都没发生先检查状态更新逻辑例如是否从未真正踏上过狼格、或判胜条件写反Q-Learning 训练完成后应比随机游走更少落水、更多取胜——如果完全无改善优先检查奖励函数是否完整覆盖了五条规则以及 epoch 是否足够长。十、作业完成度对照官方评分标准原作业末尾给出了一张三档评分表Rubric是判断“做到什么程度算完成”的权威标准此处完整转述标准优秀Exemplary合格Adequate需改进Needs Improvement综合要求提交的笔记本包含新世界规则的完整定义、Q-Learning 算法与必要的文字解释且 Q-Learning 相对随机游走显著提升了结果提交了笔记本Q-Learning 已实现并相对随机游走有所改进但提升不显著或笔记本文档化不足、代码结构混乱只做了重新定义世界规则的初步尝试但 Q-Learning 算法不工作或奖励函数没有被完整定义对照该表做交付前的自检是否有对五条规则的清晰文字描述随机游走与 Q-Learning 是否在同一条统计流程下可对比奖励函数是否把所有终局胜利/战败/溺水/补给都覆盖到笔记本里是否解释了关键设计决策这四点都做到就满足“优秀”档的核心要求。十一、延伸阅读与仓库文件索引如果你想进一步深入本节的实现细节可以直接阅读以下仓库文件本作业英文原版8-Reinforcement/1-QLearning/assignment.md孟加拉语译本translations/bn/8-Reinforcement/1-QLearning/assignment.md课程讲义含随机游走、Q-Table、贝尔曼方程、explore/exploit 的完整推导8-Reinforcement/1-QLearning/README.md入门实验笔记本8-Reinforcement/1-QLearning/notebook.ipynb环境模拟模块Board类等8-Reinforcement/1-QLearning/rlboard.py本作业的官方示例解答含state类与能量/疲劳奖励实现8-Reinforcement/1-QLearning/solution/assignment-solution.ipynb在本地运行笔记本时请务必让rlboard.py与 notebook 位于同一目录cloud 环境同样需要先上传该文件这是课程 README 明确给出的运行前提。此外你可以按作业要求“把随机游走代码保留在解决方案里”让两个策略的胜负统计并排出现——这既是评分标准的要求也是检验学习算法真实价值的科学做法。【免费下载链接】ML-For-Beginners12 weeks, 26 lessons, 52 quizzes, classic Machine Learning for all项目地址: https://gitcode.com/GitHub_Trending/ml/ML-For-Beginners创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考