今天依旧中午起,背单词,吃午饭,2点开始学习。
lc:2、226、22、74、55、45
继续学习RoPE,昨天提到分组依旧是两两分组,延续了SinPE的做法
对向量 q的维度进行分组操作, 相邻的两个维度作为一组, 一共可以分成d/2组, 每组旋转m*θi度。$\theta_i=10000^{-2i/d}$
$\left.\mathbf{R}_m=\left[\begin{array}{cccccccc}\cos m\theta_0&-\sin m\theta_0&0&0&\cdots&\cdots&0&0\\\sin m\theta_0&\cos m\theta_0&0&0&\cdots&\cdots&0&0\\0&0&\cos m\theta_1&-\sin m\theta_1&\cdots&\cdots&0&0\\0&0&\sin m\theta_1&\cos m\theta_1&\cdots&\cdots&0&0\\\vdots&\vdots&\vdots&\vdots&\ddots&\ddots&\vdots&\vdots\\\vdots&\vdots&\vdots&\vdots&\ddots&\ddots&\vdots&\vdots\\0&0&0&0&\cdots&\cdots&\cos m\theta_{d/2-1}&-\sin m\theta_{d/2-1}\\0&0&0&0&\cdots&\cdots&\sin m\theta_{d/2-1}&\cos m\theta_{d/2-1}\end{array}\right.\right]$
$score(\mathbf{q}_m,\mathbf{k}_n)=(\mathbf{R}_m\cdot\mathbf{q}_m)^\mathsf{T}\cdot(\mathbf{R}_n\cdot\mathbf{k}_n)$ m是token的绝对位置索引
RoPE 的区别在于:不是把位置信息“加”到向量上,而是通过旋转,把位置信息“融合进”向量的方向中
最终 attention 对 相对位置敏感,而非绝对位置
这种Rm保证θ0只和q0、q1相乘,以此类推,所以可以展开并化简,避免直接创建一个这么大的矩阵
最后将θ和q严格分开后,可得到下面的式子。
$\mathbf{R}_{m}\cdot\mathbf{q}_{m}=\begin{bmatrix}q_{0}\\q_{1}\\q_{2}\\q_{3}\\\vdots\\q_{d-2}\\q_{d-1}\end{bmatrix}\otimes\begin{bmatrix}\cos m\theta_{0}\\\cos m\theta_{0}\\\cos m\theta_{1}\\\cos m\theta_{1}\\\cos m\theta_{2}\\\vdots\\\cos m\theta_{d/2-1}\\\cos m\theta_{d/2-1}\end{bmatrix}+\begin{bmatrix}q_{1}\\q_{0}\\q_{3}\\q_{2}\\\vdots\\q_{d-1}\\q_{d-2}\end{bmatrix}\otimes\begin{bmatrix}-\sin m\theta_{0}\\\sin m\theta_{0}\\-\sin m\theta_{1}\\\sin m\theta_{1}\\\vdots\\-\sin m\theta_{d/2-1}\\\sin m\theta_{d/2-1}\end{bmatrix}$
MHA大体流程:线性投影Q K V,把dmodel维度拆成h个头,分别计算注意力得分(点积、scale、掩码mask(optional)、softmax、和V加权求和),最后多头拼接,投影回原词向量空间。scale除以√dk 防止梯度爆炸或者坍缩。
在缩放点积注意力的内部,通过屏蔽(设置为−∞)softmax 输入中所有非法连接对应的值,从而实现了防止解码器中的信息向左流动。
Attention is all you need里提到了“我们还尝试使用可学习的位置嵌入 [9],发现这两种方法结果几乎相同”,一定程度上说明了还在用RoPE的原因,他们采用的可学习位置嵌入翻译效果和SinPE差不多,几乎可以断定一般的可学习的位置嵌入方案,不如RoPE。
经典论文使用的正则化规则
Residual Dropout:某个训练轮次随机丢弃某些参数,避免过度依赖某些参数。经典方案$P_{drop}=0.1$,防止模型过度依赖某个子层的输出,也一定程度上避免了过拟合
Label Smoothing:标准分类(或语言模型)训练使用 one-hot 标签:
- 正确词:概率 = 1
- 其他词:概率 = 0
会导致模型变得过度自信、logits 越拉越极端、泛化能力下降
标签平滑系数为 ε\varepsilonε(论文中是 0.1):
正确标签:
$1-\varepsilon$
其余标签:
$\frac{\varepsilon}{V-1}$
也就是说
不再告诉模型“这个词 绝对正确,其他 绝对错误”
L1、L2正则化约束的是参数,显式惩罚参数,不影响前向结构。
Residual Dropout、Label Smoothing约束的是激活和目标,不显式地惩罚参数,但明确地出现在前向结构上。
关于多头注意力的一些疑惑
研究了一下,发现多头并不是“单头做不到的魔法”,而是一种结构性取舍。
单头 理论上 可以捕捉所有关系;多头的优势主要是更容易学到、学得更稳定、更可分解,而不是表达能力的本质提升。
“多头更容易让模型把不同关系拆开来学” —— 更准确
表达能力角度
- 一个足够大的单头注意力(d_model 维) → 在函数空间上,可以模拟多头注意力
- 多头注意力 → 本质是多个低维注意力并行,再拼接 + 线性映射
我之前的疑问是,把 d_model 切成 8 个 head,会不会“人为破坏维度之间的关系”?
分 head 并不等于“维度被硬切、关系被丢掉”。实际做的不是
embedding = [x1, x2, …, xd]
head1 = [x1..x64]
head2 = [x65..x128]
实际做的是 $Q_h=XW_h^QK_h=XW_h^KV_h=XW_h^V$
代码里的做法是self.linear_q = nn.Linear(d_model, d_model) # 注意这里:输入维度是 d_model,输出维度也是 d_model
# 这里发生的计算就是 X * W_Total
# W_Total 是一个 (d_model, d_model) 的大矩阵
x = linear(x)
# 步骤 B: 切分维度 (Split Heads)
# 将投影后的结果切分成 h 个头
x = x.view(batch_size, -1, self.h, self.d_k).transpose(1,2)
nn.Linear做的是矩阵乘法。这意味着输出向量的每一个维度,都是输入向量所有维度的加权和。
第一步做过全局混合,所以后面的view和transpose虽然看起来是“硬切”,但实际上切出来的每一份(每一个 Head),都已经包含了原始输入中全局信息经过特定角度投影后的结果。
解释了该点疑惑后,就轮到多头的优点了。
关于多头的优点阐述
从表达能力角度,只要算力够,多头单头几乎是一样的,多头 ≈ 对注意力矩阵做结构化分解
多头并不是为了“模型能表示什么”,而是为了“模型怎么更容易学到”。
从优化与训练稳定性角度:几乎必要(在原始 Transformer 框架下)
在实践中,单头注意力容易出现:
- 注意力塌缩(过度集中在少数 token)
- 不同关系相互干扰(位置、语义、指代混在一起)
- 梯度不稳定(softmax 的竞争更激烈)
这些不是“理论缺陷”,而是优化路径非常差。
多头把一次复杂注意力 → 拆成多次简单注意力,每个 head:维度更小、softmax 更“平缓”、竞争更少。这相当于对优化空间做了重参数化(reparameterization),极大改善了训练地形。
多头强行引入了一个假设:
“不同类型的关系可以被近似解耦”
多头不是“唯一正确假设”,只是“一个好用的假设”。
实证上很多 head学到几乎相同的模式或几乎不被用到,剪掉 30%–70% 的 head性能几乎不变。
head存在事实性的冗余。
理论先学这么多。
尝试默写MHA核心框架的代码,发现很不熟练,明天再继续。
注册了huggingface,打算开始看一些文档和部署一些项目