在自然语言处理和计算机视觉中,Transformer 已经成为主流大模型的基础组件。它最核心的算子,就是自注意力机制:
Attention(Q,K,V)=softmax(dkQKT)V
真正值得细究的问题是:为什么这里一定要除以 dk,而不是别的量?
自注意力的定义
先给出标准定义。
定义 1:缩放点积注意力(Scaled Dot-Product Attention)
给定 Query 矩阵 Q∈Rn×dk、Key 矩阵 K∈Rm×dk、Value 矩阵 V∈Rm×dv,
其输出定义为
Attention(Q,K,V)=softmax(dkQKT)V
其中:
- n 和 m 分别表示 Query 序列与 Key/Value 序列长度。
- dk 表示 Query 与 Key 的通道维度。
- dk 就是所谓的缩放因子。
为什么是 dk
如果不做缩放,注意力分数会随着维度变大而迅速膨胀。
定理 1:高维随机向量点积的方差与维度成正比
设 q,k∈Rdk 为相互独立的随机向量,并假设每个分量都满足
E[qi]=E[ki]=0,Var(qi)=Var(ki)=1
则它们的点积
q⋅k=i=1∑dkqiki
满足
E[q⋅k]=0,Var(q⋅k)=dk
证明并不复杂。
首先,由独立性可得
E[qiki]=E[qi]E[ki]=0
所以
E[q⋅k]=E[i=1∑dkqiki]=i=1∑dkE[qiki]=0
再看单项的方差:
Var(qiki)=E[(qiki)2]−(E[qiki])2
由于 qi 与 ki 独立,且都满足单位方差,
Var(qiki)=E[qi2]E[ki2]=1
于是总方差为
Var(q⋅k)=Var(i=1∑dkqiki)=i=1∑dkVar(qiki)=dk
说明
这意味着维度越高,未缩放点积的波动越大。比如当 dk=1024 时,注意力分数已经可能进入一个非常尖锐的范围。
为什么这会伤害 Softmax
注意力最终还要经过 Softmax:
si=softmax(z)i=∑j=1Kezjezi
如果输入向量 z 的不同分量差异很大,那么 Softmax 会迅速接近 one-hot:
sm≈1,sj≈0(j=m)
这会带来两个后果:
- 梯度接近饱和区,训练容易不稳定。
- 注意力会过于集中,难以在多个相关位置之间平滑分配权重。
因此,我们需要把点积的方差重新拉回常数量级。对 q⋅k 除以 dk 后,
Var(dkq⋅k)=dk1Var(q⋅k)=dkdk=1
这就是缩放因子取 dk 的核心原因。它并不是经验魔法,而是一个直接来自方差控制的数学结论。
一个简短的数值验证
下面用 PyTorch 做个简单实验:
import torch
dk = 1024
batch_size = 10000
q = torch.randn(batch_size, dk)
k = torch.randn(batch_size, dk)
dot_product = (q * k).sum(dim=-1)
print("unscaled variance:", dot_product.var().item())
scaled_dot_product = dot_product / (dk ** 0.5)
print("scaled variance:", scaled_dot_product.var().item())
理论上,第一项应接近 1024,第二项应接近 1。
小结
如果把 Transformer 看成一套优雅的数值系统,那么 dk 的作用就是把注意力分数重新归一到可训练的尺度上。没有这一步,Softmax 很快会进入过度尖锐的区域;有了这一步,模型才能在高维空间里保持稳定的梯度流动。
后面如果继续写这个系列,我很想再把 RoPE、LayerNorm 和 FlashAttention 的数学直觉也整理出来。
交流讨论