14

缩放与 softmax——为什么要除以 √d

阅读约 7 分钟第 14/60 课

上一章节走完了自注意力的四步计算,但跳过了两个细节:分数为什么要除以一个数,以及 softmax 是怎么工作的。这两个细节看似微小,但对模型能否正常训练至关重要。

问题:点积会随维度变大

回忆点积的计算:两个 1,536 维的向量,对应位置相乘再求和,得到一个数字。

1,536 个数各自相乘之后加起来。如果每个维度的值大约在 -1 到 1 之间,那 1,536 次相乘得到的 1,536 个数字(有正有负)加起来,结果的绝对值大约在几十的量级。

但如果维度从 1,536 增加到 6,144(四倍),点积的绝对值也会大致增加到原来的四倍——因为有四倍多的项被加了起来。

数学上可以证明:如果向量的每个维度独立地从均值 0、方差 1 的分布中采样,那两个 d 维向量的点积的方差等于 d。标准差(方差的平方根)就是 √d。

维度 1,536 的标准差 ≈ 39。维度 6,144 的标准差 ≈ 78。维度越大,点积的值域越宽——有些分数会变得很大,有些很小。

这为什么是问题?因为下一步是 softmax。

Softmax 做什么

Softmax 把一组任意的数字变成一个概率分布:所有数字变为非负,且加起来等于 1。

公式:对每个数字取 e 的幂次,然后除以所有幂次的总和。

输入:[2.0, 1.0, 0.1]

e^2.0 = 7.389
e^1.0 = 2.718
e^0.1 = 1.105

总和 = 7.389 + 2.718 + 1.105 = 11.212

softmax = [7.389/11.212, 2.718/11.212, 1.105/11.212]
        = [0.659, 0.242, 0.099]

最大的数字(2.0)得到了最大的概率(65.9%),最小的(0.1)得到了最小的概率(9.9%)。

用 Python 写出来:

import math

def softmax(scores):
    exps = [math.exp(s) for s in scores]      # 对每个分数取 e 的幂次
    total = sum(exps)                          # 求总和
    return [e / total for e in exps]           # 每个除以总和

print(softmax([2.0, 1.0, 0.1]))
# 输出 [0.659, 0.242, 0.099]

Softmax 对极端值非常敏感

现在看看如果输入的数字很大会怎样:

输入:[20.0, 10.0, 1.0]

e^20 = 485,165,195
e^10 = 22,026
e^1  = 2.718

softmax ≈ [0.99998, 0.00005, 0.00000]

几乎所有的概率都集中在了最大的那个值上。softmax 变成了「赢者通吃」——只有分数最高的 token 得到关注,其他 token 的注意力权重几乎为零。

这不是我们想要的行为。语言理解需要综合考虑多个上下文 token,而不是只盯着一个。如果注意力分布过于尖锐,模型就失去了融合多方信息的能力。

反过来,如果输入的数字都很小且很接近:

输入:[0.02, 0.01, 0.01]

softmax ≈ [0.337, 0.332, 0.332]

几乎是均匀分布——所有 token 得到的注意力差不多,模型无法区分哪些 token 更重要。

softmax 需要输入的数字在一个「温和」的范围内——有差异但不极端——才能产生有区分度又不过于尖锐的注意力分布。

缩放:除以 √d

现在把两件事连起来:点积随维度增大而增大,softmax 对极端值非常敏感。

如果不做任何处理,维度 1,536 的点积可能产生绝对值几十的分数。放进 softmax 之后就会出现上面的情况——几乎全部注意力集中在一两个 token 上。

解决方法很简单:把点积除以 √d。

d = 1,536 时,√d ≈ 39.2。除以 39.2 之后,原本绝对值几十的分数被压缩到 ±1 左右——正好是 softmax 工作得最好的范围。

缩放后的分数 = (Q × K的转置) / √d
注意力权重 = softmax(缩放后的分数)
输出 = 注意力权重 × V

这就是著名的 Scaled Dot-Product Attention 的完整公式。2017 年的论文 “Attention Is All You Need” 正式提出了这个设计,之后所有的 Transformer 模型——包括 Sophia——都沿用了这个做法。

为什么是 √d 而不是别的数

除以 √d 的目的是让缩放后的点积的方差约等于 1,无论 d 多大。

前面说过,d 维向量的点积的方差 = d。方差为 d 的随机变量,除以 √d 之后,方差变为 d / (√d)² = d / d = 1。

方差为 1 意味着点积的典型值大约在 -2 到 +2 之间(正负两个标准差)。这个范围让 softmax 产生的分布既有区分度又不会过于极端——恰好是注意力机制需要的。

这不是拍脑袋的选择,而是统计学上自然的归一化方式。

完整的注意力计算总结

把上一章节和本章节合起来,自注意力的完整计算是:

1. 生成 Q、K、V(三次矩阵乘法)
2. 计算分数 = Q × K的转置(矩阵乘法)
3. 缩放 = 分数 / √d
4. 掩码(把不允许看到的位置设为负无穷)
5. 注意力权重 = softmax(缩放后的分数)
6. 输出 = 注意力权重 × V(矩阵乘法)

六步,其中三步是矩阵乘法。这就是 Transformer 中最核心的计算模块。

但 Sophia 的注意力不止于此。下一章节要看多头注意力——不只用一组 Q、K、V,而是同时用 16 组,让不同的「头」关注不同类型的信息。