# 部署校正 **作者:** [Yingru Li](https://richardli.xyz/) 最后更新时间:10/30/2025。 --- > **📖 文档结构** > - **本文档** - 实用使用指南:配置、预设、故障排除 > - **[数学公式](rollout_corr_math.md)** - 理论基础、推导和算法细节 > > 开始这里了解实现,请参考数学文档了解理论和设计原理。 --- 本文档提供了对 verl 中部署校正实现的全貌概述。 **命名说明**:这个功能称为“部署校正”,以反映完整的功能性:重要性采样(IS)权重、拒绝采样(RS)和否决机制。内部变量 `rollout_is_weights` 保留其名称,因为它专门指 IS 权重组件。 ### BibTeX 引用 ```bibtex @misc{liu-li-2025, title = {When Speed Kills Stability: Demystifying RL Collapse from the Training-Inference Mismatch}, url = {https://yingru.notion.site/When-Speed-Kills-Stability-Demystifying-RL-Collapse-from-the-Training-Inference-Mismatch-271211a558b7808d8b12d403fd15edda}, author = {Jiacai Liu and Yingru Li and Yuqian Fu and Jiawei Wang and Qian Liu and Yu Shen}, year = {2025}, month = sep, } ``` ## 概述 部署校正提供了一个统一的框架来处理 RL 训练中的**一般离线策略问题**。任何数据收集分布与训练分布不同的场景都可以从这些方法中受益。 **常见离线策略场景:** 1. **策略不匹配**(实现差异) - 不同的精度:FP8 vs FP16 vs BF16 vs FP32 - 不同的后端:vLLM vs SGLang vs FSDP vs Megatron - 即使权重相同,不同的实现 2. **时滞**(模型陈旧) - 部署时使用较旧的检查点,而训练已进展 - 异步部署工作进程,具有陈旧参数 - 分布式/异步 RL 系统中的常见问题 3. **回放缓冲区** - 在早期迭代的历史轨迹上训练 - 来自不同策略版本的经验回放 - 数据增强或重采样策略 4. **离线策略算法** - 专家演示的行为克隆 - DAPO(来自辅助策略的数据) - 使用来自不同策略轨迹的任何算法 5. **数据质量过滤** - 对收集数据进行重新加权或过滤 - 参考学习中修改的分布 - 具有分布转移的课程学习 这些离线策略差距可能导致训练不稳定和策略崩溃。部署校正使用重要性采样(IS)权重和拒绝采样(RS)来校正数据收集和训练之间的任何分布转移。 **重要说明:常见实现错误** 许多 LLM-RL 实现错误地应用 PPO,**忽略实际部署策略** π_rollout 并假设训练参考策略 π_old 是行为策略。这在 π_rollout ≠ π_old 时在数学上是错误的(这在 LLM-RL 中由于部署和训练之间的精度/后端差异而典型)。 **这不是 PPO 的错** - PPO 本身在数学上是正确的。问题在于错误假设 π_old = π_rollout 在天真的实现中。 这份文档中引入的部署校正框架,正是为了解决这种关键实现错误。这种错误导致 RL 训练崩溃,被博客文章 ["When Speed Kills Stability: Demystifying RL Collapse from the Training-Inference Mismatch"](https://yingru.notion.site/When-Speed-Kills-Stability-Demystifying-RL-Collapse-from-the-Training-Inference-Mismatch-271211a558b7808d8b12d403fd15edda) 识别并激发其开发。 **数学上正确的方法:** - **解耦模式**:三个策略(π_rollout, π_old, π_θ),IS 从 π_rollout 到 π_old 校正 - **旁路模式**:两个策略(π_rollout = π_old, π_θ),使用实际部署策略作为 PPO 锚点 - **旁路 + 策略梯度模式**:两个策略(π_rollout, π_θ),IS/RS 校正和无 PPO 裁剪 详见[数学公式](rollout_corr_math.md#38-common-implementation-mistake)部分。 ### 关键设计原则:IS 权重和拒绝采样的分离 实现干净地分离了两个正交机制: 1. **IS 权重**(`rollout_is_weights`):用于梯度校正的连续重新加权 - 策略比率:解耦模式下的 π_old/π_rollout 或旁路模式下的 π_θ/π_rollout - **安全绑定**:截断到 [exp(-20), exp(20)] ≈ [2e-9, 5e8] 以防止溢出 * 令牌级别:绑定每令牌比率 * 序列级别:绑定比率乘积(广播到所有令牌) - **截断**:通过 `.clamp(max=rollout_is_threshold)` 上限截断(TIS:截断重要性采样) - **在填充处置零**:乘以 response_mask 以在填充位置置零 - 用于加权策略梯度(方差降低) 2. **拒绝采样**(`modified_response_mask`):二进制过滤用于异常排除 - 创建二进制掩码:1 = 保留,0 = 拒绝 - 拒绝 IS 比率在 [lower_threshold, upper_threshold] 之外的令牌/序列 - **否决机制**:独立拒绝包含灾难性令牌的序列 - 修改 response_mask 以从训练中排除被拒绝样本 - 用于损失聚合(被拒绝样本不贡献梯度或分母) 这种分离确保: - ✅ IS 权重提供连续重新加权(降低方差) - ✅ 拒绝采样提供硬过滤(移除极端异常值) - ✅ 两个机制可以独立启用或一起使用 - ✅ 正确损失归一化(被拒绝样本从所有计算中排除) - ✅ 安全绑定防止所有情况下的数值溢出 ## 快速开始:使用验证预设 **新增**:我们现在提供了类型化配置与验证预设,用于常见场景。这些预设已在各种模型和训练场景中经过数万 GPU 小时验证。 ### Python API ```python from verl.trainer.config.algorithm import RolloutCorrectionConfig # 解耦模式与令牌级别 IS config = RolloutCorrectionConfig.decoupled_token_is() # 解耦模式与序列级别 IS config = RolloutCorrectionConfig.decoupled_seq_is() # 解耦模式与序列 IS + 拒绝采样 config = RolloutCorrectionConfig.decoupled_seq_is_rs() # 解耦模式与几何 RS + 否决(最大异常敏感度) config = RolloutCorrectionConfig.decoupled_geo_rs() # 性能模式:PPO 与旁路 config = RolloutCorrectionConfig.ppo_is_bypass() # 高级:纯策略梯度与 IS config = RolloutCorrectionConfig.pg_is() # 高级:纯策略梯度与拒绝采样(旁路 + 纯 + 几何 RS) config = RolloutCorrectionConfig.pg_rs() # 指标仅模式(无校正) config = RolloutCorrectionConfig.disabled() ``` ### YAML 配置(高级) 对于高级定制或 YAML 基础配置: ```yaml algorithm: rollout_correction: rollout_is: token # IS 权重:"token", "sequence", 或 null rollout_is_threshold: 2.0 # IS 权重上阈值 rollout_is_batch_normalize: false # 批量归一化 IS 权重到 mean=1.0 rollout_rs: null # 拒绝采样:"token", "sequence", "geometric", 或 null rollout_rs_threshold: null # RS 上阈值(如果启用 rollout_rs 则必需) rollout_rs_threshold_lower: null # RS 下阈值(如果为 null 则自动取倒数) rollout_token_veto_threshold: null # 每令牌否决阈值(null = 禁用) bypass_mode: false # 跳过 old_log_prob 计算 use_policy_gradient: false # 使用策略梯度损失(vs PPO 损失) # 必需:启用 log prob 计算 actor_rollout_ref: rollout: calculate_log_probs: true ``` ## 文件 ### **核心实现** - `verl/trainer/ppo/rollout_corr_helper.py` - 包含 `compute_rollout_correction_and_rejection_mask()` 和 `compute_offpolicy_metrics()` - `verl/trainer/ppo/core_algos.py` - 部署校正与 PPO 和纯 IS 模式的集成(`compute_policy_loss_with_rollout_correction()`) - `verl/trainer/ppo/ray_trainer.py` - 旁路模式实现(跳过 `old_log_prob` 计算) - `verl/workers/actor/dp_actor.py` - 模式选择逻辑和指标收集 ### **配置文件** - `verl/trainer/config/algorithm.py` - `AlgoConfig` 中的部署校正参数 - `verl/workers/config/actor.py` - `ActorConfig` 中的部署校正参数 - `verl/trainer/config/actor/actor.yaml` - 部署校正配置节 - `verl/trainer/config/ppo_trainer.yaml` - 带有部署校正的算法配置 ### **文档** - `docs/examples/config.rst` - 配置参数描述 ### **示例脚本** - `recipe/dapo/run_dapo_qwen2.5_32b_rollout_corr.sh` - 带有部署校正的 DAPO 示例 - `examples/rollout_correction/run_with_rollout_corr.sh` - 基本示例 ### **测试** - `tests/trainer/ppo/test_rollout_corr.py` - IS/RS 机制的单元测试 - `tests/trainer/ppo/test_rollout_corr_integration.py` - 集成测试 ## 配置参数 所有参数位于 `algorithm.rollout_correction` 下: ### `rollout_is` (str 或 null) 重要性采样权重聚合级别: - `null` = 不计算 IS 权重(指标仅模式) - `"token"`:每令牌 IS 权重 - **解耦模式**:ρ_t = π_old(t)/π_rollout(t) - **旁路/纯 IS 模式**:ρ_t = π_θ(t)/π_rollout(t) - 每令牌独立截断 - 典型阈值:1.5 - 5.0 - `"sequence"`:每序列权重 ρ_seq = ∏_t ρ_t - 跨序列乘法聚合 - 典型阈值:2.0 - 10.0 所有 IS 权重安全绑定到 [exp(-20), exp(20)] ≈ [2e-9, 5e8] ### `rollout_is_threshold` (float) IS 权重截断的上阈值。默认:`2.0` - 通过 `.clamp(max=rollout_is_threshold)` 截断 IS 权重(TIS:截断重要性采样) - 用于方差降低的应用到 IS 权重 - 与拒绝采样分离(由 `rollout_rs` 参数控制) ### `rollout_rs` (str 或 null) 拒绝采样聚合级别: - `null` = 无拒绝采样 - `"token"`:拒绝异常比率的个别令牌 - `"sequence"`:拒绝异常比率的整个序列 - `"geometric"`:几何平均聚合用于拒绝 - 典型阈值:1.0002 - 1.001 ### `rollout_rs_threshold` (float 或 null) 拒绝采样的上阈值。默认:`null` - **必需** 当 `rollout_rs` 启用时(必须明确设置) - 比率 > 阈值的令牌/序列被屏蔽 ### `rollout_rs_threshold_lower` (float 或 null) 拒绝采样的下阈值。默认:`null` - 如果 `null`,使用上阈值的倒数(1/upper) - 比率 < 阈值的令牌/序列被屏蔽 ### `rollout_token_veto_threshold` (float 或 null) 灾难异常下的每令牌否决。默认:`null` - 检查**未绑定每令牌比率**,在安全绑定之前 - 如果**任何**令牌比率 < 阈值,整个序列被拒绝 - 独立于 `rollout_is` 和 `rollout_rs` 设置 - 典型值:启用时为 `1e-4` 到 `1e-6` - 示例:`1e-4` 捕获 10,000x 不太可能的令牌 ### `rollout_is_batch_normalize` (bool) 对 IS 权重应用批量归一化。默认:`False` - `True`:归一化 IS 权重以在每个批次中具有 mean=1.0 - **令牌级别 IS**:归一化所有令牌权重 - **序列级别 IS**:归一化序列平均值(每序列一个权重) - `False`:使用原始(截断)IS 权重 - 通过确保每批次平均权重为 1.0 降低方差 - 应用于截断后以保留截断语义 - 仅影响 IS 权重值,不影响拒绝采样 ## 理解框架:组件和组合 部署校正框架建立在**正交组件**上,可以灵活组合。理解这些组件有助于为你的场景选择正确的配置。 ### 关键组件 1. **操作模式**(节:[操作模式](#operation-modes)) - **解耦**:三个策略(π_rollout, π_old, π_θ),分离 π_old 计算 - **旁路**:两个策略(π_rollout = π_old, π_θ),跳过 π_old 计算 2. **损失函数** - **PPO**:带裁剪的标准 RL 训练 - **纯 IS**:策略梯度仅(无裁剪) 3. **IS/RS 聚合级别** - **令牌**:每令牌 IS 权重/拒绝 - **序列**:序列级别 IS 权重/拒绝 - **几何**:几何平均(仅用于拒绝) 4. **安全机制** - **否决**:拒绝包含灾难令牌的序列 详见[数学公式](rollout_corr_math.md#3-algorithmic-components-and-combinations)。 --- ## 预设配置指南 本节提供了对验证预设的详细指导,每个预设是针对常见场景优化的组件特定组合。 ### 理解预设 #### 可用预设方法 | 预设方法 | 模式 | IS 级别 | RS 级别 | 属性 | |---------------|------|----------|----------|------------| | `decoupled_token_is()` | 解耦 | token | - | 每令牌 IS 权重 | | `decoupled_seq_is()` | 解耦 | sequence | - | 序列级别 IS 权重 | | `decoupled_seq_is_rs()` | 解耦 | sequence | sequence | 序列 IS + 序列 RS | | `decoupled_geo_rs()` | 解耦 | - | geometric + veto | 几何 RS + 否决,无 IS 权重 | | `ppo_is_bypass()` | 旁路 | - | - | 旁路模式,跳过 old_log_prob | | `pg_rs()` | 旁路 | - | geometric + veto | 策略梯度与 RS(无 IS 权重) | | `pg_is()` | 旁路 | sequence | - | 策略梯度与 IS | | `disabled()` | - | - | - | 指标仅,无校正 | **注意:** 所有预设都使用 PPO 损失,除了 `pg_is()` 和 `pg_rs()`,它们使用策略梯度(两者都需要 `use_policy_gradient=True`)。 #### 其他支持组合(需要手动配置) **预设方法之外的其他支持组合:** - 令牌 IS + 令牌 RS:在配置中手动组合 - 纯令牌 RS:令牌级别 RS 仅,无 IS 权重 - 纯序列 RS:序列级别 RS 仅,无 IS 权重 详见下面的[详细配置示例](#additional-useful-configurations-not-exposed-as-presets)。 **关键属性:** - 任何聚合级别(token/sequence/geometric)在解耦或旁路模式下均有效 - 所有组合由实现完全支持 - 拒绝采样独立于 IS 加权 - 纯 RS(`pg_rs`)使用旁路 + 几何 RS 与 `use_policy_gradient=True`(无 IS 权重) --- ### 1. 解耦模式与令牌级别重要性采样(`decoupled_token_is`) **配置:** ```python config = RolloutCorrectionConfig.decoupled_token_is(threshold=2.0) ``` **组件:** - **操作模式**:解耦(3 个策略) - **损失**:带裁剪的 PPO - **IS 聚合**:令牌级别 - **RS**:无(可单独添加) **等价 YAML:** ```yaml algorithm: rollout_correction: rollout_is: token rollout_is_threshold: 2.0 rollout_rs: null bypass_mode: false # 解耦模式 ``` **属性:** - 每令牌独立截断 - 低于序列级别方差(比率乘积单独绑定) - 典型阈值:1.5 - 5.0 **理论:** 详见[rollout_corr_math.md §3.3.1](rollout_corr_math.md#331-token-level-aggregation) --- ### 2. 解耦模式与序列级别重要性采样(`decoupled_seq_is`) **配置:** ```python config = RolloutCorrectionConfig.decoupled_seq_is(threshold=2.0) ``` **组件:** - **操作模式**:解耦(3 个策略) - **损失**:带裁剪的 PPO - **IS 聚合**:序列级别 - **RS**:无(可单独添加) **等价 YAML:** ```yaml algorithm: rollout_correction: rollout_is: sequence rollout_is_threshold: 2.0 rollout_rs: null bypass_mode: false # 解耦模式 ``` **属性:** - 跨序列乘法聚合 - 对异常值比序列级别更敏感 - 典型阈值:2.0 - 10.0(高于令牌级别) **理论:** 详见[rollout_corr_math.md §3.3.2](rollout_corr_math.md#332-sequence-level-aggregation) --- ### 3. 解耦模式与序列级别 IS + 拒绝采样(`decoupled_seq_is_rs`) **配置:** ```python config = RolloutCorrectionConfig.decoupled_seq_is_rs(is_threshold=2.0, rs_threshold=2.0) ``` **组件:** - **操作模式**:解耦(3 个策略) - **损失**:带裁剪的 PPO - **IS 聚合**:序列级别 - **RS**:序列级别拒绝 **等价 YAML:** ```yaml algorithm: rollout_correction: rollout_is: sequence rollout_is_threshold: 2.0 rollout_rs: sequence rollout_rs_threshold: 2.0 rollout_rs_threshold_lower: 0.5 # 阈值倒数 bypass_mode: false # 解耦模式 ``` **属性:** - 双机制:IS 重新加权 + 拒绝过滤 - 降低有效样本大小(拒绝异常值) - 用于严重的离线策略差距 **理论:** 详见[rollout_corr_math.md §3.4](rollout_corr_math.md#34-rejection-sampling-rs) --- ### 4. 解耦模式与几何拒绝采样(`decoupled_geo_rs`) **配置:** ```python config = RolloutCorrectionConfig.decoupled_geo_rs(rs_threshold=1.001, veto_threshold=1e-4) ``` **组件:** - **操作模式**:解耦(3 个策略) - **损失**:带裁剪的 PPO - **IS 聚合**:无(纯拒绝) - **RS**:几何级别拒绝 - **否决**:启用 **等价 YAML:** ```yaml algorithm: rollout_correction: rollout_is: null rollout_rs: geometric rollout_rs_threshold: 1.001 rollout_rs_threshold_lower: 0.999 rollout_token_veto_threshold: 1e-4 bypass_mode: false # 解耦模式 ``` **属性:** - 无 IS 权重(纯拒绝) - 几何平均聚合(比算术乘积更敏感) - 典型阈值:1.0001 - 1.001(比序列/令牌级别更紧) - 基于平均每令牌比率偏差拒绝序列 **为什么紧阈值?** 几何平均非常敏感。对每个比率 1.01 的 100 个令牌: - 乘积:1.01^100 ≈ 2.7 - 几何平均:1.01 阈值 1.001 拒绝平均每令牌偏差 > 0.1% 的序列。 **理论:** 详见[rollout_corr_math.md §3.3.3](rollout_corr_math.md#333-geometric-aggregation) --- ### 5. PPO 与旁路模式(`ppo_is_bypass`) **配置:** ```python config = RolloutCorrectionConfig.ppo_is_bypass(threshold=2.0) ``` **组件:** - **操作模式**:旁路(2 个策略:π_rollout = π_old, π_θ) - **损失**:带裁剪的 PPO - **IS 聚合**:不需要(π_old = π_rollout) - **RS**:无 **等价 YAML:** ```yaml algorithm: rollout_correction: rollout_is: token # 指标占位符 rollout_is_threshold: 2.0 rollout_rs: null bypass_mode: true # 旁路模式 use_policy_gradient: false ``` **属性:** - 跳过 `actor.compute_log_prob()` 前向传递 - PPO 针对 π_rollout 裁剪(行为策略) - 设置 π_old = π_rollout(两策略设置) - 不分离近端策略和行为策略 **配置要求:** - 设置 `actor_rollout_ref.rollout.calculate_log_probs: true` **理论:** 详见[rollout_corr_math.md §3.1.2](rollout_corr_math.md#312-bypass-mode-two-policies) --- ### 6. 策略梯度与 IS(`pg_is`) **配置:** ```python config = RolloutCorrectionConfig.pg_is(threshold=2.0) ``` **组件:** - **操作模式**:旁路(2 个策略:π_rollout, π_θ) - **损失**:纯 IS(策略梯度仅,无 PPO 裁剪) - **IS 聚合**:序列级别 - **RS**:无 **等价 YAML:** ```yaml algorithm: rollout_correction: rollout_is: sequence rollout_is_threshold: 2.0 rollout_rs: null bypass_mode: true # 必需 use_policy_gradient: true # 使用策略梯度损失(无 PPO 裁剪) ``` **属性:** - 策略梯度损失(无 PPO 裁剪) - 单前向传递(跳过 old_log_prob 计算) - IS 权重即时计算在损失函数中 **理论:** 详见[rollout_corr_math.md §3.2.2](rollout_corr_math.md#322-pure-is-loss-policy-gradient) --- ### 7. 策略梯度与拒绝采样(`pg_rs`) **配置:** ```python config = RolloutCorrectionConfig.pg_rs( rs_threshold=1.001, veto_threshold=1e-4 ) ``` **组件:** - **操作模式**:旁路(2 个策略:π_rollout, π_θ) - **损失**:纯策略梯度(无 PPO 裁剪,`use_policy_gradient=True` 通过) - **IS 聚合**:无 - **RS**:几何级别拒绝 - **否决**:启用 **等价 YAML:** ```yaml algorithm: rollout_correction: rollout_is: null rollout_rs: geometric rollout_rs_threshold: 1.001 rollout_rs_threshold_lower: 0.999 rollout_token_veto_threshold: 1e-4 bypass_mode: true use_policy_gradient: true ``` **属性:** - 纯几何 RS(无 IS 权重,仅拒绝) - 跳过 `actor.compute_log_prob()` 前向传递(旁路模式) - 否决机制启用 - 典型阈值:1.0001 - 1.001(比序列/令牌级别更紧) **理论:** [§3.1.2 (Bypass)](rollout_corr_math.md#312-bypass-mode-two-policies) + [§3.3.3 (Geometric)](rollout_corr_math.md#333-geometric-aggregation) --- ## 附加有用配置(未作为预设公开) 这些配置**完全支持**但还没有便利预设方法。 ### 1. 令牌 IS + 令牌 RS(`token_is_rs`) 令牌级别 IS 权重与令牌级别 RS 遮罩。 **Python:** ```python config = RolloutCorrectionConfig( rollout_is="token", rollout_is_threshold=2.0, rollout_rs="token", rollout_rs_threshold=2.0, ) ``` **属性:** 每令牌 IS 权重 + 每令牌 RS 遮罩。 ### 2. 纯令牌 RS(`token_rs`) 令牌级别 RS 仅,无 IS 权重。 **Python:** ```python config = RolloutCorrectionConfig( rollout_is=None, rollout_rs="token", rollout_rs_threshold=2.0, ) ``` **属性:** 令牌级别 RS 遮罩,无 IS 重新加权。 ### 3. 纯序列 RS(`seq_rs`) 序列级别 RS 仅,无 IS 权重。 **Python:** ```python config = RolloutCorrectionConfig( rollout_is=None, rollout_rs="sequence", rollout_rs_threshold=2.0, ) ``` **属性:** 序列级别 RS 遮罩,无 IS 重新加权。 --- ### 总结:IS 权重的处理方式 IS 权重(`rollout_is_weights`)经过固定处理管道: **阶段 1:安全绑定(防止溢出)** - 令牌级别:`exp(clamp(log_ratio, -20, 20))` 每令牌 → 绑定每个令牌到 [2e-9, 5e8] - 序列级别:`exp(clamp(sum(log_ratio), -20, 20))` → 绑定乘积到 [2e-9, 5e8],广播到所有令牌 **阶段 2:截断(降低方差)** - `.clamp(max=rollout_is_threshold)` → 在上阈值处截断权重(TIS:截断重要性采样) - 无下限截断(保留小权重下的无偏性质) **阶段 3:填充零化(正确聚合)** - `weights * response_mask` → 在填充位置置零 **阶段 4:可选批量归一化** - 如果 `rollout_is_batch_normalize=True`:归一化权重到每批次 mean=1.0 - 在截断后应用以保留截断语义 **拒绝采样(分离机制)** 拒绝采样通过 `compute_rollout_rejection_mask()` 修改 `response_mask`(NOT 权重): - 独立计算安全绑定比率 - 创建二进制遮罩:在 [lower_threshold, upper_threshold] 之外的令牌/序列 → 0(拒绝) - 否决:检查**未绑定每令牌比率**(在安全绑定前),拒绝包含灾难令牌的整个序列 - 修改遮罩用于损失聚合(被拒绝样本从训练中排除) ## 操作模式 框架提供**两种操作模式**用于计算 π_old,可以与不同损失函数组合。 ### 操作模式和配置 | 配置 | `bypass_mode` | `use_policy_gradient` | 操作模式 | 损失函数 | 描述 | |---------------|----------------------------------|------------------------------|----------------|---------------|-------------| | **解耦** | `false` | `false` | 解耦 | PPO | 单独计算 `old_log_prob` 通过 `actor.compute_log_prob()` | | **旁路** | `true` | `false` | 旁路 | PPO | 设置 `old_log_prob = rollout_log_prob`,PPO 针对部署策略裁剪 | | **旁路 + PG** | `true` | `true` | 旁路 | 策略梯度 | 具有策略梯度损失的旁路模式(无 PPO 裁剪) | ### 操作模式细节 #### 解耦模式(三个策略) **策略设置:** - π_rollout:行为策略(数据收集) - π_old:近端策略(在训练时期开始通过 `actor.compute_log_prob()` 计算) - π_θ:当前策略(正被更新) **配置:** `bypass_mode = false` **属性:** - ✅ 实现批次大小不变性 - ✅ 单独校正漂移 1(rollout→old)和漂移 2(old→current) - ✅ 高效陈旧数据利用 - ❌ 需要额外前向传递(`actor.compute_log_prob()`) **理论:** 详见[rollout_corr_math.md §3.1.1](rollout_corr_math.md#311-decoupled-mode-three-policies) #### 旁路模式(两个策略) **策略设置:** - π_rollout:行为策略(数据收集) - π_old = π_rollout:近端策略等于行为策略 - π_θ:当前策略(正被更新) **配置:** `bypass_mode = true` **属性:** - ✅ 跳过 `actor.compute_log_prob()` 调用(更快) - ✅ 处理使用策略梯度的离线策略校正(IS/RS 时) - ✅ 使用两个策略而不是三个(π_rollout = π_old) - ⚠️ 不分离近端策略和行为策略(与解耦模式不同) **理论:** 详见[rollout_corr_math.md §3.1.2](rollout_corr_math.md#312-bypass-mode-two-policies) --- ### IS/RS 聚合级别(相对于操作模式正交) 聚合级别可以**独立**于操作模式选择。任何聚合级别在解耦或旁路模式下有效。 | `rollout_is` | `rollout_rs` | 行为 | |--------------|--------------|----------| | `null` | `null` | **禁用**:无计算,无指标,无拒绝 | | `null` | `"token"`, `"sequence"`, 或 `"geometric"` | **拒绝仅**:计算指标,NO 权重校正,YES 拒绝采样 | | `"token"` 或 `"sequence"` | `null` | **IS 权重仅**:权重校正启用,NO 拒绝采样 | | `"token"` 或 `"sequence"` | `"token"`, `"sequence"`, 或 `"geometric"` | **全校正**:权重校正和拒绝采样均启用 | ### 关键洞察 - ✅ 任何 IS/RS 聚合级别(token/sequence/geometric)可在**任一**解耦或旁路模式下使用 - ✅ 你可以**单独使用拒绝采样**而无 IS 权重校正(`rollout_is=null, rollout_rs="token"`) - ✅ 你可以**单独使用 IS 权重**而无异常值拒绝(`rollout_is="token", rollout_rs=null`) - ✅ 你可以**一起使用两者**(`rollout_is="token", rollout_rs="token"`) - ✅ 你可以**仅监控指标**而不应用校正,通过设置两者为 null 但仍提供 rollout_log_probs **否决拒绝**(如果通过 `rollout_token_veto_threshold` 启用)**独立**于 IS 和 RS 设置应用。 **理论:** 详见[rollout_corr_math.md §3.3](rollout_corr_math.md#33-isrs-aggregation-levels) 以获取聚合级别详情。 ### 示例工作流 **推荐:旁路 + 策略梯度模式** 此工作流使用旁路模式和纯策略梯度损失以提高效率。 1. **首先使用指标仅**来理解离线策略差距: ```yaml algorithm: rollout_correction: rollout_is: null rollout_rs: null bypass_mode: true # 旁路模式(推荐) use_policy_gradient: true # 纯策略梯度(推荐) ``` 监控 `rollout_corr/kl`, `rollout_corr/log_ppl_abs_diff`, `rollout_corr/chi2_token` 来评估离线策略差距。 2. **如果看到高异常分数,则启用拒绝采样**: ```yaml algorithm: rollout_correction: rollout_is: null rollout_rs: sequence # 或 "geometric" 以获得更高敏感度 rollout_rs_threshold: 2.0 bypass_mode: true # 旁路模式 use_policy_gradient: true # 纯策略梯度 ``` 这将异常值从训练中排除而不修改梯度。 3. **一旦舒适指标,则启用全 IS 校正**: ```yaml algorithm: rollout_correction: rollout_is: sequence # 推荐:无偏,适合大多数情况 rollout_is_threshold: 2.0 rollout_rs: sequence # 或 "geometric" 以进行更激进过滤 rollout_rs_threshold: 2.0 bypass_mode: true # 旁路模式 use_policy_gradient: true # 纯策略梯度 ``` **旁路 + 策略梯度模式的优势:** - ✅ 跳过昂贵的 `actor.compute_log_prob()` 前向传递(更快) - ✅ 在损失函数中即时计算 IS 权重(π_θ / π_rollout) - ✅ 比 PPO 简单(无裁剪,纯策略梯度带 IS/RS) - ✅ 对所有 IS/RS 组合有效 ## 使用 ### 基本设置 ```yaml algorithm: rollout_correction: rollout_is: token # 在令牌级别启用 IS 权重 rollout_is_threshold: 2.0 # IS 权重阈值 rollout_rs: null # 无拒绝采样 rollout_token_veto_threshold: null # 无否决 actor_rollout_ref: rollout: calculate_log_probs: true # 必需! ``` ### 指标 所有指标以 `rollout_corr/` 为前缀记录。例如,`rollout_is_mean` 显示为 `rollout_corr/rollout_is_mean`。 这些涵盖: - **诊断指标**:KL 发散,困惑度差异(测量离线策略差距) - **校正统计**:IS 权重,拒绝率,否决统计(测量应用校正) #### **核心 IS 权重指标** - **`rollout_is_mean`**:跨所有有效令牌的平均重要性采样权重 - 值接近 1.0 表示最小离线策略差距 - **`rollout_is_std`**:IS 权重的标准差 - 更高值表示 IS 权重更大的方差 - **`rollout_is_min`**:观察到的最小 IS 权重 - 显示最欠权重令牌/序列 - 对于 sequence/geometric:从未绑定对数空间比率计算(真最小) - 对于 token:从安全绑定权重计算 - **`rollout_is_max`**:观察到的最大 IS 权重 - 显示最过权重令牌/序列 - 对于 sequence/geometric:从未绑定对数空间比率计算(真最大,在安全绑定前) - 对于 token:从安全绑定权重计算(在阈值裁剪前) - 与 `rollout_is_threshold` 比较以查看裁剪影响 #### **有效样本大小** - **`rollout_is_eff_sample_size`**:IS 加权后的有效样本大小 - **公式**:`1 / mean(weights²)` ,权重归一化 - **范围**:0.0 到 1.0(作为原始批次的比率) - 较低值表示权重集中在较少样本上 #### **否决机制指标** - **`rollout_is_veto_fraction`**:被否决机制拒绝的序列分数 - **重要**:序列通过 `response_mask=0` 拒绝,NOT 通过修改 IS 权重 - **IS 权重不受否决影响**:已安全绑定和截断 - 否决检查**未绑定每令牌比率**(在安全绑定前的真比率) - 解耦模式:π_old(t)/π_rollout(t) - 旁路/纯 IS 模式:π_θ(t)/π_rollout(t) - 检测灾难令牌(真比率 < veto_threshold,如 < 1e-4) - **`rollout_is_catastrophic_token_fraction`**:低于否决阈值的令牌分数 - 在应用序列级别否决之前识别问题令牌 - 检查**未绑定每令牌比率**(真比率,不安全绑定) - 每个灾难令牌导致其整个序列拒绝 #### **阈值超出指标** - **`rollout_is_ratio_fraction_high`**:超过上阈值的权重分数 - 显示高位的裁剪/遮罩发生频率 - 对于 sequence/geometric:从未绑定对数空间比率计算(真超出) - 对于 token:从安全绑定权重计算(在阈值裁剪前) - **`rollout_is_ratio_fraction_low`**:低于下阈值(1/upper_threshold)的权重分数 - 诊断指标显示低于倒数阈值的权重有多少 - 对于 sequence/geometric:从未绑定对数空间比率计算(真超出) - 对于 token:从安全绑定权重计算(在裁剪前) #### **序列级别指标**(序列聚合) - **`rollout_is_seq_mean`**:序列级别平均 IS 权重 - 对于序列级别聚合应匹配 `rollout_is_mean` - **`rollout_is_seq_std`**:序列级别 IS 权重的标准差 - **`rollout_is_seq_min`**:最小序列级别 IS 权重 - **`rollout_is_seq_max`**:最大序列级别 IS 权重 - **`rollout_is_seq_max_deviation`**:序列级别最大绝对偏差从 1.0 - 显示最坏序列离线策略差距 - **`rollout_is_seq_fraction_high`**:超过上阈值的序列分数 - **`rollout_is_seq_fraction_low`**:低于下阈值的序列分数 #### **拒绝采样指标**(`rollout_rs` 启用时) - **`rollout_rs_masked_fraction`**:拒绝采样遮罩的令牌分数 - **重要**:拒绝采样修改 `response_mask`(将拒绝令牌置 0) - **独立于 IS 权重**:IS 权重仍被截断;拒绝是独立的过滤步骤 - 仅在 `rollout_rs` 启用时(token/sequence/geometric)出现 - **`rollout_rs_seq_masked_fraction`**:具有至少一个拒绝令牌的序列分数 - 显示拒绝采样的序列级别影响 - 令牌级别 RS:如果任何令牌在 [lower, upper] 之外则序列拒绝 - 序列级别 RS:基于序列级别比率接受/拒绝整个序列 - 几何 RS:基于几何平均接受/拒绝整个序列 #### **离线策略诊断指标**(训练 vs 部署策略) **术语说明:** 这些指标使用 "training" 来指训练参考策略,使用 "rollout" 来指 π_rollout(用于数据收集的行为策略)。 - **解耦模式**:"training" = π_old(在训练时期开始计算) - **旁路/纯 IS 模式**:"training" = π_θ(正被训练的当前策略) 在旁路/纯 IS 模式下,指标直接测量 π_θ 和 π_rollout 之间的漂移。 - **`training_ppl`**:训练参考策略的困惑度(解耦模式中的 π_old,旁路/纯 IS 模式中的 π_θ) - **公式**:`exp(-mean(log_probs))` - 较低值表示更高模型信心 - **`rollout_ppl`**:部署策略 π_rollout 的困惑度(例如 vLLM BF16) - **`ppl_ratio`**:训练 PPL 与部署 PPL 的比率 - **公式**:`exp(mean(log(training_ppl / rollout_ppl)))` - **含义**:> 1.0 意味着训练比部署更没信心 - **`training_log_ppl`**:训练策略的对数困惑度 - 用于识别趋势(线性刻度) - **`rollout_log_ppl`**:部署策略的对数困惑度 - **`log_ppl_diff`**:对数困惑度的平均差异 - **公式**:`mean(log_ppl_rollout - log_ppl_training)` - 符号指示哪种策略更自信 - **`log_ppl_abs_diff`**:平均绝对对数困惑度差异 - 不考虑方向的离线策略差距大小 - **`log_ppl_diff_max`**:跨序列的最大对数困惑度差异 - 识别最坏序列 - **`log_ppl_diff_min`**:最小对数困惑度差异 - **`kl`**:KL 发散 KL(π_rollout || π_training) - **公式**:`mean(log_prob_rollout - log_prob_training)` - **注意**:可以为负(deploy 是更没信心) - **`k3_kl`**:K3 KL 估计器 - **公式**:`mean(exp(log_ratio) - log_ratio - 1)` - 小 KL 值更稳定 - 始终非负 - **`chi2_token`**:令牌级别的卡方发散 - **公式**:`mean(ratio²) - 1` ,比率 = π_training/π_rollout - 测量 IS 权重分布的二阶矩 - 始终非负 - **`chi2_seq`**:序列级别的卡方发散 - **公式**:`mean((∏_t ratio_t)²) - 1` - IS 权重的序列级别二阶矩 - 比令牌级别对卡方更敏感 #### **示例:代码中访问指标** ```python # 指标从 compute_rollout_correction_and_rejection_mask 返回 from verl.trainer.ppo.rollout_corr_helper import compute_rollout_correction_and_rejection_mask # 返回 3 值(权重,modified_response_mask,指标) weights_proto, modified_response_mask, metrics = compute_rollout_correction_and_rejection_mask( old_log_prob=training_log_probs, # 从训练策略 rollout_log_prob=rollout_log_probs, # 从部署策略 response_mask=response_mask, rollout_is="token", # 在令牌级别启用 IS 权重 rollout_is_threshold=2.0, rollout_rs="token", # 在令牌级别启用拒绝采样 rollout_rs_threshold=2.0, rollout_rs_threshold_lower=0.5, rollout_token_veto_threshold=1e-4, # 为灾难异常启用否决 ) # 提取 IS 权重(处理过,在填充处置零) is_weights = weights_proto.batch["rollout_is_weights"] # IS 权重处理(令牌级别 IS 启用时): # 1. 安全绑定:exp(clamp(log_ratio, -20, 20)) 每令牌 # 2. 截断:.clamp(max=2.0) 以限制极端权重 # 3. 置零填充:在填充位置置零 # 注意:IS 权重始终截断(TIS:截断重要性采样) # modified_response_mask 应用拒绝(由于 rollout_rs="token"): # 1. RS 拒绝:在 [0.5, 2.0] 之外的令牌通过 response_mask 置零 # 2. 否决拒绝:具有灾难令牌的序列通过置零 # 注意:否决检查未绑定每令牌比率(在安全绑定前) # 注意:RS 和 IS 是分离机制 - 两者均可独立启用 # 所有指标具有 'rollout_corr/' 前缀 print(f"Mean IS weight: {metrics['rollout_corr/rollout_is_mean']:.3f}") print(f"Effective sample size: {metrics['rollout_corr/rollout_is_eff_sample_size']:.3f}") print(f"Veto fraction: {metrics['rollout_corr/rollout_is_veto_fraction']:.3f}") print(f"RS masked fraction: {metrics['rollout_corr/rollout_rs_masked_fraction']:.3f}") print(f"KL divergence: {metrics['rollout_corr/kl']:.3f}") # 检查有效令牌的 IS 权重(非填充) valid_weights = is_weights[response_mask.bool()]