长上下文外推与位置编码的边界

一个训练上下文长度假设在 4K 或 8K 档的模型,服务端把 --max-model-len 连同位置编码的 rope_scaling 一起调到 32K、128K,服务能正常起、长文档请求也返回结果。分段测质量时出现两类异常:长文档任务的检索命中率随上下文变长而下降;几百到几千 token 的短任务,质量也低于调整前的基线。两类现象指向同一处:训练长度与推理时可设置的最大长度是两回事,把后者调大只放宽了运行时的序列上限,超出训练长度的位置编码由外推配置给出,而外推配置会改变注意力分布。

训练长度与可设推理长度上限

训练上下文长度是模型属性:位置编码在训练时见过的最大位置就是 original_max_position_embeddings 一类的配置值,权重的旋转频率是在这个范围里学出来的。

推理时的 --max-model-len 是运行时上限,它的取值受 KV 预算约束,由可用显存反推,与训练长度没有绑定。两个值可以不一致:--max-model-len 设到远超训练长度时,框架不一定拒绝,服务照常启动,超出训练长度的那一段位置由外推规则映射,而不是模型学过的表示。

量 属于 由什么决定 允许与另一项不一致
训练上下文长度 模型属性 训练配置,如 original_max_position_embeddings 是
--max-model-len 运行时参数 KV cache 预算、并发、显存余量 是
外推配置 推理配置 rope_scaling 的 type 与 factor 是,不设则按训练区间原样使用

位置编码外推的方法与代价

RoPE 把位置编码成一组旋转角,不同维度对的频率不同,随位置增长。当推理位置超出训练区间,外推方法做的是把超出部分的位置轴压回、或改频率基,使角度落在训练见过的分布附近。常见做法:

type 做法 代价
linear 位置索引按 factor 线性内插,压回训练区间 所有位置被压缩,短序列的相对位置分辨率下降
dynamic 在短序列上保持原频率,仅在超长时按需缩放 阈值附近行为不完全连续
ntk(dynamic_ntk 是 DynamicNTKScalingRotaryEmbedding 实现类名,不是配置取值) 改变频率基 rope_theta,让高频维度少被压缩 基的取值需按模型校准,取错会扰动短上下文
yarn 分频段处理,低频内插、高频外推,可加长度缩放 参数更多,效果依模型而异
# 位置编码外推配置(键名取自开源框架,取值按模型与实测填)
rope_scaling:
  type: linear          # linear / dynamic / ntk / yarn ...
  factor: 4.0           # 目标长度 / 训练长度
original_max_position_embeddings: 8192   # 训练长度,外推的基准
rope_theta: <base>      # 频率基,ntk 类方法会改它
# 服务侧序列上限
max_model_len: 32768

外推配置的核心代价是改变注意力分布。linear 一类内插把整个位置轴压回训练区间,训练区间内的相对距离也被改写,短序列的分辨率随之下降;ntk 一类改频率基,若基值不按当前模型校准,短上下文质量同样会退化。外推换来的是长位置的可用性,代价落在短位置的质量上。

现象

触发条件

组合 撞上的问题 先看哪一项
外推开启 + 长文档任务 命中率随上下文变长下降 长度—质量曲线
外推开启 + 短请求为主 短任务质量低于基线 factor 是否过大
--max-model-len 超训练长度、未配外推 超界位置无外推规则 是否设了 rope_scaling
滑动窗口 + 压缩池一类稀疏结构 窗口外的 KV 不参与注意力 窗口长度与压缩池配置

滑动窗口一类结构把注意力限制在窗口内,超出窗口的历史不进入计算。这是处理长上下文的另一条路径,靠稀疏化,不靠位置编码外推。判断一个模型能用多长上下文时,先分清它走的是外推还是稀疏路径,两者的失效表现不同:外推的边界在训练长度,稀疏的边界在窗口与压缩池的配置。

定位步骤

  1. 查训练长度。 读模型配置里的 original_max_position_embeddings 或等价字段,确定位置编码的基准区间,不用服务的 --max-model-len 反推。
  2. 查外推配置是否开启。 读 rope_scaling 的 type 与 factor;未配置即表示按训练区间使用,此时把 --max-model-len 调到训练长度之外,超界位置没有外推规则,行为不可预期。
  3. 按上下文长度分段测质量,不只测一个点。 设一组档位(示意:1K、4K、16K、32K、128K),每档内混入不同植入深度(示意:10% / 50% / 90%),记录每档的检索命中率与逐深度明细;命中率随档位下降的曲线即长度—质量曲线,逐深度明细用于看中间位置更难找的现象。
  4. 性能维度与质量维度分开测。 prefill 耗时随长度递增属于性能维度,只判注入是否正常;检索命中率属于质量维度,才是长上下文是否可用的判据。两者用不同用例,不混算。
  5. 得分与通过线分开、缺失不等于零。 命中率是要记录、要对比的得分,不直接当通过/失败线;没测过的档位记为 not_measured,不填 0。

记录字段

# 长上下文外推结论记录清单(字段名示意)
model_name:                # 模型
train_context_len:         # 训练长度,original_max_position_embeddings
max_model_len:             # 服务侧序列上限
rope_scaling_type:         # linear / dynamic / ntk / yarn / 未设
rope_scaling_factor:       # factor
rope_theta:                # 频率基
context_ladder:            # 质量测试档位,如 1K/4K/16K/32K/128K
needle_depths:             # 植入深度,如 10%/50%/90%
retrieval_accuracy_pct:    # 每档命中率
by_depth:                  # 逐深度明细
quality_version:           # 质量口径版本,改口径须同步升版本号
quality_status:            # measured / not_measured
verdict:                   # 外推可用 / 短任务退化 / 超训练长度无外推规则

小结

训练上下文长度是模型属性,--max-model-len 是受 KV 预算约束的运行时上限,两者可以不一致。把 --max-model-len 调到训练长度之外时,超出部分由 rope_scaling 的外推规则给出,外推改变注意力分布,长端可用的代价常落在短端质量上。判定时先读训练长度与外推配置,再按一组上下文长度档位分段测检索命中率并记录逐深度明细,把性能维度的注入速度与质量维度的检索命中率分开记录;max_model_len 能设到多大,不等于模型能读懂多长。