NanoTransformer Courses

🏠 Back to Lab 📚 All Courses

Ch2: RoPE — 旋转位置编码

🏛️ 模型架构

参数 说明
d_model 256 嵌入维度
n_heads 8 注意力头数
n_layers 6 Decoder 层数
d_ff 1024 FFN 隐藏层
vocab_size 20 0-9 + 运算符 + 特殊token
位置编码 RoPE 零参数,旋转式
总参数 4.74M 比 Ch1 少 39,936 参数

标准 Transformer Decoder,唯一架构改动:删除 Learned Positional Embedding,在 Attention 内部用 RoPE 旋转 Q/K


📍 先回顾:Ch1 我们用的是什么?

在 Ch1 中,我们的模型用的是 可学习位置编码 (Learned Positional Embedding)

self.pos_emb = nn.Embedding(max_len, d_model)  # [64, 256] 查找表

# forward:
x = tok_emb(input_ids) + pos_emb(positions)   # 直接相加!

每个位置 0, 1, 2, ... 都有一个独立的 256 维向量,和 token embedding 直接做加法


图1:Ch1 加法式位置编码 — 工作原理

Ch1 Learned Positional Embedding 工作原理


🚨 加法式位置编码的三大致命缺陷

加法式位置编码的三大缺陷

缺陷 1:绝对位置 ≠ 相对位置

模型学到的是"位置3的向量",但它真正需要的是"这两个 token 相隔多远"。

想象一下 CoT 中:23*45=S1: 3*5=15S2: 3*4=12 → ...

模型需要知道 35 是"相邻的乘法因子"(相对关系),而不是"一个在位置7、一个在位置9"(绝对位置)。

缺陷 2:长度外推能力为零

pos_emb = nn.Embedding(64, 256) — 训练时只见过 64 个位置。

如果 CoT 推理链变长到 80 步?直接崩溃 —— 位置 65~79 根本没有对应向量。

缺陷 3:加法污染语义

tok_emb("3") + pos_emb(7) 把位置信息混进了语义向量

到了 Attention 计算时,Q·K 既包含"这两个 token 语义相关吗?"也包含"位置编码加法残留的干扰",无法干净分离。


💡 RoPE 的核心思想:用旋转代替加法

RoPE 核心思想:旋转编码位置

RoPE 不再把位置信息"加"到 embedding 上,而是在 Attention 计算时,旋转 Q 和 K 向量

关键洞察: - 位置 m 的 Q 向量旋转 m·θ 角度 - 位置 n 的 K 向量旋转 n·θ 角度 - Q·K 点积时,旋转效果变成 (m-n)·θ —— 只取决于相对距离


📐 2D 旋转矩阵 — 直觉解释

对于 d_model=256 的向量,RoPE 把每个 head 的 32 维向量拆成 16 个 2D 子空间。

每个 2D 子空间独立旋转,旋转角度为 m × θᵢ

θᵢ = 1 / 10000^(2i/d_head)

RoPE 多频率旋转


🔑 RoPE 为什么天然编码相对距离?

RoPE 相对距离证明

这是 RoPE 最精妙的数学性质:

Q_m · K_n = R(mθ)q · R(nθ)k = q · R((m-n)θ) · k

旋转矩阵的正交性让点积只依赖角度差 (m-n)θ,即相对位置


📊 特性对比:Learned PE vs RoPE

特性 Learned PE (Ch1) RoPE (Ch2)
位置信息类型 绝对位置 相对位置
注入方式 加到 embedding 上 旋转 Q, K 向量
影响 V? ✅ 是(污染了输入) ❌ 否(只影响注意力分数)
长度外推 ❌ 不行,超出就崩 ✅ 理论上无限外推
额外参数 max_len × d_model 零参数!
代码位置 Embedding 层 Attention 内部

🎯 为什么 CoT 乘法任务特别需要 RoPE?

RoPE 对 CoT 任务的价值

1. CoT 链长度不固定

23*45 的 CoT 有 ~40 tokens,347*892 有 ~80+ tokens。Learned PE 需要预设 max_len 大到足够,浪费参数;RoPE 天然支持任意长度。

2. 步骤间的相对关系更重要

CoT 中 S1→S2→S3→... 每步引用上一步的结果。模型需要知道"上一步在哪"(相对位置),而不是"S2 在绝对位置 25"。

3. 减少参数 = 更好泛化

Learned PE 在 Ch1 中占 64×256 = 16,384 个参数。RoPE 零参数。对我们这种小模型来说,省下的参数量显著降低过拟合风险。


🏗️ 代码改动预览

  # Ch1: 加法式位置编码
- self.pos_emb = nn.Embedding(max_len, d_model)
- x = tok_emb + pos_emb

  # Ch2: RoPE(在 Attention 内部应用)
+ # 不再需要 pos_emb!
+ # 在 Attention 中,计算 Q, K 之后:
+ Q = apply_rope(Q, positions)   # 旋转 Q
+ K = apply_rope(K, positions)   # 旋转 K
+ # V 不旋转!
+ scores = Q @ K^T / sqrt(d_head)  # 点积自动编码相对位置

📁 项目结构

courses/ch2_rope/
├── README.md              ← 中文完整文档 (原理 + 对比 + RoPE vs FFT)
├── README_en.md           ← 英文完整文档
├── COMPARISON.md          ← Ch1 vs Ch2 独立对比报告
├── ROPE_VS_FFT.md         ← RoPE 与 FFT 相位旋转的数学联系
│
├── model.py               ← 🧠 RoPE 模型 (核心改动在这里)
├── train.py               ← 训练脚本 (Teacher Forcing + CosineLR)
├── generate_data.py       ← CoT 乘法数据生成 (与 Ch1 格式相同)
├── eval_full.py           ← 全量 9801 组合评估
├── infer.py               ← 交互式推理
│
├── images/                ← 7 张 SVG 图解
│   ├── svg_learned_pe.svg       图1: Ch1 加法式位置编码原理
│   ├── svg_pe_problems.svg      图2: 加法编码的三大缺陷
│   ├── svg_rope_idea.svg        图3: RoPE 旋转核心思想
│   ├── svg_rope_multifreq.svg   图4: 多频率多分辨率旋转
│   ├── svg_rope_relative.svg    图5: 相对距离编码数学证明
│   ├── svg_rope_cot_value.svg   图6: RoPE 对 CoT 任务的价值
│   └── svg_rope_vs_doa.svg      图7: RoPE 与 DOA 波束形成对比
│
├── checkpoints_v3/        ← 最终模型 (全量 9801/9801 = 100%)
│   ├── best.pt                  测试集首次 100% 时的快照
│   ├── latest.pt                最终训练完成的模型 ← 推荐使用
│   └── train_log.csv            训练日志 (epoch, loss, acc)
│
└── data/                  ← 训练/测试数据
    ├── train.txt                5864 条训练样本 (乘法+加法+基本功)
    ├── test.txt                 500 条测试样本
    └── train_pairs.txt          3000 个乘法组合对

🚀 使用方法

Step 1: 生成数据

python generate_data.py --num_train 3000 --num_test 500 --num_add_practice 2000 --output_dir data

数据格式与 Ch1 完全相同,例如:

23*45=S1:23*40=920;S2:23*5=115;A1:920+115=5301;Z1035

Step 2: 训练

python train.py \
  --train_file data/train.txt \
  --test_file data/test.txt \
  --epochs 200 \
  --batch_size 32 \
  --d_model 256 --n_heads 8 --n_layers 6 --d_ff 1024 \
  --max_len 64 \
  --lr 3e-4 \
  --ckpt_dir checkpoints

训练过程: - 每 epoch 在 mini-batch 上随机抽 100 条评估 ans_acc - 每 5 epochs 在完整 500 条测试集上评估 - 自动保存 best.pt(首次 100%)和 latest.pt(最新) - 训练日志写入 checkpoints/train_log.csv

预期收敛: - ~25 epochs → 90% acc - ~42 epochs → 99% acc - ~47 epochs → 测试集 100%

Step 3: 全量评估

python eval_full.py --checkpoint checkpoints/latest.pt

遍历 1~99 × 1~99 = 9801 个乘法组合,逐一自回归生成并验证最终答案。

输出示例:

🏆 最终结果: 9801/9801 = 100.00%
   错误数: 0
🎉🎉🎉 完美! 全部 9801 个组合 100% 正确!

可加 --save_errors 保存错误详情到文件。

Step 4: 交互推理

python infer.py --checkpoint checkpoints/latest.pt

输入任意乘法题(如 67*89),观察模型的完整 CoT 推理过程:

🔢 输入: 67*89=
🤖 输出: S1:67*80=5360;S2:67*9=603;A1:5360+603=3695;Z5963
✅ 正确! 67×89 = 5963

🏆 实验结果:Ch1 vs Ch2 对比

参数量对比

Ch1 (Learned PE) Ch2 (RoPE) 差异
总参数 4,784,128 4,744,192 Ch2 少 39,936
位置编码参数 16,384 0 ✅ RoPE 零参数

收敛速度对比(同一数据集 3000+2000)

里程碑 Ch1 (Learned PE) Ch2 (RoPE) 加速比
50% acc Epoch 23 Epoch 16 1.4×
80% acc Epoch 36 Epoch 22 1.6×
90% acc Epoch 49 Epoch 25 2.0×
95% acc Epoch 67 Epoch 33 2.0×
99% acc Epoch 131 Epoch 42 3.1×

RoPE 收敛速度是 Learned PE 的 2~3 倍!

全量 9801 评估

Ch1 (Learned PE) Ch2 (RoPE)
测试集 (500) 100% ✅ 100% ✅
全量 9801 100% ✅ 100% ✅ (9801/9801)

为什么 RoPE 收敛更快?

  1. 无交叉污染:Q·K 点积中没有 tok·pos 交叉项干扰,注意力学习更纯粹
  2. 相对位置天然编码:相同模式 "3*5" 无论出现在序列哪个位置,注意力 pattern 一致
  3. 少了 ~40K 参数:对小数据集来说,参数越少越不容易过拟合

🔗 附录:RoPE 与 FFT 相位旋转 — 同一个数学本质

RoPE 的旋转编码和信号处理中的频域波束形成 (DOA) 使用的是完全相同的数学操作。

RoPE 与 DOA 频域波束形成的数学联系

代码级别一模一样

DOA 波束形成(声学相机):

// 时间延迟 → 相位角
float phase_shift = -2πf * Δt;

// 复向量旋转补偿
intensity_real += real(freq) * cos(phase_shift) - imag(freq) * sin(phase_shift);
intensity_imag += real(freq) * sin(phase_shift) + imag(freq) * cos(phase_shift);

RoPE 位置编码(Transformer):

# 位置 → 旋转角度
angle = m * θᵢ   # θᵢ = 1/10000^(2i/d)

# 复向量旋转编码
x_complex = torch.view_as_complex(x)
x_rotated = x_complex * (cos(angle) + j*sin(angle))

对应关系

DOA 波束形成 RoPE 位置编码
输入 麦克风信号(复数) Q/K 向量(两两配对成复数)
旋转角度 -2πf·Δt(补偿延迟) m·θᵢ(编码位置)
数学操作 signal × e^{-jΔφ} q × e^{jmθ}
代码 r*cos - i*sin, r*sin + i*cos 完全一样!
目的 消除绝对延迟 → 对齐信号 编码绝对位置 → 提取相对距离

深层联系:傅里叶时移定理

傅里叶时移定理: f(t - Δt) ⟷ F(ω) · e^{-jωΔt}
时域的"平移/延迟"  =  频域的"相位旋转"

DOA 是"旋回来消除差异",RoPE 是"旋过去编码差异"——方向相反,数学一样。

多频率 = 多分辨率

一句话:RoPE 就是 Transformer 的"频域波束形成"——用相位旋转把位置信息编码进向量,让注意力机制能像声学相机对齐信号一样,自动感知 token 之间的相对距离。


📝 总结

Learned PE:  位置 → 查表 → 加到token上 → 绝对位置,无法外推
RoPE:        位置 → 算角度 → 旋转Q,K   → 相对位置,无限外推,零参数

一句话:RoPE 用旋转几何取代了加法,让位置信息在注意力计算中以"相对距离"的形式自然涌现,同时不增加任何可学习参数。收敛速度比 Learned PE 快 3 倍,全量 9801 测试 100% 正确。