Transformer 的线代基础
数据结构
标量 (Scalar)
就是一个数字:比如
3
,-2.5
,0.1
零维:没有方向,只有大小
例子:温度、价格、单个神经元的输出
向量 (Vector)
一组有序的数字:比如
[1, 2, 3]
,[0.5, -0.2]
一维:有大小和方向
在 NLP 中:一个词可以表示为一个向量(嵌入,Embedding)

矩阵 (Matrix)
二维的数字表格:有行和列
在 Transformer 中:权重参数通常都是矩阵

张量 (Tensor)
多维数组:标量是 0 维张量,向量是 1 维,矩阵是 2 维,更高维的就是张量
深度学习中的基本数据结构

向量点积(Dot Product)
两个相同维度的向量对应元素相乘后求和
在注意力中:用点积衡量Query
和Key
的相似度。
点积越大,表示两个向量方向越相似
点积为 0,表示两个向量垂直(不相关)
几何意义:
两个向量a
和b
的点积有一个重要的几何公式:a · b = ||a|| × ||b|| × cosθ
。
其中:
||a||
是向量a
的长度(模)||b||
是向量b
的长度(模)θ
是两个向量之间的夹角
当a · b = 0
时,有三种可能:
||a|| = 0
(a
是零向量)||b|| = 0
(b
是零向量)cosθ = 0
对于非零向量,只有第 3 种情况:cosθ = 0
。在0°
到180°
范围内,意味着θ = 90°
或θ = 270°
——也就是说,两个向量的夹角是直角。
矩阵乘法(Matrix Product)
两个矩阵
A
和B
可以相乘的条件:A
的列数 =B
的行数结果矩阵
C
的形状:(A
的行数,B
的列数)
示例:

转置(Transpose)
线性代数中的“转置”
一个矩阵的转置,简单来说就是将一个矩阵的行和列进行互换。
记法:矩阵
A
的转置记为Aᵀ
或A'
。数学表达:如果有一个
m×n
的矩阵A
,那么它的转置Aᵀ
是一个n×m
的矩阵,并且满足Aᵀ[i, j] = A[j, i]
。
假设我们有一个2×3
的矩阵A
:
那么它的转置Aᵀ
就是一个3×2
的矩阵:

对于高维张量(3 维及以上),转置可以推广为对指定的两个维度进行交换。
例如,一个形状为(2, 2, 3)
的张量,如果交换第一个和第三个维度,就变成(3, 2, 2)
。
转置改变维度顺序,涉及元素位置的重新排列。如上例交换第一个和第三个维度,新张量(1, 0, 0)
位置的元素与原张量(0, 0, 1)
位置元素对应。

转置操作有一些非常实用的数学性质:
(Aᵀ)ᵀ = A
:对矩阵转置两次,会得到原矩阵。(A + B)ᵀ = Aᵀ + Bᵀ
:加法转置等于转置相加。(AB)ᵀ = Bᵀ Aᵀ
:乘法转置等于反向转置相乘。
Transformer 中的“转置”
自注意力机制
自注意力机制的核心目标,是让序列中的每一个元素(例如一个单词或词元)能够根据序列中所有元素的重要性进行信息聚合,从而生成一个融合了全局上下文的全新表示。
该机制通过为每个元素创建三个不同的向量来实现这一目标:
查询向量(
Query
):代表“当前元素想要寻找什么信息”。键向量(
Key
):代表“当前元素可用于被匹配的特征”,用于和查询向量进行相似度比较。值向量(
Value
):代表“当前元素实际需要被提取和传递的内容”。
在技术实现上,模型会维护三组共享的、可学习的权重矩阵(WQ
, WK
,WV
)。对于输入序列中的每个元素,其对应的输入向量(如词嵌入)会分别与这三个权重矩阵相乘,从而生成该元素独有的查询(Q
)、键(K
)和值(V
)向量。最终,通过计算所有查询向量和键向量之间的相关性,并对所有值向量进行加权求和,得到每个元素新的上下文感知表示。
转置的应用
我们想知道一个单词(Query
)与序列中所有单词(Key
)的关联程度。这通常通过点积来完成。
计算:Q · Kᵀ
假设我们的序列有
n
个单词,每个单词的Q
、K
向量维度是d_k
。那么Q
是一个n × d_k
的矩阵,K
也是一个n × d_k
的矩阵。我们需要计算的是每一个
Query
和每一个Key
的点积。如果直接计算Q · K
,则维度不匹配。将K
转置后,Kᵀ
的维度是d_k × n
。Q · Kᵀ
的结果就是一个n × n
的矩阵。这个矩阵的第i
行、第j
列的元素,就是第i
个单词的Query
与第j
个单词的Key
的点积,即相似度分数。
转置的作用
在自注意力机制中,Kᵀ
的操作是实现批量点积计算的关键技巧。
功能上:它使我们能够在一个高效的矩阵乘法操作中,一次性计算出序列中所有单词对之间的关联强度。如果没有转置,我们就需要繁琐的循环来逐个计算点积,计算效率会极其低下。
计算上:现代深度学习框架(如 PyTorch、TensorFlow)和硬件(如 GPU)都对大型矩阵乘法做了极致优化。使用
Q · Kᵀ
这种形式,可以充分利用硬件并行计算的能力,极大地加速模型训练和推理过程。几何上:点积可以衡量两个向量的相似度。
Q · Kᵀ
实质上是在计算每一个Query
向量与所有Key
向量在空间中的夹角余弦值,夹角越小(余弦值越大),代表相似度越高,注意力权重也就越大。
重塑(Reshape)
线性代数中的“重塑”
重塑是指改变一个张量的维度或形状,而不改变其包含的原始数据的总数和顺序。
简单来说,就是把同样的数据元素,以不同的方式“排列”或“观看”。
关键特性:
数量不变:原始张量的总元素数必须等于新形状张量的总元素数。即,所有维度的乘积必须相等。
数据不变:重塑操作不会增加、删除或修改任何数据元素的值。
顺序不变:元素在底层内存中的线性顺序保持不变。重塑只是改变了我们“解读”这个线性序列的维度规则。
假设我们有一个包含12
个元素的向量(1 维张量):
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

我们可以将它重塑为:
一个
3×4
的矩阵(2 维张量)

一个
2×3×2
的张量(3 维张量)

所有这些形状都包含12=3×4=2×2×3
个元素,并且数据的顺序从头到尾都是1, 2, 3, ..., 12
。
Transformer 中的“重塑”
假设我们有一个批次(batch)的句子,经过嵌入层后,输入张量的形状通常为:
(batch_size, sequence_length, embedding_dim)
例如:(batch_size=2, seq_len=5, d_model=4)
,表示输入是2
个句子,每个句子5
个词,每个词用4
维向量表示。
实现多头注意力
Transformer 的核心思想是并行地计算多个“头”的注意力。每个头从不同的角度(不同的表示子空间)来关注输入序列的不同部分。重塑是实现这种并行的关键技术。
1、线性变换生成 Q, K, V
首先,我们对输入张量进行线性变换,生成查询、键、值。如果我们有h
个头(例如h=2
),我们不会创建h
个独立的矩阵,而是一次性地生成一个更大的矩阵。
原始输入
X
:(2, 5, 4)
经过线性层
WQ
后,我们得到Q
:(2, 5, d_k)
。但为了分割成多头,我们不会输出这个形状。我们输出(2, 5, h * d_k)
,其中d_k = d_model / h
(例如d_k = 4 / 2 = 2
)。所以Q
的形状是(2, 5, 4)
。
2、第一次重塑:引入“头”的维度
现在,我们将最后一个维度 (h * d_k)
重塑为两个独立的维度(h, d_k)
。
重塑前
Q
:(2, 5, 4)

重塑后
Q
:(2, 5, 2, 2)
,含义是(batch_size, seq_len, num_heads, head_dim)

这一步在概念上等于将一个大矩阵“切割”成了h
个小矩阵,每个小矩阵对应一个注意力头。
3、转置:为矩阵乘法做准备
为了便于在序列维度上计算注意力分数,我们需要将num_heads
维度提前。
转置
Q
:(2, 2, 5, 2)
,新的形状是(batch_size, num_heads, seq_len, head_dim)

现在,我们可以把batch_size
和num_heads
看作一个“批”维度,然后在这个大“批”中,并行地对每一个 (seq_len, head_dim)
的矩阵计算注意力。
4、计算注意力分数
注意力分数的计算是:Softmax((Q · KT) / sqrt(d_k)) · V
。
由于我们的Q
, K
, V
现在都是(2, 2, 5, 2)
的形状,这个矩阵乘法可以在所有头上并行执行。计算后,输出的形状仍然是(2, 2, 5, 2)
。
5、第二次重塑:合并多头输出
计算完注意力后,我们需要将多个头的输出合并回一个统一的表示。
首先,将num_heads
和seq_len
的维度顺序恢复:转置输出张量,得到 (2, 5, 2, 2)
。
然后,重塑这个张量,将num_heads
和head_dim
这两个维度合并回原来的d_model
维度。
重塑前:
(2, 5, 2, 2)
重塑后:
(2, 5, 4)
,回到了(batch_size, seq_len, d_model)
,这个合并后的张量就是所有注意力头信息的聚合。
重塑的重要性
计算效率:通过重塑,我们可以利用现代硬件(如 GPU)强大的并行计算能力,一次性计算所有头的注意力,而不是用循环逐个计算。
模型表达力:它优雅地实现了“多头”的概念,让模型能够同时从多个角度关注信息。
代码简洁性:在深度学习框架(如 PyTorch,TensorFlow)中,只需几行重塑和矩阵乘法的代码就能实现复杂的多头注意力机制。
广播 (Broadcasting)
广播机制
广播机制是一种允许不同形状的张量(多维数组)进行数学运算的规则。它的核心思想是:自动扩展较小形状的张量,使其与较大形状的张量的维度兼容,从而执行逐元素操作。
广播的规则:
从尾部维度开始对齐:比较两个张量的形状时,从最右边的维度开始向左依次比较。
维度兼容的条件:两个维度在以下情况下是兼容的——它们相等、其中一个为 1、其中一个不存在(即维度缺失)。
如果所有维度都兼容,则可以广播。广播时,大小为 1 或缺失的维度会被“拉伸”或“复制”,以匹配另一个张量对应维度的大小。
广播的步骤:
在所有张量的形状前面补 1,直到所有张量拥有相同的维数。
检查每个维度,对于每个维度,其大小必须相等,或者其中一个为 1,或者其中一个不存在。
执行扩展,在大小为 1 的维度上,将数据沿该维度复制,以匹配另一个张量在该维度上的大小。
广播的示例:
示例 1:向量 + 标量
示例 2:矩阵 + 行向量
Transformer 中的“广播”
自注意力机制中的掩码
场景:在计算注意力分数后,我们需要对某些位置进行掩码(Mask)。例如,在解码器中掩码未来的词元,或者掩码填充的<pad>
符号。
具体过程:
我们有一个注意力分数矩阵
Attn_scores
,其形状为(batch_size, num_heads, seq_len, seq_len)
。例如(2, 8, 10, 10)
。我们有一个掩码矩阵
Mask
,其形状通常为(batch_size, 1, 1, seq_len)
或(1, 1, seq_len, seq_len)
。例如(2, 1, 1, 10)
。当我们执行
Attn_scores = Attn_scores + Mask
时,广播机制开始工作:Mask
的形状从(2, 1, 1, 10)
被自动扩展(广播)到与Attn_scores
的形状(2, 8, 10, 10)
相匹配。Mask
的第二个维度(1)被复制 8 次以匹配 8 个头,第三个维度(1)被复制 10 次以匹配seq_len
(查询),而第四个维度(10)已经匹配seq_len
(键)。最终,每个批次、每个头、每个查询位置,都加上了同一个掩码向量。这样就高效地实现了对整个注意力矩阵的掩码。
位置编码与词嵌入相加
场景:将位置信息注入到输入词嵌入中。
具体过程:
词嵌入矩阵
Word_Embedding
的形状是(batch_size, seq_len, d_model)
,例如(32, 100, 512)
。位置编码矩阵
Pos_Encoding
的形状通常是(1, seq_len, d_model)
或(seq_len, d_model)
,例如(1, 100, 512)
。当我们执行
Input = Word_Embedding + Pos_Encoding
时:Pos_Encoding
的形状被广播到(32, 100, 512)
。这意味着对于批次中的每一个句子,都加上了完全相同的位置编码。我们不需要为批次中的 32 个句子分别创建一个
(100, 512)
的位置编码矩阵,广播机制帮我们自动完成了。
层归一化中的可学习参数
场景:对每个样本的激活值进行归一化。
具体过程:
层归一化(Layer Normalization)有两个可学习的参数:缩放参数
gamma
和偏移参数beta
,它们的形状都是(d_model,)
,例如(512,)
。输入张量
x
的形状是(batch_size, seq_len, d_model)
,例如(32, 100, 512)
。在归一化后,我们执行
y = gamma * x_norm + beta
。这里,
gamma
和beta
的形状(512,)
会被广播到与x_norm
的形状(32, 100, 512)
相匹配。具体来说,gamma
和beta
会在批次维度和序列长度维度上进行复制,对批次中 32 个句子的 100 个位置上的每一个 512 维向量,都应用同一套缩放和偏移参数。
广播机制的好处
代码简洁:无需手动编写循环或使用
expand
/repeat
函数来显式复制张量。内存节省:我们只需要存储小尺寸的张量(如掩码、位置编码),而不是它们复制后的大尺寸版本。
计算高效:框架底层在实现广播时是高度优化的,避免了不必要的内存分配和复制,计算效率高。
版权声明: 本文为 InfoQ 作者【陈一之】的原创文章。
原文链接:【http://xie.infoq.cn/article/79c6ca1850ed3245046097e96】。文章转载请联系作者。
评论