第6章 Self-Attention
本章目标:
完整推导 Self-Attention 的计算过程,手算一个例子,理解每一步的含义。
6.1 Self-Attention 的输入
Self-Attention 的输入是一个矩阵 XXX:
X∈Rseq_len×dmodelX \in \mathbb{R}^{seq\len \times d{model}}X∈Rseq_len×dmodel
例如,句子 I love AI(3个Token),d_model=4:
X=101002021111X = \begin{bmatrix} 1 & 0 & 1 & 0 \\ 0 & 2 & 0 & 2 \\ 1 & 1 & 1 & 1 \end{bmatrix}X= 101021101021
每一行是一个 Token 的向量表示。
6.2 三个权重矩阵
Self-Attention 有三个可学习的权重矩阵:
WQ∈Rdmodel×dkW_Q \in \mathbb{R}^{d_{model} \times d_k}WQ∈Rdmodel×dk
WK∈Rdmodel×dkW_K \in \mathbb{R}^{d_{model} \times d_k}WK∈Rdmodel×dk
WV∈Rdmodel×dvW_V \in \mathbb{R}^{d_{model} \times d_v}WV∈Rdmodel×dv
通常 dk=dv=dmodel/num_headsd_k = d_v = d_{model} / num\_headsdk=dv=dmodel/num_heads,即三个矩阵的 Shape 实际相同。
💡 为什么 WVW_VWV 写成 dvd_vdv 而不是 dkd_kdk? Q 和 K 必须维度相同(都是 dkd_kdk),因为它们需要做点积来计算注意力分数。但 V 的作用是提供"被加权求和的内容",它的输出维度 dvd_vdv 决定了最终 Attention 输出的维度,理论上可以和 dkd_kdk 不同。论文中用不同符号是为了强调这一设计自由度。但在所有主流实现中(包括原始论文),dk=dvd_k = d_vdk=dv,你可以直接把它们当成同一个值。
📌 本章聚焦单头 Attention 的完整计算过程 。这里的 dkd_kdk 实际上是多头注意力中每个 Head 分配到的维度(例如 dmodel=512d_{model}=512dmodel=512, 8 个 Head 时 dk=64d_k=64dk=64)。关于为什么要拆成多个 Head、以及多头之间如何协作,将在第7章详细展开。本章你只需要把它当作"一个 Head 内部的维度"来理解即可。
6.3 计算 Q、K、V
Q=X⋅WQQ = X \cdot W_QQ=X⋅WQ
K=X⋅WKK = X \cdot W_KK=X⋅WK
V=X⋅WVV = X \cdot W_VV=X⋅WV
#mermaid-svg-7L102uKhu4k57kr3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7L102uKhu4k57kr3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7L102uKhu4k57kr3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7L102uKhu4k57kr3 .error-icon{fill:#552222;}#mermaid-svg-7L102uKhu4k57kr3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7L102uKhu4k57kr3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7L102uKhu4k57kr3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7L102uKhu4k57kr3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7L102uKhu4k57kr3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7L102uKhu4k57kr3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7L102uKhu4k57kr3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7L102uKhu4k57kr3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7L102uKhu4k57kr3 .marker.cross{stroke:#333333;}#mermaid-svg-7L102uKhu4k57kr3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7L102uKhu4k57kr3 p{margin:0;}#mermaid-svg-7L102uKhu4k57kr3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7L102uKhu4k57kr3 .cluster-label text{fill:#333;}#mermaid-svg-7L102uKhu4k57kr3 .cluster-label span{color:#333;}#mermaid-svg-7L102uKhu4k57kr3 .cluster-label span p{background-color:transparent;}#mermaid-svg-7L102uKhu4k57kr3 .label text,#mermaid-svg-7L102uKhu4k57kr3 span{fill:#333;color:#333;}#mermaid-svg-7L102uKhu4k57kr3 .node rect,#mermaid-svg-7L102uKhu4k57kr3 .node circle,#mermaid-svg-7L102uKhu4k57kr3 .node ellipse,#mermaid-svg-7L102uKhu4k57kr3 .node polygon,#mermaid-svg-7L102uKhu4k57kr3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7L102uKhu4k57kr3 .rough-node .label text,#mermaid-svg-7L102uKhu4k57kr3 .node .label text,#mermaid-svg-7L102uKhu4k57kr3 .image-shape .label,#mermaid-svg-7L102uKhu4k57kr3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-7L102uKhu4k57kr3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7L102uKhu4k57kr3 .rough-node .label,#mermaid-svg-7L102uKhu4k57kr3 .node .label,#mermaid-svg-7L102uKhu4k57kr3 .image-shape .label,#mermaid-svg-7L102uKhu4k57kr3 .icon-shape .label{text-align:center;}#mermaid-svg-7L102uKhu4k57kr3 .node.clickable{cursor:pointer;}#mermaid-svg-7L102uKhu4k57kr3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7L102uKhu4k57kr3 .arrowheadPath{fill:#333333;}#mermaid-svg-7L102uKhu4k57kr3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7L102uKhu4k57kr3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7L102uKhu4k57kr3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7L102uKhu4k57kr3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7L102uKhu4k57kr3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7L102uKhu4k57kr3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7L102uKhu4k57kr3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7L102uKhu4k57kr3 .cluster text{fill:#333;}#mermaid-svg-7L102uKhu4k57kr3 .cluster span{color:#333;}#mermaid-svg-7L102uKhu4k57kr3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7L102uKhu4k57kr3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7L102uKhu4k57kr3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-7L102uKhu4k57kr3 .icon-shape,#mermaid-svg-7L102uKhu4k57kr3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7L102uKhu4k57kr3 .icon-shape p,#mermaid-svg-7L102uKhu4k57kr3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7L102uKhu4k57kr3 .icon-shape .label rect,#mermaid-svg-7L102uKhu4k57kr3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7L102uKhu4k57kr3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7L102uKhu4k57kr3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7L102uKhu4k57kr3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} X
seq, d_model
× W_Q
d_model, d_k
× W_K
d_model, d_k
× W_V
d_model, d_v
Q
seq, d_k
K
seq, d_k
V
seq, d_v
注意:Q、K、V 都来自同一个输入 X,这就是"Self"的含义。
6.4 计算 Attention Score
Score=Q⋅KT\text{Score} = Q \cdot K^TScore=Q⋅KT
Shape:
seq,dk\]×\[dk,seq\]=\[seq,seq\]\[seq, d_k\] \\times \[d_k, seq\] = \[seq, seq\]\[seq,dk\]×\[dk,seq\]=\[seq,seq
这是一个 seq × seq 的矩阵,表示每对 Token 之间的相关度。
6.4.1 为什么用点积(Dot Product)?
计算两个向量"有多像"有很多方法,为什么 Transformer 选择了点积?
| 方法 | 公式 | 优点 | 缺点 |
|---|---|---|---|
| 欧氏距离 | ∑(qi−ki)2\sqrt{\sum(q_i-k_i)^2}∑(qi−ki)2 | 直觉简单 | 需要开根号,GPU 慢;距离越小越相似(反直觉) |
| 余弦相似度 | Q⋅K∣Q∣∣K∣\frac{Q \cdot K}{|Q||K|}∣Q∣∣K∣Q⋅K | 只关注方向 | 需要算向量长度再除法,GPU 慢 |
| 点积 | ∑qiki\sum q_i k_i∑qiki | 只需矩阵乘法,GPU 极快 | 受向量长度影响 |
Transformer 每次 Attention 需要计算 N² 个 Score(N 个 Query × N 个 Key)。如果每次都算欧氏距离或余弦,计算量会大幅增加。
而点积本质上就是"不归一化的余弦":
Q⋅K=∣Q∣⋅∣K∣⋅cosθQ \cdot K = |Q| \cdot |K| \cdot \cos\thetaQ⋅K=∣Q∣⋅∣K∣⋅cosθ
方向相同时(θ≈0\theta \approx 0θ≈0),点积最大;方向相反时(θ=180°\theta = 180°θ=180°),点积为负。所以点积天然包含了方向信息,同时只需要一次矩阵乘法------GPU 最擅长的操作。
💡 工程优先 :Transformer 选点积不是因为它数学最优,而是因为它工程最优 ------用一次
tf.matmul(Q, K, transpose_b=True)就能同时算出所有 Token 对的相关度。
💡 语义层面:语言中真正重要的是"相关性"(方向一致),而非"距离近"。例如 doctor 和 nurse 可能 Embedding 长度不同(距离远),但方向一致(语义相关)。点积测的正是方向一致性,这比欧氏距离更适合语言任务。
6.5 为什么要除以 √d_k?
Score=Q⋅KTdk\text{Score} = \frac{Q \cdot K^T}{\sqrt{d_k}}Score=dk Q⋅KT
原因:
当 dkd_kdk 很大时,Q⋅KTQ \cdot K^TQ⋅KT 的值会很大,导致 Softmax 的梯度接近 0(梯度消失)。
直觉理解:
假设 Q 和 K 的每个元素都是均值为 0、方差为 1 的随机变量。
那么 Q⋅KTQ \cdot K^TQ⋅KT 的每个元素是 dkd_kdk 个随机变量的和,方差为 dkd_kdk。
除以 dk\sqrt{d_k}dk 后,方差变为 1,Softmax 的输入不会太大。
#mermaid-svg-jqnDmVd1EWODBtUP{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jqnDmVd1EWODBtUP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jqnDmVd1EWODBtUP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jqnDmVd1EWODBtUP .error-icon{fill:#552222;}#mermaid-svg-jqnDmVd1EWODBtUP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jqnDmVd1EWODBtUP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jqnDmVd1EWODBtUP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jqnDmVd1EWODBtUP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jqnDmVd1EWODBtUP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jqnDmVd1EWODBtUP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jqnDmVd1EWODBtUP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jqnDmVd1EWODBtUP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jqnDmVd1EWODBtUP .marker.cross{stroke:#333333;}#mermaid-svg-jqnDmVd1EWODBtUP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jqnDmVd1EWODBtUP p{margin:0;}#mermaid-svg-jqnDmVd1EWODBtUP .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-jqnDmVd1EWODBtUP .cluster-label text{fill:#333;}#mermaid-svg-jqnDmVd1EWODBtUP .cluster-label span{color:#333;}#mermaid-svg-jqnDmVd1EWODBtUP .cluster-label span p{background-color:transparent;}#mermaid-svg-jqnDmVd1EWODBtUP .label text,#mermaid-svg-jqnDmVd1EWODBtUP span{fill:#333;color:#333;}#mermaid-svg-jqnDmVd1EWODBtUP .node rect,#mermaid-svg-jqnDmVd1EWODBtUP .node circle,#mermaid-svg-jqnDmVd1EWODBtUP .node ellipse,#mermaid-svg-jqnDmVd1EWODBtUP .node polygon,#mermaid-svg-jqnDmVd1EWODBtUP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jqnDmVd1EWODBtUP .rough-node .label text,#mermaid-svg-jqnDmVd1EWODBtUP .node .label text,#mermaid-svg-jqnDmVd1EWODBtUP .image-shape .label,#mermaid-svg-jqnDmVd1EWODBtUP .icon-shape .label{text-anchor:middle;}#mermaid-svg-jqnDmVd1EWODBtUP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-jqnDmVd1EWODBtUP .rough-node .label,#mermaid-svg-jqnDmVd1EWODBtUP .node .label,#mermaid-svg-jqnDmVd1EWODBtUP .image-shape .label,#mermaid-svg-jqnDmVd1EWODBtUP .icon-shape .label{text-align:center;}#mermaid-svg-jqnDmVd1EWODBtUP .node.clickable{cursor:pointer;}#mermaid-svg-jqnDmVd1EWODBtUP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-jqnDmVd1EWODBtUP .arrowheadPath{fill:#333333;}#mermaid-svg-jqnDmVd1EWODBtUP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jqnDmVd1EWODBtUP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jqnDmVd1EWODBtUP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jqnDmVd1EWODBtUP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jqnDmVd1EWODBtUP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jqnDmVd1EWODBtUP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-jqnDmVd1EWODBtUP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jqnDmVd1EWODBtUP .cluster text{fill:#333;}#mermaid-svg-jqnDmVd1EWODBtUP .cluster span{color:#333;}#mermaid-svg-jqnDmVd1EWODBtUP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-jqnDmVd1EWODBtUP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jqnDmVd1EWODBtUP rect.text{fill:none;stroke-width:0;}#mermaid-svg-jqnDmVd1EWODBtUP .icon-shape,#mermaid-svg-jqnDmVd1EWODBtUP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jqnDmVd1EWODBtUP .icon-shape p,#mermaid-svg-jqnDmVd1EWODBtUP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-jqnDmVd1EWODBtUP .icon-shape .label rect,#mermaid-svg-jqnDmVd1EWODBtUP .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jqnDmVd1EWODBtUP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jqnDmVd1EWODBtUP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jqnDmVd1EWODBtUP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Score 很大
例如 100, -50, 80
Softmax
\~1, \~0, \~0
梯度消失
Score 适中
例如 3, -1.5, 2.5
Softmax
0.7, 0.05, 0.25
梯度正常
为什么维度越高,点积越大?
这是理解缩放的关键直觉。
点积 = dkd_kdk 个元素逐一相乘后相加。维度越高,累加的项越多,结果自然越大:
text
2维向量点积: 2项相加 → 结果量级约 2
64维向量点积: 64项相加 → 结果量级约 64
512维向量点积:512项相加 → 结果量级约 512
如果不缩放,d_k=512 时 Score 动辄上百,Softmax 会饱和------输出接近 one-hot(某个位置接近1,其余接近0),梯度几乎消失,模型学不动。
为什么偏偏是 √d_k?(统计学推导)
假设 Q 和 K 的每一维都是独立的随机变量(均值0,方差1):
- 每一项 Qi⋅KiQ_i \cdot K_iQi⋅Ki 的方差 = 1
- 点积 = dkd_kdk 项相加,总方差 = dkd_kdk(独立变量方差相加)
- 标准差 = dk\sqrt{d_k}dk
所以 Score 的波动幅度 随维度增长到 dk\sqrt{d_k}dk 。除以 dk\sqrt{d_k}dk 后,标准差恢复为1------无论 d_k=64 还是 d_k=4096,Softmax 输入的数值范围始终在同一尺度。
💡 一句话 :除以 √d_k 不是为了让结果变小,而是为了让不同维度的模型,Score 的统计分布保持一致,从而让 Softmax 始终工作在最有效的区域。
6.5.1 训练后缩放还有意义吗?
一个自然的疑问:上面的推导假设 Q 和 K 的元素是"均值为0、方差为1"的。但训练之后,Q 和 K 的分布可能不满足这个假设了。缩放还有用吗?
答案是有用,原因有三:
-
Pre-Norm 的帮助:现代 LLM 在 Attention 之前有 LayerNorm/RMSNorm,它会将输入归一化到接近零均值、单位方差的分布。所以 Q 和 K 的输入始终被"约束"在合理范围内。
-
缩放是必要条件 :即使分布不完美,dkd_kdk 个元素求和的量级 仍然会随 dkd_kdk 增长。不缩放时,d_k=64 和 d_k=128 的 Score 数值范围完全不同,Softmax 的行为也完全不同。缩放保证了 Score 的数值范围不随维度变化。
-
实验验证:去掉缩放后,模型训练确实会不稳定或收敛更慢。这已被大量实验证实。
类比:空调的温度设定是基于"室温大约25度"的假设。即使实际室温波动到20-30度,空调设定仍然有意义------它不需要假设完美成立,只要保证了系统在合理范围内工作。
6.6 Softmax
Attention Weight=Softmax(Q⋅KTdk)\text{Attention Weight} = \text{Softmax}\left(\frac{Q \cdot K^T}{\sqrt{d_k}}\right)Attention Weight=Softmax(dk Q⋅KT)
Softmax 将 Score 转换为概率分布(每行之和为 1):
Softmax(zi)=ezi∑jezj\text{Softmax}(z_i) = \frac{e^{z_i}}{\sum_j e^{z_j}}Softmax(zi)=∑jezjezi
6.6.1 Softmax 的本质:偏好放大器
Softmax 不只是"把分数变成概率"。它做了两件事:
- 把所有值变成正数 (ex>0e^x > 0ex>0,即使 x 为负)→ 确保能归一化为概率
- 放大差异:原来相差 1 的分数,经过指数后差距变成 e≈2.7 倍
text
Score: [8.5, 8.0, 2.0]
直接归一化: [46%, 43%, 11%] ← 差异不明显
Softmax: [62%, 33%, 5%] ← 模型的"偏好"被放大了
💡 为什么不直接除以总和?因为 Score 可以是负数(点积结果),直接归一化会出问题。指数函数保证全部为正。
6.6.2 从语义空间视角理解 Softmax
结合第4章4.9.4的"语义空间移动"视角:
- Transformer Block (Attention + FFN)负责移动Hidden State到正确的位置
- Softmax 负责测量:在当前位置,与谁最相似?
Softmax 从头到尾没有创造新知识------它只是把 Transformer 已经学出来的结果,翻译成人类和采样算法可以使用的概率分布。
6.7 加权求和
Output=Attention Weight×V\text{Output} = \text{Attention Weight} \times VOutput=Attention Weight×V
Shape:
seq,seq\]×\[seq,dv\]=\[seq,dv\]\[seq, seq\] \\times \[seq, d_v\] = \[seq, d_v\]\[seq,seq\]×\[seq,dv\]=\[seq,dv
6.8 完整公式
Attention(Q,K,V)=Softmax(QKTdk)V\text{Attention}(Q, K, V) = \text{Softmax}\left(\frac{Q K^T}{\sqrt{d_k}}\right) VAttention(Q,K,V)=Softmax(dk QKT)V
6.9 完整流程图
#mermaid-svg-TNBC8HBErgMzMFpE{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TNBC8HBErgMzMFpE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TNBC8HBErgMzMFpE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TNBC8HBErgMzMFpE .error-icon{fill:#552222;}#mermaid-svg-TNBC8HBErgMzMFpE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TNBC8HBErgMzMFpE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TNBC8HBErgMzMFpE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TNBC8HBErgMzMFpE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TNBC8HBErgMzMFpE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TNBC8HBErgMzMFpE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TNBC8HBErgMzMFpE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TNBC8HBErgMzMFpE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TNBC8HBErgMzMFpE .marker.cross{stroke:#333333;}#mermaid-svg-TNBC8HBErgMzMFpE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TNBC8HBErgMzMFpE p{margin:0;}#mermaid-svg-TNBC8HBErgMzMFpE .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TNBC8HBErgMzMFpE .cluster-label text{fill:#333;}#mermaid-svg-TNBC8HBErgMzMFpE .cluster-label span{color:#333;}#mermaid-svg-TNBC8HBErgMzMFpE .cluster-label span p{background-color:transparent;}#mermaid-svg-TNBC8HBErgMzMFpE .label text,#mermaid-svg-TNBC8HBErgMzMFpE span{fill:#333;color:#333;}#mermaid-svg-TNBC8HBErgMzMFpE .node rect,#mermaid-svg-TNBC8HBErgMzMFpE .node circle,#mermaid-svg-TNBC8HBErgMzMFpE .node ellipse,#mermaid-svg-TNBC8HBErgMzMFpE .node polygon,#mermaid-svg-TNBC8HBErgMzMFpE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TNBC8HBErgMzMFpE .rough-node .label text,#mermaid-svg-TNBC8HBErgMzMFpE .node .label text,#mermaid-svg-TNBC8HBErgMzMFpE .image-shape .label,#mermaid-svg-TNBC8HBErgMzMFpE .icon-shape .label{text-anchor:middle;}#mermaid-svg-TNBC8HBErgMzMFpE .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TNBC8HBErgMzMFpE .rough-node .label,#mermaid-svg-TNBC8HBErgMzMFpE .node .label,#mermaid-svg-TNBC8HBErgMzMFpE .image-shape .label,#mermaid-svg-TNBC8HBErgMzMFpE .icon-shape .label{text-align:center;}#mermaid-svg-TNBC8HBErgMzMFpE .node.clickable{cursor:pointer;}#mermaid-svg-TNBC8HBErgMzMFpE .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TNBC8HBErgMzMFpE .arrowheadPath{fill:#333333;}#mermaid-svg-TNBC8HBErgMzMFpE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TNBC8HBErgMzMFpE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TNBC8HBErgMzMFpE .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TNBC8HBErgMzMFpE .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TNBC8HBErgMzMFpE .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TNBC8HBErgMzMFpE .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TNBC8HBErgMzMFpE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TNBC8HBErgMzMFpE .cluster text{fill:#333;}#mermaid-svg-TNBC8HBErgMzMFpE .cluster span{color:#333;}#mermaid-svg-TNBC8HBErgMzMFpE div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TNBC8HBErgMzMFpE .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TNBC8HBErgMzMFpE rect.text{fill:none;stroke-width:0;}#mermaid-svg-TNBC8HBErgMzMFpE .icon-shape,#mermaid-svg-TNBC8HBErgMzMFpE .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TNBC8HBErgMzMFpE .icon-shape p,#mermaid-svg-TNBC8HBErgMzMFpE .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TNBC8HBErgMzMFpE .icon-shape .label rect,#mermaid-svg-TNBC8HBErgMzMFpE .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TNBC8HBErgMzMFpE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TNBC8HBErgMzMFpE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TNBC8HBErgMzMFpE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 输入 X
seq, d_model
Q = X·W_Q
seq, d_k
K = X·W_K
seq, d_k
V = X·W_V
seq, d_v
Q·K^T
seq, seq
÷ √d_k
seq, seq
Softmax
seq, seq
每行和为1
× V
seq, d_v
输出
seq, d_v
6.10 手算一个完整例子
设 seq_len=3,d_model=4,d_k=d_v=3。
输入 X:
X=101001011100X = \begin{bmatrix} 1 & 0 & 1 & 0 \\ 0 & 1 & 0 & 1 \\ 1 & 1 & 0 & 0 \end{bmatrix}X= 101011100010
权重矩阵(简化):
WQ=WK=WV=100010101010W_Q = W_K = W_V = \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 1 & 0 & 1 \\ 0 & 1 & 0 \end{bmatrix}WQ=WK=WV= 101001010010
Step 1:计算 Q、K、V
Q=K=V=X⋅W=201020111Q = K = V = X \cdot W = \begin{bmatrix} 2 & 0 & 1 \\ 0 & 2 & 0 \\ 1 & 1 & 1 \end{bmatrix}Q=K=V=X⋅W= 201021101
Step 2:计算 Score = Q·K^T
Score=201020111⋅201021101=503042323\text{Score} = \begin{bmatrix} 2 & 0 & 1 \\ 0 & 2 & 0 \\ 1 & 1 & 1 \end{bmatrix} \cdot \begin{bmatrix} 2 & 0 & 1 \\ 0 & 2 & 1 \\ 1 & 0 & 1 \end{bmatrix} = \begin{bmatrix} 5 & 0 & 3 \\ 0 & 4 & 2 \\ 3 & 2 & 3 \end{bmatrix}Score= 201021101 ⋅ 201020111 = 503042323
Step 3:除以 √d_k = √3 ≈ 1.73
Scaled Score=2.8901.7302.311.151.731.151.73\text{Scaled Score} = \begin{bmatrix} 2.89 & 0 & 1.73 \\ 0 & 2.31 & 1.15 \\ 1.73 & 1.15 & 1.73 \end{bmatrix}Scaled Score= 2.8901.7302.311.151.731.151.73
Step 4:Softmax(对每行)
Attention Weight≈0.790.060.150.100.670.230.380.240.38\text{Attention Weight} \approx \begin{bmatrix} 0.79 & 0.06 & 0.15 \\ 0.10 & 0.67 & 0.23 \\ 0.38 & 0.24 & 0.38 \end{bmatrix}Attention Weight≈ 0.790.100.380.060.670.240.150.230.38
Step 5:乘以 V
Output=Attention Weight×V\text{Output} = \text{Attention Weight} \times VOutput=Attention Weight×V
第一行(Token "I")的输出:
0.79×2,0,1+0.06×0,2,0+0.15×1,1,1=1.73,0.27,0.940.79 \times 2,0,1 + 0.06 \times 0,2,0 + 0.15 \times 1,1,1 = 1.73, 0.27, 0.940.79×2,0,1+0.06×0,2,0+0.15×1,1,1=1.73,0.27,0.94
这个向量包含了 "I" 对整个句子的理解。
6.11 Attention Matrix 的含义
Attention Weight 矩阵可以可视化:
I love AI
I [0.79 0.06 0.15]
love [0.10 0.67 0.23]
AI [0.38 0.24 0.38]
I主要关注自己(0.79),少量关注AI(0.15)love主要关注自己(0.67),少量关注AI(0.23)AI均匀关注所有词
6.12 TensorFlow 实现
python
import tensorflow as tf
import numpy as np
def scaled_dot_product_attention(Q, K, V, mask=None):
"""
Q: [batch, seq, d_k]
K: [batch, seq, d_k]
V: [batch, seq, d_v]
"""
d_k = tf.cast(tf.shape(K)[-1], tf.float32)
# Score: [batch, seq, seq]
scores = tf.matmul(Q, K, transpose_b=True) / tf.math.sqrt(d_k)
# Mask(用于 Decoder 的 Causal Attention)
if mask is not None:
scores += (mask * -1e9)
# Attention Weight: [batch, seq, seq]
weights = tf.nn.softmax(scores, axis=-1)
# Output: [batch, seq, d_v]
output = tf.matmul(weights, V)
return output, weights
# 测试
batch, seq, d_k, d_v = 2, 5, 64, 64
Q = tf.random.normal([batch, seq, d_k])
K = tf.random.normal([batch, seq, d_k])
V = tf.random.normal([batch, seq, d_v])
output, weights = scaled_dot_product_attention(Q, K, V)
print(output.shape) # (2, 5, 64)
print(weights.shape) # (2, 5, 5)
6.13 Self-Attention 的复杂度
| 操作 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| Q·K^T | O(N² · d_k) | O(N²) |
| Softmax | O(N²) | O(N²) |
| Attention·V | O(N² · d_v) | O(N · d_v) |
| 总计 | O(N² · d) | O(N²) |
这就是为什么 Transformer 处理长序列时很慢:复杂度是序列长度的平方。
FlashAttention 通过分块计算解决了内存问题(第15章会讲)。
本章总结
Self-Attention 的完整公式:
Attention(Q,K,V)=Softmax(QKTdk)V\text{Attention}(Q, K, V) = \text{Softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)VAttention(Q,K,V)=Softmax(dk QKT)V
五步计算:
#mermaid-svg-Dvta8sUTAIqx2CJV{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Dvta8sUTAIqx2CJV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Dvta8sUTAIqx2CJV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Dvta8sUTAIqx2CJV .error-icon{fill:#552222;}#mermaid-svg-Dvta8sUTAIqx2CJV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Dvta8sUTAIqx2CJV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Dvta8sUTAIqx2CJV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Dvta8sUTAIqx2CJV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Dvta8sUTAIqx2CJV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Dvta8sUTAIqx2CJV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Dvta8sUTAIqx2CJV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Dvta8sUTAIqx2CJV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Dvta8sUTAIqx2CJV .marker.cross{stroke:#333333;}#mermaid-svg-Dvta8sUTAIqx2CJV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Dvta8sUTAIqx2CJV p{margin:0;}#mermaid-svg-Dvta8sUTAIqx2CJV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Dvta8sUTAIqx2CJV .cluster-label text{fill:#333;}#mermaid-svg-Dvta8sUTAIqx2CJV .cluster-label span{color:#333;}#mermaid-svg-Dvta8sUTAIqx2CJV .cluster-label span p{background-color:transparent;}#mermaid-svg-Dvta8sUTAIqx2CJV .label text,#mermaid-svg-Dvta8sUTAIqx2CJV span{fill:#333;color:#333;}#mermaid-svg-Dvta8sUTAIqx2CJV .node rect,#mermaid-svg-Dvta8sUTAIqx2CJV .node circle,#mermaid-svg-Dvta8sUTAIqx2CJV .node ellipse,#mermaid-svg-Dvta8sUTAIqx2CJV .node polygon,#mermaid-svg-Dvta8sUTAIqx2CJV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Dvta8sUTAIqx2CJV .rough-node .label text,#mermaid-svg-Dvta8sUTAIqx2CJV .node .label text,#mermaid-svg-Dvta8sUTAIqx2CJV .image-shape .label,#mermaid-svg-Dvta8sUTAIqx2CJV .icon-shape .label{text-anchor:middle;}#mermaid-svg-Dvta8sUTAIqx2CJV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Dvta8sUTAIqx2CJV .rough-node .label,#mermaid-svg-Dvta8sUTAIqx2CJV .node .label,#mermaid-svg-Dvta8sUTAIqx2CJV .image-shape .label,#mermaid-svg-Dvta8sUTAIqx2CJV .icon-shape .label{text-align:center;}#mermaid-svg-Dvta8sUTAIqx2CJV .node.clickable{cursor:pointer;}#mermaid-svg-Dvta8sUTAIqx2CJV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Dvta8sUTAIqx2CJV .arrowheadPath{fill:#333333;}#mermaid-svg-Dvta8sUTAIqx2CJV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Dvta8sUTAIqx2CJV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Dvta8sUTAIqx2CJV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Dvta8sUTAIqx2CJV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Dvta8sUTAIqx2CJV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Dvta8sUTAIqx2CJV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Dvta8sUTAIqx2CJV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Dvta8sUTAIqx2CJV .cluster text{fill:#333;}#mermaid-svg-Dvta8sUTAIqx2CJV .cluster span{color:#333;}#mermaid-svg-Dvta8sUTAIqx2CJV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Dvta8sUTAIqx2CJV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Dvta8sUTAIqx2CJV rect.text{fill:none;stroke-width:0;}#mermaid-svg-Dvta8sUTAIqx2CJV .icon-shape,#mermaid-svg-Dvta8sUTAIqx2CJV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Dvta8sUTAIqx2CJV .icon-shape p,#mermaid-svg-Dvta8sUTAIqx2CJV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Dvta8sUTAIqx2CJV .icon-shape .label rect,#mermaid-svg-Dvta8sUTAIqx2CJV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Dvta8sUTAIqx2CJV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Dvta8sUTAIqx2CJV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Dvta8sUTAIqx2CJV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} X
Q=XW_Q
K=XW_K
V=XW_V
Score=QK^T
Scale ÷√d_k
Softmax
Output=Weight·V
本章思考题
- 为什么 Self-Attention 叫"Self"?和普通 Attention 有什么区别?
- 如果 d_k 很大,不除以 √d_k 会发生什么?
- Attention Weight 矩阵的每一行之和是多少?为什么?
- 如果序列长度从 128 增加到 1024,计算量增加了多少倍?
下一章预告
下一章我们讲 Multi-Head Attention。
为什么不是一个 Head?多个 Head 各自学到了什么?