Skip to content

注意力机制:Q/K/V、缩放与两种实现

记录于 2026-05-20,对话整理
来源:李沐《Attention is All You Need》论文精读(B站)


论文核心符号对照

符号全称原论文值含义
d_modelmodel dimension512每个词的向量总维度
hheads8多头注意力的头数
d_kkey/query dimension64每个头里 Q 和 K 的维度
d_vvalue dimension64每个头里 V 的维度
d_fffeed-forward dimension2048前馈网络中间层维度
Nlayers6Transformer 堆了几层

核心关系:

d_k = d_v = d_model ÷ h = 512 ÷ 8 = 64

注意力机制在整体架构中的位置

注意力机制
    └── 是 Transformer 的核心组件

Transformer
    └── 是现代 AI 的地基(GPT、Claude、BERT 全基于此)

512维(d_model)
    └── 注意力机制运作的"舞台"

Q / K / V
    └── 注意力机制的计算方式

64维(d_k)
    └── 每个头分到的维度

Q、K、V 是什么

每个词的 64 维向量通过三次独立的线性变换得到:

输入向量(64维)
    ├── × W_Q  →  Q(Query,查询)  "我想关注什么"
    ├── × W_K  →  K(Key,键)      "我能被谁关注"
    └── × W_V  →  V(Value,值)    "如果被关注,我传递什么内容"

直觉类比:

角色类比
Q(Query)你在图书馆的搜索关键词
K(Key)每本书的书名/标签
V(Value)书里真正的内容

Q 和 K 的相似度决定"要不要读这本书",V 是"读到的内容"。


完整注意力公式,逐步拆解

Attention(Q, K, V) = softmax( Q × K^T ÷ √d_k ) × V

矩阵转置:为什么是 K^T 而不是 K

Q 和 K 的形状都是 (n × d_k),目标是算每个词对每个词的相似度(n×n 矩阵):

不转置:Q × K = (n × d_k) × (n × d_k)  ← 维度不匹配,无效

转  置:Q × K^T = (n × d_k) × (d_k × n) = (n × n) ✓
                          ↑ 这里对上了

转置就是把矩阵的行和列互换:

K(n × d_k):          K^T(d_k × n):
┌              ┐         ┌          ┐
│— 词1的key — │         │ ↑  ↑  ↑ │
│— 词2的key — │   →     │词1 词2 词3│
│— 词3的key — │         │ ↓  ↓  ↓ │
└              ┘         └          ┘

转置后每一是一个词的 key 向量,Q 的每一和它做点积,得到相似度分数。

结果矩阵里每个元素的含义:

Q × K^T 的第[i][j]个元素 = Q[i] · K[j] = 词i 和 词j 的相似度

转置的本质作用:让维度对齐,把"n个向量两两点积"变成一次矩阵运算,GPU 并行完成。

为什么维度必须"对齐"

矩阵乘法有一个硬规定:

(m × n) × (n × p) = (m × p)
        ↑    ↑
   这两个必须相等,否则无法计算

Q 和 K 形状都是 (n × d_k),比如 (5 × 64)

Q × K   = (5×64) × (5×64)  ❌  64 ≠ 5,不能乘
Q × K^T = (5×64) × (64×5)  ✓   64 = 64,结果是 (5×5)

转置把 K 的行和列互换,让中间那个数字对上,乘法才能进行。


第一步:Q × K^T → 原始分数

Q 矩阵形状:n × d_k   (n 个词,每词一个 query)
K 矩阵形状:n × d_k
K^T 形状:  d_k × n

Q × K^T = n × n 矩阵

              "The"  "cat"  "sat"
"The" 问  →  [ 0.9,   0.1,   0.4 ]
"cat" 问  →  [ 0.2,   0.8,   0.3 ]
"sat" 问  →  [ 0.4,   0.5,   0.7 ]

每一行是这个词对所有词的"感兴趣程度"原始分数。

第二步:÷ √d_k → 缩放,防止数值爆炸

为什么要除以 √64?

Q 和 K 里每个数字均值为 0,方差为 1。做点积时 64 项相加,方差累积变成 64,标准差变成 8,数值范围从 [-1,1] 变成 [-8,8]。

python
# 没有缩放(数值爆炸)
scores = [16.0, 4.0, 0.8]
softmax → [0.9999, 0.0001, 0.000]  # 几乎 one-hot,梯度消失

# 除以 √64 之后(数值正常)
scores = [2.0, 0.5, 0.1]
softmax → [0.67, 0.25, 0.08]       # 分布合理,梯度健康

除以 √d_k 把方差从 64 压回 1,让 softmax 不饱和。

第三步:softmax → 变成概率分布

每行所有分数加起来 = 1,代表"这个词的注意力分配方案"。

第四步:× V → 加权求和,得到新表示

用注意力权重把 Value 加权求和,输出每个词更新后的向量。


加型注意力 vs 点积注意力

两种计算注意力分数的方法:

点积注意力加型注意力(Bahdanau)
公式Q·K^T ÷ √d_kW_v · tanh(W_q·Q + W_k·K)
额外参数有(W_q、W_k、W_v)
速度快(矩阵乘法,GPU 极度优化)
提出时间2017(Transformer)2015(Bahdanau)
现状主流,GPT/Claude 都用少数场景

为什么点积赢了?

  • d_k 较小时两者效果差不多
  • d_k 较大时,点积不缩放会崩,但除以 √d_k 修正后效果相当
  • 点积的矩阵乘法在 GPU 上有极度优化的实现,速度远快于加型

加型注意力需要额外的神经网络层,无法充分利用 GPU 并行能力。工程效率决定了胜负。


自回归:LLM 生成文字的方式

GPT、Claude 这类模型生成文字不是一口气蹦出来的,而是每次只生成一个 token,把它拼到上下文里,再预测下一个

输入:「今天天气」
→ 生成「很」
→ 输入变成「今天天气很」→ 生成「好」
→ 输入变成「今天天气很好」→ 生成「。」

每一步都在"回归"自己已经生成的内容,所以叫自回归生成(Autoregressive Generation)

这就是为什么 LLM 的输出是逐字出来的——它本质上是在循环做预测。


完整流程总结

输入词

d_model = 512 维 Embedding(词的"身份证")

多头注意力(h=8 个头,每头 d_k=64 维)
  ├─ 每头得到 Q、K、V(各 64 维)
  ├─ score = softmax( Q·K^T ÷ √64 ) × V
  └─ 8 个头拼回 512 维

前馈网络(512 → d_ff=2048 → 512)

重复 N=6 层

输出

Built with VitePress