ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

深入理解强化学习中的重要性采样:原理、应用与工程实践

深入理解强化学习中的重要性采样:原理、应用与工程实践 1. 为什么强化学习总是绕不开重要性采样1.1 一句话理解把数据分布“扳”回来重要性采样这四个字我在刚学强化学习的时候没少头疼。当时看到策略梯度公式里突然冒出一个π(a|s) / b(a|s)第一反应是“凭什么多乘这么个东西”。后面把这件事彻底想明白之后才发现它其实就是一句话你的数据是用旧策略采的但你想算的是新策略下的期望中间差了一个分布那就用重要性采样把它扳回来。这句话值得反复咀嚼。强化学习里有个很尴尬的矛盾你想评估一个策略好不好但真正能采集到的数据几乎永远来自另一个策略。比如你训练机械臂一组轨迹是上一版控制策略跑出来的现在你想知道当前这版策略跑同样的任务能拿多少分总不能再花几小时重新采一轮数据吧更现实的需求是把往期所有历史轨迹攒成一个数据集反复用来训练新策略省时省力。可一旦这么做数据分布和目标分布就不一致了。直接用这些数据估计目标策略的期望回报必然有偏。重要性采样解决的就是这个“分布错位”问题。它不改变样本本身只是给每个样本乘一个权重。这个权重是目标分布和采样分布的密度比值。简单说某个样本在目标策略下更容易出现就把它的重要性放大在目标策略下几乎不可能出现就把它的影响力压下去。逻辑非常朴素但当你真正在代码里跑起来会发现这个“朴素”的工具有一堆隐藏的坑。这篇文章我想从数学原理讲到代码实现再把我实际操作中踩过的坑一次说清楚。1.2 从期望的定义出发看懂那个权重先不急着往强化学习上靠我们把问题抽象成一个纯概率题。假设有一个函数f(x)你想计算它在分布p(x)下的期望[ E_{x \sim p}[f(x)] \int f(x)p(x)dx ]但你手上没有从p(x)采样的数据只有从另一个分布q(x)采的样本。这时候做个简单的代数变换[ E_{x \sim p}[f(x)] \int f(x)\frac{p(x)}{q(x)}q(x)dx E_{x \sim q}\left[f(x)\frac{p(x)}{q(x)}\right] ]于是用q的样本估计p的期望只需要给每个样本乘上权重[ w(x)\frac{p(x)}{q(x)} ]这就是重要性采样这个工具的完整推导。说白了它就是把积分里的“采样测度”换掉用密度比来补偿换测度带来的偏差。公式看起来干净利落但这里有一个前提条件必须满足当p(x) 0时q(x)也必须大于 0。用行话说就是p的支撑集必须包含在q的支撑集里。如果某个点在目标策略下可能发生但行为策略完全采不到那这个权重就是无穷大整个估计直接失控。放到强化学习场景里p是目标策略 π 诱导的轨迹分布q是行为策略 b 诱导的轨迹分布。轨迹是一串状态动作对所以密度比也不是单个动作的概率之比而是一整条轨迹上所有动作概率比的乘积[ \rho_{0:T} \prod_{t0}^{T}\frac{\pi(a_t|s_t)}{b(a_t|s_t)} ]在后面的实战部分你会看到这个“乘积”恰恰是最容易爆掉的地方。1.3 on-policy 和 off-policy重要性采样为什么在这里扎了根如果只看公式重要性采样是个普适的数学工具。但它在强化学习里被讨论得最多根本原因是强化学习分成了两类学习范式on-policy 和 off-policy。on-policy 的意思是采数据的策略和你要更新的策略是同一个。此时π b每个时间步的密度比都等于 1重要性权重完全退化成常数。这类算法比如 REINFORCE 的最简单版本、A2C 之类训练时天然规避了分布错位问题。但代价是数据利用率低采完一批数据更新一步策略这批数据就“作废”了下个版本的策略还得重新采。off-policy 的意思是采数据的策略和目标策略不是同一个。数据可能来自旧版本策略来自一个专门加了噪声的探索策略甚至来自一个别人记录下来的静态数据集。这种情况下如果不修正分布差异更新方向就是错的。所以你会发现几乎所有 off-policy 算法——无论是经验回放里的 DQN还是离线强化学习里的 IQL再到策略梯度家族里的 PPO——都得在“如何处理这个分布差”上做文章。有的通过重要性采样显式加权有的通过“软”加权隐式修正。理解了这层关系再看各种算法你就能把它们串到同一条线上了。2. 强化学习里重要性采样最常见的四个落点2.1 策略梯度里的显式权重PPO 和 TRPO 的真相策略梯度方法是最直白使用重要性采样的地方。原版策略梯度的目标是最大化期望回报J(π)E_{τ~π}[R(τ)]。对参数求梯度后变成对一个带 log 概率的式子求期望[ \nabla J E_{\tau \sim \pi}\left[\sum_t \nabla_\theta \log \pi_\theta(a_t|s_t) R_t\right] ]问题来了期望是相对于 π 的但手头数据来自旧策略 π_old。把这批数据直接代入上面的公式梯度就有偏。用重要性采样把期望换到旧策略下[ \nabla J E_{\tau \sim \pi_{old}}\left[\left(\prod_t \frac{\pi_\theta(a_t|s_t)}{\pi_{old}(a_t|s_t)}\right)\sum_t \nabla_\theta \log \pi_\theta(a_t|s_t) R_t\right] ]TRPO 和 PPO 本质上都在处理这个式子。因为完整轨迹级权重乘积的方差太大真实工程里普遍使用一种简化的“per-step”版本每个时间步只乘当前步的密度比ρ_t再配一个 clip 操作把权重限制在可信区间内。PPO 的截断目标函数长这样[ L^{CLIP} E_t \left[\min\left(\rho_t A_t, \operatorname{clip}(\rho_t, 1-\epsilon, 1\epsilon) A_t\right)\right] ]这里A_t是优势函数ρ_t是新旧策略在当前动作上的概率比。clip 的意义是如果某个样本的权重超出[1-ε, 1ε]说明这个采样点在旧策略下很稀罕它的信息可靠性存疑那就别让它对你的梯度产生过大影响。这就是 PPO 所谓“保守更新”的数学本质它不是为了剪而剪是防重要性采样的方差爆炸。2.2 经验回放与离线强化学习有时“隐形”有时“同源异流”DQN 这类值函数算法也做 off-policy 学习却没有显式的密度比权重。原因是 Q-learning 家族的更新目标是[ Q(s,a) \leftarrow Q(s,a) \alpha\left(r \gamma \max_{a} Q(s,a) - Q(s,a)\right) ]TD 目标里的 max 算子已经把“策略会选最大 Q 对应动作”这个信息内置了行为策略是什么并不直接影响目标值。所以 DQN 不需要乘重要性权重就能在经验回放里工作。这是值函数类方法相对于策略梯度类方法的一个天然优势。但离线强化学习没那么轻松。静态数据集来自某个未知行为策略如果再沿用 max 算子很容易把一个在数据集中极少出现的动作的 Q 值“幻觉”得极高。IQLImplicit Q-Learning论文里对这一点的处理就是不再对下一个状态做 max而是用分位数回归只保留优势为正的经验变相降低了那些异常动作的影响。ARW 和 AWR 系列方法则直接对 advantage 做指数加权权重形式是[ \exp\left(\frac{A(s,a)}{\beta}\right) ]这个指数加权跟密度比加权有相似气质都是在给“分布里少见但收益大”的样本重新分配权重。理解重要性采样之后再去看这类离线算法你会觉得它们只是在用不同的软权重解决同一个问题如何从行为策略的分布中去估计目标策略想要的东西。2.3 离线策略评估用老数据预估新策略的回报这是重要性采样用得最“纯”的一个场景。强化学习里有个实际问题训练了一个新策略但在真机或真环境里评估太贵能不能直接用已有的一批历史轨迹估算一下这个新策略能拿多少分这个问题叫 Off-Policy EvaluationOPE。最简单的做法就是用轨迹级重要性权重每条历史轨迹都乘以这条轨迹在新策略下的概率与行为策略下的概率之比再对加权回报求平均。如果历史轨迹里恰好有一条路线与新策略高度吻合它的权重就大对最终估计的贡献就大。同时用加权重要性采样WIS代替普通重要性采样让权重和归一化能显著压低方差。后面第 3 节会细讲这两种估计器的差别。实际做策略筛选的时候我一般会同时算普通 IS、WIS再看两者差距大小。如果两者结果差得很远基本可以确定数据分布与新策略重叠太少这个评估结果信不得。2.4 多源数据混合训练时的分布校正另一个不那么被注意、但工程上很常见的场景你把不同版本策略采的数据合并成一个训练池。比如机械臂强化学习上午用旧版控制器跑了 200 条轨迹下午改了个参数又跑了 300 条晚上直接一股脑喂给训练程序。这时候每个样本的真实来源是一个混合分布而不是单一行为策略。严格来说密度比应该是目标策略和这个混合分布的比值。混合分布的密度函数是各成分策略密度的加权平均[ b_{\text{mix}}(a|s) \sum_k \alpha_k b_k(a|s) ]你可以在采样时记录每个轨迹实际来自哪个策略然后计算π(a|s) / b_k(a|s)再按混合系数加权。很多人踩过这样的坑直接把所有历史数据当成同一个行为策略处理导致重要性权重忽大忽小训练曲线抖动得像心电图。把多源数据做分布校正你会发现训练稳定度有明显提升。3. 为什么重要性采样的方差会爆炸以及几种主流补救方案3.1 期望修对了但分布的“尾巴”藏着一个放大器很多人第一次跑带重要性采样的代码时会很困惑权重明明算对了期望的理论修正也无懈可击为什么训练还是发疯原因在于期望只是“一阶矩”方差是“二阶矩”而方差直接受权重的大小波动影响。看方差公式[ \mathrm{Var}_{q}[w(x)f(x)] E_q[(w(x)f(x))^2] - (E_q[w(x)f(x)])^2 ]如果p和q的形状差异很大w(x)的取值会剧烈变化。举个极端例子90% 的样本权重是 0.0110% 的样本权重是 9.01期望上平均权重是 1但平方之后那 10% 的样本贡献了绝大部分方差。这时候你的估计值基本被极少数大权重样本牵着走训练曲线就会疯狂震荡。更糟的是重要性采样的权重是无上界的。如果目标策略在某个动作上的概率为 0.1行为策略只在 0.001 的概率下做过这个动作那么这单个样本的权重就是 100。在梯度计算里一个样本的梯度有可能比其余几百个样本加起来的梯度还大。这个现象就是强化学习里常见的“梯度爆炸”根源之一。3.2 普通 IS、加权 IS、裁剪 IS怎么选既然标准 IS 方差大常见补救有三条路线我直接放在一起对比估计器更新方式偏差方差适用场景普通 IS(\hat{V}_{IS}\frac{1}{n}\sum_i w_i f(x_i))无偏大权重无上界样本量大、分布偏移可控加权 IS (WIS)(\hat{V}_{WIS}\frac{\sum_i w_i f(x_i)}{\sum_i w_i})有偏偏差约 O(1/n)小权重被归一化样本有限、离线策略评估裁剪 IS(\rho \leftarrow \min(\rho, 1\epsilon)) 或区间裁剪有偏但偏差可解释受控严格有界策略优化、PPO 类算法普通 IS 的“无偏”听起来很棒但在强化学习这种方差本来就很高的场景里无偏往往意味着“偶尔给你一个离谱值”。WIS 虽然产生了微小偏差但这个偏差会随着样本量增大而消失而方差的抑制是立竿见影的。裁剪 IS 最激进直接把权重的上限封死但它是有意引入偏差的——本质上是信任区域思想的体现如果某个权重太大说明这个样本在旧策略下已经不可信了那就不让它指导更新了。我自己的经验是在做策略评估时优先用 WIS同时汇报普通 IS 作为暴力检查在做策略优化时用裁剪 IS上限一般设到 10 或 20PPO 的标准做法是 clip 到1±εε 常取 0.2这个值在多数任务里都算稳。绝对不要裸用普通 IS 去训练一个长 horizon 强化学习任务那不是训练是赌博。3.3 有效样本量ESS用数字判断你的估计还活着很多人不知道重要性采样有一个很方便的诊断指标有效样本量Effective Sample SizeESS。它的公式是[ \text{ESS} \frac{(\sum_i w_i)^2}{\sum_i w_i^2} ]直观理解是手上虽然有 n 个样本但由于权重分布极度不均真正在“起作用”的样本可能只有几个。ESS 越接近 n说明权重越均匀估计器越可靠。ESS 接近 1意味着整个估计几乎由单个样本决定结果完全不可信。我在日志里一定会打印ESS / n这个比例。如果低于 0.1我会立刻停下来检查行为策略和目标策略的差距。这个指标比直接观察 loss 要敏感得多因为 loss 震荡你可能会以为是学习率问题但 ESS 跌破阈值时原因几乎永远是分布偏移过大。3.4 长轨迹里的乘积诅咒别让权重指数爆炸前面提到轨迹级权重是每一步密度的连乘。这带来一个非常致命的问题随着轨迹长度增加权重会呈指数级膨胀或衰减。假设每一步平均密度比是 1.1100 步之后权重就是 1.1 的 100 次方约 13780。如果平均密度比是 0.9100 步之后权重几乎归零。这就是为什么很多人在实现 REINFORCE 的 off-policy 版本时发现梯度要么爆炸要么消失。工程上有几个缓兵之计。第一在 log 域计算权重全部概率取对数相加最后再exp避免中间数值溢出。第二做 per-step 近似只保留每个时间步的即时密度比不乘历史累积项。第三用优势函数加 GAELambda 的平滑机制把远期项用折扣系数压下去。第四把 clip 直接作用于权重概率比的 log 值限制极端情况。这几个手段组合用长轨迹场景才勉强压得住。4. 从零手写一个重要性采样小实验4.1 实验一高斯分布均值估计的“恐怖现场”用一个最简单的例子感受 IS 的方差问题。设目标分布p是均值 3 的正态分布数据来源q是均值 0 的正态分布。我想估计E_p[x]真值是 3。用重要性采样来估计import numpy as np rng np.random.default_rng(42) n 2000 # 从行为分布 q ~ N(0,1) 采样 x rng.normal(0.0, 1.0, n) # 计算每个样本的密度比权重 w p(x) / q(x) log_q -0.5 * x**2 log_p -0.5 * (x - 3.0)**2 log_w log_p - log_q w np.exp(log_w) # 普通重要性采样估计 naive_is np.mean(w * x) # 加权重要性采样估计 wis np.sum(w * x) / np.sum(w) # 有效样本量 ess np.sum(w)**2 / np.sum(w**2) print(fnaive IS estimate: {naive_is:.3f}) print(fWIS estimate: {wis:.3f}) print(fESS / n: {ess / n:.3f})我本地跑出来的结果大概是naive IS estimate: 2.512 WIS estimate: 2.934 ESS / n: 0.032注意ESS / n只有 0.03意味着虽然有 2000 个样本真正有效的等效样本只有 60 个左右。原因是q在 x3 附近几乎没有样本偶尔飘过来一两个落在 2.5 到 3.5 之间的点权重就会大得离谱把整个均值拉来拉去。naive IS这一次巧合地接近 2.5如果再换一个随机种子它可能在 1.5 到 4.0 之间乱跳。而 WIS 的估计在多次运行中更稳定地落在 2.8 到 3.1 之间代价就是有一点点向下偏。这个实验一百行不到建议每个学 RL 的人都亲手跑一遍你立刻就能体会为什么论文里都强调方差问题。4.2 实验二离策略策略梯度里的权重应该怎么算从玩具的高斯估计回到强化学习实现一个简化版 off-policy 策略梯度。假设行为策略固定它产出了一批(state, action, reward)数据。目标策略是我们的策略网络我们要用这批数据算梯度。import torch import torch.nn.functional as F def compute_rho(pi_logits, b_logits, actions): # pi_logits: 目标策略网络输出的动作 logits # b_logits: 行为策略预计算好的动作 logits # actions: batch 动作索引 log_pi F.log_softmax(pi_logits, dim-1).gather(1, actions.unsqueeze(-1)) log_b F.log_softmax(b_logits, dim-1).gather(1, actions.unsqueeze(-1)) log_rho log_pi - log_b return torch.exp(log_rho) RHO_CLIP 10.0 def off_policy_loss(pi_logits, b_logits, actions, advantages): rho compute_rho(pi_logits, b_logits, actions) rho rho.clamp(maxRHO_CLIP) log_pi F.log_softmax(pi_logits, dim-1).gather(1, actions.unsqueeze(-1)) loss -(rho * log_pi * advantages).mean() return loss几个关键点值得说明。第一log_pi - log_b必须用 log 域计算直接算两个概率的比值再求 log 很容易在连续控制场景下因为密度值极小而溢出。第二RHO_CLIP设成 10 是我常用的经验值在 PPO 中它等价于把 ρ 限制在1±ε内但如果是面向离线数据做策略优化我会更保守上限设置在 5 左右。第三这里用的是 per-step 权重而非轨迹级权重长轨迹下更稳定。这个代码不是完整可跑的 PPO但它展示了 IS 权重在真实训练代码里是怎么被接进去的。4.3 训练日志里必须盯住的三根线把重要性采样接进训练流程之后只盯着 reward 曲线是不够的。我通常会在日志里额外记录三个指标mean_rho、max_rho、ess_ratio。mean_rho理论上应该接近 1如果明显偏离 1说明行为策略和目标策略之间存在系统性差异。max_rho更重要它一旦超过阈值训练随时可能开始发散。ess_ratio前面讲过降到 0.1 以下就该停机检查。我发现很多人训练强化学习模型时不太看这种中间诊断量只关心最终回报。但在带 IS 的场景里这三根线就是安全气囊。有一次我离线训练一个机械臂抓取策略reward 曲线前 2000 步看着相当漂亮后来突然崩溃式下跌回看日志才发现max_rho在崩溃前半小时就已经悄悄涨到了 300 以上。如果我早一点注意到它完全来得及调整权重 clip 上限或者增大行为策略的探索熵。5. 实战中踩过的坑和自查速查5.1 行为策略的熵不能太小最常见的坑是行为策略被训练得太“自信”。行为策略如果近乎确定性那它在某个状态下几乎只输出一个动作。目标策略一旦对这个状态输出了另一个动作π(a|s)可能还有值但b(a|s)已经极小密度比瞬间冲到几百。解决思路有两个方向一是训练行为策略时保留一定熵正则二是显式使用混合策略0.5π_target 0.5 uniform去采数据保证探索动作有概率下限。5.2 归一化权重的坑用不用取决于你要干嘛我在离线策略评估时倾向于用 WIS因为它的方差表现更适合小样本场景。但如果你在训练策略梯度对 loss 做归一化时要谨慎。WIS 的归一化是把所有权重的和当分母这会让梯度的大小依赖一个 batch 内部的权重分布损失函数就失去原始量纲。PPO 的解决方案不是 WIS 归一化而是 clip它保留每个样本的原始梯度贡献只在单样本层面做约束。一句话评估用 WIS训练用 clip别混着用。5.3 权重分布看着正常不代表安全有时候平均值正常最大值也没超阈值但训练还是不稳。这时候要检查权重方差或者更直接地看权重直方图。偶尔出现一个极大的权重会被平均掩盖但它在梯度里可不会“平均”。我在代码里会额外记录top 10 rho的均值而不是最大值因为真正影响训练的是那极小部分极端样本加起来的合力。5.4 常见问题排查速查表现象可能原因排查方向loss 剧烈震荡、reward 曲线突然跳水重要性权重方差过大查max_rho、ESS / n压低 clip 上限权重大部分接近 0两个策略分布重叠极小增大行为策略探索检查行为策略是否过于确定性离线评估结果与直接仿真差距巨大普通 IS 被少数大权重样本带偏改用 WIS并汇报 ESS 数据长任务训练后期梯度消失轨迹级权重连乘导致数值消失检查是否误用轨迹权重改用 per-step 权重clip 后 loss 变化慢clip 约束太强检查 clip 区间P 的 PPO 里从 0.2 开始调试WIS 估计看起来有系统性偏差样本量太小、分布重叠不足增加采样量或者重新设计行为策略靠近目标策略5.5 和 GAE 一起使用时的顺序问题最后补一个非常实操的细节。如果你同时用 GAE广义优势估计和重要性采样注意计算顺序。GAE 本身就是通过 λ 加权的时间差分累计来低偏差估计优势它是在单个策略内部推导出来的。如果你在算 GAE 时用了行为策略数据那你的优势估计是对行为策略而言的接着又想去修正目标策略这时的权重不应该再对 advantage 本身做二次重要性加权而应乘在策略梯度那一项上。很多人在实现时把ρ_t乘到了 GAE 的每步 TD 误差上结果相当于把一个原本低方差的估计器又变成了高方差估计器。我现在的习惯是先用行为策略数据干净地算出A_t然后在策略梯度的log π(a|s) * A_t外乘ρ_t顺序不要反。6. 我在实战里的几个默认操作写到最后分享几个我自己固定下来的习惯不一定是标准答案但至少帮我少踩了很多坑。我现在的默认配置是只要出现数据分布不一致第一件事不是直接套重要性采样而是先评估两个策略的分布差距有多大。如果 KL 散度已经大到离谱我会先回去调整行为策略而不是硬着头皮用 IS 硬撑。重要性采样能修正的偏差有限它最怕的不是偏离而是支撑集不重合。第二个习惯是所有权重一律在 log 域算最后再 exp。尤其是在连续动作空间高斯策略的密度极小值可能在1e-20这个量级直接算比值必溢出。log 域只是简单一行代码的事却能让训练稳定档次提高一截。第三个习惯是每次训练都要打印 ESS。它比我见过的任何训练曲线都更早暴露问题。一个ess_ratio低于 0.1 的训练过程无论眼前的 reward 有多漂亮最终大概率会崩。这不绝对但在我自己的实验里几乎没有例外。重要性采样是个数学上极其干净、工程上极其“脏”的中间层。把这个工具理解透你会发现不只是 PPO连离线强化学习、模仿学习、元强化学习里那些花里胡哨的加权设计内核其实都在做同一件事给不同分布下的样本重新分配合适的话语权。把这一件事想明白很多复杂的算法就不再是黑盒了。
返回列表