为什么 Transformer 注意力要除以 √dₖ

本文从符号开始,一步步讲清楚 Scaled Dot-Product Attention 中 /√dₖ 的来历。重点是把每个数学符号、每个公式、每个等号的含义都说明白,不跳步。


0. 先把符号约定讲清楚

在看任何公式之前,先认识这些记号。后面所有推导都基于它们。

符号 读法 含义
q query 向量 一个长度为 dₖ 的向量,代表"当前 token 想查询什么"
k key 向量 一个长度为 dₖ 的向量,代表"某个 token 能提供什么"
dₖ d-k 向量的维度(分量个数)。比如 dₖ = 64
qᵢ q 下标 i 向量 q 的第 i 个分量(一个标量数字)。i 从 1 取到 dₖ
kᵢ k 下标 i 向量 k 的第 i 个分量(一个标量数字)
q · k q 点乘 k 点积:对应分量相乘再求和,结果是一个标量
Σᵢ sigma,对 i 求和 把后面的式子对 i = 1, 2, ..., dₖ 全部加起来
E[X] X 的期望 expectation,见下一节详解
Var(X) X 的方差 variance,衡量 X 波动有多大,见后文

点积写成求和形式就是:

复制代码
q · k = q₁k₁ + q₂k₂ + ... + q_dₖ·k_dₖ = Σᵢ qᵢ kᵢ

也就是说,点积是 dₖ 个"乘积项 qᵢkᵢ"加在一起。记住这一点,后面整个分析都围绕它。


1. E 到底是什么意思 ------ 期望

E[X] 读作"X 的期望"(Expectation),它的意思是:

如果把随机变量 X 重复采样无数次,这些值的平均值会趋向于多少。

期望就是"理论上的平均值 / 长期平均值"。

举两个直观例子:

  • 掷一个公平的六面骰子,点数 X 可能是 1~6,每个概率 1/6。

    复制代码
    E[X] = 1·(1/6) + 2·(1/6) + ... + 6·(1/6) = 3.5

    虽然你永远掷不出 3.5,但掷很多很多次,平均点数会无限接近 3.5。这个 3.5 就是期望。

  • 如果一个随机变量"(总体)均值为 0",意思就是 E[X] = 0,即它正负波动,长期平均下来是 0。

在我们的场景里,为什么 qᵢ、kᵢ 是随机变量?

因为在模型刚初始化时,权重是随机生成的,所以 q、k 的每个分量 qᵢ、kᵢ 都可以看成"从某个分布里随机抽出来的数"。我们没法预知某一个具体的值,但可以谈论它们的统计性质(期望、方差)。这就是为什么要用 E[ ]

⚠️ 重要区分:期望、总体均值、样本均值不是一回事

"均值"这个词在中文(和英文 mean)里指两种不同的东西,务必分清,否则后面会被绕晕:

名称 是什么 是否随机 和期望的关系
期望 E[X] 分布定义的理论中心 固定常数 ------ 它本身
总体均值 μ 同上,就是 E[X] 的另一个叫法 固定常数 相等,μ = EX
样本均值 手里一组实际数据的算术平均 (x₁+...+xₙ)/n 随机,换批数据就变 不相等,只是 EX 的估计

两条结论:

  1. 期望 = 总体均值,这两者确实相等(都是分布的理论中心)。

  2. 样本均值 ≠ 期望 。它只是期望的一个估计;由大数定律 ,当样本数 n → ∞ 时样本均值才收敛到期望:

    复制代码
    样本均值 x̄  ──(n 越大越接近)──→  期望 E[X]
     (随机,会变)                      (固定的理论值)

用掷骰子说明区别: E[X] = 3.5期望(总体均值) ------一个理论值;而你真掷 6 次得到的平均(比如 (2+5+1+6+3+4)/6)是样本均值,几乎不可能正好等于 3.5。两者是不同的东西,只是次数越多,样本均值越接近 3.5。

所以本文里所有 E[ ] = ...、所有"均值为 0/方差为 1",指的都是期望(总体均值)这一支 ,是针对分布的理论假设,而不是说"我手上这批 qᵢ 的算术平均恰好等于 0"。


2. 逐符号拆解 E[qᵢ kᵢ] = E[qᵢ] · E[kᵢ] = 0 · 0 = 0

这是你特别想搞懂的一步。我们把它拆成三个等号,一个一个看。

这个式子整体在问什么?

qᵢ kᵢ 是两个随机数相乘,结果还是一个随机数。我们想知道:这个乘积的期望(长期平均值)是多少? 也就是求 E[qᵢ kᵢ]

第一个等号:E[qᵢ kᵢ] = E[qᵢ] · E[kᵢ]

这一步用到一条概率论的规则:

如果两个随机变量 X 和 Y 相互独立,那么"乘积的期望"等于"期望的乘积":

复制代码
E[X · Y] = E[X] · E[Y]

注意:这条规则只有在 X、Y 独立时才成立。所以原文括号里写了"(因为独立)"------这是在声明我们用了独立这个前提条件。

  • "独立"是什么意思? 指 qᵢ 取什么值,不影响 kᵢ 取什么值,反之亦然。q 来自当前 token,k 来自另一个 token,初始化时它们由不同的随机权重生成,所以假设独立是合理的。
  • 套进来:把 X 换成 qᵢ,Y 换成 kᵢ,就得到 E[qᵢ kᵢ] = E[qᵢ] · E[kᵢ]

第二个等号:E[qᵢ] · E[kᵢ] = 0 · 0

这一步用到我们的假设:q、k 的每个分量的**期望(总体均值)**都是 0。

"期望为 0"用符号写就是:

复制代码
E[qᵢ] = 0    且    E[kᵢ] = 0

(回忆上一节的区分:这里说的是期望 / 总体均值------分布的理论中心是 0,而不是"某一批采样出来的 qᵢ 算术平均恰好为 0"。)

把这两个 0 代进去,E[qᵢ] 变成 0,E[kᵢ] 变成 0,于是右边就成了 0 · 0

第三个等号:0 · 0 = 0

这就是普通的算术了,0 乘 0 等于 0。

合起来读这一行

"乘积 qᵢkᵢ 的期望,等于(因为独立)各自期望的乘积,而各自期望都是 0(因为我们假设均值为 0),所以整个乘积的期望是 0。"

直觉解释: qᵢ 和 kᵢ 各自都是围绕 0 上下波动的(有时正有时负)。它们相乘,有时得正、有时得负,长期平均下来正负抵消,平均值就是 0。


3. 接着算方差:为什么点积方差 = dₖ

知道了均值是 0,下一步看"波动有多大",也就是方差 Var

Var 是什么?

Var(X) 衡量 X 偏离它均值的程度,波动越大方差越大。它的定义是:

复制代码
Var(X) = E[ (X − E[X])² ]   即"X 减去均值,平方,再取期望"

有一个常用的等价公式:

复制代码
Var(X) = E[X²] − (E[X])²

特例:当均值 EX = 0 时,上式第二项是 0,于是:

复制代码
Var(X) = E[X²]        (均值为0时,方差就等于平方的期望)

这个特例后面要用。

单项 qᵢkᵢ 的方差

复制代码
Var(qᵢ kᵢ) = E[(qᵢkᵢ)²] − (E[qᵢkᵢ])²       (方差的等价公式)
           = E[qᵢ² kᵢ²] − 0²                (上一节已证 E[qᵢkᵢ]=0)
           = E[qᵢ²] · E[kᵢ²]                 (独立,乘积期望=期望乘积)
           = Var(qᵢ) · Var(kᵢ)               (均值为0,所以 E[x²]=Var(x))
           = 1 · 1 = 1                       (假设方差为1)

所以每一个乘积项 qᵢkᵢ 的方差都是 1

整个点积的方差

点积是 dₖ 个独立项相加。独立随机变量相加时,方差可以直接相加:

复制代码
Var(q · k) = Var(Σᵢ qᵢkᵢ) = Σᵢ Var(qᵢkᵢ) = dₖ × 1 = dₖ

结论:点积的方差是 dₖ,标准差是 √dₖ。 这就是"维度越大,点积数值越大"的数学根源------维度 dₖ 越大,累加的项越多,波动(标准差)就按 √dₖ 增长。


4. 为什么除以 √dₖ 之后方差变回 1

我们要把点积的尺度拉回稳定。利用方差的一条性质:

复制代码
Var(a · X) = a² · Var(X)      (常数 a 提出来要平方)

a = 1/√dₖ,对点积做缩放:

复制代码
Var( (q·k) / √dₖ ) = (1/√dₖ)² · Var(q·k)
                   = (1/dₖ) · dₖ
                   = 1

这就是为什么是 √dₖ,而不是 dₖ 或别的数: 因为点积的标准差是 √dₖ,除以标准差正好把方差归一化回 1。本质上这是一次标准化操作 (X − μ)/σ,这里 μ = 0 所以只剩"除以 σ = √dₖ"。

⭐ 关键澄清:为什么不是除以 dₖ?

这是最容易卡住的地方,根源在于方差对常数是"平方"敏感的 ------即上面那条性质 Var(a·X) = a²·Var(X),常数 a 提出来时要平方

我们的目标是把方差从 dₖ 拉回 1。设缩放系数是 a,要求:

复制代码
Var(a · q·k) = a² · Var(q·k) = a² · dₖ  =  1(目标)

解这个方程:

复制代码
a² · dₖ = 1   ⟹   a² = 1/dₖ   ⟹   a = 1/√dₖ

所以缩放系数必然是 1/√dₖ,也就是除以 √dₖ

那如果错误地除以 dₖ(即 a = 1/dₖ)会怎样? 代入算一下:

复制代码
Var( (q·k) / dₖ ) = (1/dₖ)² · dₖ = (1/dₖ²) · dₖ = 1/dₖ

方差变成了 1/dₖ ,而不是 1!dₖ 越大(比如 64、128),方差被压得越小,点积分布几乎全挤到 0 附近,softmax 输出趋近均匀分布,注意力失去了区分不同位置的能力。这同样是坏的。

三种情况一张表对比:

缩放系数 a 缩放后方差 a²·dₖ 结果
不缩放(a = 1) dₖ 方差太大 → softmax 饱和、梯度消失 ❌
除以 √dₖ(a = 1/√dₖ) 1 ✅ 刚好归一,softmax 尺度健康
除以 dₖ(a = 1/dₖ) 1/dₖ 方差太小 → softmax 过平、没区分度 ❌

一句话记牢: 方差里常数要平方,所以"消掉 dₖ 倍方差"需要的系数是 1/√dₖ(平方后才是 1/dₖ),除以 √dₖ 平方后恰好抵消 dₖ;而除以 dₖ 平方后是 1/dₖ²,把 dₖ 抵消得"过头"了。

另一个等价直觉:方差的单位是"平方量纲",标准差才和原数据同量纲。 点积的标准差是 √dₖ。要把一个标准差为 √dₖ 的量标准化成标准差 1,当然是除以它的标准差 √dₖ,而不是除以方差 dₖ。除以方差会把量纲也除错。

⭐ 再追问一步:为什么 √dₖ 写在 Var 里面,而不是外面?到底在给谁做归一化?

很多人会卡在这里:凭什么是 Var( (q·k)/√dₖ )(√dₖ 在里面),而不是 Var(q·k)/√dₖ(√dₖ 在外面)?这两种写法意思完全不同,先并排看清:

写法 式子 读作
A(正确,Transformer 实际做的) Var( (q·k)/√dₖ ) 先把每个点积除以 √dₖ,再看这批缩放后的数波动多大
B(错误理解) Var(q·k) / √dₖ 先算原始点积的方差,再把这个方差值除以 √dₖ

决定 √dₖ 写在哪的,是"你在归一化谁"。

关键:模型真正动手除的,是点积本身,不是方差。 注意力公式是 softmax(QKᵀ/√dₖ)------模型在前向传播里,对**每一个算出来的点积 q·k(一个具体的数)**除以 √dₖ,得到缩放后的分数再喂给 softmax。

  • 被 √dₖ 操作的对象 = 点积这个随机变量(每个 token 对都会产生一个)。
  • 模型从来没有、也无法"对方差除以 √dₖ"------方差是我们事后为了分析这批分数的波动才计算的统计量,模型前向时根本不算方差。

所以"被归一化的对象"是 q·k 这个随机变量本身 。既然被除的是随机变量,√dₖ 就和这个随机变量绑在一起,写在 Var( )里面:

复制代码
Var(  (q·k)/√dₖ  )
      └────┬────┘
   这一整个,才是我们关心的"缩放后的随机变量"

我们想知道的是:"经过缩放后的那个分数,波动还大不大?" 既然问的是缩放后的分数的方差,√dₖ 当然要写进 Var 里、成为被考察对象的一部分。

为什么写法 B(除在外面)是错的? 真去算一下:

复制代码
Var(q·k) / √dₖ = dₖ / √dₖ = √dₖ   ← 不等于 1,没归一化好

而且"给方差这个数除一下"在模型前向里压根没有对应的操作,和模型实际做的事对不上,所以无意义。只有写法 A 才得到 1:

复制代码
Var( (q·k)/√dₖ ) = (1/√dₖ)² · Var(q·k) = (1/dₖ)·dₖ = 1   ✓

注意写法 A 里 √dₖ 被提出来时变成了平方 (1/√dₖ)² = 1/dₖ------正因为它本来在 Var 里面,被方差性质提到外面时才要平方;如果它本来就在外面(写法 B),就不会平方,也就抵消不掉那个 dₖ。

一句话: 你归一化的是点积这个随机变量 (模型真正除的就是它),不是"点积的方差"。(q·k)/√dₖ 是一个整体的、缩放后的新随机变量,我们要算的是这个新随机变量的方差,所以 √dₖ 必须写在 Var( ) 里面;它在里面,才会在被提出来时平方成 1/dₖ,从而和点积自身的方差 dₖ 抵消成 1。


5. 为什么要假设"均值 0、方差 1"

这是一个为了简化分析的基准假设,不是说真实数据严格满足。原因有三层:

  1. 它是合理的近似。 q、k 来自初始化过的线性层,配合 Xavier/He 初始化和 LayerNorm,激活值的分布本就大致是零均值、单位方差,和假设相差不大。

  2. 数学上最干净。 假设均值 0,推导里一堆交叉项直接消失;假设方差 1,"点积方差 = dₖ"这个结论变得极其简洁。如果方差是 σ²,结论会变成 dₖσ²,缩放因子变成 σ√dₖ------形式一样,只是多个常数,不影响核心洞察。

  3. 目的是孤立出 dₖ 的影响。 我们真正关心的是"尺度如何随维度 dₖ 变化"。把均值、方差固定成基准值(0 和 1),dₖ 就成了唯一变量,于是 √dₖ 这个缩放因子自然浮现。


6. 一句话总结

我们 假设 q、k 是零均值、单位方差(基准坐标系),由此 推导出 点积方差正比于 dₖ(这就是问题),于是 除以 √dₖ 把方差重新归一化到 1(这就是解决方案),让 softmax 始终工作在梯度健康的尺度上,避免饱和与梯度消失。


附录:延伸 ------ 期望与信息熵的关系

在第 1 节我们用掷骰子算了期望:

复制代码
E[X] = 1·(1/6) + 2·(1/6) + ... + 6·(1/6) = 3.5

这个式子的结构和信息熵的公式很像。它们的相似不是巧合------本质上是同一类东西。

两个公式并排

期望(均值):

复制代码
E[X] = Σᵢ  xᵢ · p(xᵢ)

信息熵:

复制代码
H(X) = − Σᵢ  p(xᵢ) · log p(xᵢ)

(这里 p(xᵢ) 表示取值 xᵢ 出现的概率,log 是对数。)

两者都是 Σᵢ (某个量) · p(xᵢ) 的形式------对所有取值做"概率加权求和"。这就是它们长得像的根源。

关键:熵本身就是一种"期望"

这是最深刻的联系。先定义每个取值的自信息(惊讶程度):

复制代码
I(xᵢ) = − log p(xᵢ)
  • 概率越小的事件,发生时越"惊讶",信息量越大(−log 一个小数 = 一个大正数);
  • 概率越大的事件越"不惊讶",信息量越小。

于是熵可以写成:

复制代码
H(X) = Σᵢ p(xᵢ) · I(xᵢ) = E[ I(X) ] = E[ −log p(X) ]

所以:熵 = 自信息的期望。 它和 E[X] 是同一个模具刻出来的,只是把被平均的"量"换掉了:

求和的"被加权对象" 权重 整体含义
期望 E[X] 取值本身 xᵢ 概率 p(xᵢ) 平均取到多大的数
H(X) 自信息 −log p(xᵢ) 概率 p(xᵢ) 平均有多少信息 / 不确定性

把熵写成 E[−log p(X)],会发现它就是把 E[X] 里被平均的 X 换成了 −log p(X),形式完全一致。

为什么熵多了个 log 和负号

  • log :让信息量满足"可加性"。两个独立事件一起发生,概率相乘 p·q,但我们希望它们的信息量相加。log 正好把乘法变加法:log(p·q) = log p + log q
  • 负号 :因为概率 p ≤ 1,所以 log p ≤ 0(是负数或 0),加个负号让信息量变成正数,符合"信息量不为负"的直觉。

小结

期望和熵共享同一个骨架 Σ (量) × 概率,区别只在于"被平均的量"是什么:

  • 期望平均的是取值 xᵢ;
  • 熵平均的是自信息 −log p(xᵢ)。

所以与其说熵"像"期望,不如说 熵本身就是一种期望 :H(X) = E[−log p(X)]