X→Q,K,V→QKT→Mask→Softmax→PV→Merge HeadsX \rightarrow Q, K, V \rightarrow QK^T \rightarrow \text{Mask} \rightarrow \text{Softmax} \rightarrow PV \rightarrow \text{Merge Heads}

Multi-head Attention·

设 Transformer 某一层的输入为

X∈RB×S×HX\in\mathbb R^{B\times S\times H}

这里:

  • BB:batch size,一次处理多少条序列;
  • SS:sequence length,每条序列多少个 token;
  • HH:hidden size,每个 token 的隐藏向量维度。

Attention 首先对 XX 做三个线性变换:

Q=XWQ,K=XWK,V=XWV.Q = XW_Q,\qquad K = XW_K,\qquad V = XW_V.

如果暂时不拆 head,它们通常都是

Q,K,V∈RB×S×H.Q, K, V\in\mathbb R^{B\times S\times H}.

然后进入 Multi-Head Attention。

设 head 数量为 NhN_h,每个 head 的维度为 DhD_h。通常有

H=NhDh.H = N_hD_h.

原始:

Q∈RB×S×H.Q\in\mathbb R^{B\times S\times H}.

因为 H=NhDhH = N_hD_h,先 reshape:

[B,S,H]→[B,S,Nh,Dh].[B, S, H] \rightarrow [B, S, N_h, D_h].

然后为了方便矩阵乘法,把 head 维提前:

[B,S,Nh,Dh]→[B,Nh,S,Dh].[B, S, N_h, D_h] \rightarrow [B, N_h, S, D_h].

于是:

Q,K,V∈RB×Nh×S×Dh.Q, K, V \in \mathbb R^{B\times N_h\times S\times D_h}.

例如:

[2,32,1024,128].[2,32,1024,128].

可以理解为每个 batch 有 NhN_h 个独立的 Attention head,每个 head 都看到 S 个 token,每个 token 在这个 head 中是 DhD_h 维。

对于一个 head:

Qh∈RS×DhQ_h\in\mathbb R^{S\times D_h}

而

Kh∈RS×Dh.K_h\in\mathbb R^{S\times D_h}.

为了做矩阵乘法,对 KK 最后两个维度转置:

KhT∈RDh×S.K_h^T \in \mathbb R^{D_h\times S}.

所以:

QhKhT:(S×Dh)(Dh×S)Q_hK_h^T : (S\times D_h)(D_h\times S)

得到:

S×SS\times S

的矩阵。

因此完整 batch + multi-head 情况下:

QKT∈RB×Nh×S×S.QK^T \in \mathbb R^{B\times N_h\times S\times S}.

为了避免和 sequence length SS 混淆,可以把 score matrix 记为 AA:

A=QKTDh∈RB×Nh×S×S.A = \frac{QK^T}{\sqrt{D_h}} \in \mathbb R^{B\times N_h\times S\times S}.

假设一句话有 4 个 token:

1
我  喜欢  深度  学习

那么 S=4S = 4。每个 token 都要询问:“我应该关注哪些 token?”

所以每一个 Query 都要和所有 Key 做点积。得到:

A=(a11a12a13a14a21a22a23a24a31a32a33a34a41a42a43a44).A = \begin{pmatrix} a_{11}&a_{12}&a_{13}&a_{14}\\ a_{21}&a_{22}&a_{23}&a_{24}\\ a_{31}&a_{32}&a_{33}&a_{34}\\ a_{41}&a_{42}&a_{43}&a_{44} \end{pmatrix}.

其中:

aij=qiTkjDha_{ij} = \frac{q_i^Tk_j}{\sqrt{D_h}}

表示第 ii 个 token 的 Query,认为第 jj 个 token 的 Key 有多相关。

因此:

  • 行 ii:当前 Query token;
  • 列 jj:它正在观察的 Key token。

例如 a34a_{34} 就是:第 3 个 token 对第 4 个 token 的注意力匹配分数。

P,A∈RB×Nh×S×S.P, A \in \mathbb R^{B\times N_h\times S\times S}.

这里出现了:

S2.S^2.

因此标准 Attention 的 score matrix 元素数量:

BNhS2.BN_hS^2.

当 sequence length 增大时,增长速度是平方级。这是长上下文昂贵的原因之一。

不过 Attention 不是直接计算 QKTQK^T,而是:

QKTDh.\frac{QK^T}{\sqrt{D_h}}.

原因和数值尺度有关。假设 Query 和 Key 每一个分量大致满足均值 0、方差 1。点积

qTk=∑r=1Dhqrkrq^Tk = \sum_{r = 1}^{D_h}q_rk_r

的方差会随着 DhD_h 增长。如果直接进入 Softmax,例如:

softmax⁡(100,0,−100)\operatorname{softmax}(100,0,-100)

分布会非常尖锐,容易导致梯度不好训练。除以 Dh\sqrt{D_h} 可以把点积的典型尺度稳定下来:

qTkDh.\frac{q^Tk}{\sqrt{D_h}}.

这就是 Scaled Dot-Product Attention 中 “Scaled” 的来源。

自回归语言模型有一个非常重要的限制:第 ii 个 token 不能偷看未来 token。

例如:

1
我 喜欢 深度 学习

预测“喜欢”时,不能提前看到后面的“深度 学习”。因此需要 causal mask。

对于 S=4S = 4,mask 可以理解成:

M=(0−∞−∞−∞00−∞−∞000−∞0000).M = \begin{pmatrix} 0&-\infty&-\infty&-\infty\\ 0&0&-\infty&-\infty\\ 0&0&0&-\infty\\ 0&0&0&0 \end{pmatrix}.

然后计算:

A+M.A+M.

第一行:

(a11,−∞,−∞,−∞)(a_{11},-\infty,-\infty,-\infty)

第二行:

(a21,a22,−∞,−∞)(a_{21}, a_{22},-\infty,-\infty)

因为后面马上做 Softmax:

P=softmax⁡(A+M).P =\operatorname{softmax}(A+M).

而 e−∞=0e^{-\infty}=0,所以一个被 mask 的位置最终概率就是 0。

例如:

softmax⁡(3,1,−∞)\operatorname{softmax}(3,1,-\infty)

第三项概率:

e−∞e3+e1+e−∞=0.\frac{e^{-\infty}} {e^3+e^1+e^{-\infty}} = 0.

因此 causal mask 最终实现:

未来 token 的 Attention 权重=0.\text{未来 token 的 Attention 权重}= 0.


现在:

A∈RB×Nh×S×S.A\in \mathbb R^{B\times N_h\times S\times S}.

对于每一个:

  • batch bb
  • head hh
  • query token ii

都有一整行:

Ab,h,i,:.A_{b, h, i,:}.

Softmax 对最后一个维度,也就是 Key/token 维度 进行:

Pb,h,i,:=softmax⁡(Ab,h,i,:).P_{b, h, i,:} = \operatorname{softmax} (A_{b, h, i,:}).

所以:

∑j=1SPb,h,i,j=1.\sum_{j = 1}^S P_{b, h, i, j}= 1.

每一个 Query token 都得到一组对所有 Key token 的概率权重。例如:

Pi,:=(0.1,0.2,0.6,0.1).P_{i,:} = (0.1,0.2,0.6,0.1).

表示 token ii 把 10% 注意力放第 1 个 token,20% 放第 2 个,60% 放第 3 个,10% 放第 4 个。

得到 Attention probability:

P∈RB×Nh×S×S.P\in \mathbb R^{B\times N_h\times S\times S}.

而:

V∈RB×Nh×S×Dh.V\in \mathbb R^{B\times N_h\times S\times D_h}.

计算:

O=PV.O = PV.

观察最后两个矩阵维度:

(S×S)(S×Dh)(S\times S)(S\times D_h)

得到:

S×Dh.S\times D_h.

所以:

O∈RB×Nh×S×Dh.O \in \mathbb R^{B\times N_h\times S\times D_h}.

跟原来的 Q,K,VQ, K, V shape 一样。

对于 token ii:

oi=∑j=1SPijvj.o_i = \sum_{j = 1}^S P_{ij}v_j.

例如 Attention 权重:

Pi,:=(0.1,0.2,0.6,0.1).P_{i,:} = (0.1,0.2,0.6,0.1).

那么:

oi=0.1v1+0.2v2+0.6v3+0.1v4.o_i = 0.1v_1 + 0.2v_2 + 0.6v_3 + 0.1v_4.

所以 Attention 的真正含义是 根据 Query-Key 相似度,对不同 Value 做加权求和。

因此:

  • QQ:我在寻找什么?
  • KK:我有什么特征可供匹配?
  • VV:如果你关注我,你真正获得什么信息?

现在:

O∈RB×Nh×S×Dh.O \in \mathbb R^{B\times N_h\times S\times D_h}.

先 transpose:

[B,Nh,S,Dh]→[B,S,Nh,Dh].[B, N_h, S, D_h] \rightarrow [B, S, N_h, D_h].

然后把最后两个维度拼起来:

NhDh=H.N_hD_h = H.

所以:

[B,S,Nh,Dh]→[B,S,H].[B, S, N_h, D_h] \rightarrow [B, S, H].

最终:

O∈RB×S×H.O\in\mathbb R^{B\times S\times H}.

和原始输入

X∈RB×S×HX\in\mathbb R^{B\times S\times H}

shape 完全一致。

Flash Attention·

Flash Attention 的核心不是改变 Attention 数学公式,而是不把完整 S×SS\times S 中间矩阵写回 HBM。

普通实现逻辑上可能类似:

Q,K→QKT.Q, K \rightarrow QK^T.

把巨大的 S×SS\times S 矩阵写入 GPU HBM。然后再:

HBM→Softmax\text{HBM} \rightarrow \text{Softmax}

再写回。然后:

P,V→PV.P, V \rightarrow PV.

大量时间浪费在:

HBM 读写.\text{HBM 读写}.

而不仅仅是 FLOPs。Flash Attention 不一次生成整个:

S×S.S\times S.

而是把 Q,K,VQ, K, V 切成小块。例如概念上:

1
2
3
4
5
6
7
Q block
↓
K block → 算局部 QKᵀ
↓
局部 Softmax
↓
和 V block 累积

只在 GPU 更快但容量更小的片上存储中保留当前 block 所需数据,例如:

  • registers;
  • shared memory / SRAM。

算完这个块后立即把贡献累积到输出。因此不需要:

完整 materialize 一个 S×S score matrix.\text{完整 materialize 一个 }S\times S\text{ score matrix}.

普通 Softmax:

pj=exj∑kexkp_j = \frac{e^{x_j}} {\sum_k e^{x_k}}

通常需要知道整行:

x1,…,xS.x_1,\dots, x_S.

而数值稳定版本还需要:

m=max⁡jxj.m =\max_jx_j.

然后:

pj=exj−m∑kexk−m.p_j = \frac{e^{x_j-m}} {\sum_ke^{x_k-m}}.

看起来必须一次拿到所有 SS 个元素。

Flash Attention 使用一种 online softmax 思想分块处理时持续维护当前行的最大值和归一化因子。

假设已经处理了一部分,保存:

moldm_{\text{old}}

和:

lold=∑exi−mold.l_{\text{old}} = \sum e^{x_i-m_{\text{old}}}.

新 block 出现新的最大值之后,可以重新缩放旧累积值,从而得到正确的新 normalization。因此无需保存整行 score,也能精确完成稳定 Softmax。事实上,Flash Attention 没有消除标准 Attention 的平方计算复杂度。Flash Attention 主要是 IO-aware exact attention,显著降低了 HBM IO 带宽从而降低显存压力。

标准 self-attention 仍然需要比较:

S×SS\times S

个 Query-Key 对。所以主要计算复杂度仍然近似:

O(S2Dh).O(S^2D_h).

多头总体近似:

O(S2H).O(S^2H).