Transformer 与大语言模型:第6章 Self-Attention自注意

第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 的分布可能不满足这个假设了。缩放还有用吗?

答案是有用,原因有三:

  1. Pre-Norm 的帮助:现代 LLM 在 Attention 之前有 LayerNorm/RMSNorm,它会将输入归一化到接近零均值、单位方差的分布。所以 Q 和 K 的输入始终被"约束"在合理范围内。

  2. 缩放是必要条件 :即使分布不完美,dkd_kdk 个元素求和的量级 仍然会随 dkd_kdk 增长。不缩放时,d_k=64 和 d_k=128 的 Score 数值范围完全不同,Softmax 的行为也完全不同。缩放保证了 Score 的数值范围不随维度变化

  3. 实验验证:去掉缩放后,模型训练确实会不稳定或收敛更慢。这已被大量实验证实。

类比:空调的温度设定是基于"室温大约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 不只是"把分数变成概率"。它做了两件事:

  1. 把所有值变成正数 (ex>0e^x > 0ex>0,即使 x 为负)→ 确保能归一化为概率
  2. 放大差异:原来相差 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=201020111201021101=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


本章思考题

  1. 为什么 Self-Attention 叫"Self"?和普通 Attention 有什么区别?
  2. 如果 d_k 很大,不除以 √d_k 会发生什么?
  3. Attention Weight 矩阵的每一行之和是多少?为什么?
  4. 如果序列长度从 128 增加到 1024,计算量增加了多少倍?

下一章预告

下一章我们讲 Multi-Head Attention

为什么不是一个 Head?多个 Head 各自学到了什么?

相关推荐
richard_first1 小时前
Transformer 与大语言模型:第2章 Transformer 总体架构
深度学习·架构·transformer
深念Y1 小时前
约束工程:如何让 AI 没法跑偏
人工智能·ai·软件工程·codex·opencode·ccsiwtch
赛逸会展s1 小时前
2027赛逸展机器人展新闻发布会在京举行
人工智能·机器人
月光船幽幽1 小时前
真实即粗糙,光滑是伪造
人工智能·python
智购科技自动售货机工厂1 小时前
2026自动售货机语音支付模块集成:从声纹识别到支付闭环的工程实践~YH
大数据·服务器·网络·数据库·人工智能
一次旅行1 小时前
Attention机制从数学到工程:拆解缩放点积+多头注意力|附可运行PyTorch实现与踩坑指南
人工智能·pytorch·python
不是株2 小时前
Agent Memory 架构
人工智能·agent
richard_first2 小时前
Transformer 与大语言模型:第10章 Residual (残差连接)
人工智能·深度学习·机器学习·transformer
zhongerzixunshi2 小时前
深耕绿色建材认证 赋能建筑行业低碳高质量发展
人工智能