上一回笔记写到免模型的时序差分方法今天聊聊另一条主线基于模型的强化学习Model-based RL以及算法家族里两个非常有代表性的成员——Dyna算法和蒙特卡洛树搜索MCTS。这三者是连在一起的先搞清楚Agent怎么在内部“脑补”出一个环境模型再想办法让这个模型帮策略加速收敛最后落到树搜索这种可落地的规划方式上。这篇笔记适合两类人看一是传统深度强化学习方法快学完、想知道“模型还能干点啥”的初学者二是已经在跑DDPG/PPO这类免模型算法但感觉样本效率上不去的实践者。我自己走过一段弯路刚开始总觉得“有模型还要在线学习干嘛直接规划不就对了”后来才发现模型和规划之间隔着模型误差、探索策略、模拟深度一堆坑。这篇笔记就是把这条路上的坑一个个标出来。1. 为什么需要模型从“试错”到“脑补”的转变1.1 强化学习里的“模型”到底指什么先对齐术语。强化学习一般被分成两条路免模型Model-free和基于模型Model-based。它们的区别不在算法名而在Agent手里有没有一张关于环境动态的“说明书”。模型在标准设定里指的是两个东西的组合状态转移概率和奖励函数。在一个马尔可夫决策过程MDP里环境可以被描述成 (P(s|s,a)) 和 (R(s,a,s))前者是“我在s做了a之后世界会变成什么样”后者是“这次变化给我多少回报”。如果你知道这两样东西理论上就不需要跟环境做太多交互直接动态规划求解策略就行。但实际上我们几乎从没见过真实环境的转移概率长什么模样。机器人关节上的摩擦力、游戏物理引擎里的碰撞检测、推荐系统里用户下一秒会不会点击这些都不是能直接写出来的函数。所以基于模型的方法本质上是在“猜”环境Agent一边跟环境互动收集数据一边用监督学习拟合一套近似模型再用这套近似模型去模拟未来、规划动作。我把这个过程叫“脑补”不是开玩笑。你想象一个Agent在一个简化版网格世界里它走一步、撞一次墙、拿一次奖励同时在心里默默记住“从格子A往左推大概率会撞墙、从格子B往下走能拿到1”。等到它记住了这套规律它就可以在脑子里把未来的轨迹“预演”好几遍而不必每一步都真的去物理世界跑。这就是基于模型方法在样本效率上吊打免模型方法的根本原因。1.2 免模型方法为什么样本效率上不去对比一下你就能理解大家为什么宁愿去拟合模型。免模型方法比如DQN、PPO本质上是在跟环境死磕观察状态-动作-奖励-下一状态这条经验轨迹然后用TD误差或策略梯度把这些经验“蒸干”成策略或者值函数。问题在于高维、连续、真实物理环境里一条轨迹能提供的信息非常有限。一个机械臂关节运动状态维度可能几十维动作也是连续的哪怕策略已经接近最优也很难靠几千条轨迹把Q值估计准。更麻烦的是免模型方法通常在训练前期大量探索而探索期间产生的很多轨迹是无效的——撞到障碍物、走到死角这些经验虽然有用但信息密度太低。我在一个简单的连续控制环境上试过免模型方法要从完全随机策略收敛到能稳定走起来的策略大概需要采集百万级时间步。而在同样环境下如果给Agent一个准确模型几百步的规划就能找到近似最优轨迹。这个数量级的差距就是Model-based方法最核心的吸引力。当然模型也有模型的代价。拟合模型本身会引入偏差偏差放大之后可能让规划器在“错误的世界模型”里自信地执行错误动作。这就像是照着过期的地图开车比不看地图更危险。后面第5部分专门讲这个问题的排查办法。1.3 有了模型之后规划怎么落地有了环境模型Agent还需要回答“怎么使用这个模型”的问题。主流做法有三种第一种是把模型当成经验扩充器。Agent在真实环境里交互一段时间然后利用模型生成大量虚拟轨迹当作额外样本参与策略或值函数的训练。这种方式被Dyna框架采用实现简单效果好是本文重点。第二种是把模型当成优化目标本身。既然有了转移函数和奖励可以直接把期望累计回报写成关于动作的函数然后用最优控制、随机优化甚至梯度上升直接求解。这种做法在连续控制里常见比如MPC模型预测控制每一步都重新规划一小段轨迹只执行第一步。第三种是把模型整合进搜索过程。从当前状态出发用模型展开一棵树反复模拟未来轨迹最终通过节点访问统计选出最优动作。这就是MCTS的思路也是现代棋类AI成功的基石。这条脉络理顺之后你再看Dyna和MCTS它们其实是同一个问题在两个尺度上的回答。Dyna是“用模型造样本融入原有学习循环”MCTS是“用模型造模拟在动作空间上做搜索”。两者可以分开用也可以组合成更深的结构比如“Dyna式学习循环 MCTS式规划器”这种混合体。2. Dyna算法一边真实交互一边在脑内补样本2.1 Dyna-Q的思想和框架Dyna这个家族最核心的版本是Dyna-Q它把Q学习和环境模型结合起来。设计思想非常直白Agent每次真实走一步学到的东西更新两个部分一个是无模型的Q值表另一个是环境模型。然后Agent在脑内从模型里抽一小批历史状态通过模型生成模拟经验再用同样的Q更新规则刷一遍Q值。这样做的好处你如果实际跑过就会体会得很深真实经验是稀缺资源但模型模拟的经验几乎不限量。只要模型别错得离谱模拟经验就能加速值函数的收敛。如果把Dyna-Q理解成一个循环可以写成这样步骤1Agent观察当前状态 (s)按策略比如 (\epsilon)-greedy选动作 (a)步骤2在真实环境里执行 (a)拿到奖励 (r) 和下一状态 (s)步骤3用这条真实样本更新Q同时记录模型 (Model(s,a) \to (r,s))步骤4重复 (n) 次从历史状态集合里随机抽一个状态 (s_{sample})选一个曾在那个状态下做过的动作 (a_{sample})用模型得到 ((r_{sim}, s_{sim}))再用同样的Q更新规则更新Q这个设定里最关键的“旋钮”是步骤4的循环次数 (n)文献里叫规划步数planning steps。它的含义是“每走一步真棋在脑内额外预演几步”。(n0) 时Dyna-Q退化成普通Q-Learning(n) 很大时算法更依赖模型理想情况下模型足够准学习速度会显著加快但如果模型不准(n) 越大偏差累积也越严重。我在一个10×10的网格导航任务里测试过这个旋钮(n5) 和 (n50) 在前期都能快速收敛但到后期如果环境本身带有随机性(n50) 会明显振荡。原因很简单模型往往只记住了历史经验的平均趋势没有表达出噪声和随机边界模拟次数一多Q值被不真实的经验反复冲刷策略反而变差。2.2 为什么采样历史状态而不是当前状态这一小节算是我个人觉得Dyna最容易被忽略的经典设计。为什么第4步里的状态要“从历史里随机抽”而不是直接拿当前状态 (s) 生成模拟经验?两个原因。第一是数据利用齐平。如果只用当前状态展开那么Agent的规划能力就被限制在“当前所在的局部区域”碰到稀疏奖励任务比如奖励只出现在远端某个角落当前状态附近模拟一万次也碰不到奖励。而历史状态池覆盖了Agent曾经到过的所有区域随机抽就能把这些远端经验也拎出来反复加强保证值函数在已探索区域整体收敛更快。第二是避开模型在未见过的状态上的瞎猜。当前状态可能离历史见过的所有状态都很远这时候叫模型去预测下一步它只能给出一个没有根据的“幻觉”。从经验池里抽状态至少保证模型是在自己“见过”的邻域内做外推靠谱程度高很多。这个经验池我实际用下来一般存两种东西一种是原始四元组 ((s,a,r,s))另一种是额外保存一个状态清单。模拟时从状态清单里均匀抽或者按访问频次加权抽都行。优先用uniform抽样简单稳定后期可以改成用Rademacher/TD-error加权能适当提升对高价值区域的关注但收益有限刻意调反而容易过拟合经验池。2.3 表格型Dyna-Q的实现要点直接给一个可运行的轻量级参考实现。这个例子基于OpenAI Gym的FrozenLake环境4×4地图演示表格型Q-Learning加上Dyna规划。虽然玩具但足以看清全流程。import numpy as np from collections import defaultdict class TabularDynaQ: def __init__(self, env, gamma0.95, alpha0.1, epsilon0.1, n_plan10): self.env env self.gamma gamma self.alpha alpha self.epsilon epsilon self.n_plan n_plan self.q defaultdict(lambda: np.zeros(env.action_space.n)) self.model {} # 键: (s, a) 值: (r, s_done) self.state_pool set() # 历史状态集合 def choose_action(self, s): if np.random.rand() self.epsilon: return self.env.action_space.sample() qvals self.q[s] return int(np.argmax(qvals)) def update_q(self, s, a, r, s_next, done): td_target r self.gamma * (0 if done else np.max(self.q[s_next])) self.q[s][a] self.alpha * (td_target - self.q[s][a]) def learn(self, episodes2000): for ep in range(episodes): s self.env.reset() done False while not done: a self.choose_action(s) s_next, r, done, info self.env.step(a) # 步骤3: 用真实经验更新Q并记录模型 self.update_q(s, a, r, s_next, done) self.model[(s, a)] (r, s_next) self.state_pool.add(s) # 步骤4: 规划循环 for _ in range(self.n_plan): self._plan_step() s s_next def _plan_step(self): if not self.state_pool: return s np.random.choice(list(self.state_pool)) a np.random.randint(self.env.action_space.n) if (s, a) not in self.model: return r, s_next self.model[(s, a)] self.update_q(s, a, r, s_next, False)这里需要说明几个我在实践中调出来的细节第一state_pool用set存储避免同一个状态反复存在多个副本降低采样偏差。第二规划循环里如果(s,a)不在模型里就跳过不要粗暴地给一个默认零奖励否则会在模型稀疏区域产生负向误导。第三更新Q时done的处理要慎用。在模型里存终止标志done_flag是标准的做法但模拟更新时我建议doneFalse置False因为模型对于终止状态的判断本身不靠谱哪怕下一秒该结束多积累一步虚拟折扣回报对Q的影响也不大反而稳定。2.4 Dyna系列从Q扩展到其他底层算法Dyna不是一个孤立的算法而是一整个框架。在Sutton的书里Dyna-Q、Dyna-AC、Dyna-Sarsa这些都属于“基于模型 规划”的家族。Dyna-Q底层用Q-Learning更新表格型、简单、稳定适合入门。Dyna-Sarsa底层用Sarsa保留了一点在线策略的轨迹依赖适合需要保守策略的任务。Dyna-AC底层用Actor-Critic可以扩展到连续动作但模型误差带来的稳定性挑战更大。Dyna-MPC / Dyna-DQN模型用神经网络拟合规划器用DQN或者MPC是深度版本的主流形态。我建议入门的人先吃透Dyna-Q因为这个版本拆开来看就是Q表 字典模型 额外循环任何一部分出问题都很容易定位。直接上手深度版本的Dyna-DQN会有一堆隐藏问题模型网络过拟合、仿真经验与真实经验分布不一致、奖励淹没在大量模拟中……没有表格版打底排查起来非常痛苦。在后面的章节里我们将会看到跟Dyna互补的一种规划范式——MCTS。如果说Dyna是“用模型生成一批额外样本”MCTS则是“用模型驱动一棵树的精细推演”。3. MCTS把“未来推演”变成树搜索算法3.1 从规划问题到搜索树我们先想一个问题强化学习里有了模型为什么不能直接穷举未来轨迹然后选最好的?答案藏在高维度里。假设每个状态有b个可选动作未来h步的计划空间是 (b^h) 量级这种组合爆炸在真实任务里立刻就把最简单的穷举方案否决掉。MCTS的聪明之处在于不展开整棵完整的树而是用“当前信息最值得深挖”的节点反复做局部模拟。它把树的成长过程当作一个四步循环选择Selection、扩展Expansion、模拟Simulation、回传Backpropagation。这个循环重复成千上万次之后根节点的孩子节点上会积累一个关键数据结构每个候选动作被访问的次数和累积回报。最终Agent根据这些统计量选动作。我在理解MCTS时想到一个生活类比你下棋时面对一步棋正常人不会把后面所有可能的对局都摆出来而是大概推演几个主变化每条变化走几步看看局面值不值有些看着没希望的支线直接剪掉有些关键变化则越想越深。MCTS就是把这套人类直觉形式化、批量化地变成计算机能执行的过程。树生长的对称破缺来自两方面搜索策略倾向于探索访问次数少、结果不确定的节点一旦某个节点的模拟结果持续正面它的访问次数上升树会在它下面继续展开。这就是“当前信息最有价值的分支优先深入”原则。3.2 MCTS四步循环的细节拆解选择Selection从根节点出发沿着孩子节点一路往下走直到走到一个尚未完全展开的节点。关键在选择规则。标准做法是用UCB1公式计算每个孩子节点的得分然后选得分最高的[ UCB1(node) \bar{v}{node} c \cdot \sqrt{ \frac{\ln N{parent}}{N_{node}} } ]公式里的 (\bar{v}{node}) 是节点平均回报(N{parent}) 是父节点总访问次数(N_{node}) 是当前节点访问次数。第一项鼓励“看起来赢面大”的节点第二项鼓励“还没怎么试过”的节点参数 (c) 调节探索强度。这个公式配合合适的 (c)搜索就能做到“大方向集中、冷门分支也不会永远被饿死”。扩展Expansion当选择阶段走到叶节点且这个节点还没被访问过就把它加入树。注意选择阶段走到“未完全展开节点”时不一定马上扩展许多实现是“访问次数达到某个阈值再扩展”但更主流的做法是首次访问即扩展选中的叶节点。扩展后至少有一个节点变成内部节点后续选择就还能继续深入。模拟Simulation从新扩展的节点或者叶节点出发使用一个简单的默认策略如棋类里的走一步随机棋走到底得到一个终止回报。这个默认策略的质量决定了MCTS评估的方差。完全随机策略方差很大但如果真实转移次数够多大数定律能把这个噪声平均掉。回传Backpropagation把模拟得到的回报从叶节点一路上传更新路径上每个节点的平均回报和访问次数。回传只更新这一次选择所经过的路径不更新树的其余部分。这四个步骤我最初学的时候总觉得“啊这不就是随机采样取平均”真正跑起来才发现它的精髓在重复次数。模拟次数够多树的主要分支会自然生长在高回报子树上根节点孩子节点的访问频次分布逐渐成为动作优劣的投票指标。最终选动作可以从统计量里选比如选访问数最高的这在博弈理论里叫Robust Child策略也可以选平均回报最高的叫Max Child策略。实际上Max Child策略在短时间控制预算下表现更好Robust Child在精确评估下有更多优势。3.3 UCT公式理解与参数选择回到选择策略的公式这里有两个方向非常容易踩坑。第一个坑是把平均回报 (\bar{v}) 理解错了。如果任务里奖励恒为正(\bar{v}) 本身没问题。但如果奖励可正可负比如输赢返回1/-1你需要把回报映射到[0,1]区间再计算UCB公式否则探索项的相对占比会被奖励尺度拉偏。我在一个带惩罚项的棋盘任务上试过直接拿原始奖励跑搜索很快陷入“永远走安全但低收益路线”的局部最优把奖励归一化之后MCTS的探索行为才恢复正常。第二个坑是 (c) 参数的尺度。理论推导里 (c \sqrt{2}) 是UCT保证渐近最优性的上界但实际任务里这个值经常太小或太大。我的经验是把 (c) 看成“鼓励探索的强度”如果搜索树的平均访问深度明显偏浅、很多柱子下面几乎没孩子说明探索太弱调大 (c)如果树的形状宽而扁、每个分支都有大量访问但没有深度说明探索太强调小 (c)。给一个基线参考在离散控制任务里(c) 从0.5起步逐步往上加到1.5左右基本就能覆盖大多数需求在棋类这种长时程、零和回报任务里(c) 经常需要更大一些我常用区间是1.0~5.0具体必须根据每次模拟所得的回报分布量级自行缩放到[0,1]后微调。3.4 MCTS节点设计与实现要点写MCTS不用什么高级数据结构按我的习惯一个Node对象通常长这样class MCTSNode: def __init__(self, state, parentNone, last_actionNone): self.state state self.parent parent self.last_action last_action self.children {} # action - MCTSNode self.visits 0 self.total_reward 0.0 self.untried_actions [] # 尚未扩展的动作合集关键在untried_actions。这个列表保存当前状态下还没有被扩展过的动作扩展阶段每次从里面pop一个动作建孩子节点直到列表空这个节点才算完全展开。选择阶段如果某节点的untried_actions非空优先随机扩展一个新分支而不是继续走老分支。这个“先扩新后深挖”的顺序能显著提高搜索初期的动作覆盖率。回传更新时我习惯把total_reward和visits同时更新avg_reward total_reward / visits。如果你用动作空间比较大的任务每个节点的孩子可能上百这时可以给children加上访问频次的缓存或者使用先验策略比如神经网络输出的动作概率来缩小搜索分支这就是AlphaGo系列里面用策略网络指导选择阶段的雏形。没有先验策略时untried_actions的做法已经够用。真实的MCTS实现里还要注意一个问题模拟阶段的终止状态。定义模拟函数时要确保它能返回一个标量回报并且在整个树搜索期间我们要保证模拟不会走进违反物理规则的死循环。我在一个弹球控制任务上就因为模拟函数偶尔弹不出边界导致模拟最多跑100步就会进入“原地振荡”的死循环回传的回报毫无区分度。后来给模拟加上步数上限并在达到上限时返回一个中间值或惩罚值整个搜索质量立刻恢复。3.5 MCTS在实践中的魔改版本经典MCTS在许多场景下暴露出了明显短板因此衍生出几个常用魔改。第一是PUCTPolynomial Upper Confidence Tree带先验的UCT把策略函数的输出加入选择公式让选择阶段一开始就偏向先验概率高的分支避免前期完全随机探索。这个改动让搜索在动作空间特别大、模拟成本很高时依旧能保持有意义的树结构也是现代棋类AI的核心改造。第二是渐进红黑剪枝Progressive Widening不是一次性把所有的untried_actions排开而是随着节点访问次数增加逐步开放更多动作。这对连续动作空间很关键因为你不可能为每个连续动作建一个孩子节点。实践上先按动作离散化网格排列每访问 (k) 次开放一个新动作前期搜索集中后期拓宽效果很好。第三是解决问题结构比如运用可传递的状态特征做“元树”融合把相似状态的节点合并减少树规模。这种改造偏向研究性质工程里用得不多但如果你是复现某篇论文的实验会发现很多改进都是围绕“减少节点数、提高每节点信息质量”展开。4. Dyna与MCTS两张“模型使用说明书”4.1 同样是用模型为什么路径完全不同从使用模型的视角看Dyna和MCTS走的是不同层次的重用。Dyna把模型当作“数据增强器”思路是“我学到的Q更新公式很便宜模拟经验也能用同一套公式学习”。它的优点在于跟表格型Q学习无缝集成工程成本极低缺点是模型生成的经验分布跟真实经验分布往往有偏模拟次数过多时会被模型偏差主导导致最终策略比真实交互学到的还要偏离。MCTS则把模型当作“想象中台”每一轮都是从根状态重新做树搜索树的结构承载了当前状态下哪些路径值得重点推演的判断。它的优点是每轮搜完选出动作后树可以带着新的访问统计继续生长复用或冷启动重置天然适合“算一步走一步”的场景缺点是搜索预算有限时对非常稀疏的奖励环境不够敏感因为模拟次数太少树根本长不到能触碰奖励的深度。我的经验判断如果你有一个可交互的真实环境希望在相同步数内拿到更好策略优先考虑Dyna因为收获/改动比更高如果你有一个可靠的模拟环境需要在一次动作决策上做精细化推演比如下棋、上一步打牌、机器人规划下一步优先考虑MCTS。4.2 常见的混合实战写法两者不是严格二选一。我在一个室内导航任务里试过一种可行的组合底层模型用表格或者小网络规划器先用Dyna循环降低Q值估计方差然后用当前Q值作为默认策略辅助MCTS做更精细的搜索最后选择MCTS推荐的动作执行。注意这里搜索只在决策或“关键时刻”进行而不是每一步都完整跑完整棵树搜索预算压缩到几十次模拟即可。这样既享受了Dyna带来的全局快速收敛也利用MCTS对关键节点的局部精优化。如果你用神经网络的Actor-Critic架构混合路径类似用环境模型执行多步虚拟 rollout让Critic用这些路径的累计回报学习在决策的最后一环再用策略网络作为MCTS的默认策略policy prior提高模拟质量。这种混搭几乎是现代深度强化学习在样本效率瓶颈期的标准解法。4.3 什么时候不要用基于模型的方法说完了什么时候用也要聊聊什么时候别用。两种情况我强烈不建议上Model-based第一环境极不稳定比如同样是同一个状态动作下一次环境返回的下一状态和奖励跟之前大相径庭。这种情况下Agent拟合出来的模型永远来不及更新误差会迅速吞掉规划收益。第二奖励极其稀疏但状态动作空间很大并且没有可靠的先验策略。这时模型虽然能模拟但模拟本身缺乏“好轨迹”的引导树和Dyna都会在无效区域反复刷新白白耗费算力。我踩过最大的教训是在一个多机器人协作的仿真任务里用MCTS做主决策层协作对手策略频繁变化模型根本跟不上对手变化速度MCTS树里几乎全是过时信息。最后换成纯免模型的PPO变体才把问题解决。所以综合判断里有一条铁律模型必须是环境动态的可靠近似否则规划越多迷惑越大。5. 常见问题与排查技巧实录5.1 问题1模型越规划效果越差现象加Dyna规划后收敛速度不升反降甚至出现策略退化。原因模型误差太大。最常见的是表格型模型在连续状态里对边界行为描述不准或者神经网络模型在未充分覆盖的状态空间外推过头。排查思路把n_plan先调成0确认基础算法本身没问题。单独评估模型的“预测准确率”比如抽样历史状态让模型预测下一步状态看预测误差。误差大就说明模型拟合不足。把规划样本的来源从“模型生成”换成“真实经验池重放”如果效果回升说明问题一定出在模型偏差而不是规划机制本身。解决方案要么加真实样本的采集轮数要么调低规划步数要么用ensemble模型多个模型取均值对冲模型偏差。5.2 问题2MCTS选择阶段全往一个节点钻现象搜索树长成一根长条根节点只有一条路径被反复深挖其他分支无人问津。原因探索系数太小。UCB公式里的第二项几乎不起作用导致平均回报最高且访问次数最多的节点永远被选中。解决方案调大 (c)或者检查回报值是否未归一化。回报尺度远大于探索项时公式被第一项绑架。我习惯把回报转成[0,1]再配合 (c) 调参。5.3 问题3模拟阶段的默认策略太弱现象树节点访问次数涨得很快但最终选出来的动作和随机策略差不多没有提升。原因默认策略太随机模拟回报几乎全是噪声MCTS的评估结果跟抛硬币一样没有区分度。排查思路单独运行100次模拟看看模拟回报的方差有多大。方差过大时考虑用历史上表现好的轨迹池作为默认策略的一部分或者给模拟步数设定预算避免无意义地拖长。小技巧在棋类任务里默认策略可以用“前k步服从一个轻微优化的策略之后全随机”这样既能评估中点局面又能保留随机探索比全程模拟到底稳定得多。5.4 问题4树搜索预算分配不合理现象总模拟次数很大但树浅而扁深层的有效推演很少。原因节点访问次数门槛设太高模拟次数被大量消耗在新节点的探索上。解决检查扩展策略。每次进入未完全展开节点时是否“一定先扩未尝试动作”如果你的动作空间极大几乎不可能完全展开就改用渐进拓宽策略只在访问达到一定次数时开放新动作避免把预算浪费在冷门分支上。5.5 问题5模型在训练中逐渐“漂移”现象算法前期收敛顺利训练久了反而开始振荡甚至退化。原因策略不断进化的同时Agent访问的状态分布也在变化原本训练好的模型在新区间上预测不准而策略已经依赖旧模型的模拟经验新旧偏差叠加形成漂移循环。解决需要持续用最新真实经验去微调模型不要用全量旧数据加静态权重。我在实现里采用滑动窗口式经验池只保留最近N条经验用于模型更新并定期用验证集评估模型的预测误差一旦误差超过阈值就暂停规划或强制降低规划步数。6. 一些个人偏好的操作习惯顺着这个问题分享几个我在实际MCTS和Dyna调式过程中积累的操作习惯不一定通用但大多数场景都能减少无效折腾。第一所有基于模型的方法都值得先把模型单独拆出来做检验。不要一上来就端到端训练先单独训练模型用独立的测试状态集合评估预测误差误差合格后再把规划部分接回去。这样每个模块的错误都被限定在可控范围内定位问题会非常快。第二建议MCTS的根节点每次决策后缓存。如果上一轮搜索的根节点动作变成了本轮实际执行动作下一轮搜索的根节点可以直接从上一轮的孩子节点续接把已累积的访问次数、回报统计保留下来这样搜索预算可以大幅减少并且策略连续性也更好。第三如果任务允许在Dyna的模型更新里优先使用“增量学习”而不是重新全量训练。比如表格型模型有新经验就覆盖旧记录神经网络模型则用最近数据做几步梯度下降即可不要每次全部重训。否则算力会成倍上涨但收益甚微。第四把模型偏差当成一个可调的“自检信号”。我在很多项目里都会记录模型预测误差的滑动平均一旦误差超过阈值自动调低规划权重而不是固定不变。这个自适应机制让Dyna在环境变化时的鲁棒性明显提升。这些习惯不是书上教的都是跑实验跑到崩溃之后长出来的。基于模型的方法在原理上很吸引人但工程的细节往往比数学公式更影响最终效果。拿MCTS来说只用教科书里的四步骤描述去实现跟实际工程里反复调出来的版本差别可能很大。这一篇笔记我尽可能把能踩的坑和对应的解法都写下来了剩下就看你在自己的环境里怎么把参数和机制配平。