【Bug已解决】Vllm_importance_sampling_correction sequence-level mode aggregates per-token log-ratios with sum instead of mean 解决方案原始报错Vllm_importance_sampling_correction sequence-level mode aggregates per-token log-ratios with sum instead of mean 场景vLLM 的重要性采样校正importance sampling correction用来修正训练策略与生成时用的旧策略之间的分布偏差。在 token 级模式下逐 token 的 log-ratio 求和是对的因为 exp(sum) 连乘 序列的 IS 权重。但切到序列级sequence-level模式时代码仍用sum把逐 token 的 log-ratio 加起来导致长序列的校正因子被长度放大——序列越长sum 越大IS 权重被人为放大训练被长度绑架。序列级模式的本意是整条序列一个校正因子应当用mean长度归一化而非sum。 关键词重要性采样校正、IS correction、log-ratio、sequence-level、token-level、sum vs mean、长度归一化、vLLM、离线策略、RL。一、现象长什么样序列越长校正越离谱token 级模式校正 exp(Σ log-ratio)即各 token 比率连乘正确序列级模式代码简单地把逐 token log-ratio 也sum了然后exp但序列级本应代表整条序列一个校正因子长度应被归一用sum时长序列的 log-ratio 累加值更大exp 后权重被指数放大短序列则被压小后果训练被序列长度偏见主导长序列的梯度被不当地放大短序列被忽视表现loss/优势随长度系统性偏移且只在序列级模式而非 token 级出现。核心问题序列级模式的聚合方式错用 token 级的sum没做长度归一mean。二、背景token 级 sum 对但序列级 mean 才对重要性采样校正的核心是策略比π_new(a|s) / π_old(a|s)取 log 得到log-ratio。对一条序列token 级序列的 IS 权重 各 token 比率的连乘exp(Σ log-ratio)。这里sum是正确的因为 sum of logs log of product。序列级把整条序列当作一个动作来校正目标是得到一个与长度无关的、代表整条序列偏差的标量因子。若仍用sum这个因子会随长度线性增长在 log 空间exp 后指数增长——长短序列完全不可比。正确做法是mean或sum / 长度让校正因子反映平均每个 token 的偏差长度归一。一句话token 级要的是乘积sum of logs序列级要的是平均偏差mean of logs。两种模式的聚合语义不同sum只适用于 token 级。三、根因序列级模式复用了 token 级的 sum 聚合根因拆解模式混用序列级直接调用 token 级的sum聚合没区分语义无长度归一序列级没除以 token 数log-ratio 随长度累积指数放大exp(sum)让长序列权重被指数放大短序列被压长度偏见训练目标被序列长度绑架偏离真实策略偏差仅序列级暴露token 级用 sum 是对的所以只在切序列级时出错缺模式分支聚合函数没按modetoken/sequence分支处理。下面用最小模型复现序列级用 sum 被长度放大再给序列级用 mean的修复。四、最小可运行复现import math def is_correction(log_ratios, modetoken): if mode token: return math.exp(sum(log_ratios)) # token 级sum 正确 # 序列级错误写法仍 sum return math.exp(sum(log_ratios)) # 被长度放大 if __name__ __main__: short [0.1, 0.1] # 2 个 token long [0.1] * 20 # 20 个 token平均偏差一样 print(token 级 short:, is_correction(short, token)) print(token 级 long :, is_correction(long, token)) # 序列级用 sum 时长序列权重被放大 10 倍exp(2.0) vs exp(0.2) print(序列级(错,sum) short:, is_correction(short, sequence)) print(序列级(错,sum) long :, is_correction(long, sequence))运行可见序列级用 sum 时长短序列权重差 10 倍尽管平均偏差相同——长度偏见现场。五、方案序列级用 mean 做长度归一第一层序列级模式把逐 token log-ratio取均值再 exp得到长度无关的校正因子def is_correction_fixed(log_ratios, modetoken): if not log_ratios: return 1.0 if mode token: return math.exp(sum(log_ratios)) # token 级sum连乘 # 序列级mean长度归一 return math.exp(sum(log_ratios) / len(log_ratios)) if __name__ __main__: short [0.1, 0.1] long [0.1] * 20 print(序列级(对,mean) short:, is_correction_fixed(short, sequence)) print(序列级(对,mean) long :, is_correction_fixed(long, sequence)) # 现在长短一致平均偏差相同 - 校正因子相同序列级用 mean 后长短序列得到相同的校正因子长度偏见消除。六、方案按模式分支聚合统一入口第二层把聚合收成按模式分支的单一函数token 级 sum、序列级 mean调用方只传 modedef aggregate_log_ratios(log_ratios, mode): 按模式聚合逐 token log-ratio。 if mode token: return sum(log_ratios) # 用于连乘exp 后 if mode sequence: if not log_ratios: return 0.0 return sum(log_ratios) / len(log_ratios) # 长度归一 raise ValueError(mode) def is_correction_v2(log_ratios, mode): agg aggregate_log_ratios(log_ratios, mode) return math.exp(agg) if __name__ __main__: print(统一入口 token:, is_correction_v2([0.1, 0.1], token)) print(统一入口 seq :, is_correction_v2([0.1]*20, sequence))统一入口保证两种模式用各自正确的聚合调用方不踩坑。七、方案空序列与长度归一边界守卫第三层序列级聚合要处理空序列长度为 0和极短序列避免除零或无效校正def aggregate_log_ratios_safe(log_ratios, mode): if not log_ratios: # 空序列返回中性值log-ratio0 - 校正1 return 0.0 if mode token: return sum(log_ratios) # 序列级长度归一此处 length 已保证 0 return sum(log_ratios) / len(log_ratios) def is_correction_safe(log_ratios, mode): agg aggregate_log_ratios_safe(log_ratios, mode) return math.exp(agg) if __name__ __main__: print(空序列 token:, is_correction_safe([], token)) # 1.0 print(空序列 seq :, is_correction_safe([], sequence)) # 1.0中性空序列返回中性校正1.0不除零、不崩边界安全。八、验证把序列级用 mean、token 级用 sum锁进测试def test_token_uses_sum(): # token 级 exp(sum) 连乘 assert abs(is_correction_v2([0.1, 0.2], token) - math.exp(0.3)) 1e-9 def test_sequence_uses_mean(): short [0.1, 0.1] long [0.1] * 20 # 序列级平均偏差相同 - 校正因子相同 assert abs(is_correction_v2(short, sequence) - is_correction_v2(long, sequence)) 1e-9 def test_empty_neutral(): assert is_correction_safe([], token) 1.0 assert is_correction_safe([], sequence) 1.0 if __name__ __main__: test_token_uses_sum() test_sequence_uses_mean() test_empty_neutral() print(IS 校正聚合测试通过。)九、排查清单序列级 IS 校正被长度放大按顺序查模式分支聚合是否按 token/sequence 模式分支没有则序列级误用 sum。长度归一序列级是否除以 token 数mean没除则被长度放大。指数放大是否 exp(sum) 让长序列权重指数增长是则长度偏见。token 级正确token 级用 sum连乘是否正确是勿误改成 mean。仅序列级暴露是否 token 级正常、切序列级才错则聚合语义混用。空序列序列级聚合是否处理空序列除零需返回中性值。统一入口是否单一函数按 mode 分支有则调用方不踩坑。十、小结序列级 IS 校正用 sum 而非 mean是聚合语义混用token 级要连乘sum of log-ratios 正确序列级要平均偏差mean of log-ratios长度归一但代码在序列级仍复用 token 级的 sum使长序列的校正因子被长度指数放大训练被长度偏见绑架。修复三层序列级 mean逐 token log-ratio 取均值再 exp长度归一长短序列可比模式分支单一聚合函数按mode分支token 级 sum、序列级 mean空序列守卫空序列返回中性校正1.0避免除零。核心原则逐 token 量聚合到序列级时token 级要连乘sum of logs序列级要平均mean of logs。凡是序列级模式仍用 sum 聚合 per-token log-ratio的写法都应改为 mean 做长度归一——否则序列越长校正越强训练被长度而非策略偏差主导。