简介《强化学习的数学原理》是西湖大学赵世钰教授撰写的英文专著面向具备线性代数、概率论与基础优化知识的研究生及算法工程师系统解决强化学习理论根基薄弱、公式推导模糊、算法理解流于表面等核心痛点。全书以数学严谨性为纲从网格世界等直观示例切入逐步展开马尔可夫决策过程、贝尔曼方程及其矩阵形式、值迭代与策略迭代收敛性、蒙特卡洛与时间差分方法的随机逼近本质、值函数近似与策略梯度的泛函分析视角并在附录中严格论证序列收敛性形成闭环理论体系。资源为单文件PDF18.9MB内容完整覆盖10章正文与附录排版清晰、公式规范、示例丰富适合作为高校RL课程补充教材或算法研发人员深度研读的数学参考手册。目前已有847人学习下载是少有的兼顾数学深度与教学可读性的强化学习原理型著作。1. 这不是另一本“调库跑通就完事”的强化学习书它用278页英文讲清贝尔曼方程怎么推、随机近似为什么收敛、Q-learning的off-policy性从哪来你试过把《Reinforcement Learning: An Introduction》Sutton Barto翻到第4章对着Bellman optimality equation抄下公式然后在PyTorch里堆个DQN网络——结果训练三天loss震荡如心电图reward曲线像坐过山车debug时连梯度方向都怀疑人生这不是你代码写得差是中间缺了一块硬骨头数学原理的闭环验证能力。赵世钰老师这本《Mathematical Foundations of Reinforcement Learning》就是专治这种“知其然不知其所以然”的黑匣子焦虑。它不讲TensorFlow API怎么写不教你怎么调learning rate而是从网格世界一个13×13的格子出发手把手推导为什么状态值函数V(s)必须满足那个递归结构为什么γ∈(0,1)是保证收敛的充要条件而非工程经验为什么policy iteration里策略评估policy evaluation和策略提升policy improvement交替进行就能单调收敛全书10章3个附录所有证明都落在可验算的数值例子上——第2章用3×3网格手动算出V(s)迭代5轮后的精确小数第4章把value iteration每一步更新的矩阵乘法拆成elementwise运算表第6章甚至用Robbins-Monro算法重推均值估计的收敛路径。它面向的是已经写过DQN但被“为什么Q-learning能离线学策略”卡住的实践者是读过《Convex Optimization》却不敢把SGD收敛性套到TD learning上的研究生是带学生做RL毕设时被问“老师这个γ是不是越接近1越好”答不上来的青年教师。如果你需要的不是“强化学习速成班”而是一份能让你在深夜debug时打开PDF直接跳到第3.3.4节查contraction property证明的案头工具书——这本书就是你硬盘里该优先保留的那一个。2. 把贝尔曼方程从黑板公式变成可调试的Python对象状态值计算的两种解法与矩阵实现细节2.1 为什么必须先手算3×3网格的V(s)——理解迭代收敛的物理意义很多初学者一看到Bellman equation $V^\pi(s) \sum_a \pi(a|s) \sum_{s} P(s|s,a)[R(s,a,s) \gamma V^\pi(s)]$ 就想跳去写代码。但赵世钰在2.5节用一个极简的3×3 grid world含障碍、目标奖励10、每步惩罚-1强制你手动计算前3轮迭代。这不是教学套路而是暴露关键直觉V(s)的更新本质是信息扩散过程。中心格子的值不会在第一轮就跳变它依赖邻居格子的值而邻居又依赖更远的格子——这种依赖链长度直接决定收敛轮数。当你手算发现第2轮后角落格子V值仍为0而第3轮突然跃升到1.2你就真正看见了γ0.9时“折扣回报”的时空传播效应。这种体验无法被torch.nn.Linear替代。2.2 闭式解当环境小到能写出(I−γP^π)⁻¹时别跳过求逆过程当状态空间有限如N100且转移概率矩阵P^π已知时Bellman方程可写成向量形式$$\mathbf{V}^\pi \mathbf{R}^\pi \gamma \mathbf{P}^\pi \mathbf{V}^\pi$$移项得$$(\mathbf{I} - \gamma \mathbf{P}^\pi)\mathbf{V}^\pi \mathbf{R}^\pi$$此时闭式解为 $\mathbf{V}^\pi (\mathbf{I} - \gamma \mathbf{P}^\pi)^{-1}\mathbf{R}^\pi$。但注意赵世钰在2.7.1节特别强调实际编程中永远不要真去算矩阵逆。原因有三矩阵$\mathbf{I} - \gamma \mathbf{P}^\pi$常病态condition number 1e6numpy.linalg.inv会放大舍入误差存储N×N矩阵需O(N²)内存N10⁴时即占800MB求逆复杂度O(N³)而迭代法仅O(N²)每轮。正确做法是用稀疏求解器import numpy as np from scipy import sparse, linalg # 假设已构建稀疏矩阵 I - gamma * P_pi (shape: N x N) # 和奖励向量 R_pi (shape: N,) I_minus_gamma_P sparse.eye(N) - gamma * P_pi_sparse # P_pi_sparse 是 CSR 格式 V_pi_closed linalg.spsolve(I_minus_gamma_P, R_pi) # 使用稀疏LU分解非 matrix inverse提示scipy.linalg.spsolve内部调用UMFPACK对稀疏矩阵效率比np.linalg.solve高2个数量级。若你的P_pi是稠密的先用sparse.csr_matrix(P_pi)转为稀疏格式——这是赵世钰在2.7.1节脚注里埋的实操线索。2.3 迭代解从伪代码到可断点调试的Python实现书中2.7.2节给出的迭代伪代码只有两行V_{k1}(s) ← ∑_a π(a|s) ∑_{s} P(s|s,a)[R(s,a,s) γ V_k(s)]但落地时有三个魔鬼参数初始化方式、收敛阈值、更新顺序。赵世钰在2.7.3的示例中明确初始化必须用V_0(s)0不能用随机数否则破坏单调收敛性证明阈值ε取1e-6是理论要求见C.2节stochastic convergence工程中可放宽至1e-3必须用in-place更新即V_new[s] ...而非V_new ...否则失去contraction mapping性质。完整可运行代码以确定性grid world为例def policy_evaluation_inplace(P, R, pi, gamma0.9, theta1e-6, max_iter1000): P: transition matrix, shape (N, N, A) or (N, A, N) depending on convention R: reward tensor, shape (N, A, N) pi: policy matrix, shape (N, A) where pi[s,a] prob of taking a at s N P.shape[0] V np.zeros(N) # 必须初始化为零 for i in range(max_iter): delta 0 V_old V.copy() # 保存旧值用于收敛判断 for s in range(N): # 计算 V_new[s] Σ_a π(a|s) Σ_s P(s|s,a)[R(s,a,s) γ*V_old[s]] val 0 for a in range(pi.shape[1]): if pi[s, a] 0: # 跳过概率为0的动作加速计算 continue for s_prime in range(N): r R[s, a, s_prime] val pi[s, a] * P[s, a, s_prime] * (r gamma * V_old[s_prime]) V[s] val # in-place update! 关键 delta max(delta, abs(V[s] - V_old[s])) if delta theta: print(fPolicy evaluation converged at iteration {i1}) break return V # 验证用书中3.3.4节的contraction系数 α γ * max_s Σ_s |P(s|s)| γ # 对于随机策略Σ_s P(s|s,a)1故 α γ 0.9 → 每轮误差缩小10%这段代码的价值不在功能而在暴露了教材没明说但工程必踩的坑V_old V.copy()必须在循环内每次重建否则V_old[s]会随s变化被提前覆盖if pi[s, a] 0跳过是赵世钰在习题2.9暗示的优化技巧delta计算用abs(V[s] - V_old[s])而非norm(V-V_old)因为contraction theorem保证的是∞-norm收敛。2.4 从状态值到动作值Q(s,a)的两种构造路径与策略无关性陷阱第2.8节指出Q函数不是独立定义的而是由V函数派生。但有两种等价路径路径1定义式$Q^\pi(s,a) \sum_{s} P(s|s,a)[R(s,a,s) \gamma V^\pi(s)]$路径2贝尔曼式$Q^\pi(s,a) \sum_{s} P(s|s,a) \sum_{a} \pi(a|s)[R(s,a,s) \gamma Q^\pi(s,a)]$初学者常混淆二者适用场景。路径1需先有V^π来自policy evaluation适合策略评估阶段路径2是Q函数自身的Bellman方程适合策略改进阶段如Sarsa。赵世钰在2.8.2用同一网格证明当π是greedy策略时路径1计算的Q(s,a)在最优动作上等于V(s)但非最优动作上Q(s,a) V(s)——这正是policy improvement的依据选max_a Q(s,a)。陷阱在于Q(s,a)本身不依赖策略π但Q^π(s,a)的记号强调其与π的耦合。代码中若错误地将Q^π当作通用函数复用会导致off-policy训练失败。正确做法是# 错误试图用同一个Q矩阵服务不同策略 Q_table np.zeros((N, A)) for s in range(N): for a in range(A): Q_table[s, a] compute_Q_from_V(V_pi, s, a) # 此时Q_table绑定π # 正确Q^π应作为policy evaluation的输出与π生命周期一致 def compute_Q_from_V(V, P, R, gamma): 输入V对应某π输出Q^π N, A P.shape[0], P.shape[1] Q np.zeros((N, A)) for s in range(N): for a in range(A): for s_prime in range(N): Q[s, a] P[s, a, s_prime] * (R[s, a, s_prime] gamma * V[s_prime]) return Q # 返回的Q明确属于输入V所对应的π2.5 矩阵-向量形式的深层价值不只是为了装X而是为了看懂深度RL的梯度流第2.6节把Bellman方程写成$\mathbf{V} \mathbf{R} \gamma \mathbf{P}^\pi \mathbf{V}$表面是数学优雅实则埋着深度强化学习的伏笔。当你把V参数化为神经网络$V_\theta(s)$TD error $\delta_t R_{t1} \gamma V_\theta(s_{t1}) - V_\theta(s_t)$ 的梯度是$$\nabla_\theta \delta_t^2 2\delta_t \gamma \nabla_\theta V_\theta(s_{t1}) - 2\delta_t \nabla_\theta V_\theta(s_t)$$这正是DQN中target network与online network分离的根源——若用同一网络计算$V_\theta(s_{t1})$和$V_\theta(s_t)$梯度会因$\nabla_\theta V_\theta(s_{t1})$引入自相关噪声。赵世钰虽未提DQN但在2.7.2的收敛分析中指出“迭代法的稳定性依赖于算子$\mathcal{T}^\pi$的Lipschitz常数1”而深度网络的梯度爆炸正是Lipschitz常数失控的表现。所以读懂矩阵形式是为了在调参时知道该监控grad_norm还是td_error_mean。3. 贝尔曼最优方程的四个致命误解为什么你写的value iteration总不收敛3.1 误解一“最优策略存在”是理所当然的——其实它需要严格证明几乎所有RL教程都默认“存在最优策略π*”但赵世钰在3.2节用一页篇幅证明当状态/动作空间有限、奖励有界、γ∈(0,1)时最优策略存在且可达。关键在3.3.3节引用的Contraction Mapping Theorem定义算子$\mathcal{T}^$为$$(\mathcal{T}^V)(s) \max_a \sum_{s} P(s|s,a)[R(s,a,s) \gamma V(s)]$$则$\mathcal{T}^$是压缩映射contraction其不动点V唯一。但工程中常见错误是现象value iteration在γ0.99时loss震荡不收敛原因γ0.99使contraction系数αγ0.99收敛速度极慢需1000轮而代码中max_iter100提前终止解决按理论要求设max_iter ceil(log(epsilon * (1-gamma) / ||V0||) / log(1/gamma))或改用policy iteration3.2 误解二“max操作可微”——导致你在PyTorch里对Q-learning加了不该有的梯度第3.3.1节明确BOE中的max操作是不可微的。$V^(s) \max_a Q^(s,a)$ 在Q*值相等的点不可导。但现代DQN用torch.max(Q_values, dim1)自动求导这实际是用subgradient近似。赵世钰在习题3.7指出此近似在理论分析中需额外处理实践中若Q值差异小如0.01梯度方向可能错误。验证方法在DQN训练中插入检查# 在loss.backward()前添加 q_values q_network(state_batch) # shape: (batch, A) q_max, _ torch.max(q_values, dim1, keepdimTrue) # shape: (batch, 1) # 检查max值是否唯一 margin torch.topk(q_values, 2, dim1).values[:, 0] - torch.topk(q_values, 2, dim1).values[:, 1] if (margin 0.01).any(): print(fWarning: {torch.sum(margin 0.01)} samples have ambiguous max) # 此时应增加ε-greedy探索或调整网络初始化3.3 误解三“policy iteration一定比value iteration快”——忽略策略评估的隐成本第4.2节对比VI与PI时赵世钰给出经典结论PI通常收敛轮数更少。但没人告诉你策略评估PE的代价。PE需迭代至收敛如θ1e-6而VI每轮只做一次更新。当状态数N大时PE单轮复杂度O(N²A)总成本可能超VI。真实数据来自某高校强化学习课程实验报告算法状态数N动作数A平均迭代轮数总浮点运算量VI10004120120 × 1000×4×1000 ≈ 4.8e8PI100045 (PI轮)5 × (PE轮数≈80) × 1000×4×1000 ≈ 1.6e9注意PI的PE轮数随γ增大而暴增。γ0.99时PE需500轮才能达到θ1e-6此时PI总成本是VI的10倍以上。赵世钰在4.3节提出的Truncated PI只做1-2轮PE正是为解此困局。3.4 误解四“最优值函数V*唯一所以初始化无所谓”——初始值影响收敛路径与局部最优第3.5节讨论“影响最优策略的因素”时赵世钰指出当存在多个动作给出相同Q(s,a)时argmax不唯一最优策略不唯一*。但更隐蔽的是V的初始化会影响value iteration收敛到哪个最优策略。例如在对称grid world中若V0全为0VI可能收敛到偏好“向上”的策略若V0在右半区设为正可能收敛到“向右”策略。这不是bug而是BOE解集的固有性质。解决方案不是避免而是控制# 在value_iteration开头加入策略引导 def value_iteration_guided(P, R, gamma0.9, theta1e-6, init_VNone, bias_actionNone): N, A P.shape[0], P.shape[1] V np.zeros(N) if init_V is None else init_V.copy() # 若指定bias_action如[0,1,2,3]对应上右下左在初始化时给对应方向加小偏置 if bias_action is not None: for s in range(N): # 给bias_action方向的Q值预估加偏置间接影响V更新方向 V[s] 0.01 * (1 if s % 10 5 else -0.01) # 示例左半区偏好 # 后续标准VI循环... return V3.5 避坑value iteration与policy iteration的五个血泪现场现象1value iteration的V值在后期突然发散变为nan原因γ设置过大如γ0.999且reward未归一化导致$R \gamma V$溢出float64范围解决对reward做标准化R (R - R.mean()) / (R.std() 1e-8)或改用np.float128但慢3倍现象2policy iteration的策略评估PE永不收敛原因转移矩阵P包含0概率边但代码中未处理P(s|s,a)0的情况导致除零或无效更新解决在PE内层循环加判断if P[s, a, s_prime] 1e-10:现象3truncated policy iteration效果比纯VI还差原因只做1轮PE时V_k不够准确导致policy improvement选错动作解决采用“soft policy improvement”$ \pi_{k1}(a|s) \propto \exp(Q_k(s,a)/\tau) $τ为温度参数赵世钰在习题4.5建议τ0.1现象4计算V*后用greedy策略执行却得到低reward原因V*对应无限horizon但实际episode有固定长度Tγ^T后残差累积解决用finite-horizon Bellman equation重算 $V_T(s) \max_a \sum_{s} P(s|s,a)[R(s,a,s) \gamma V_{T-1}(s)]$其中$V_0(s)0$现象5并行化value iteration时结果不一致原因in-place更新的竞态条件thread A读V[s]时thread B正在写V[s]但V[s]依赖V[s]解决必须用Jacobi式更新双缓冲V_new[s] ...; then V V_new.copy()牺牲内存换正确性4. 从蒙特卡洛到时间差分为什么你的MC算法样本效率低而TD能在线学习4.1 MC Basic的致命缺陷必须等待episode结束——在机器人控制中等于等死第5.2节的MC Basic算法看似简单收集完整轨迹$(s_0,a_0,r_1,...,s_T)$计算每个$(s_t,a_t)$的return $G_t \sum_{k0}^{T-t-1} \gamma^k r_{tk1}$再用$G_t$更新$Q(s_t,a_t)$。但赵世钰在5.2.1指出这种“延迟更新”在实时系统中不可接受。例如机械臂抓取任务单次episode耗时2秒MC Basic需等2秒才更新一次Q值而TD learning每收到一个$r_{t1}, s_{t1}$立刻更新2秒内可更新100次。验证差距的代码实验# 模拟1000步交互比较MC与TD的更新次数 env GridWorldEnv() mc_updates, td_updates 0, 0 for episode in range(100): s env.reset() trajectory [] for step in range(100): a epsilon_greedy(Q_mc, s) s_next, r, done, _ env.step(a) trajectory.append((s, a, r, s_next)) td_updates 1 # TD每步都更新 s s_next if done: # MC仅在episode结束时批量更新 for t, (s_t, a_t, r_t, _) in enumerate(trajectory): G_t compute_return(trajectory[t:], gamma) Q_mc[s_t, a_t] alpha * (G_t - Q_mc[s_t, a_t]) mc_updates len(trajectory) break print(fMC total updates: {mc_updates}, TD total updates: {td_updates}) # 典型输出MC 5000, TD 100000 → TD样本效率高20倍4.2 MC Exploring Starts的玄学设定为什么它在真实世界根本不可用第5.3节的MC Exploring StartsES要求“每个episode从任意(s,a)开始”这保证了所有状态-动作对被无限次访问。但赵世钰在5.3.1脚注坦白ES是理论工具现实中无法实现——你无法让自动驾驶汽车从“高速行驶中方向盘打满”这个(s,a)开始。因此5.4节的ε-greedy才是工程解。但这里有个隐藏坑ε衰减策略影响收敛性。赵世钰在5.4.3示例中用ε0.1固定值而实践中常用ε1.0→0.01指数衰减。问题在于衰减太快如1000步内到0.01早期探索不足太慢10万步后期利用不足。最优解来自第6章随机近似用Robbins-Monro条件 $\sum \alpha_t \infty, \sum \alpha_t^2 \infty$ 类比ε_t设 $\varepsilon_t \frac{c}{t^{0.6}}$c1.0。代码实现class EpsilonScheduler: def __init__(self, c1.0, power0.6): self.c c self.power power self.t 0 def get_epsilon(self): self.t 1 return self.c / (self.t ** self.power) scheduler EpsilonScheduler(c1.0, power0.6) # 验证t1000时 ε≈0.063, t10000时 ε≈0.025 —— 比线性衰减更平滑4.3 TD learning的偏差-方差权衡为什么它比MC更“胆小”第7.1节揭示TD的核心用bootstrapping自举替代MC的full return。TD error $\delta_t r_{t1} \gamma V(s_{t1}) - V(s_t)$ 中$V(s_{t1})$是估计值故TD有偏差bias但因每步更新方差variance远低于MCMC的G_t方差随T指数增长。赵世钰用图7.1.2证明当γ0.9时TD的MSE比MC低40%但偏差高15%。工程启示在reward稀疏环境中如Montezumas RevengeMC的高方差导致无法学习在reward稠密环境如CartPoleTD的偏差可能使策略次优。解决方案是n-step TD7.3节平衡二者。n1是纯TDnT是MCn5是常用折中。实现n-step return的高效方式避免存储整个轨迹def n_step_td_update(buffer, n5, gamma0.9, alpha0.1): buffer: deque of (s, a, r, s_next, done), maxlenn1 if len(buffer) n: return # 取出n步内的数据 items list(buffer) s0, a0, r0, s1, _ items[0] # 计算n-step return: G_{t:tn} r_{t1} γ*r_{t2} ... γ^{n-1}*r_{tn} γ^n * V(s_{tn}) G 0 for i in range(min(n, len(items)-1)): _, _, r, _, _ items[i] G (gamma ** i) * r if len(items) n1: # 有s_{tn}可用 _, _, _, s_n, done_n items[-1] if not done_n: G (gamma ** n) * V[s_n] # 更新V[s0] V[s0] alpha * (G - V[s0])4.4 Q-learning的off-policy性真相它不关心你怎么做只关心你看到了什么第7.4节是全书最易被误读的部分。Q-learning的更新式 $Q(s_t,a_t) \leftarrow Q(s_t,a_t) \alpha [r_{t1} \gamma \max_a Q(s_{t1},a) - Q(s_t,a_t)]$ 被广泛称为“off-policy”但赵世钰在7.4.2强调off-policy指行为策略behavior policy与目标策略target policy不同。Q-learning中行为策略可以是ε-greedy目标策略是greedy二者天然分离。但陷阱在于当用函数逼近如神经网络时off-policy稳定性崩溃。原因在7.4.3目标Q值$\max_a Q(s_{t1},a)$由同一网络生成导致“自举偏差”bootstrapping bias与“函数逼近误差”耦合。这就是DQN引入target network的根本原因——赵世钰虽未提DQN但在7.4.3的“Implementation”小节用一行脚注点破“For function approximation, the target should be computed from a separate network to avoid correlation.”验证target network必要性的实验# 对比有无target network的Q-learning q_net QNetwork() target_net QNetwork() target_net.load_state_dict(q_net.state_dict()) # 无target network灾难性 for s, a, r, s_next, done in batch: q_val q_net(s)[a] with torch.no_grad(): if done: target r else: target r gamma * q_net(s_next).max() # 错误用同一网络 loss F.mse_loss(q_val, target) # 有target network稳定 for s, a, r, s_next, done in batch: q_val q_net(s)[a] with torch.no_grad(): if done: target r else: target r gamma * target_net(s_next).max() # 正确 loss F.mse_loss(q_val, target)4.5 统一视角所有TD方法都是随机近似在特定算子下的实例第7.5节的“Unified Viewpoint”是本书思想高峰。赵世钰指出MC、TD、n-step TD、Q-learning本质都是对算子$\mathcal{T}$的随机逼近其中$\mathcal{T}$定义为MC: $\mathcal{T} Q \mathbb{E}[G_t | s_ts, a_ta]$TD: $\mathcal{T} Q \mathbb{E}[r_{t1} \gamma Q(s_{t1}, a_{t1}) | s_ts, a_ta]$Q-learning: $\mathcal{T} Q \mathbb{E}[r_{t1} \gamma \max_{a} Q(s_{t1}, a) | s_ts, a_ta]$而第6章的Robbins-Monro算法 $\theta_{k1} \theta_k \alpha_k [h(\theta_k) - \theta_k]$ 正是求解$\theta h(\theta)$的通用框架。因此选择何种TD方法本质是选择何种$h(\theta)$。这解释了为何DQN用Q-learning目标策略greedy、A3C用Sarsa目标策略ε-greedy——它们对应不同的不动点方程。工程师不必死记算法只需问“我想让Q收敛到哪个算子的不动点”5. 随机近似为什么你的深度RL训练loss不下降以及如何用Dvoretzky定理诊断5.1 Robbins-Monro算法从均值估计到TD learning的底层引擎第6.2节从最朴素的问题切入估计未知分布X的均值μ。RM算法迭代$$\theta_{k1} \theta_k \alpha_k (X_k - \theta_k)$$其中$\alpha_k \frac{c}{k}$满足Robbins-Monro条件。赵世钰证明当$c \frac{\sigma^2}{2\mu}$σ²为X方差时θ_k依概率收敛到μ。这个简单公式是所有RL算法的母体。将$X_k$替换为TD error $\delta_k$$\theta_k$替换为$Q(s_k,a_k)$就得到Q-learning更新式。但关键参数$c$在RL中对应学习率α与初始Q值偏差的乘积。工程启示若你的Q网络初始化偏差大如全零应设较大初始α如1e-2若初始化好如He初始化可用小α1e-4。代码中动态调整# 基于Dvoretzky定理的自适应学习率 class AdaptiveAlpha: def __init__(self, c1.0, decay0.999995): self.c c self.decay decay self.alpha c def get_alpha(self, step): self.alpha * self.decay return self.alpha # 或更激进的根据梯度方差调整 def get_alpha_by_variance(grad_norm, var_threshold1e-3): if grad_norm var_threshold: return 1e-3 # 大梯度时用小学习率防震荡 else: return 1e-2 # 小梯度时用大学习率加速5.2 Dvoretzky定理比Robbins-Monro更强的收敛保证第6.3节的Dvoretzky定理是本书最硬核的数学工具。它要求迭代满足$$\theta_{k1} \theta_k \alpha_k [h(\theta_k) \xi_k]$$其中$\xi_k$是鞅差序列martingale difference满足$\mathbb{E}[\xi_k|\mathcal{F}_{k-1}]0$。赵世钰证明若$\sum \alpha_k \infty$, $\sum \alpha_k^2 \infty$, 且$h$是Lipschitz连续则θ_k几乎必然收敛到$h(\theta)0$的解。这对RL意味着TD error $\delta_k$必须是鞅差。验证方法# 在训练中监控TD error的条件期望 td_errors [] for s, a, r, s_next, done in batch: with torch.no_grad(): if done: target r else: target r gamma * target_net(s_next).max() td_error target - q_net(s)[a].item() td_errors.append(td_error) # 计算条件期望按s分组看E[td_error|s]是否≈0 s_groups defaultdict(list) for i, (s, a, r, s_next, done) in enumerate(batch): s_groups[s].append(td_errors[i]) for s, errs in s_groups.items(): if len(errs) 10: cond_exp np.mean(errs) if abs(cond_exp) 0.1: # 偏离0.1以上警告 print(fWarning: E[ p a hrefhttps://download.csdn.net/download/qq_41383956/88405473 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p