「大模型学习」(14)Long Context之线性插值、NTK-aware、Yarn、LongRoPE

Long Context要解决的问题是什么?

你整理得非常清晰,我来为你补充和系统化讲解一下 大语言模型(LLMs)中的 Long Context 插值技术,帮助你建立起一整套认知框架,尤其聚焦在位置编码插值策略。


🔧 为什么需要“位置插值”?

大语言模型如 GPT、LLaMA 等在训练时使用的是有限长度的上下文窗口(如 2048 token)。如果想在推理阶段扩展上下文到 4K、16K、100K 甚至百万 token,必须解决一个核心问题:

原始位置编码机制不支持未见过的位置索引!

例如:

  • RoPE(旋转位置编码)是 GPT 系列中使用的机制,训练时只学习到 2048 个位置索引。
  • 如果你强行外推,比如输入 4096 个 token,它就必须处理未见过的位置,从而性能显著下降。

于是引入插值:把原本“稀疏的”位置信息,插值成“密集的”分布,让模型在训练过的位置区间“内”继续工作,而不是外推“未知位置”。


回顾rope

m m m 是 token 在序列中的位置编号(position index),比如在一句话中:

“The cat sat on the mat”

如果我们将这句话 tokenize 为:

TokenIndex $m$
The0
cat1
sat2
on3
the4
mat5

那么:

  • 第一个 token “The” 的位置是 m = 0 m = 0 m=0
  • 第二个 token “cat” 的位置是 m = 1 m = 1 m=1
  • 以此类推。

在位置编码中,我们通常对每个 token 使用它的 m m m 值来生成对应的旋转编码。

i i i 是 embedding 向量维度中的索引(维度编号),特别是 RoPE 中的偶数-奇数维度对

假设一个 token 的嵌入向量维度 d = 8 d = 8 d=8,我们可以将它分为 4 个维度对:

i i i维度对 ( 2 i , 2 i + 1 ) (2i, 2i+1) (2i,2i+1)
0(0, 1)
1(2, 3)
2(4, 5)
3(6, 7)

RoPE 在每个维度对 ( 2 i , 2 i + 1 ) (2i, 2i+1) (2i,2i+1) 上使用不同的频率 θ i \theta_i θi 来控制旋转角度:

θ i = 1 10000 2 i / d \theta_i = \frac{1}{10000^{2i / d}} θi=100002i/d1

  • i i i 越大,频率 θ i \theta_i θi 越小;
  • 这意味着高维旋转更慢、低维旋转更快;

RoPE 编码每个 token 的嵌入向量时,使用如下公式在维度对 $(2i, 2i+1)$ 上进行旋转:

[ x 2 i rot x 2 i + 1 rot ] = [ cos ⁡ ( θ i ⋅ m ) − sin ⁡ ( θ i ⋅ m ) sin ⁡ ( θ i ⋅ m ) cos ⁡ ( θ i ⋅ m ) ] [ x 2 i x 2 i + 1 ] \begin{bmatrix} x_{2i}^{\text{rot}} \\ x_{2i+1}^{\text{rot}} \end{bmatrix} =\begin{bmatrix} \cos(\theta_i \cdot m) & -\sin(\theta_i \cdot m) \\ \sin(\theta_i \cdot m) & \cos(\theta_i \cdot m) \end{bmatrix} \begin{bmatrix} x_{2i} \\ x_{2i+1} \end{bmatrix} [x2irotx2i+1rot]=[cos(θim)sin(θim)sin(θim)cos(θim)][x2ix2i+1]

  • m m m 决定位置(输入序列的 token 序号);
  • i i i 决定频率(某个维度对使用哪一个频率);
  • 二者共同决定了该 token 的旋转角度,从而编码位置信息。

📏 常见插值技术的逻辑与演化

1️⃣ 位置插值(Positional Interpolation,PI)🔧

本质:缩放位置索引,而不扩展位置数量

在这里插入图片描述

  • 训练:L = 2048,索引为 0, 1, …, 2047

  • 推理想扩展到 L′ = 4096,只需将每个位置 m m m 映射为:

    m ′ = m ⋅ L L ′ = m ⋅ 2048 4096 = 0.5 m m' = \frac{m \cdot L}{L'} = \frac{m \cdot 2048}{4096} = 0.5m m=LmL=4096m2048=0.5m

实现方式(针对 RoPE)

RoPE 编码本质是:

RoPE ( m ) = [ cos ⁡ ( m / θ i ) , sin ⁡ ( m / θ i ) ] i = 1 d / 2 \text{RoPE}(m) = \left[\cos(m/\theta_i), \sin(m/\theta_i)\right]_{i=1}^{d/2} RoPE(m)=[cos(m/θi),sin(m/θi)]i=1d/2

我们只需要将 m m m 替换为:

m ← m ⋅ L L ′ m \leftarrow m \cdot \frac{L}{L'} mmLL

即可完成 PI。

这在代码中通常只涉及 一个位置索引缩放参数 scale = L / L’,插入到 RoPE 位置计算函数中。

RoPE 可以接收非整数位置,于是直接对 f ( q , m L L ′ ) f(q, \frac{mL}{L'}) f(q,LmL) 进行插值。

✅ 优点:

  • 不增加参数
  • 不外推,安全、稳定
  • 微调很快,仅适应新坐标空间

⚠️ 缺点:

  • 高频维度被“挤压”,周期短 → 精度损失

2️⃣ NTK-aware 插值

对不同频率做差异化处理:“高频外推、低频内插”

在 NTK-aware 插值中我们对这两个变量做了如下变换:

变量原始 RoPENTK-aware 插值
m m m(位置)原始 token index缩放为 m / α m / \alpha m/α
θ i \theta_i θi(频率) 10000 − 2 i / d 10000^{-2i/d} 100002i/d ( 10000 − 2 i / d ) 1 / α (10000^{-2i/d})^{1/\alpha} (100002i/d)1/α

最终计算的是:

cos ⁡ ( m α ⋅ θ i 1 / α ) \cos\left( \frac{m}{\alpha} \cdot \theta_i^{1/\alpha} \right) cos(αmθi1/α)

✅ 优点:

  • 低频更重要,因此优先保留低频结构
  • 保证部分维度高频可外推

⚠️ 缺点:

  • 存在“越界”频率,带来训练不稳定
  • 理论上的 k 并不总能匹配真实推理窗口 → 微调必不可少

3️⃣ NTK-by-parts 插值

观察每个 RoPE 维度的波长,分段做插值,只对长波长(低频)维度进行位置插值,保留短波长(高频)维度的原始结构,对中间波长做平滑过渡。

核心思想

令:

  • L L L 为训练时支持的最大上下文长度

  • L ′ L' L 为推理时目标上下文长度

  • s = L ′ L s = \frac{L'}{L} s=LL 为扩展倍数(插值因子)

  • d d d 维的 RoPE 原始角频率为 θ d = 10000 − 2 d / D \theta_d = 10000^{-2d/D} θd=100002d/D,其中 D D D 为维度总数

  • 对应的 波长 为:

    λ d = 2 π θ d = 2 π ⋅ 10000 2 d D \lambda_d = \frac{2\pi}{\theta_d} = 2\pi \cdot 10000^{\frac{2d}{D}} λd=θd2π=2π10000D2d

✅ 比率定义

定义维度 d d d 的相对位置比率:

r d = L λ d r_d = \frac{L}{\lambda_d} rd=λdL

也即:当前训练上下文长度 L L L 中,RoPE 第 d d d 维经历了多少个周期。

✅ 插值策略

设阈值 α \alpha α β \beta β

  • r d > β r_d > \beta rd>β(即波长 λ _ d ≪ L \lambda\_d \ll L λ_dL,频率高) → 不做插值
  • r d < α r_d < \alpha rd<α(即波长 λ _ d ≫ L \lambda\_d \gg L λ_dL,频率低) → 完全线性插值PI
  • 否则:按线性斜坡函数平滑插值

引入过渡因子 γ d \gamma_d γd

γ d = { 0 , r d > β 1 , r d < α β − r d β − α , else \gamma_d = \begin{cases} 0, & r_d > \beta \\ 1, & r_d < \alpha \\ \frac{\beta - r_d}{\beta - \alpha}, & \text{else} \end{cases} γd= 0,1,βαβrd,rd>βrd<αelse

然后每个维度的角频率缩放为:

θ d ′ = ( 1 − γ d ) ⋅ θ d + γ d ⋅ θ d s \theta_d' = (1 - \gamma_d) \cdot \theta_d + \gamma_d \cdot \frac{\theta_d}{s} θd=(1γd)θd+γdsθd

✅ 优点:

  • 保留高频维度,避免插值过度损害短距离语义建模能力
  • 提高长距离建模能力
  • 插值策略更加灵活,对多种任务更稳健

4️⃣ Yarn 插值 (PI + 动态温度调整)

在 attention logits 上引入温度参数 t:

softmax ( q T k t ⋅ d ) \text{softmax}\left(\frac{q^T k}{t \cdot \sqrt{d}} \right) softmax(td qTk)
可以根据上下文长度动态调整温度:

  • 短上下文:t 较小,插值强度低,保持原始位置编码特性。
  • 长上下文:t 增大,增强低频段的插值强度,避免位置编码碰撞。

结合 NTK-by-parts 插值,得到 Yarn 插值方案

✅ 特点:

  • RoPE插值 + 动态 softmax 调节
  • 理论与工程效果良好,无需改动模型结构

6️⃣ LongRoPE

在这里插入图片描述

非均匀插值 + 动态伸缩 + RoPE 维度搜索

在保持兼容性的前提下增强 RoPE 的长距离建模能力,尤其在超过预训练上下文长度时减少性能损失。

  • 非均匀位置插值:
    • LongRoPE中发现,有效的位置编码插值应考虑两种非均匀性:不同的RoPE维度( θ i 中的 i \theta_i中的i θi中的i)和不同的token位置( c o s ( m θ i ) 中的 m cos(m\theta_i)中的m cos(mθi)中的m)。低维和初始token位置存储着关键信息,因此需要进行更少程度的插值。相比之下,高维存储的信息相对较为稀疏,可进行较大程度的插值。通过搜索RoPE每个维度以及不同token位置的旋转角度缩放因子,有效地保留了原始 RoPE 位置编码中的信息。这种方法最大程度地减小了位置插值引起的信息损失,从而为微调提供了更好的初始化。
  • 上下文渐进式扩展:
    • LongRoPE首先在预训练的LLM上进行256k长度的微调,然后对微调后的模型进行第二次位置插值,以实现2048k的上下文窗口。
  • 短上下文窗口性能恢复:
    • 在扩展到2048k上下文窗口后,LongRoPE通过调整RoPE位置插值因子来恢复短上下文窗口的性能。LongRoPE在扩展后的大模型上对8K长度内的RoPE缩放因子进行了重新搜索,以鼓励在较短上下文长度上进行较少的位置插值。在推理过程中,大模型可根据输入长度动态调整相应的 RoPE 缩放因子。
实现过程:

一、主要计算模块及作用

  1. RoPEPositionalEncoding
angles = positions.unsqueeze(-1) * self.theta
pos_embed = torch.stack([angles.cos(), angles.sin()], dim=-1).flatten(-2)

对应公式:

对于 token 位置 m m m,RoPE 原始位置编码为:

RoPE ( m ) i = [ cos ⁡ ( m ⋅ θ i ) , sin ⁡ ( m ⋅ θ i ) ] \text{RoPE}(m)_i = \begin{bmatrix} \cos(m \cdot \theta_i), \quad \sin(m \cdot \theta_i) \end{bmatrix} RoPE(m)i=[cos(mθi),sin(mθi)]

其中:

θ i = base − 2 ( i / / 2 ) d \theta_i = \text{base}^{-\frac{2(i//2)}{d}} θi=based2(i//2)

  1. non_uniform_interpolation
scale = torch.where(mask, torch.ones_like(...), 1 / lambda_factors[i])

对第 i i i 维度(偶数维)进行插值调整:

👉 对应插值公式(非均匀插值):

若  m < n ^ : θ m , i = m ⋅ θ i \text{若 } m < \hat{n}:\quad \theta_{m,i} = m \cdot \theta_i  m<n^:θm,i=mθi

若  m ≥ n ^ : θ m , i = m λ i ⋅ θ i \text{若 } m \geq \hat{n}:\quad \theta_{m,i} = \frac{m}{\lambda_i} \cdot \theta_i  mn^:θm,i=λimθi

即在 RoPE 中将 大于阈值 n ^ \hat{n} n^ 的位置使用维度特定的缩放 λ i \lambda_i λi 进行调整。

最终插值嵌入为:

LongRoPE ( m , i ) = [ cos ⁡ ( θ m , i ) , sin ⁡ ( θ m , i ) ] \text{LongRoPE}(m, i) = \begin{bmatrix} \cos\left(\theta_{m,i}\right), \quad \sin\left(\theta_{m,i}\right) \end{bmatrix} LongRoPE(m,i)=[cos(θm,i),sin(θm,i)]

二、进化搜索:寻找最优 λ i \lambda_i λi

  1. initialize_population 设定三类初始化策略:
  • PI 初始化 λ i = s \lambda_i = s λi=s (线性压缩)
  • NTK-aware λ i = s 2 i / d \lambda_i = s^{2i/d} λi=s2i/d (对数压缩)
  • YaRN 分段式插值:前128维为1,中间段为 s 1 / 3 s^{1/3} s1/3,后段为 s s s
  1. search_lambda_factors

通过进化搜索进行迭代优化,目标是找到:

arg ⁡ min ⁡ { λ i } Perplexity ( LongRoPE ( λ i ) ) \arg\min_{\{\lambda_i\}} \text{Perplexity}(\text{LongRoPE}(\lambda_i)) arg{λi}minPerplexity(LongRoPE(λi))

完整插值流程
  1. 初始构建 RoPE:生成角度 θ i \theta_i θi(频率向量)

  2. 位置编码生成:使用 cos ⁡ ( m ⋅ θ i ) \cos(m \cdot \theta_i) cos(mθi) sin ⁡ ( m ⋅ θ i ) \sin(m \cdot \theta_i) sin(mθi)

  3. 应用插值

    • n ^ \hat{n} n^ 位置:保持原始 RoPE
    • 后位置:插值为 m λ i \frac{m}{\lambda_i} λim
  4. 与输入相加:RoPE 编码 + token embedding

  5. 输入 Transformer 编码器

✅ 优点:

  • 精度保持最好
  • 支持百万 token 推理
  • 与原模型兼容,可微调迁移

总结一张图(简化版)

插值策略插值对象高频维低频维特点
PI统一缩放所有位置插值插值简单、高效
NTK-aware高频外推/低频插值外推插值提升低频精度
NTK-by-parts按波长动态选择策略保留插值减少高频损失
Yarn+ attention 温度缩放动态动态插值 + logits 调节
LongRoPEtoken 维度双非均匀插值动态动态精度最优,支持百万级 context
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值