ML-For-Beginners 强化学习作业实战:为「彼得与狼」Q-Learning 世界加入能量与疲惫机制
发布时间:2026/9/5 16:48:40 作者:尧图编辑部 阅读量:1,286

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 周「Peter and the Wolf」强化学习作业A More Realistic World展开在原始棋盘世界中引入能量energy与疲惫fatigue机制重新定义状态空间与奖励函数用 Q-Learning 训练出能击杀狼的获胜策略并保留随机游走基线做量化对比。读完并动手完成后你将掌握「状态扩展—奖励重定义—超参数调整—策略评估」这一完整的环境改造流程这是把 Q-Learning 应用到任何带资源约束的 MDP 问题的通用套路。任务背景原始世界与基线算法本作业建立在第 8 周第一课的「彼得与狼」路径规划世界之上详见 课程讲义 与 课程笔记本。原始设定中Peter 在一块width x height的棋盘上行走格子类型由 rlboard.py 中Board.Cell枚举定义empty空地、water水域不可进入、wolf狼、tree树绿色休息区、apple苹果。原始目标仅仅是避开狼与水域、找到苹果奖励函数只有三档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_rewardQ-Learning 侧的 Q-Table 形状为width x height x len(actions)初始化为均匀值 0.25等价于随机游走策略Q np.ones((width, height, len(actions)), dtypenp.float) * 1.0 / len(actions)训练循环按 Bellman 方程更新Q(s,a) ← (1-α)·Q(s,a) α·(r γ·max_a Q(s,a))动作按 Q 值概率化采样以平衡探索与利用probs()函数。基线策略是随机游走其平均路径长度约为 30–40 步远高于最近苹果约 5–6 步的理论距离。本作业要求在这个基础上把世界「变得更真实」并验证强化学习在新规则下依然有效。新世界规则完整继承的 5 条设定根据 assignment.zh-cn.md 的定义假想中 Peter 原本几乎可以一直走动而不感到疲倦或饥饿在更真实的世界里需要时不时坐下休息、也要吃东西。为此引入以下 5 条规则从一个地方走到另一个地方Peter失去能量energy并获得疲惫fatiguePeter 可以通过吃苹果来获得更多能量Peter 可以通过在树下或草地上休息来消除疲惫即走进有树和草的棋盘位置——绿色格子Peter 需要找到并杀死狼为了杀死狼Peter 需要保持一定级别的能量与疲惫能量要占优否则他会输掉这场战斗。注意规则 4 带来的本质变化终点从「摘到苹果」变成了「击杀狼」苹果降级为恢复能量的补给品树/草地Board.Cell.tree从普通可走格子升级为恢复疲劳的功能格子。这意味着状态、转移、终止条件和奖励函数全部需要重做。作业要求与三条关键提示作业的「说明」部分assignment.zh-cn.md给出了硬性要求逐条列出以便对照执行起点使用原始 notebook.ipynb 笔记本作为解决方案的起点核心工作根据游戏规则修改奖励函数运行强化学习算法学习赢得游戏的最佳策略并在赢/输场次上将自己的算法与随机游走算法对比提示一状态表示新世界中状态更复杂除位置外还包括疲惫和能量水平。你可以选择把状态表示为元组(Board, energy, fatigue)或为状态定义一个类也可以从Board派生甚至直接修改 rlboard.py 中Board的源码提示二保留基线方案中必须保留负责随机走动策略的代码并在最后将两者结果对比提示三超参数可能需要调整超参数才能跑通尤其是epoch 数——因为游戏成功与狼搏斗是一个罕见事件需要更长的训练时间。状态建模官方参考方案的 state 类仓库在 solution/assignment-solution.ipynb 中提供了参考实现。它没有去改Board源码而是定义了一个独立的state类来承载「棋盘 能量 疲惫」三元信息这是对作业提示一的第一种推荐的落地方式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 # 规则 3树上/草地上休息消除疲惫 if self.at() Board.Cell.apple: self.energy 10 # 规则 2吃苹果恢复能量 def move(self, a): self.board.move(a) self.energy - 1 # 规则 1每走一步失去 1 点能量 self.fatigue 1 # 规则 1每走一步增加 1 点疲惫 self.update() def is_winning(self): return self.energy self.fatigue # 规则 5能量占优才算赢下战斗几个实现细节值得注意规则 1–3 全部收敛在move()与update()中移动先改变energy/fatigue再调用update()让格子效果生效顺序保证了「走进树格即清疲惫、走进苹果格即回满能量」的语义清晰random_start()复用自Board从 rlboard.py 源码看random_start()会在Cell.empty格子上随机落子不会落在狼、苹果或水上这保证了初始局面合法棋盘布局本身由randomize(seed13)生成num_wolves1、num_apples3、num_trees5等参数可调实验时建议固定 seed 以便两次对比随机 vs Q-Learning使用同一张地图规则 4、5 体现在终止判定is_winning()简单地用energy fatigue判胜。你可以按作业要求把它改成更严格的阈值例如能量必须高于某个固定值这正是作业鼓励自行设计的空间。另外state只持有board的引用而不复制它训练循环中反复state(m)重建时棋盘布局保持不变、仅重置能量/疲惫与起点这是保持实验一致性的关键。基线改造面向胜负统计的随机游走原始 notebook 中的walk()以「到苹果」为成功作业要求以「击杀狼」为成功、以溺水/战斗失败为失败因此基线本身也要重写。参考方案的做法def random_policy(state): return random.choice(list(actions)) def walk(board, policy): n 0 # number of steps s state(board) while True: if s.at() Board.Cell.wolf: if s.is_winning(): return n # 成功击杀 else: return -n # 战斗失败 if s.at() Board.Cell.water: return 0 # 溺水死亡 a actions[policy(m)] s.move(a) n 1 walk(m, random_policy)返回值语义被编码进符号正数击杀步数负数战斗失败零溺水。统计函数相应扩展为三类计数并满足作业「保留随机游走代码并在最后对比」的要求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) print_statistics(random_policy)先运行这段基线记录 100 局中的获胜/战败/溺水分布——这就是你最终要超越的数字。奖励函数重定义把「资源差」变成稠密信号新世界的奖励函数不再只有三档终点奖励而是把energy - fatigue的差值作为中间步的稠密奖励让每一步的「资源状态好坏」都直接反馈给 Q-Tabledef reward(s): r s.energy - s.fatigue if s.at() Board.Cell.wolf: return 100 if s.is_winning() else -100 if s.at() Board.Cell.water: return -100 return r设计逻辑终端事件 ±100击杀狼 100、战败 -100、溺水 -100量级远大于中间奖励保证「结果导向」依然压倒一切中间步返回energy - fatigue这是一条天然的塑形shaping信号——吃苹果会抬升该值、连续赶路会压低该值等于把作业规则 1–3 的资源经济直接翻译成了梯度方向帮助稀疏奖励问题中「难得才发生一次的击杀」传播信用你完全可以按作业要求尝试自己的版本例如按疲惫程度惩罚、对休息格给小额正奖励并用「胜率是否提升」来检验哪种塑形更合理——这正是评判标准中「奖励函数是否完全定义」的考察点。Q-Learning 训练循环与超参数调整Q-Table 的结构不需要变——作业提示三中关于「元组/类/改Board源码」的三种状态表示方案参考方案选择了独立state类Q-Table 仍按(width, height, len(actions))索引以棋盘位置为主键能量与疲惫通过奖励间接进入学习信号Q np.ones((width, height, len(actions)), dtypenp.float) * 1.0 / len(actions) def probs(v, eps1e-4): v v - v.min() eps v v / v.sum() return v训练主循环与原始 notebook 几乎同构只把「走到即终止」改成了「进入狼格/水格才终止」lpath [] for epoch in range(10000): # Pick initial point s state(m) # Start travelling n 0 cum_reward 0 while True: x, y s.board.human v probs(Q[x, y]) while True: # 只在棋盘内采样动作 a random.choices(list(actions), weightsv)[0] dpos actions[a] if s.board.is_valid(s.board.move_pos(s.board.human, dpos)): break s.move(dpos) r reward(s) if abs(r) 100: # end of game击杀/战败/溺水 lpath.append(n) break alpha np.exp(-n / 3000) 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()) n 1 m.plot(Q)对照作业提示三这里有三个值得动手调的超参数超参数参考值调整依据epoch 数10000原始世界为 5000击杀狼是罕见事件正样本稀少需要更长训练时间让「通往狼格且资源占优」的经验被反复强化学习率衰减alpha exp(-n/3000)衰减尺度 3000 步原始世界用exp(-n/10e5)几乎不衰减新世界步内奖励波动大更快的衰减能在训练后期稳定 Q 值、减少「覆盖破坏」折扣因子gamma0.5资源状态的价值高度依赖近期决策较大折扣让 agent 更看重眼前能量/疲惫的变化训练收敛后的 Q-Table 可视化m.plot(Q)应该呈现指向狼格的偏好流向策略评估与随机游走对比胜负评估阶段复用同一套walkprint_statistics框架只替换策略函数。与原始世界相同的两种策略选项在这里同样适用def qpolicy(m): x, y m.human v probs(Q[x, y]) a random.choices(list(actions), weightsv)[0] return a print_statistics(qpolicy)qpolicy概率采样保留少量探索参考方案实测它能显著减少溺水次数但 Peter 仍不能每次都赢——作业明确鼓励你继续调超参数去改善这一结果贪心版本qpolicy_strict直接取argmax可作对照但注意原始课程提到的「两个状态互指导致死循环」问题在新世界里依然存在建议像课程 Challenge Task 1 那样给walk加上最大步数限制避免统计被卡死拖垮。对比口径要严格遵循作业要求在同一张地图固定seed13、同样 100 局下比较随机策略与 Q 策略的won / killed by wolf / drown三项分布。判定「显著改善」时最好把随机基线多跑几组不同 seed给出波动范围避免单局运气造成的误判。评判标准Rubric作业给出了三档评分标准对照它自检你的方案标准优秀中规中矩仍需努力笔记本上有新世界规则的定义、Q-Learning 算法和一些文字解释与随机游走相比Q-Learning 能够显著改善结果介绍了 Notebook实现了 Q-Learning 并与随机走动算法相比提高了结果但不显著或者 notebook 文档不完善、代码结构不合理有一些重新定义世界规则的尝试但 Q-Learning 算法不起作用或奖励函数没有完全定义从这份 rubric 可以提炼出「优秀档」的交付清单笔记本开头有 5 条新规则的完整文字定义本文第二节即模板状态表示、walk/终止判定、奖励函数、Q-Learning 主循环四个代码块齐全且保留随机游走代码末尾有同一张地图下的胜负对比输出与结论性文字解释对超参数epoch、学习率衰减、gamma有明确的调整记录——因为「击杀」是罕见事件这段记录本身就是评分证据。延伸与相关资源原始课程讲义世界设定、随机游走、Bellman 方程、探索/利用权衡的完整推导8-Reinforcement/1-QLearning/README.md原始课程笔记本本作业的起点代码8-Reinforcement/1-QLearning/notebook.ipynb环境模块源码Board类、randomize参数、random_start、格子渲染8-Reinforcement/1-QLearning/rlboard.py官方参考方案笔记本本文第三至六节代码出处8-Reinforcement/1-QLearning/solution/assignment-solution.ipynb英文原版作业8-Reinforcement/1-QLearning/assignment.md完成本作业后下一课将转向 Gym 环境中的 Q-Learning 实践8-Reinforcement/2-Gym/README.md本作业积累的「状态扩展 奖励塑形」经验可以直接迁移。【免费下载链接】ML-For-Beginners12 weeks, 26 lessons, 52 quizzes, classic Machine Learning for all项目地址: https://gitcode.com/GitHub_Trending/ml/ML-For-Beginners创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考