返回专辑
·Johan·17 分钟阅读

离线强化学习的支持集外动作:为什么 Q 值会自我抬高

从 Bellman backup 的分布外 maximization 解释外推误差;比较行为约束、保守价值与策略正则,并用覆盖率切片与 FQE 交叉验证部署边界。

离线强化学习的支持集外动作:为什么 Q 值会自我抬高

离线强化学习吃的是固定日志,却在 backup 里对动作做 maximization 或高熵采样。一旦 落到行为策略从未覆盖的区域,函数逼近器可以自由幻想高回报;下一轮 backup 再引用这些幻想,Q 被自我抬高,策略看上去很美,上线即崩。这不是「再多训几个 epoch」能修的,而是支持集外推的结构问题。

主线:先识别 OOD 动作如何进入 Bellman 目标,再对照 CQL / IQL / AWAC / TD3+BC 的约束机制,把连续动作密度与 OPE 接成同一条验收链,最后用失败案例与发布门禁挡住虚高策略。

1. 问题不在离线,在 backup 的动作论点

经验分布 由行为策略 产生。标准 DQN/SAC 风格目标依赖

或随机策略的 。训练样本的 来自 ,但 却来自 。当

甚至 时, 没有数据锚点。拟合误差在 算子下偏向上偏,形成外推误差(extrapolation error)。

注意:OOD 状态与 OOD 动作不同。日志可能访问过某状态,却从未在该状态尝试某动作;max 恰最爱挑这类洞。离线算法若只做「状态分布匹配」而放任动作论点,仍会在熟悉路面上选出从未试过的危险转向角。

2. 自我抬高的正反馈

近似误差 在数据外不必零均值。带 maximization 的备份像在选

若正则不足以压制 ,目标 被抬高 → 下一轮 抬高 → 策略更倾向 OOD 动作。离线设定没有环境交互来纠正,于是「训练指标很好、部署很差」成为缺省结果。

这也能解释为什么朴素 off-policy 算法直接喂日志会发散或虚高:不是实现写错,而是目标本身在问数据外的问题。把 target network 变慢只能让虚高更平滑,不能取消 这一项。

3. 三条防御主线

行为约束策略。 强迫 靠近 :BC 正则、优势加权 BC、支持集投影等。目标是少问 OOD 动作。代价是策略改进幅度受限; 本身很差时,上界明显。

保守价值估计。 如 CQL 一类方法,显式压低数据外动作的 Q,或对 采样动作加惩罚:

思路是:数据内 Q 保持 TD 拟合,数据外 Q 不被抬成最优幻想。 过大则过度保守,过小则回到虚高。

支持集感知的 backup。 用行为密度、计数、或 VAE/流模型估计 ,在密度低于阈值时改写目标:改用 V 函数、拒绝该动作、或切换到安全策略。难点是连续动作密度估计本身不稳,需要校准。

三者可组合:先约束 的支持集,再对残余 OOD 做保守 Q。不要指望单靠更大网络「学到正确外推」。

4. CQL / IQL / AWAC / TD3+BC:公式与取舍

下面按「惩罚写在哪、还能改进多少、实现脆点」对齐四个常用族。符号上 为离线数据集, 为优势。

CQL(Conservative Q-Learning)。 在 TD 损失外增加保守项,压低策略(或均匀/随机)动作上的 Q,抬高数据动作上的 Q:

连续控制里 常用当前策略或重要性采样的动作样本近似; 可固定或对偶调整。CQL 直接打在 Q 上,对「max 幻想」抑制强,但对 敏感:过大则策略改进停滞,过小则 OPE 仍乐观。

IQL(Implicit Q-Learning)。 不显式对 OOD 动作查询 max-Q,而是用 expectile 回归学 ,再用优势加权更新策略。价值侧核心是

其中 为 expectile 损失;策略侧类似优势加权 BC:

IQL 的工程含义是:备份尽量留在数据集支持的 ,用 承接状态价值,避免 。代价是对 expectile 与温度 敏感,且改进幅度受制于数据中已有的高优势动作。

AWAC(Advantage Weighted Actor-Critic)。 策略目标可写为

即按优势重加权的 BC。Critic 仍是 off-policy TD,但 actor 强行待在数据支持附近。与 IQL 的差别在于:AWAC 更「显式 actor-critic + 加权 BC」,IQL 用 expectile 隐式避开 OOD max。AWAC 在 尚可时稳定;数据噪声大时,优势估计偏差会变成错误的加权先验。

TD3+BC。 在 TD3 的确定性策略梯度上加行为克隆项:

常用对 Q 的归一(如除以均值绝对值)防止量纲吞掉 BC。实现简单、与连续控制栈好接,但对 调度敏感:Q 抬升后若未归一,BC 项相对变弱,策略会突然「挣脱」行为支持——这是线上常见的延迟事故。

对比表:

算法主要防 OOD 手段是否显式查 OOD Q典型脆点更合适的数据画像
CQL保守正则压低 OOD Q是(采样/策略动作) 难调、过保守覆盖尚可、需压虚高
IQLexpectile + 优势加权 BC基本否;改进上限专家/混合日志、求稳
AWAC优势加权 BC 约束 actorCritic 仍可能优势偏差→错权重 较强
TD3+BC显式 BC 项Critic 的 target 仍 max/min 与 Q 尺度耦合连续控制、实现约束紧

没有「一律最强」:覆盖差时先修数据或加安全层; 强时 IQL/AWAC/TD3+BC 往往比猛抬 CQL- 更省事;虚高已被 OPE 抓到时,CQL 类保守项更对症。

5. 连续动作里的密度、变换与「程度 OOD」

离散动作还能数「没试过的键」;连续动作里,OOD 是程度上的。设行为与目标在同一坐标下的条件密度为 。策略均值偏离行为均值几个 ,Q 就可能进入外推。若动作经 压缩到环境盒 ,密度还必须含 Jacobian:

其中 。训练与 OPE 若一个在 空间、一个在 空间算 log_prob,支持集判断会整体错位——表现为「似然看起来很大,车却在撞墙」。

检查建议:

  • 对每个动作维画 的边际、分位数与 - 带;
  • 在高优势区域单独看动作偏离(虚高常集中在「看起来能赚」的区域);
  • 做分位数监控:数据外若系统性大幅为正,要警惕;
  • 报告 的 p10/p50,而不仅是均值(均值会被易场景抬高)。

归一化也重要:动作缩放错误会让「看起来在 」的 实际对应物理上从未执行的推力。发布前用环境 API 的合法范围做一次反归一化抽检。

密度模型本身会错。用 VAE/流模型估 时,应在 held-out 转移上做校准:高密度区命中率、低密度区拒绝率。未校准的 不能单独充当安全门,只能当探针之一。

6. 和离策略评估的接口(再细一档)

OPE(重要性采样、FQE、DR)回答「 可能怎样」,离线 RL 回答「怎么得到 」。二者应共享同一套支持集定义,而不是各算各的。把链路拆开写,避免「训完再随手跑个 FQE」。

训练期。 每次保存 checkpoint 时附带:策略版本、归一化统计、coverage_report 快照、保守项系数。缺任一字段的产物不得进入评估队列。

评估期。 只在 held-out 轨迹上估 OPE;训练 batch 上的 ESS 只作调试,不进门禁。FQE 的拟合同样禁止使用评估轨迹的回报标签做泄漏式调参——超参应在更早的折上选定。

部署期。 影子模式对比的是「若执行 的建议动作」与「实际 动作」的回报表征,而不是再训一个更乐观的 Q。

部署前串联:

  1. 训练出 ,冻结版本号与预处理统计;
  2. 在与训练同分布的 held-out 日志上,报告密度比 (或代理)的分位数、最大值与 ESS;
  3. 用 FQE / DR / self-normalized IS 估计 并给区间;
  4. 按场景切片(拥堵、近障碍、低速),拒绝「全局均值好看、危险片 ESS≈0」;
  5. 只有覆盖与 OPE 同时过线才允许进影子流量。

若 ESS 极低,说明 大量待在日志稀薄区——这与 Q 虚高是同一类病,只是观测角度不同。不能用训练 TD loss 替代 OPE:TD 可以拟合一个自我抬高的目标而显得很小。

双重稳健(DR)在模型与重要性权重之间做抵消,但对支持集缺失同样无助:权重不存在时,纠偏项没有定义。工程上把「绝对连续是否近似成立」当成硬前置,比纠结 DR 与 SNIS 谁更优更重要。

与离线 RL 训练期探针对齐的做法:

持续上升而 held-out 行为回报不动,往往是虚高前兆;此时即便 FQE 偏乐观,也应提高发布门槛或加大保守系数。FQE 本身也会在 OOD 动作上外推,因此需要 FQE ↔ SNIS/DR 同向,而不能只看一个乐观估计器。

伪代码(训练期 + 评估期共用):

python
def coverage_report(batch, pi, mu, q):
    a_pi = pi.sample(batch.s)          # 与部署同一随机源约定
    log_mu = mu.log_prob(batch.s, a_pi)
    log_pi = pi.log_prob(batch.s, a_pi)
    rho = torch.exp(log_pi - log_mu)
    ess = (rho.sum() ** 2) / (rho.pow(2).sum() + 1e-12)
    q_gap = q(batch.s, a_pi) - q(batch.s, batch.a)
    return {
        "log_mu_p10": torch.quantile(log_mu, 0.10),
        "rho_p99": torch.quantile(rho, 0.99),
        "ess": ess,
        "q_gap_p95": torch.quantile(q_gap, 0.95),
    }

def release_gate(cov, ope, thresholds):
    assert cov["ess"] >= thresholds.ess_min
    assert cov["log_mu_p10"] >= thresholds.log_mu_p10
    assert ope.ci_low >= thresholds.min_return
    assert ope.method_sign_agree  # FQE 与 SNIS 同向

7. 算法伪代码:把「不问 OOD」写进循环

下面给出可对照实现的骨架(省略网络细节)。重点不在框架 API,而在动作样本从哪来、损失打在哪一侧

python
# CQL-style critic update (conceptual)
a_data = batch.a
a_pi = pi.sample(batch.s)              # 或混合均匀/策略样本
y = batch.r + gamma * q_tgt(batch.s2, pi.sample(batch.s2))
td = (q(batch.s, a_data) - y.detach()).pow(2).mean()
cql = (q(batch.s, a_pi) - q(batch.s, a_data)).mean()
loss_q = td + alpha * cql
loss_q.backward(); optim_q.step()

# IQL-style: V by expectile; policy by advantage-weighted BC
adv = q(batch.s, batch.a) - v(batch.s)
w = exp_clip(adv / beta)               # 截断防止权爆炸
loss_v = expectile_loss(q.detach() - v(batch.s), tau)
loss_pi = -(w.detach() * pi.log_prob(batch.s, batch.a)).mean()

# TD3+BC actor
a_pi = pi(batch.s)
lam = lambda_scale / q(batch.s, a_pi).abs().mean().detach().clamp_min(eps)
loss_pi = -lam * q(batch.s, a_pi).mean() + ((a_pi - batch.a) ** 2).mean()

审查清单:a_pi 是否与部署时同一分布;y 是否仍对 OOD 动作取 max/min;BC 项是否随 Q 尺度自动变弱;权重 是否在 held-out 上也会爆炸。把这份清单做成 PR 模板里的勾选项,比事后在 notebook 里补画 q_gap 更有效——虚高往往在合并后的第一次全量重训才显现,那时再改损失已经贵了。

8. 实现时容易踩的坑

  • 目标网络滞后不足:更慢的 target 不消除 OOD max,只是把虚高变平滑;
  • 与 BC 损失量纲失衡:BC 系数随 Q 尺度漂移,需要归一或自适应(TD3+BC 的 正为此);
  • 评估策略用了贪婪 max,部署用了随机策略(或相反),导致验收与上线不是同一个
  • 把验证集 TD 误差当泛化指标:数据外 TD 可以很小(拟合的是虚高目标);
  • CQL 采样动作与部署动作分布不一致:保守项打在错误的动作集上,线上仍虚高;
  • IQL 把 expectile 当成普通 MSE 时行为接近最大值回归,OOD 压力回流;
  • 连续动作 log_prob 漏 Jacobian:覆盖率与 OPE 同时被骗;
  • 用训练 batch 的 ESS 代替 held-out ESS:过拟合策略会在训练集上「看起来很稳」。

更有用的训练期探针是:数据外动作的 Q 分位数、策略-行为 KL、保守项有效权重,以及与 OPE 仪表盘同一套 coverage_report

9. 与行为克隆、安全层的分工

离线 RL 不是 BC 的唯一替代,也常常不该是第一刀。实践中更稳的分层是:

  1. BC / 加权 BC 先给出可部署基线,门禁只检验模仿质量与安全约束;
  2. 离线 RL 在 BC 基线之上做有限改进,并强制行为约束或保守 Q;
  3. 安全层(动作投影、屏蔽、恢复策略)处理残余 OOD,即使 Q 仍偏乐观也不把非法动作送给执行器。

若跳过第 1 步直接上 max-Q,团队会同时调试「有没有学会任务」和「有没有幻想」,成本极高。若只有第 3 步没有第 2 步的保守机制,安全层会变成高频托底,本质仍是行为策略。支持集问题应在学习目标里被看见,而不是全部推给运行时 if。

10. 失败案例(应用侧)

案例 A:仓储底盘「更短路径」。
行为日志来自保守驾驶员(大转弯、早减速)。无约束 SAC 离线微调后,Q 显示抄近路价值更高;仿真里擦架成功率上升。根因是窄通道处转向角落在 低密度区,Q 外推。门禁若只看平均完成时间会放行。加上 TD3+BC 且固定 归一后,路径变长但影子模式碰撞率回到基线以下。

案例 B:推荐限流策略。
离散动作(推送/不推送)。CQL 的 过小,验证 TD 很好,FQE 估的长期留存高于行为策略。上线后在冷启动用户片崩溃——该片日志几乎只有「不推送」。切片 ESS 早就接近 0,但全局 ESS 被头部用户抬高。失败在「未按切片门禁」,不在优化器。

案例 C:机械臂力度。
动作维含力矩。预处理时力矩缩放表更新,策略仍用旧统计反归一化; 输出在网络里看像 BC,在环境里是 OOD 推力。Q 与 都在错误坐标里自洽。此类事故要用「环境真实动作直方图 vs 日志直方图」对齐,而不是只看张量范围。

案例 D:注入实验未做就上线。
团队删掉保守项做 AB,训练曲线更漂亮。没有「挖空支持集应复现虚高」的回归测试,代码审查无法拒绝。两周后在新场景复现案例 A。注入实验应进 CI:人造 OOD 洞上,无约束算法的 必须显著高于保守算法。

案例 E:只信 FQE。
FQE 在函数类足够大时同样会在 OOD 动作上外推,于是「离线 RL 虚高」与「OPE 虚高」共振,门禁全部亮绿。交叉 SNIS 后发现权重集中在不到百分之几的轨迹。结论:FQE 是必要组件,但不能单飞;与 ESS/密度比绑死才构成门禁。

11. 验收协议与发布门禁清单

准备行为日志的 held-out 轨迹与(若允许)小规模仿真/影子部署。

  1. 覆盖率切片:按状态访问频率分层,报告 动作落在 高密度区的比例;低覆盖层不得贡献「乐观」上线结论。
  2. 行为似然 或核密度代理;设下界(含 p10)。
  3. FQE/IS 交叉:FQE 估的 不得显著高于行为策略回报上界太多而不触发告警;与 self-normalized IS 同向才通过。
  4. 仿真回放或影子模式:若有环境模型/仿真,比较乐观 Q 与实际回报;系统性高估则拒绝发布。
  5. 注入实验:人为把一批状态的动作支持集挖空,再跑无约束算法,应能复现 Q 虚高;加上保守项后虚高幅度下降。这是算法回归测试,不是业务指标。
  6. 版本钉扎:数据哈希、动作归一化统计、策略 checkpoint、OPE 代码版本一并记录;任一漂移则门禁作废重跑。

发布门禁清单(可直接贴进发布单):

  • [ ] 训练与部署使用同一动作坐标与 log_prob 定义(含 Jacobian)
  • [ ] held-out 全局 ESS / 分位密度比超过阈值
  • [ ] 关键场景切片 ESS 均超过阈值(允许「不发布该片」而非平均稀释)
  • [ ] 相对上期或行为基线无失控上涨
  • [ ] FQE 与 SNIS/DR 同向,且保守 CI 下界 业务底线
  • [ ] 影子流量或仿真无系统性高估
  • [ ] OOD 注入 CI 全绿
  • [ ] 回滚开关:一键回到 或上一版
python
# 探针:数据外相对数据内的 Q 间隙
q_pi = q(s, pi.sample(s))
q_mu = q(s, a_mu)
gap = (q_pi - q_mu).detach()
log_scalar("q_gap_p95", torch.quantile(gap, 0.95))

12. 数值直觉:虚高如何在几轮 backup 里长出来

考虑简化表格设定:状态一个,动作 ,日志只含 ,真值 (危险动作)。随机初始化让 。带 的备份会反复读到 ,若没有数据锚点,函数逼近可以把 继续抬高;策略随之把质量挪向 。加上 CQL 项后,对 的 Q 被显式下压,对 的 TD 仍拟合回报,策略改进被限制在「数据里见过的更好用法」。

连续动作把「没见过的键」换成「偏离 几个 的方向」。若 沿某维均值偏移 ,而 Critic 在该方向的 Lipschitz 未被数据约束,则

可以稳定为正,即使真实回报在该方向下降。 监控的就是这类系统性正间隙,而不是单点噪声。

13. 数据集诊断:在选算法之前先回答的问题

算法选型若先于数据诊断,讨论会停在「CQL 还是 IQL」。更有效的顺序是先度量支持集,再决定要不要上离线 RL:

  1. 行为策略族:单一专家、混合多策略,还是含明显随机探索?混合日志会让 变「又宽又浅」,BC 难、OPE 方差大。
  2. 动作覆盖热图:按状态聚类或关键特征分箱,估计每箱的动作熵与样本数;熵高但样本少的箱子是外推重灾区。
  3. 回报可识别性:同状态异动作的回报差是否在数据里出现过?若几乎只有一种动作,离线 RL 没有改进所需的对比信息,只剩幻想空间。
  4. 倾向得分是否可复现:当时若未存 ,事后拟合必须当成 nuisance,并加宽 OPE 区间,而不是假装精确 IS。

只有当「有限改进空间存在、覆盖在关键片可接受、倾向可审计」时,才进入第 4 节的算法表。否则优先补采、加安全层或停留在 BC。

14. 训练期仪表盘最小集

不必上完整可观测性平台,但至少固定这些曲线,且与发布门禁同名同定义:

  • q_data_mean / q_pi_mean:数据动作与策略动作的 Q,观察间隙;
  • q_gap_p95:第 6 节定义;
  • policy_bc_mseneg_log_mu_pi:策略相对行为的偏离;
  • cql_termbc_term_weight:保守/BC 项有效权重,防静默失效;
  • ope_ess_heldout:定期(非逐步)在 held-out 上估 ESS,贵但关键。

q_pi_mean 单边爬升而 policy_bc_mse 同步变差、ope_ess_heldout 下降,即可在上线前判负,无需等仿真撞完。

15. 何时不该上离线 RL

下列信号出现时,继续调 CQL 的 多半是沉没成本:关键切片动作几乎单一;倾向得分无法审计;业务不允许影子流量;安全约束无法在动作层硬拦截。更合适的路径是补数据、加约束 BC,或把问题改写成「在行为支持内做评分重排」。离线 RL 的价值在于有对比信息的支持集内改进;支持集外的「最优」不属于统计可辩护的目标。把这句话写进项目章程,比事后解释一次事故便宜。

若管理层只给「离线提升」指标、不给覆盖率预算,工程上应把门禁失败解释为数据产品未就绪,而不是模型组能力不足。否则团队会被逼出一套只会在 FQE 上发光的虚高策略,最终仍要在线上还债。

16. 选型建议

  • 已经较强、改进空间小:偏行为约束 / BC 正则(IQL、AWAC、TD3+BC),稳定优先;
  • 嘈杂但覆盖尚可:保守价值(CQL)+ 中等策略改进;
  • 覆盖有明显空洞:先修数据或加安全层,而不是上更强的 max-Q 算法;
  • 必须部署时:把 OPE 与覆盖门槛写进发布门禁,和离线准确率一样硬;
  • 连续控制优先核对密度坐标与 /Q 尺度;离散控制优先做场景切片 ESS;
  • 资源只够做一件事时:先做覆盖率切片 + ESS 门禁,再谈换算法——选错门禁比选错希腊字母更致命。

离线 RL 的核心矛盾是:要改进策略就难免看数据外动作,看数据外动作就难免幻想。工程上能做的,是把幻想变成可惩罚、可测量、可拒绝的对象,而不是在训练曲线上庆祝一个自我抬高的 Q。支持集不是细节,是算法是否有权说「最优」的前提。

17. 收束前的一条硬规则

若 held-out 覆盖率与双估计器 OPE 未进发布门禁,就把「离线策略已验证」从上线材料里删掉。训练曲线上的 Q 与 TD 只证明拟合了某个目标,不证明目标有权代表环境。支持集审计通过之前,任何「最优」字样都应视为未定义。

对评审者而言,只需追问三件事:关键切片的 ESS 是否过线;FQE 与 SNIS 是否同向;部署动作与训练时 log_prob 是否同一坐标。三问都能指向门禁配置与报表,才有资格讨论算法字母表;否则字母表再好看也不该合并。先过门禁,再谈提升幅度——没有覆盖就没有最优。

← 全部文章

johan's blog