注意力机制:Q/K/V、缩放与两种实现
记录于 2026-05-20,对话整理
来源:李沐《Attention is All You Need》论文精读(B站)
论文核心符号对照
| 符号 | 全称 | 原论文值 | 含义 |
|---|---|---|---|
d_model | model dimension | 512 | 每个词的向量总维度 |
h | heads | 8 | 多头注意力的头数 |
d_k | key/query dimension | 64 | 每个头里 Q 和 K 的维度 |
d_v | value dimension | 64 | 每个头里 V 的维度 |
d_ff | feed-forward dimension | 2048 | 前馈网络中间层维度 |
N | layers | 6 | Transformer 堆了几层 |
核心关系:
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]。
# 没有缩放(数值爆炸)
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_k | W_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 层
↓
输出