ML-For-Beginners 实战:为“彼得与狼“Q-Learning 环境扩展能量与疲劳状态,让世界更真实
2026/9/9 20:46:10 网站建设 项目流程

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)中,基础版"彼得与狼"环境允许智能体(Peter)几乎无代价地移动:既不饿也不累。本节作业(Assignment:A More Realistic World)要求学习者基于原版 notebook.ipynb 重写游戏规则,为状态引入**能量(energy)疲劳(fatigue)**两个维度,并据此重构奖励函数与胜负判定,让 Q-Learning 学习到一套能够真正"战胜狼"的最优策略。完成本文档后,你将掌握:如何把连续生理状态引入离散网格强化学习、如何重设计奖励函数驱动稀疏目标(猎狼成功是稀有事件)的学习、以及如何用"随机行走 vs Q-Learning"对照实验公平评价算法效果。


一、任务背景:从"取苹果"升级为"猎狼求生"

在原版课程 README.md 的设定中,Peter 的目标是在一张8×8棋盘上找到苹果,同时避开狼与水。他每走一步要么胜利到达苹果,要么失败落水或被狼吃掉,移动本身没有"体力成本",因此状态仅是位置,Q-Table 也只需覆盖width × height × len(actions)个格子。

而作业A More Realistic World(捷克语翻译版见 translations/cs/8-Reinforcement/1-QLearning/assignment.md,英文原文见 8-Reinforcement/1-QLearning/assignment.md)提出了 5 条新规则,彻底改变了游戏难度与目标:

规则编号规则内容引入的系统性影响
1每从一个位置移动到另一个位置,Peter消耗能量累积疲劳移动不再"免费",最短路径不一定是最优路径
2Peter 可以通过吃苹果获得更多能量苹果从"终点目标"降级为"补给资源",价值语义发生变化
3Peter 可以通过在树下或草地上休息(即走进树/草的绿色格子)消除疲劳绿色格子的语义从"装饰"变为可交互的"恢复区"
4Peter 需要找到并杀死狼游戏终止条件与成功定义整体重写
5杀死狼需要足够的能量与足够低的疲劳,否则战斗失败引入"战前状态门槛",成败取决于到达狼格时的瞬时体力

对比原版可见:狼不再是单纯需要避开的"死亡格",而是可在满足条件时被击败的最终目标;苹果与绿色格子的作用也从"目标/普通地形"变成了维持续航的"资源"。这套设计迫使学习者必须重新思考状态表示与奖励函数,正是本节作业的训练重点。


二、状态表示升级:把能量与疲劳"装进"环境

作业注释(assignment 原文)明确指出了实现路线:状态变得更复杂,除人的位置外还要包含疲劳与能量水平。并给出了三种可选表示法:

  1. 元组(Board, energy, fatigue)表示状态;
  2. 定义状态类(可从Board派生);
  3. 直接修改 rlboard.py 中的原始Board类。

2.1 从源码看原版Board的可扩展性

先看Board底层结构(rlboard.py):

class Board: class Cell: empty = 0 water = 1 wolf = 2 tree = 3 apple = 4

Cell枚举里treeapplewolfwater已全部存在,说明新规则2、3可以直接基于现有地形语义实现,无需新增格子类型。原版还提供了几个关键原语,支撑"移动消耗 + 触地判定"的重构:

  • at(pos=None):返回当前位置或指定位置的格子类型(rlboard.py);
  • move_pos(pos, dpos)move(dpos, check_correctness=True):先算目标位置、再执行位移,其中move支持在check_correctness=False时越界移动以终止回合(rlboard.py);
  • random_start():把 Peter 放到一个empty格作为回合起点(rlboard.py)。

2.2 参考答案的状态封装:一个轻量state

仓库中的参考解法 solution/assignment-solution.ipynb 选择了方案二:独立状态类,把能量与疲劳的全部演进逻辑收敛在一个对象里,值得借鉴:

class state: def __init__(self, board, energy=10, fatigue=0, init=True): 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 self.fatigue += 1 self.update() def is_winning(self): return self.energy > self.fatigue

这个类逐条落实了新规则:

  • move(a)每步统一energy -= 1fatigue += 1——对应规则 1
  • update()检测到apple时把能量拉回上限10——对应规则 2
  • update()检测到tree时把疲劳清零——对应规则 3,这也是作业所说的"绿色格子可休息";
  • is_winning()energy > fatigue判定是否有资格猎狼——对应规则 5的"能量水平门槛"。

该解法的精巧之处在于:棋盘Board本身未做任何修改,仍通过组合方式被state持有,Q-Learning 的格子索引逻辑(x, y = s.board.human)得以沿用原版结构。


三、胜负判定与随机行走基线:一切评价的锚点

作业要求"在解决方案中保留随机行走策略代码,并在结尾比较你的算法与随机行走的结果(以赢、输场次计)"——因此随机行走必须被重构为兼容新规则的基线。参考答案的行走逻辑如下:

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 # success! else: return -n # failure! if s.at() == Board.Cell.water: return 0 # died a = actions[policy(m)] s.move(a) n += 1

与原版walk(见 README.md 的 Random walk 一节,用return -1表示被狼吃/落水)相比,新逻辑有两个关键变化:

  1. 遇见狼不再等于失败:进入狼格时先调用is_winning()判定战斗胜负。胜利返回步数n,能量不足则返回-n(失败);
  2. 落水单独计为0,与被狼打败的负值区分开,便于统计三类结局:胜、败于狼、死于水。

为输出可比的对照统计,可继续沿用课程里的print_statistics(policy)思路(跑 100 局统计平均路径与失败次数),把返回值分成胜利次数失败次数两个口径即可。


四、奖励函数重设计:用"体力差"驱动全过程学习

4.1 原版奖励的局限

原版课程给出了极简奖励函数(代码块 5,见 README.md):

move_reward = -0.1 goal_reward = 10 end_reward = -10 def reward(m, pos=None): 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

在该框架下,狼必然是end_reward=-10的绝对禁区。但在新规则中狼格可能是"通关点",因此奖励函数必须重写。

4.2 新奖励:即时反馈 + 终局胜负

参考答案给出的新奖励函数设计得相当精炼:

def 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

它的核心思想是"奖励函数要能被分解成即时收益与终局收益两层":

  • 逐格即时项energy - fatigue:把状态自带的生理信息直接转成奖励数值。能量高、疲劳低时分数为正,鼓励 Peter 先去补给再推进;随着能量耗尽、疲劳累积,分数转负,自然抑制无意义的原地兜圈,等价于一种"自带步数惩罚"的连续化版本——不再需要手工设定move_reward=-0.1,体力差本身就是自适应步数成本。
  • 终局胜负项±100:胜利给+100、被狼反杀或落水给-100,用一个远大于即时项的绝对量级保证"赢下整局"的长期回报优先于任何单步即时收益。这正是课程 README.md 反复强调的核心洞察:多数情况下游戏只在结束时给出实质性奖励,算法必须能回溯记住那些通向正奖励的"好步骤"——这正是 Q-Table 与 Bellman 公式存在的意义。

对自研方案而言,只要保证:(a) 每个非终局状态都有连续可学习的即时反馈;(b) 终局奖励在量级上显著压倒即时项,使"猎狼成功"这种稀有事件能被 Q 值逐步扩散传播,即符合本作业对"奖励函数完整定义"的验收要求(对应评分表中"Needs Improvement:奖励函数未完整定义"的规避点)。


五、Q-Learning 主循环改造:状态变复杂,学习不变

课程核心算法 Q-Learning 依然适用,唯一变化是每个训练步都要经state封装读写能量与疲劳。以下给出参考答案在 solution/assignment-solution.ipynb 中的完整训练循环:

from IPython.display import clear_output lpath = [] for epoch in range(10000): clear_output(wait=True) print(f"Epoch = {epoch}", end='') # 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), weights=v)[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[x + dpos[0], y + dpos[1]].max()) n += 1

对照原版训练循环(README.md 的 Python implementation 一节),可见改动高度局部化:

要素原版新规则版原因
Q-Table 结构np.ones((width, height, len(actions))) * 1/len(actions)完全不变状态新增维度未展开进 Q-Table,仍按物理坐标索引;能量/疲劳体现在"该状态是否可达/何时终止"
动作选择直接m.move(dpos, check_correctness=False)先校验is_valids.move(dpos)新规则下越界应被阻止而非用于终结回合,终局只由狼/水触发
回合结束判定r == end_reward或累计奖励过小abs(r) == 100终局奖励正负都是100,统一判绝对值
学习率衰减alpha = np.exp(-n / 10e5)(即 /100000)alpha = np.exp(-n / 3000)单回合更长、信息更密集,衰减加快以稳定收敛
回合数 epoch500010000见下方"稀有事件"分析
折扣因子 γ0.50.5可保持不变

5.1 为什么必须加大 epoch 并调学习率:猎狼是稀有事件

作业注释特别强调:"游戏成功(与狼战斗并取胜)是稀有事件,你可能需要多得多(much longer)的训练时间。"结合新世界进一步解读:

  1. 原版里"找到苹果"的成功率尚可,5000 个 epoch 足够让奖励沿路径传播;新世界要求 Peter 先积累足够能量、清空疲劳,再恰好走到狼格并以energy > fatigue收场,是一条"补给 → 赶路 → 挑战"的长链条,单次成功所需步数大幅上升;
  2. 终端奖励 +100 只在回合真正胜利时出现一次,若训练轮数不足,Q-Table 中多数格子仍未收到可用的传播信号;
  3. 因此除调大 epoch 外,通常还需配合学习率衰减曲线(让后期小幅微调 Q 值,避免如课程 README.md "Investigating the learning process"一节所描述的训练后期路径长度陡增的"Q 值被新样本覆盖污染"现象)与回合上限约束。

课程末尾的挑战任务(给walk限长、禁止回退)在此同样适用,可作为防止死循环的辅助手段。


六、策略评估与作业验收:用"胜/负场次"说话

训练收敛后,用课程标准的两种 Q-Table 策略做评测:

def qpolicy(m): x, y = m.human v = probs(Q[x, y]) a = random.choices(list(actions), weights=v)[0] return a

qpolicy即"按 Q-Table 值成正比的概率采样动作",是训练期探索与利用平衡策略的推理版。最后调用print_statistics(random_policy)print_statistics(qpolicy)各跑 100 局,统计胜负场次即可完成作业要求的对照。

6.1 作业评分表(Rubric)解读

assignment 提供的评分标准直接定义了"优秀"的边界:

等级判定条件对学习者的自查点
优秀(Exemplary)提交含新世界规则定义、Q-Learning 算法与文字解释的 notebook,且 Q-Learning显著优于随机行走是否写了规则/奖励设计的文字说明?胜负场次差异是否肉眼可见?代码是否结构清晰?
合格(Adequate)Q-Learning 已实现并有改善但不显著;或 notebook 文档化差、代码结构不佳确认了 Q 值已更新但提升幅度小?补足注释与模块划分
需改进(Needs Improvement)只尝试重定义了世界规则,但 Q-Learning 不工作,或奖励函数未完整定义检查回合是否永远不结束、reward是否覆盖所有格子类型(狼/水/树/苹果/普通地面)

结论核验的标准简单而硬核:同一个print_statistics,把随机行走与 Q-Learning 的胜场数、负场数并排输出。若 Q-Learning 在"猎狼成功次数"上明显压过随机行走,即完成作业核心目标。


七、实验建议与避坑清单

基于上述源码与参考实现,实际动手时建议按以下顺序迭代:

  1. 先冻结基线:不改任何学习代码,先让随机行走在"新规则 walk + 新 reward"下能稳定输出统计口径(胜/败于狼/死于水),保证后续对照可信;
  2. 再改状态与奖励:推荐先照抄state类封装能量/疲劳逻辑,把update()的树/苹果/水分支写对,再用energy - fatigue做即时项、±100 做终局项;
  3. 最后调参训练:以epoch(参考实现用 10000)、alpha衰减分母(参考实现用 3000)、gamma(课程用 0.5)为主轴做小规模网格尝试,每轮训练后立刻跑qpolicy统计看趋势;
  4. 验证"奖励完整定义":显式检查reward(s)是否覆盖wolf / water / apple / tree / 普通地面五种情形——这是评分表"Needs Improvement"档位最典型的丢分点;
  5. 保留解释性输出m.plot(Q)把学习后的策略画回棋盘,配合plt.plot(lpath)观察每回合步数随 epoch 的变化曲线(成功步数应逐渐下探),可显著提升 rubric 中"文字解释"维度的得分。

小结

A More Realistic World是本课程的经典进阶作业:它在不更换算法框架的前提下,迫使你重建状态语义、重写奖励函数并重定义"成功",从而真正理解状态设计、奖励塑形(reward shaping)与稀疏回报这三个 RL 工程的核心命题。仓库中的 solution/assignment-solution.ipynb 提供了一套可直接对照的完整参考实现,而8-Reinforcement/2-Gym课程则承接本作业的结论,进一步把相同思想迁移到 Gym 环境中处理连续状态空间——完整学习路径可沿8-Reinforcement/1-QLearning/README.md的 Navigation 与课后资源继续深入。

【免费下载链接】ML-For-Beginners12 weeks, 26 lessons, 52 quizzes, classic Machine Learning for all项目地址: https://gitcode.com/GitHub_Trending/ml/ML-For-Beginners

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询