如果你去坐过旋转木马,一定注意过:每匹木马都骑在一根会转的柱子上,随着音乐缓缓公转。游客记住的不是柱子的“绝对角度”,而是“我和旁边那匹马隔了几格”——就算转盘整体再多转半圈,这个相对差也不会变。大模型处理文本时,最缺的正是这种“位置感”: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 记忆与代码库问答的核心技术,也是大模型工程师面试的高频考点。
可视化演示
本节配有交互动效(进入视口后自动加载,约 3KB)。
旋转矩阵编码 Q/K向量按位置旋转角度θ 内积仅依赖相对位置m-n θ_i = 10000^(-2i/d) 将位置编码为二维旋转变换
编码动机
假如你是旋转木马的检票员,游客递给你一叠纸条,每张都写着“他迟到了”“因为堵车”“所以会议改到明天”。纸条没编号,你把它们按任意顺序排,故事都讲不通,可木马上的小马却对顺序毫无感觉——它们只认“谁和谁挨着”,不认“谁先谁后”。Transformer 的自注意力正是这样:它同时看所有词,词的先后顺序在计算里完全对称,交换两个词的输入位置,输出一模一样。
早期的 Transformer 研究者很快发现这个问题,于是给每个位置发“号码牌”(绝对位置编码)。号码牌要么是学出来的、要么是固定函数,模型只能死记“第 7 号配第 9 号的关系”,很难泛化到更长的序列;而且它表达的是绝对地址,两个词相隔多远要模型自己从编号里“心算”。这就像游客拿了座位号还要自己算“我隔你两个马位还是二十个马位”——既费劲又容易错。
从数学上看得更清楚。自注意力的核心只是一个“查询-检索”过程:每个词作为查询 Q,去所有键 K 里找相关的内容,再按相关度去取对应的值 V。请看公式:
回到木马乐园:与其发号码牌让模型心算,不如让每个马位自己带一个“会转的角度表盘”。RoPE 的出发点就是这么朴素——用旋转把“相对距离”直接写进点积。谁能完成这个任务?答案是中学就学过的旋转矩阵,下一节先认识这根“机械曲柄”。
- 查询与键点积Q 的每一行是“我正在找什么”,K 的每一行是“我是什么”,两者的点积给出所有词对之间的相关分。
- 维度缩放维度 d 越大,点积的方差越大,除以根号 d 能让分数回到稳定区间,避免 softmax 饱和。
- 概率归一softmax 把原始分数变成和为 1 的注意力权重,表示“我该看谁多少”。
- 信息聚合V 是“我要输出的内容”,按权重混合所有词的信息得到最终输出。
- 排列对称之痛交换任意两行 Q、K、V,整个式子结果不变——所以没有位置信息,Transformer 就是一叠“洗乱的纸条”。
旋转矩阵
旋转木马能转起来,靠的是转盘下一根曲柄——把直线踩踏变成圆周运动。RoPE 的“曲柄”就是二维旋转矩阵 R(φ):给定平面上的向量 (x, y),把它绕原点转 φ 角。高中数学里,这组变换写成两行两列的矩阵,见公式。
旋转有三个让人放心的性质,正好对应上面的三条副产物:第一,它是线性变换,可以无缝嵌入神经网络的矩阵运算;第二,它保长度、保内积,不会破坏词向量的语义;第三,也是最重要的,它把“旋转相减”变成了极其自然的操作。在复数平面上,旋转矩阵对应乘以单位复数 e^{iφ},角度就是辐角,所以“角度差”本身就是复数除法的天然结果。
这把曲柄之所以对位置编码有用,是因为位置可以天然映射成角度:第 m 个词就转 mθ,第 n 个词就转 nθ,两者一相减,绝对位置 m、n 全部抵消,只剩 (n−m)θ——这正是木马上“我俩隔了几个座”的答案。下一节,我们把曲柄装进注意力机制。
- 旋转矩阵构造向量 (x, y) 旋转 φ 角后变成 (x cosφ − y sinφ, x sinφ + y cosφ),矩阵的两列恰是旋转后的两个坐标轴。
- 正交保长度R(φ)ᵀR(φ) = I,旋转不改变向量长度与夹角,内积天然稳定,不会把数值放大或压扁。
- 旋转合成先转 ψ 再转 φ,等于一次转 φ+ψ——两次旋转可以合并成一次。
- 转置即反向正交矩阵的转置等于逆,所以“取转置”就是“拧回去”。
- 两转相减把两个旋转合并到一步里,得到 R(ψ−φ),绝对转过的角度互相抵消,只剩下角度差。
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 匹马,只要转盘整体再多转一圈,它们的绝对角度都在变,但“隔了几格”永远不变。位置信息不再依赖绝对编号,而是内建在相对角度里。不过——木马上真的有“快马”和“慢马”之分,下一节我们就来看这个频率谱系。
- 展开内积内积写成“转置乘”的形式,转置先穿透前一个矩阵:qᵀR(mθ)ᵀR(nθ)k。
- 转置反向利用上一节的性质把 R(mθ)ᵀ 换成 R(−mθ),“正向转”变成“反向拧”。
- 两次旋转合并矩阵乘法天然结合,R(−mθ)·R(nθ) = R((n−m)θ),中间没有任何交叉项。
- 绝对位置消失最终内积只依赖差值 n−m 与频率 θ,绝对位置 m、n 双双蒸发。
高频与低频
去过游乐园的人都知道,木马有快有慢:外圈的小马踩着节奏小步快跑,内圈的彩车则缓缓悠着走。RoPE 的频率表也是这个格局:d/2 个旋转频率从快到慢依次排开,每个频率由一个“底座”参数 base 统一生成。
维度越浅(i 越小),θᵢ 越大,转得越快——我们叫它“高频维度”;维度越深,θᵢ 越小,转得越慢——“低频维度”。每个频率的“步幅”用波长 λᵢ 衡量。i=0 的波长只有约 6 个位置,i 最大的维度波长可达上万——一个管“眼皮底下”,一个管“天涯海角”。
这个分工对后续扩展至关重要:高频维度分辨相邻词(“吃饭”和“吃面”的区别),低频维度负责把长距离信息(一个句号后面隔了 20 个词的名词)连起来。base=10000 是出厂设置,它决定“慢马有多慢、覆盖有多远”。注意,base 是超参数、不是学出来的权重——这为后面“只改 base 就扩容”埋下伏笔。既然快马管细节、慢马管远距,扩容时该怎么调?下一节请出工匠。
- 频率随维数衰减i 从 0 取到 d/2−1,i 越大指数越负,θᵢ 越小,旋转越慢。
- 最快维度i=0 时 θ₀=1,每个位置整整转 1 弧度,相邻位置的“精细差别”全靠它分辨。
- 最慢维度i=d/2−1 时代入得到 θ = base^(−(d−2)/d),单步转角极小,波长极长。
- 波长表达式λᵢ = 2π/θᵢ,物理含义是“转满一圈要走多少个位置”。
- base 的作用base=10000 时最慢波长约 2π·10⁴,与常见 4K 上下文同一量级;base 越大,慢马越慢、覆盖越远。
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 这样更精细的技师出场。
- 扩展比定义s = L′/L 是新窗口与旧窗口的比值,4K→32K 时 s=8。
- 高频原封不动i=0 时指数 −2i/(d−2)=0,θ₀′=θ₀,邻近位置的细节完全保留。
- 低频刚好拉开i=d/2−1 时代入指数为 −1,θ′=θ/s,波长正好放长 s 倍,恰好覆盖新窗口。
- 中间维度渐变中间维度的缩放比例介于 1 与 1/s 之间,形成“高频保守、低频激进”的梯度,天然平滑。
- 等价 base这组新频率恰好等于把 base 换成 b′=b·s^(d/(d−2)) 后重新生成——工程上只改一个 rope_theta 参数。
YaRN微调
YaRN 的名字来自一种毛线,寓意“把长度像毛线一样织起来”。它做的第一件事,是把 NTK-aware 的“一刀切经验公式”换成“按波长逐个检查”:每匹马的步幅(波长 λ)合适就保持原速,不合适才拉伸。这套机制叫 NTK-by-parts(按部件插值)。
具体规则是给每个频率算一个“插值系数”γ(λ):波长远小于窗口的高频维度 γ=0,纹丝不动;波长远超窗口的低频维度 γ=1,完全按 s 拉伸;中间用一段线性斜坡平滑过渡。由于这一版逐维度判断,它能适配不同的维度数 d 和 base,比硬改 base 更稳。
第二步是“温度系数”。窗口拉长后,注意力分数的分布会变得过尖(熵下降),模型近乎“只盯着最近的位置”。YaRN 的修正是在 softmax 前给 logits 除以一个温度 t,把注意力分布“摊开”恢复区分度。第三步才是微调:在长序列数据上做少量训练(几千步),让模型真正学会“走远路”。三者合起来,才能逼近原生长窗口的水平。
如果说 NTK-aware 是给木马换了个大底盘,YaRN 就是给每匹马做体检、给注意力装温度计、再放到新跑道上练几圈。至此,旋转木马乐园从 4K 一路扩建到 32K、甚至百万级,而游客们(token)依然能精确说出“我们隔了几个座位”。
- 波长分流开关γ∈[0,1] 表示“该插值多少”;γ=1 完全插值,γ=0 不插值。
- 短波保持波长 λ ≤ L₁ 的高频维度 γ=0,位置细节一步不动。
- 长波全拉波长 λ ≥ L₂ 的低频维度 γ=1,频率整体除以 s 去覆盖远场。
- 线性过渡L₁<λ<L₂ 时 γ 从 0 平滑升到 1,避免断崖损伤点积空间。
- 温度与微调窗口拉长后注意力分布变得过尖,需温度系数校准,典型取 t≈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 演示
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()
i = np.arange(0, dim, 2)每 2 个维度共用一个频率,8 维向量只需 4 个 θ。return base ** (-2 * i / dim)与正文公式 θᵢ = base^(−2i/d) 一一对应。x = x.reshape(-1, 2)把相邻两维 (x_{2i}, x_{2i+1}) 当成一个平面坐标点。return vec * cos + rotate_half(vec) * sin向量化实现二维旋转,零循环。q_rot = rope_rotate(q, m * freqs)位置 m 的查询向量旋转角度 m·θᵢ。k_rel = rope_rotate(k, (n - m) * freqs)只转相对角度,用于验证内积恒等式。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)做注意力时,旋转角在点积中的作用使得分数只取决于?
- A.两个词的绝对位置
- B.两个词的相对位置← 正确答案
- C.两个词的词向量距离
- D.两个词的共现频率
答案:B。RoPE 把位置 m 旋转成角度 mθ,点积后绝对角度抵消,只留下 (n−m)θ,这是 R(φ)ᵀR(ψ)=R(ψ−φ) 的直接结果。所以注意力分数只依赖相对位置,A、C、D 都与 RoPE 的核心性质无关。
Q2. 下列属于 RoPE 旋转位置编码优点的有?
- A.显式表达相对位置← 正确答案
- B.不引入新的训练参数← 正确答案
- C.几何诠释直观清晰← 正确答案
- D.可以完全替代注意力
答案:ABC。RoPE 把相对位置直接写进点积(A 对);旋转角度由固定公式生成,不增加可训练参数(B 对);旋转木马的几何直觉是它的一大卖点(C 对)。D 错误,RoPE 只是给 q/k 加旋转,不替代注意力机制。
Q3. 在 RoPE 的频率体系中,高频维度(i 较小)主要负责?
- A.保留相邻词的细腻关系← 正确答案
- B.覆盖超长距离的依赖
- C.压缩模型的参数量
- D.统一不同层级的语义
答案:A。高频维度 θᵢ 大、波长只有几个位置,负责分辨相邻词的细节;低频维度波长上万,才负责长距离依赖。B 是低频的职责,C、D 与频率维度无关。
Q4. NTK-aware 外推与朴素线性插值在频率处理上的本质区别是?
- A.对所有频率等比例缩放
- B.对高低频做差异化缩放← 正确答案
- C.完全不调整任何频率
- D.只降低高频不压低频
答案:B。朴素插值把位置整体除以 s,等价于所有频率等比例除以 s,高频细节被抹平;NTK-aware 高频不动、低频约缩至 1/s,形成差异化缩放。A 是朴素插值,C 等于没扩展,D 方向正好反了。
Q5. 若扩展比为 s、隐藏维度为 d,NTK-aware 重新标定 base 的正确公式是?
- 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)具体是指?
- A.对所有维度一律插值处理
- B.波长短的维度保持不插值← 正确答案
- C.波长长的维度永远不拉伸
- D.只关心词频不关心波长
答案:B。YaRN 按波长 λ 计算插值系数 γ:波长短(高频)γ=0 不插值,波长长(低频)γ=1 全插值,中间线性过渡。A 是朴素插值,C 与规则相反,D 完全跑题。
Q7. 设旋转频率为 θ,位置 m 的 q 与位置 n 的 k 旋转后做内积,其结果等于?
- 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. 朴素线性插值扩展上下文后,邻近词区分度明显下降,最直接的原因是?
- A.高频细节被成比例摊平丢失← 正确答案
- B.低频信号被无限度放大
- C.模型的层数变少导致欠拟合
- D.分词器被迫更换新的词表
答案:A。朴素插值把所有位置除以 s,等价于所有频率等比例除以 s,高频维度“一个位置一步”的精细度消失,相邻词分不清。低频被压缩而不是放大(B 错),层数和分词器都没变(C、D 错)。
Q9. 关于 NTK-aware 免训练外推,下列说法正确的有?
- 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 方案关键组成部分的有?
- A.波长分段的平滑过渡插值← 正确答案
- B.注意力温度系数的校准← 正确答案
- C.长序列数据的继续微调← 正确答案
- D.完全冻结全部模型的权重
答案:ABC。YaRN 三件套:NTK-by-parts 的波长分段 ramp 插值(A)、注意力温度系数 t 校准(B)、长序列数据微调(C)。D 与事实相反——微调时更新的是模型权重,RoPE 本身依旧无参数。