如果你去坐过旋转木马,一定注意过:每匹木马都骑在一根会转的柱子上,随着音乐缓缓公转。游客记住的不是柱子的“绝对角度”,而是“我和旁边那匹马隔了几格”——就算转盘整体再多转半圈,这个相对差也不会变。大模型处理文本时,最缺的正是这种“位置感”:Transformer 的自注意力天生不区分词序,同一句话把词打乱,它的计算结果几乎不变。RoPE(Rotary Position Embedding)仿照旋转木马的思路:给每个词分配一个“旋转角度”,位置 m 的词就转 mθ 度,两个词一做注意力(内积),各自转掉的绝对角度会相互抵消,只剩相对位置 (n−m)θ——就像你俩不管各自在哪儿,一眼就能算出“隔了几格”。更妙的是,当上下文窗口不够用时,NTK-aware 方法不重新训练,只需把“底座”参数从 10000 调成 10000·s^(d/(d−2)),让慢马拉长步伐、快马保住细节,就能把窗口凭空延长好几倍;再配合 YaRN 的按波长分流、温度校准和长序列微调,开源模型也能稳稳跑在 32K、甚至上百万 token 的超长上下文上。

岗位关联

掌握 RoPE 与 NTK/YaRN,你就掌握了大模型上下文窗口“扩瞳手术”的底层钥匙——从 4K 到百万 token 不再被算力卡脖子。这是长文本 RAG、Agent 记忆与代码库问答的核心技术,也是大模型工程师面试的高频考点。

目录
  1. 可视化演示
  2. 编码动机
  3. 旋转矩阵
  4. RoPE核心公式
  5. 高频与低频
  6. NTK外推法
  7. YaRN微调
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

本节配有交互动效(进入视口后自动加载,约 3KB)。

旋转矩阵编码 Q/K向量按位置旋转角度θ 内积仅依赖相对位置m-n θ_i = 10000^(-2i/d) 将位置编码为二维旋转变换

编码动机

假如你是旋转木马的检票员,游客递给你一叠纸条,每张都写着“他迟到了”“因为堵车”“所以会议改到明天”。纸条没编号,你把它们按任意顺序排,故事都讲不通,可木马上的小马却对顺序毫无感觉——它们只认“谁和谁挨着”,不认“谁先谁后”。Transformer 的自注意力正是这样:它同时看所有词,词的先后顺序在计算里完全对称,交换两个词的输入位置,输出一模一样。

早期的 Transformer 研究者很快发现这个问题,于是给每个位置发“号码牌”(绝对位置编码)。号码牌要么是学出来的、要么是固定函数,模型只能死记“第 7 号配第 9 号的关系”,很难泛化到更长的序列;而且它表达的是绝对地址,两个词相隔多远要模型自己从编号里“心算”。这就像游客拿了座位号还要自己算“我隔你两个马位还是二十个马位”——既费劲又容易错。

从数学上看得更清楚。自注意力的核心只是一个“查询-检索”过程:每个词作为查询 Q,去所有键 K 里找相关的内容,再按相关度去取对应的值 V。请看公式:

回到木马乐园:与其发号码牌让模型心算,不如让每个马位自己带一个“会转的角度表盘”。RoPE 的出发点就是这么朴素——用旋转把“相对距离”直接写进点积。谁能完成这个任务?答案是中学就学过的旋转矩阵,下一节先认识这根“机械曲柄”。

Attn(Q,K,V)=softmax(QK⊤d)V\displaystyle \text{Attn}(Q,K,V) = \text{softmax}\left(\frac{Q K^{\top}}{\sqrt{d}}\right) V
  1. 查询与键点积Q 的每一行是“我正在找什么”,K 的每一行是“我是什么”,两者的点积给出所有词对之间的相关分。
    QK⊤Q K^{\top}
  2. 维度缩放维度 d 越大,点积的方差越大,除以根号 d 能让分数回到稳定区间,避免 softmax 饱和。
    d\sqrt{d}
  3. 概率归一softmax 把原始分数变成和为 1 的注意力权重,表示“我该看谁多少”。
    softmax(⋅)\text{softmax}(\cdot)
  4. 信息聚合V 是“我要输出的内容”,按权重混合所有词的信息得到最终输出。
    ×V\times V
  5. 排列对称之痛交换任意两行 Q、K、V,整个式子结果不变——所以没有位置信息,Transformer 就是一叠“洗乱的纸条”。

旋转矩阵

旋转木马能转起来,靠的是转盘下一根曲柄——把直线踩踏变成圆周运动。RoPE 的“曲柄”就是二维旋转矩阵 R(φ):给定平面上的向量 (x, y),把它绕原点转 φ 角。高中数学里,这组变换写成两行两列的矩阵,见公式。

旋转有三个让人放心的性质,正好对应上面的三条副产物:第一,它是线性变换,可以无缝嵌入神经网络的矩阵运算;第二,它保长度、保内积,不会破坏词向量的语义;第三,也是最重要的,它把“旋转相减”变成了极其自然的操作。在复数平面上,旋转矩阵对应乘以单位复数 e^{iφ},角度就是辐角,所以“角度差”本身就是复数除法的天然结果。

这把曲柄之所以对位置编码有用,是因为位置可以天然映射成角度:第 m 个词就转 mθ,第 n 个词就转 nθ,两者一相减,绝对位置 m、n 全部抵消,只剩 (n−m)θ——这正是木马上“我俩隔了几个座”的答案。下一节,我们把曲柄装进注意力机制。

R(ϕ)=(cos⁡ϕ−sin⁡ϕsin⁡ϕcos⁡ϕ),R(ϕ)⊤R(ψ)=R(ψ−ϕ)\displaystyle R(\phi)=\begin{pmatrix}\cos\phi&-\sin\phi\\ \sin\phi&\cos\phi\end{pmatrix},\qquad R(\phi)^{\top}R(\psi)=R(\psi-\phi)
  1. 旋转矩阵构造向量 (x, y) 旋转 φ 角后变成 (x cosφ − y sinφ, x sinφ + y cosφ),矩阵的两列恰是旋转后的两个坐标轴。
    (cos⁡ϕ−sin⁡ϕsin⁡ϕcos⁡ϕ)(xy)\begin{pmatrix}\cos\phi&-\sin\phi\\ \sin\phi&\cos\phi\end{pmatrix}\begin{pmatrix}x\\ y\end{pmatrix}
  2. 正交保长度R(φ)ᵀR(φ) = I,旋转不改变向量长度与夹角,内积天然稳定,不会把数值放大或压扁。
    R(ϕ)⊤R(ϕ)=IR(\phi)^{\top}R(\phi)=I
  3. 旋转合成先转 ψ 再转 φ,等于一次转 φ+ψ——两次旋转可以合并成一次。
    R(ϕ) R(ψ)=R(ϕ+ψ)R(\phi)\,R(\psi)=R(\phi+\psi)
  4. 转置即反向正交矩阵的转置等于逆,所以“取转置”就是“拧回去”。
    R(ϕ)⊤=R(−ϕ)R(\phi)^{\top}=R(-\phi)
  5. 两转相减把两个旋转合并到一步里,得到 R(ψ−φ),绝对转过的角度互相抵消,只剩下角度差。
    R(ϕ)⊤R(ψ)=R(ψ−ϕ)R(\phi)^{\top}R(\psi)=R(\psi-\phi)

RoPE核心公式

现在给木马装角度表:位置 m 的词向量 q 被旋转 mθ,位置 n 的词向量 k 被旋转 nθ。RoPE 的名字里有个“Ro“(Rotary),关键动作就是”转”——不新增任何可训练参数,只对 query 和 key 做旋转,value 不动。位置 0 的词转角 0,位置 1 转 θ,位置 2 转 2θ……以此类推。

为什么转了就能表达相对位置?因为内积对旋转有“抵消”的魔法:两个向量一起转,夹角不变;但如果把其中一个“拧回去”转过的绝对角,剩下的夹角差正是相对位置。把 q 和 k 都旋转后求注意力,立即得到下面的公式——这是 RoPE 整个架构的“心脏”。

实际实现时,向量不是整体转同一个角度,而是按维度分组:d 维向量拆成 d/2 个二维子向量,每组配一个频率 θᵢ,分别旋转。整体看是一个块对角旋转矩阵,等价于每个二维平面各自转 mθᵢ。这样同一个位置在不同频率下“转的圈数”不同,信息量更大。RoPE 的最终效果是:注意力分数里,相对距离越近的词对得分越高——就像木马上挨得近的游客更容易聊天。

这就是 RoPE 的几何魔法:无论是第 5 匹马还是第 7 匹马,只要转盘整体再多转一圈,它们的绝对角度都在变,但“隔了几格”永远不变。位置信息不再依赖绝对编号,而是内建在相对角度里。不过——木马上真的有“快马”和“慢马”之分,下一节我们就来看这个频率谱系。

⟨R(mθ) q,  R(nθ) k⟩=⟨q,  R((n−m)θ) k⟩\displaystyle \langle R(m\theta)\,q,\; R(n\theta)\,k\rangle = \langle q,\; R((n-m)\theta)\,k\rangle
  1. 展开内积内积写成“转置乘”的形式,转置先穿透前一个矩阵:qᵀR(mθ)ᵀR(nθ)k。
    (R(mθ) q)⊤(R(nθ) k)=q⊤R(mθ)⊤R(nθ) k(R(m\theta)\,q)^{\top}(R(n\theta)\,k) = q^{\top}R(m\theta)^{\top}R(n\theta)\,k
  2. 转置反向利用上一节的性质把 R(mθ)ᵀ 换成 R(−mθ),“正向转”变成“反向拧”。
    R(mθ)⊤=R(−mθ)R(m\theta)^{\top}=R(-m\theta)
  3. 两次旋转合并矩阵乘法天然结合,R(−mθ)·R(nθ) = R((n−m)θ),中间没有任何交叉项。
    R(−mθ) R(nθ)=R((n−m)θ)R(-m\theta)\,R(n\theta)=R((n-m)\theta)
  4. 绝对位置消失最终内积只依赖差值 n−m 与频率 θ,绝对位置 m、n 双双蒸发。
    ⟨q,  R((n−m)θ) k⟩\langle q,\; R((n-m)\theta)\,k\rangle

高频与低频

去过游乐园的人都知道,木马有快有慢:外圈的小马踩着节奏小步快跑,内圈的彩车则缓缓悠着走。RoPE 的频率表也是这个格局:d/2 个旋转频率从快到慢依次排开,每个频率由一个“底座”参数 base 统一生成。

维度越浅(i 越小),θᵢ 越大,转得越快——我们叫它“高频维度”;维度越深,θᵢ 越小,转得越慢——“低频维度”。每个频率的“步幅”用波长 λᵢ 衡量。i=0 的波长只有约 6 个位置,i 最大的维度波长可达上万——一个管“眼皮底下”,一个管“天涯海角”。

这个分工对后续扩展至关重要:高频维度分辨相邻词(“吃饭”和“吃面”的区别),低频维度负责把长距离信息(一个句号后面隔了 20 个词的名词)连起来。base=10000 是出厂设置,它决定“慢马有多慢、覆盖有多远”。注意,base 是超参数、不是学出来的权重——这为后面“只改 base 就扩容”埋下伏笔。既然快马管细节、慢马管远距,扩容时该怎么调?下一节请出工匠。

θi=base−2id,λi=2πθi=2π⋅base2id\displaystyle \theta_i=\mathrm{base}^{-\frac{2i}{d}},\qquad \lambda_i=\frac{2\pi}{\theta_i}=2\pi\cdot \mathrm{base}^{\frac{2i}{d}}
  1. 频率随维数衰减i 从 0 取到 d/2−1,i 越大指数越负,θᵢ 越小,旋转越慢。
    θi=base−2id\theta_i=\mathrm{base}^{-\frac{2i}{d}}
  2. 最快维度i=0 时 θ₀=1,每个位置整整转 1 弧度,相邻位置的“精细差别”全靠它分辨。
    θ0=1\theta_0=1
  3. 最慢维度i=d/2−1 时代入得到 θ = base^(−(d−2)/d),单步转角极小,波长极长。
    θd/2−1=base−d−2d\theta_{d/2-1}=\mathrm{base}^{-\frac{d-2}{d}}
  4. 波长表达式λᵢ = 2π/θᵢ,物理含义是“转满一圈要走多少个位置”。
    λi=2π⋅base2id\lambda_i=2\pi\cdot \mathrm{base}^{\frac{2i}{d}}
  5. base 的作用base=10000 时最慢波长约 2π·10⁴,与常见 4K 上下文同一量级;base 越大,慢马越慢、覆盖越远。
    λmax⁡≈2π⋅104\lambda_{\max}\approx 2\pi\cdot 10^4

NTK外推法

乐园要扩建了:原本 4096 个座位,游客排到了 32000。木马老板有两种方案。方案一(朴素线性插值)把所有座位的间距压到原来的 1/8,让 4K 的轨道挤进 32K 的人——结果快马原本两步能分辨的细节全部挤在一起,相邻词的关系一团糊,模型开始“看谁都像邻居”。

方案二来自一个有趣的观察:频率系统是一张“信息光谱”,不同维度负责不同尺度的信息,那么合理的扩容应该“高频不缩放、低频才缩放”。这个思路恰好对应神经正切核(NTK)理论的直觉——高频成分难以学习、必须保护,低频成分可以拉伸。所以它被叫做 NTK-aware 缩放。它几乎不增加任何成本,只需把底座参数换掉。

实现上,你只在模型配置里改一个基座参数,不重新训练,窗口就从 4K 变成 32K。以 LLaMA 7B 为例,d=4096,指数 d/(d−2)≈1.0005,所以新 base 几乎等于 10000·s——这也是很多人误以为“NTK 就是线性放大 base”的原因,但别忘了它接进 θᵢ 公式后,对每个维度的效应是差异化的。

NTK 这把“粗调扳手”很神奇:没动任何权重,木马却能在更大的场地上转起来。但它毕竟是应急措施——低频被强行拉长后,模型从没在长距离上见过这么远的“风景”,长程引用仍会虚弱、甚至编造。要修好这个短板,需要 YaRN 这样更精细的技师出场。

b′=b⋅sdd−2,θi′=θi⋅s−2id−2\displaystyle b^{\prime}=b\cdot s^{\frac{d}{d-2}},\qquad \theta_i^{\prime}=\theta_i\cdot s^{-\frac{2i}{d-2}}
  1. 扩展比定义s = L′/L 是新窗口与旧窗口的比值,4K→32K 时 s=8。
    s=L′/Ls = L^{\prime} / L
  2. 高频原封不动i=0 时指数 −2i/(d−2)=0,θ₀′=θ₀,邻近位置的细节完全保留。
    θ0′=θ0⋅s0=θ0\theta_0^{\prime}=\theta_0\cdot s^{0}=\theta_0
  3. 低频刚好拉开i=d/2−1 时代入指数为 −1,θ′=θ/s,波长正好放长 s 倍,恰好覆盖新窗口。
    θd/2−1′=θd/2−1⋅s−1\theta_{d/2-1}^{\prime}=\theta_{d/2-1}\cdot s^{-1}
  4. 中间维度渐变中间维度的缩放比例介于 1 与 1/s 之间,形成“高频保守、低频激进”的梯度,天然平滑。
    θi′=θi⋅s−2id−2\theta_i^{\prime}=\theta_i\cdot s^{-\frac{2i}{d-2}}
  5. 等价 base这组新频率恰好等于把 base 换成 b′=b·s^(d/(d−2)) 后重新生成——工程上只改一个 rope_theta 参数。
    b′=b⋅sdd−2b^{\prime}=b\cdot s^{\frac{d}{d-2}}

YaRN微调

YaRN 的名字来自一种毛线,寓意“把长度像毛线一样织起来”。它做的第一件事,是把 NTK-aware 的“一刀切经验公式”换成“按波长逐个检查”:每匹马的步幅(波长 λ)合适就保持原速,不合适才拉伸。这套机制叫 NTK-by-parts(按部件插值)。

具体规则是给每个频率算一个“插值系数”γ(λ):波长远小于窗口的高频维度 γ=0,纹丝不动;波长远超窗口的低频维度 γ=1,完全按 s 拉伸;中间用一段线性斜坡平滑过渡。由于这一版逐维度判断,它能适配不同的维度数 d 和 base,比硬改 base 更稳。

第二步是“温度系数”。窗口拉长后,注意力分数的分布会变得过尖(熵下降),模型近乎“只盯着最近的位置”。YaRN 的修正是在 softmax 前给 logits 除以一个温度 t,把注意力分布“摊开”恢复区分度。第三步才是微调:在长序列数据上做少量训练(几千步),让模型真正学会“走远路”。三者合起来,才能逼近原生长窗口的水平。

如果说 NTK-aware 是给木马换了个大底盘,YaRN 就是给每匹马做体检、给注意力装温度计、再放到新跑道上练几圈。至此,旋转木马乐园从 4K 一路扩建到 32K、甚至百万级,而游客们(token)依然能精确说出“我们隔了几个座位”。

θinew=θi⋅s−γ(λi),γ(λ)=clamp ⁣(λ−L1L2−L1, 0, 1)\displaystyle \theta_i^{\mathrm{new}}=\theta_i\cdot s^{-\gamma(\lambda_i)},\qquad \gamma(\lambda)=\mathrm{clamp}\!\left(\frac{\lambda-L_1}{L_2-L_1},\,0,\,1\right)
  1. 波长分流开关γ∈[0,1] 表示“该插值多少”;γ=1 完全插值,γ=0 不插值。
    γ(λ)∈[0,1]\gamma(\lambda)\in[0,1]
  2. 短波保持波长 λ ≤ L₁ 的高频维度 γ=0,位置细节一步不动。
    γ(λ)=0,λ≤L1\gamma(\lambda)=0,\quad \lambda\le L_1
  3. 长波全拉波长 λ ≥ L₂ 的低频维度 γ=1,频率整体除以 s 去覆盖远场。
    γ(λ)=1,λ≥L2\gamma(\lambda)=1,\quad \lambda\ge L_2
  4. 线性过渡L₁<λ<L₂ 时 γ 从 0 平滑升到 1,避免断崖损伤点积空间。
    γ(λ)=λ−L1L2−L1\gamma(\lambda)=\frac{\lambda-L_1}{L_2-L_1}
  5. 温度与微调窗口拉长后注意力分布变得过尖,需温度系数校准,典型取 t≈s^0.1;再在长序列上微调把新轨道跑熟。
    t≈s0.1t\approx s^{0.1}

AI 应用场景

代码仓库的超长上下文问答

阶段:推理部署

解决的问题:传统做法把代码仓库切片后做向量检索,全局问答被割裂;用 RoPE 系方法把窗口翻 8 倍后,模型可以一次读完整个长文件,跨函数追问不再丢上下文。

具体案例:用 CodeLlama-34B(原窗口 4K)做仓库问答时,先按 NTK-aware 公式把 base 放大,模型可一次处理 32K token 的源码,回答“第 1200 行附近的函数被谁调用”这类问题;免训练版本偶有编造,改用 YaRN 在代码长序列上微调几千步后,回答准确率大幅逼近原生 32K 模型。

RAG 系统的免训练应急扩窗

阶段:应用调优

解决的问题:模型已经上线、不能随便重训,但客户的文档长度超出窗口;NTK-aware 只需改一行 rope_theta 参数即可应急,把 4K 窗口临时扩到 8K。

具体案例:法律问答系统原先要把 30 页合同切成 8 段分别检索再拼接,上下文翻倍后切成 4 段就够,跨段引用完整率明显上升,全程没有重新训练,只改了推理配置。

超长多轮 Agent 记忆微调

阶段:训练微调

解决的问题:Agent 对话轮次一多,用户最早的意图常被截断;用 RoPE 原生编码 + YaRN 长序列微调,让模型在 64K 内仍保有早期指令。

具体案例:客服 Agent 在 50 轮对话后仍能记起用户开场提出的需求,配合 YaRN 的温度系数和长对话数据微调,早期的工具调用偏好不再“失忆”,对话连贯性显著提升。

Python 演示

python55 行
import numpy as np  def build_freqs(dim, base=10000.0):    # 每个二维子空间一个旋转频率 theta_i = base^(-2i/d)    i = np.arange(0, dim, 2)    return base ** (-2 * i / dim)  def rotate_half(x):    # 把相邻两维当成平面坐标, 输出旋转 90 度的分量 (-x2, x1)    x = x.reshape(-1, 2)    x1, x2 = x[:, 0], x[:, 1]    return np.concatenate([-x2, x1], axis=-1)  def rope_rotate(vec, angles_for_pos):    # 二维旋转的向量化写法: v' = v * cos + rotate_half(v) * sin    cos = np.repeat(np.cos(angles_for_pos), 2)    sin = np.repeat(np.sin(angles_for_pos), 2)    return vec * cos + rotate_half(vec) * sin  def ntk_base(base, scale, dim):    # NTK-aware 重标定底座: b' = b * s^(d/(d-2))    return base * (scale ** (dim / (dim - 2)))  def main():    dim = 8    freqs = build_freqs(dim)     q = np.random.RandomState(0).randn(dim)    k = np.random.RandomState(1).randn(dim)    m, n = 5, 11     q_rot = rope_rotate(q, m * freqs)          # 位置 m 的查询向量旋转 m*theta    k_rot = rope_rotate(k, n * freqs)          # 位置 n 的键向量旋转 n*theta    k_rel = rope_rotate(k, (n - m) * freqs)    # 只旋转相对角度的键向量     print("绝对旋转后的点积:", round(float(q_rot @ k_rot), 6))    print("相对旋转后的点积:", round(float(q @ k_rel), 6))     scale = 4    b_new = ntk_base(10000.0, scale, dim)    print("NTK 新 base:", round(b_new, 1))     old = build_freqs(dim, 10000.0)    new = build_freqs(dim, b_new)    for idx in range(dim // 2):        print("维度组 %d: 频率 %.5f -> %.5f, 比值 %.3f" % (idx, old[idx], new[idx], new[idx] / old[idx]))  if __name__ == "__main__":    main()
目的:用 numpy 从零实现 RoPE 的分组旋转,验证“绝对旋转点积 = 相对旋转点积”,并展示 NTK-aware 重标定 base 后高频不变、低频按 1/s 缩放的规律。
  1. i = np.arange(0, dim, 2)每 2 个维度共用一个频率,8 维向量只需 4 个 θ。
  2. return base ** (-2 * i / dim)与正文公式 θᵢ = base^(−2i/d) 一一对应。
  3. x = x.reshape(-1, 2)把相邻两维 (x_{2i}, x_{2i+1}) 当成一个平面坐标点。
  4. return vec * cos + rotate_half(vec) * sin向量化实现二维旋转,零循环。
  5. q_rot = rope_rotate(q, m * freqs)位置 m 的查询向量旋转角度 m·θᵢ。
  6. k_rel = rope_rotate(k, (n - m) * freqs)只转相对角度,用于验证内积恒等式。
  7. return base * (scale ** (dim / (dim - 2)))NTK-aware 底座公式,d=8、s=4 时新 base≈63500。

常见误区

误区“旋转矩阵和频率不也是模型的一部分吗?训练的时候应该会一起更新才对。”初学者往往把 RoPE 当成可学习的位置 embedding。

正解RoPE 的旋转角度完全由位置 m 和固定频率 θᵢ 确定性计算,没有任何可训练参数;真正被训练的是 Q/K/V 的投影权重。就像旋转木马的转速是出厂设定,游客只能挑马骑,不能改转盘。

为何常见:Bert 时代的可学习位置 embedding 太深入人心,看到“编码”二字就容易联想到 Embedding 层参数。

误区“我把 base 一改,模型就变成 32K 上下文了,跟原生训练的一样强。”

正解免训练外推只是把频率重新标定,让模型在“能忍受”的范围内工作;长距离检索与推理仍有明显退化,尤其是扩展到几十万甚至上百万 token 时。YaRN 论文显示必须配合长序列微调才能逼近原生水平。类比:改大木马底座,马是能跑向远圈,但老马从没见过远圈地形,照样会迷路。

为何常见:社区宣传“无需训练”过于吸睛,忽略了性能回落的 caveat,教程也常不展示长距离 benchmark 的衰减曲线。

误区“调大 base 就是让所有旋转速度都放慢,跟线性插值差不多。”

正解恰恰相反,NTK-aware 的妙处在于差异化:高频维度(i 小)几乎不动,低频维度(i 大)才被压到原频率的 1/s。若所有频率等比例缩放,相邻位置就分不清了——那正是朴素插值失败的根源。

为何常见:直观上“窗口翻倍→频率减半”最自然,而 NTK 的差异效应藏在指数 −2i/(d−2) 里,不做推导很容易想当然。

误区“上下文窗口扩大后,显存和速度应该没什么变化吧?”

正解自注意力的复杂度是 O(L²),窗口 L 变大,计算与显存按平方增长。生产环境必须配合 FlashAttention、窗口注意力(window attention)、KV cache 管理(如 sliding window + 换位),否则“窗口能装下”和“窗口跑得动”是两回事。

为何常见:教程只讲公式不讲工程,导致把“模型能力”与“资源开销”混为一谈,上线后才发现显存爆炸。

面试题

Q1. RoPE 中两个词(位置 m 与 n)做注意力时,旋转角在点积中的作用使得分数只取决于?

单选题 · easy

  • A.两个词的绝对位置
  • B.两个词的相对位置← 正确答案
  • C.两个词的词向量距离
  • D.两个词的共现频率

答案:B。RoPE 把位置 m 旋转成角度 mθ,点积后绝对角度抵消,只留下 (n−m)θ,这是 R(φ)ᵀR(ψ)=R(ψ−φ) 的直接结果。所以注意力分数只依赖相对位置,A、C、D 都与 RoPE 的核心性质无关。

Q2. 下列属于 RoPE 旋转位置编码优点的有?

多选题 · easy

  • A.显式表达相对位置← 正确答案
  • B.不引入新的训练参数← 正确答案
  • C.几何诠释直观清晰← 正确答案
  • D.可以完全替代注意力

答案:ABC。RoPE 把相对位置直接写进点积(A 对);旋转角度由固定公式生成,不增加可训练参数(B 对);旋转木马的几何直觉是它的一大卖点(C 对)。D 错误,RoPE 只是给 q/k 加旋转,不替代注意力机制。

Q3. 在 RoPE 的频率体系中,高频维度(i 较小)主要负责?

单选题 · mid

  • A.保留相邻词的细腻关系← 正确答案
  • B.覆盖超长距离的依赖
  • C.压缩模型的参数量
  • D.统一不同层级的语义

答案:A。高频维度 θᵢ 大、波长只有几个位置,负责分辨相邻词的细节;低频维度波长上万,才负责长距离依赖。B 是低频的职责,C、D 与频率维度无关。

Q4. NTK-aware 外推与朴素线性插值在频率处理上的本质区别是?

单选题 · mid

  • A.对所有频率等比例缩放
  • B.对高低频做差异化缩放← 正确答案
  • C.完全不调整任何频率
  • D.只降低高频不压低频

答案:B。朴素插值把位置整体除以 s,等价于所有频率等比例除以 s,高频细节被抹平;NTK-aware 高频不动、低频约缩至 1/s,形成差异化缩放。A 是朴素插值,C 等于没扩展,D 方向正好反了。

Q5. 若扩展比为 s、隐藏维度为 d,NTK-aware 重新标定 base 的正确公式是?

单选题 · mid

  • A.b·s 的 (d−2)/d 次幂
  • B.b·s 的 d/(d−2) 次幂← 正确答案
  • C.b 除以 s 的 d/(d−2) 次幂
  • D.b·s 的 d−2 次幂

答案:B。正确公式是 b′=b·s^(d/(d−2))。A 是指数取反,C 方向反了,D 丢了 d/(d−2) 这个关键比值。对 LLaMA 7B(d=4096),指数≈1.0005,所以很多人误以为就是线性乘 s。

Q6. YaRN 中的“按波长分流”(NTK-by-parts)具体是指?

单选题 · mid

  • A.对所有维度一律插值处理
  • B.波长短的维度保持不插值← 正确答案
  • C.波长长的维度永远不拉伸
  • D.只关心词频不关心波长

答案:B。YaRN 按波长 λ 计算插值系数 γ:波长短(高频)γ=0 不插值,波长长(低频)γ=1 全插值,中间线性过渡。A 是朴素插值,C 与规则相反,D 完全跑题。

Q7. 设旋转频率为 θ,位置 m 的 q 与位置 n 的 k 旋转后做内积,其结果等于?

单选题 · hard

  • A.qᵀ 旋转 (m−n)θ 后再乘 k
  • B.qᵀ 旋转 (n−m)θ 后再乘 k← 正确答案
  • C.qᵀ 旋转 (m+n)θ 后再乘 k
  • D.qᵀk 加上 m−n 的数值

答案:B。由 (R(mθ)q)ᵀ(R(nθ)k) = qᵀR(mθ)ᵀR(nθ)k = qᵀR(−mθ)R(nθ)k = qᵀR((n−m)θ)k,所以选 B。A 方向反了,C 是合成错误,D 完全丢掉了旋转结构。

Q8. 朴素线性插值扩展上下文后,邻近词区分度明显下降,最直接的原因是?

单选题 · easy

  • A.高频细节被成比例摊平丢失← 正确答案
  • B.低频信号被无限度放大
  • C.模型的层数变少导致欠拟合
  • D.分词器被迫更换新的词表

答案:A。朴素插值把所有位置除以 s,等价于所有频率等比例除以 s,高频维度“一个位置一步”的精细度消失,相邻词分不清。低频被压缩而不是放大(B 错),层数和分词器都没变(C、D 错)。

Q9. 关于 NTK-aware 免训练外推,下列说法正确的有?

多选题 · hard

  • A.无需重训练即可扩展窗口← 正确答案
  • B.高频维度几乎保持原样← 正确答案
  • C.低频频率约缩至原来的 1/s← 正确答案
  • D.性能与原生训练完全一致

答案:ABC。NTK-aware 只改 base、不训练(A 对);i=0 时缩放比例为 1,高频不动(B 对);i=d/2−1 时缩放 1/s,低频波长正好拉长 s 倍(C 对)。D 错,免训练外推在长距离上仍有明显退化,不能与原生训练划等号。

Q10. 下列属于 YaRN 方案关键组成部分的有?

多选题 · hard

  • A.波长分段的平滑过渡插值← 正确答案
  • B.注意力温度系数的校准← 正确答案
  • C.长序列数据的继续微调← 正确答案
  • D.完全冻结全部模型的权重

答案:ABC。YaRN 三件套:NTK-by-parts 的波长分段 ramp 插值(A)、注意力温度系数 t 校准(B)、长序列数据微调(C)。D 与事实相反——微调时更新的是模型权重,RoPE 本身依旧无参数。