LLaMA、Mistral 和 GPT 等生成式大模型,全部采用的是 Decoder-only Transformer 架构 。理解它,就是理解至当今所有大模型的基础。
数据在这个架构中完成一次"前向传播"(也就是生成一个词的过程),需要经过以下极其严密的流水线。我们将按顺序一步步推导:
1. 词的向量化表示 (Embedding & Positional Encoding)
计算机无法直接处理文字,必须将其转化为数字矩阵。
- 词嵌入 (Word Embedding) :
假设你输入了一个长度为 S S S 的句子(比如 "我 爱 学习")。首先通过分词器(Tokenizer)将它们变成索引号,然后查表,映射成稠密向量。
此时,输入数据变成了一个矩阵 X ∈ R S × d m o d e l X \in \mathbb{R}^{S \times d_{model}} X∈RS×dmodel。其中 S S S 是序列长度(Token 数量), d m o d e l d_{model} dmodel 是模型的隐藏层维度(比如 LLaMA-7B 中通常是 4096)。 - 位置编码 (Positional Encoding) :
由于后续的矩阵乘法是"全连接"的,它本身不具备感知词语先后顺序的能力。因此,必须将位置信息 P E ∈ R S × d m o d e l PE \in \mathbb{R}^{S \times d_{model}} PE∈RS×dmodel 叠加到词向量上。
X i n p u t = X + P E X_{input} = X + PE Xinput=X+PE
2. 线性投影生成 Q、K、V (QKV Project)
进入 Decoder 层后,输入矩阵 X X X 会与三个不同的权重矩阵相乘:
- 权重矩阵 :模型在训练阶段学到了三个参数矩阵 W Q , W K , W V ∈ R d m o d e l × d k W_Q, W_K, W_V \in \mathbb{R}^{d_{model} \times d_k} WQ,WK,WV∈Rdmodel×dk。
- 线性变换 :
Q = X ⋅ W Q Q = X \cdot W_Q Q=X⋅WQ
K = X ⋅ W K K = X \cdot W_K K=X⋅WK
V = X ⋅ W V V = X \cdot W_V V=X⋅WV
此时,我们得到了查询矩阵(Query, Q Q Q)、键矩阵(Key, K K K)和值矩阵(Value, V V V),它们的维度都是 S × d k S \times d_k S×dk。这三个矩阵包含了当前序列在这一层的所有特征信息。
3. 核心引擎:掩码自注意力机制 (Masked Self-Attention)
这是 Transformer 最具革命性的一步,也是决定生成式大模型"从左到右"逐字生成特性的根本原因 。
- 第一步:计算注意力打分矩阵 (Attention Score)
我们让序列中的每一个词去和所有的词(包括自己)计算相关性。在数学上,这就是矩阵的点积运算:
S c o r e = Q ⋅ K T Score = Q \cdot K^T Score=Q⋅KT
这里的 S c o r e Score Score 是一个 S × S S \times S S×S 的方阵。矩阵中的第 i i i 行第 j j j 列的值,代表了第 i i i 个词对第 j j j 个词的关注程度。 - 第二步:缩放 (Scaling)
为了防止点积结果过大导致后续的梯度消失,需要除以一个缩放因子(通常是 d k \sqrt{d_k} dk ,即 K K K 的维度的平方根):
S c o r e ′ = S c o r e d k Score' = \frac{Score}{\sqrt{d_k}} Score′=dk Score - 第三步:掩码操作 (Masking) ------ 极其关键
在文本生成时,第 i i i 个词在预测下一个词时,绝对不能 提前"看到"它后面的词(未来的词)。
在数学上的实现方法是:引入一个下三角矩阵掩码 M M M。对于所有的 j > i j > i j>i(对角线右上方的元素),强制将 S c o r e i , j ′ Score'_{i,j} Scorei,j′ 设置为 − ∞ -\infty −∞。 - 第四步:Softmax 归一化
将打分矩阵转化为概率分布(权重):
A = softmax ( S c o r e ′ + M ) A = \text{softmax}(Score' + M) A=softmax(Score′+M)
因为掩码位置的值是 − ∞ -\infty −∞,经过 softmax 后会变成精确的 0 0 0。这确保了每个位置只能关注到它之前的位置 。 - 第五步:加权求和得出新特征
用计算出的权重矩阵 A A A 去乘上值矩阵 V V V:
Z = A ⋅ V Z = A \cdot V Z=A⋅V
最终输出的 Z ∈ R S × d k Z \in \mathbb{R}^{S \times d_k} Z∈RS×dk,就是融合了上下文语境的新向量矩阵。
4. 多头机制 (Multi-Head Attention, MHA)
为了让模型能从多个不同的维度(比如语法、情感、逻辑)去理解上下文,Transformer 不仅仅做一次上述操作,而是把隐藏维度切分成多个"头"(Heads),每个头独立进行自注意力计算,然后拼接起来。
h e a d i = Attention ( Q i , K i , V i ) head_i = \text{Attention}(Q_i, K_i, V_i) headi=Attention(Qi,Ki,Vi)
MultiHead ( Q , K , V ) = Concat ( h e a d 1 , . . . , h e a d h ) ⋅ W O \text{MultiHead}(Q, K, V) = \text{Concat}(head_1, ..., head_h) \cdot W_O MultiHead(Q,K,V)=Concat(head1,...,headh)⋅WO
这里的 W O W_O WO 是一个输出线性变换矩阵,用于将拼接后的维度重新映射回 d m o d e l d_{model} dmodel。
5. 残差连接与层归一化 (Add & Norm)
为了防止网络太深导致梯度消失或爆炸,Transformer 在每个主要模块后都加入了这两项操作。
- 残差连接 :将 MHA 的输出加上它自己原本的输入(即最初的 X X X)。
- 层归一化 :在特征维度上对数据进行标准化。
X m h a = LayerNorm ( X + MHA ( X ) ) X_{mha} = \text{LayerNorm}(X + \text{MHA}(X)) Xmha=LayerNorm(X+MHA(X))
6. 前馈神经网络 (Feed-Forward Network, FFN)
经过注意力机制后,序列中的每个词都已经包含了上下文信息,但这些信息还没有经过复杂的非线性映射。
FFN 包含两个线性操作 。它只对每个词(在 S S S 维度上的每一行)独立进行操作:
FFN ( x ) = Activation ( x ⋅ W 1 + b 1 ) ⋅ W 2 + b 2 \text{FFN}(x) = \text{Activation}(x \cdot W_1 + b_1) \cdot W_2 + b_2 FFN(x)=Activation(x⋅W1+b1)⋅W2+b2
这里的关键是: W 1 W_1 W1 会把维度大幅扩张(例如从 4096 扩大到 11008),经过激活函数后, W 2 W_2 W2 再把它压缩回原来的尺寸。这极大地增强了模型存储和提取复杂知识的能力。
最后,同样需要经过一次 Add & Norm:
X o u t = LayerNorm ( X m h a + FFN ( X m h a ) ) X_{out} = \text{LayerNorm}(X_{mha} + \text{FFN}(X_{mha})) Xout=LayerNorm(Xmha+FFN(Xmha))
至此,一层 Decoder 层的完整前向计算结束。实际的大模型(如 LLaMA2-7B)就是由 32 个这样结构完全相同、但参数权重完全不同的 Decoder 层像搭积木一样串联堆叠起来的 。
我们将构建一个微型的、但结构严格符合真实大模型运作规律的沙盘推演。
假设我们要让模型处理输入序列:"我 爱 学习",并且预测下一个词。
为了方便演算,我们假设这是一个极其微小的模型:其隐藏层维度 d m o d e l = 4 d_{model} = 4 dmodel=4(真实场景如 LLaMA2-7B 中这个值是 4096 )。
以下是这三个词在模型第一层 Decoder 中的完整流转过程(即预填充阶段 Prefill Phase):
第一步:输入矩阵化 (Embedding)
系统首先通过分词器(Tokenizer)将这三个词转化为编号,并查表得到它们的初始词向量(已加上位置编码)。
此时,我们的输入矩阵 X X X 是一个 3 × 4 3 \times 4 3×4 的矩阵(3代表序列长度 S S S,4代表维度 d m o d e l d_{model} dmodel):
X = [ 1.0 0.1 0.2 0.5 0.3 1.2 0.1 0.4 0.8 0.5 1.5 0.2 ] ← "我"的初始向量 ← "爱"的初始向量 ← "学习"的初始向量 X = \begin{bmatrix} 1.0 & 0.1 & 0.2 & 0.5 \\ 0.3 & 1.2 & 0.1 & 0.4 \\ 0.8 & 0.5 & 1.5 & 0.2 \end{bmatrix} \begin{matrix} \leftarrow \text{"我"的初始向量} \\ \leftarrow \text{"爱"的初始向量} \\ \leftarrow \text{"学习"的初始向量} \end{matrix} X= 1.00.30.80.11.20.50.20.11.50.50.40.2 ←"我"的初始向量←"爱"的初始向量←"学习"的初始向量
(注:这里不懂怎么来的看这篇词嵌入和位置编码)
第二步:生成 Q、K、V (线性投影)
接下来,矩阵 X X X 分别与模型已经训练好的三个权重矩阵 W Q , W K , W V W_Q, W_K, W_V WQ,WK,WV(假设维度都是 4 × 4 4 \times 4 4×4)进行完整的矩阵乘法:
- Q = X ⋅ W Q Q = X \cdot W_Q Q=X⋅WQ
- K = X ⋅ W K K = X \cdot W_K K=X⋅WK
- V = X ⋅ W V V = X \cdot W_V V=X⋅WV
物理意义拆解:
- Q (Query/查询):代表这个词**"想寻找什么语境"**。例如,"爱"这个动词的 Q 向量,可能在强烈地寻找它的主语和宾语。
- K (Key/键):代表这个词**"具有什么特征"**。例如,"我"的 K 向量会强烈地散发出"我是代词、我是主语"的信号。
- V (Value/值):代表这个词**"本身的实际内容"**。
此时,我们得到了 Q , K , V Q, K, V Q,K,V 三个全新的 3 × 4 3 \times 4 3×4 矩阵。
第三步:掩码自注意力的计算底色 (Masked Self-Attention)
这是整个过程中最硬核的一步。模型需要计算这三个词两两之间的关联度。
1. 点积打分 (Score)
模型用查询矩阵 Q Q Q 乘以键矩阵的转置 K T K^T KT:
S c o r e = Q ⋅ K T = [ 10 2 1 4 15 3 1 8 12 ] Score = Q \cdot K^T = \begin{bmatrix} 10 & 2 & 1 \\ 4 & 15 & 3 \\ 1 & 8 & 12 \end{bmatrix} Score=Q⋅KT= 104121581312
这是一个 3 × 3 3 \times 3 3×3 的方阵。比如第三行第二列的数值是 8,这意味着"学习"( Q 3 Q_3 Q3) 对"爱"( K 2 K_2 K2) 的原始关注度是 8。
2. 强加时间箭头:掩码操作 (Masking)
在真实的生成式模型中,第 i i i 个词绝对不能看到第 i + 1 i+1 i+1 个词。模型会强制用一个下三角的掩码矩阵 M M M 加到 S c o r e Score Score 上,把对角线右上方的数值全部变成负无穷( − ∞ -\infty −∞):
S c o r e + M = [ 10 − ∞ − ∞ 4 15 − ∞ 1 8 12 ] Score + M = \begin{bmatrix} 10 & -\infty & -\infty \\ 4 & 15 & -\infty \\ 1 & 8 & 12 \end{bmatrix} Score+M= 1041−∞158−∞−∞12
解释:"我"(第一行)只能看到自己;"爱"(第二行)能看到"我"和"爱";"学习"(第三行)能看到完整的"我 爱 学习"。
3. 概率归一化 (Softmax)
对每一行进行 Softmax 计算,将这些分数转化为总和为 1 的概率权重矩阵 A A A:
A ≈ [ 1.0 0 0 0.05 0.95 0 0.01 0.29 0.70 ] A \approx \begin{bmatrix} 1.0 & 0 & 0 \\ 0.05 & 0.95 & 0 \\ 0.01 & 0.29 & 0.70 \end{bmatrix} A≈ 1.00.050.0100.950.29000.70
可以看到, − ∞ -\infty −∞ 被转换成了绝对的 0。
4. 提取新特征 (加权求和)
最后,用权重矩阵 A A A 去乘以内容矩阵 V V V:
Z = A ⋅ V Z = A \cdot V Z=A⋅V
我们知道 V V V 是一个 3 × 4 3 \times 4 3×4 的矩阵(3 个词,每个词 4 个维度)。在数学上,我们可以把 V V V 看作是由 3 个 1 × 4 1 \times 4 1×4 的**行向量(Row Vector)**上下拼起来的:
V = [ v 11 v 12 v 13 v 14 v 21 v 22 v 23 v 24 v 31 v 32 v 33 v 34 ] = [ --- V 我 --- --- V 爱 --- --- V 学习 --- ] V = \begin{bmatrix} v_{11} & v_{12} & v_{13} & v_{14} \\ v_{21} & v_{22} & v_{23} & v_{24} \\ v_{31} & v_{32} & v_{33} & v_{34} \end{bmatrix} = \begin{bmatrix} \text{--- } V_{\text{我}} \text{ ---} \\ \text{--- } V_{\text{爱}} \text{ ---} \\ \text{--- } V_{\text{学习}} \text{ ---} \end{bmatrix} V= v11v21v31v12v22v32v13v23v33v14v24v34 = --- V我 ------ V爱 ------ V学习 ---
这里的 V 我 V_{\text{我}} V我、 V 爱 V_{\text{爱}} V爱、 V 学习 V_{\text{学习}} V学习 都是长度为 4 的一维向量。最终得到了完整、严谨的 3 × 4 3 \times 4 3×4 矩阵 Z Z Z:
Z = [ --- Z 我 --- --- Z 爱 --- --- Z 学习 --- ] = [ 1.0 ⋅ V 我 0.05 ⋅ V 我 + 0.95 ⋅ V 爱 0.01 ⋅ V 我 + 0.29 ⋅ V 爱 + 0.70 ⋅ V 学习 ] Z = \begin{bmatrix} \text{--- } Z_{\text{我}} \text{ ---} \\ \text{--- } Z_{\text{爱}} \text{ ---} \\ \text{--- } Z_{\text{学习}} \text{ ---} \end{bmatrix} = \begin{bmatrix} 1.0 \cdot V_{\text{我}} \\ 0.05 \cdot V_{\text{我}} + 0.95 \cdot V_{\text{爱}} \\ 0.01 \cdot V_{\text{我}} + 0.29 \cdot V_{\text{爱}} + 0.70 \cdot V_{\text{学习}} \end{bmatrix} Z= --- Z我 ------ Z爱 ------ Z学习 --- = 1.0⋅V我0.05⋅V我+0.95⋅V爱0.01⋅V我+0.29⋅V爱+0.70⋅V学习
至此,原本孤立的词汇"学习",它的向量里已经精准地按比例融合了前文"我"和"爱"的特征信息!
上文为了演示注意力公式,我们假设整个 4 4 4 维空间只做了一次注意力计算。但在真实的 Transformer 中,模型会将这 4 4 4 个维度"劈开",分给多个不同的"头"去独立计算 。假设我们设定多头数量 h = 2 h = 2 h=2。
1. 降维分发:
原本的输入 X X X 会乘上不同的投影矩阵,分别生成两组缩小版的 Q , K , V Q, K, V Q,K,V。
每一个头的维度 d k = 4 / 2 = 2 d_k = 4 / 2 = 2 dk=4/2=2。
- 头 1 (Head 1) :关注"语法"逻辑。它计算出一个 3 × 2 3 \times 2 3×2 的结果矩阵 Z 1 Z_1 Z1。
- 头 2 (Head 2) :关注"情感"逻辑。它计算出另一个 3 × 2 3 \times 2 3×2 的结果矩阵 Z 2 Z_2 Z2。
2. 结果拼接 (Concatenate):
将这两个头各自算出的特征横向拼起来,恢复成完整的 3 × 4 3 \times 4 3×4 矩阵:
Z c o n c a t = [ Z 1 , Z 2 ] Z_{concat} = [Z_1, Z_2] Zconcat=[Z1,Z2]
3. 输出线性映射 (Output Projection):
拼接后的矩阵还不能直接用,必须再乘上一个权重矩阵 W O W_O WO(维度 4 × 4 4 \times 4 4×4),进行最后一次特征融合。这就是论文中提到的"通过一个最终的线性层进行处理" 。
Z M H A = Z c o n c a t ⋅ W O Z_{MHA} = Z_{concat} \cdot W_O ZMHA=Zconcat⋅WO
此时得到的 Z M H A Z_{MHA} ZMHA 依然是 3 × 4 3 \times 4 3×4 的矩阵。
第四步:残差连接与层归一化 (Add & Norm)
模型绝不会直接把 Z M H A Z_{MHA} ZMHA 送进下一步。随着网络加深(比如 32 层),如果只做矩阵乘法,数值会迅速膨胀或衰减(梯度消失/爆炸)。
1. 残差连接 (Add):
将这一层的原始输入 X X X 直接加到刚刚算出的 Z M H A Z_{MHA} ZMHA 上:
X r e s = X + Z M H A X_{res} = X + Z_{MHA} Xres=X+ZMHA
物理意义 :这相当于给信息开了一条"VIP 快速通道"。即使刚才的注意力计算跑偏了,最原始的词义信息 X X X 依然保留着。
2. 层归一化 (Layer Normalization):
这一步针对 X r e s X_{res} Xres 中的每一行(每一个词的向量)独立进行 。我们以第三行("学习"的 4 4 4 维向量)为例,假设 X r e s X_{res} Xres 第三行为 [ 2.0 , 4.0 , 6.0 , 8.0 ] [2.0, 4.0, 6.0, 8.0] [2.0,4.0,6.0,8.0]。
- 求均值 μ \mu μ : 2.0 + 4.0 + 6.0 + 8.0 4 = 5.0 \frac{2.0+4.0+6.0+8.0}{4} = 5.0 42.0+4.0+6.0+8.0=5.0
- 求方差 σ 2 \sigma^2 σ2 : ( 2 − 5 ) 2 + ( 4 − 5 ) 2 + ( 6 − 5 ) 2 + ( 8 − 5 ) 2 4 = 5.0 \frac{(2-5)^2 + (4-5)^2 + (6-5)^2 + (8-5)^2}{4} = 5.0 4(2−5)2+(4−5)2+(6−5)2+(8−5)2=5.0
- 标准化 :用每个元素减去均值,除以标准差 σ 2 + ϵ \sqrt{\sigma^2 + \epsilon} σ2+ϵ ( ϵ \epsilon ϵ 是极小值防除零)。这会把数据拉回均值为 0,方差为 1 的标准分布。
- 缩放与平移 :最后乘以一个可学习的参数 γ \gamma γ,加上参数 β \beta β。
X n o r m = LayerNorm ( X r e s ) X_{norm} = \text{LayerNorm}(X_{res}) Xnorm=LayerNorm(Xres)
经过这套极其严密的缩放, 3 × 4 3 \times 4 3×4 的矩阵 X n o r m X_{norm} Xnorm 的数值变得极其稳定。
第五步:前馈神经网络 (FFN) 的升维打击与降维提取
FFN 包含两个线性操作,先扩大隐藏层维度,再压缩回原始大小。
1. 升维 (Up-Projection):
将 3 × 4 3 \times 4 3×4 的 X n o r m X_{norm} Xnorm 乘以一个巨大的权重矩阵 W 1 W_1 W1(例如维度 4 × 16 4 \times 16 4×16),加上偏置 b 1 b_1 b1:
H 1 = X n o r m ⋅ W 1 + b 1 H_1 = X_{norm} \cdot W_1 + b_1 H1=Xnorm⋅W1+b1
此时,数据从 3 × 4 3 \times 4 3×4 瞬间膨胀成 3 × 16 3 \times 16 3×16。
物理意义:把原本紧凑的 4 维特征投射到极其宽广的 16 维空间,让模型有足够的容量去展开和重组复杂的逻辑。
2. 非线性激活 (Activation):
对 H 1 H_1 H1 里的每一个数字使用激活函数(比如 ReLU,即将小于 0 的数全部变成 0)。
H a c t = max ( 0 , H 1 ) H_{act} = \max(0, H_1) Hact=max(0,H1)
物理意义:这是模型产生"逻辑推理"能力的核心。没有这一步,前面算再多都只是简单的线性叠加。
3. 降维 (Down-Projection):
将 3 × 16 3 \times 16 3×16 的 H a c t H_{act} Hact 乘以第二个权重矩阵 W 2 W_2 W2(维度 16 × 4 16 \times 4 16×4),加上偏置 b 2 b_2 b2:
H 2 = H a c t ⋅ W 2 + b 2 H_2 = H_{act} \cdot W_2 + b_2 H2=Hact⋅W2+b2
数据重新被压缩回 3 × 4 3 \times 4 3×4。随后,再进行一次残差连接与层归一化 :
X o u t = LayerNorm ( X n o r m + H 2 ) X_{out} = \text{LayerNorm}(X_{norm} + H_2) Xout=LayerNorm(Xnorm+H2)
至此,一层 Decoder 的计算才算以极其严谨的数学形式彻底闭环。 这个 X o u t X_{out} Xout 会被送入下一层,重复上述所有计算,直到跑完 32 层。
第六步:最终的词表映射 (预测 "AI")
假设 3 × 4 3 \times 4 3×4 的矩阵终于跑出了最后一层。此时,整句话的终极信息已经完全凝结在了矩阵的最后一行 (因为我们是根据前 3 个词预测第 4 个词)。
我们提取最后一行的一维向量 v l a s t v_{last} vlast(长度为 4)。
模型自带一个极其庞大的词表嵌入矩阵 W v o c a b W_{vocab} Wvocab 。如果你的词表有 100,000 个词,这个矩阵的维度就是 4 × 100 , 000 4 \times 100,000 4×100,000。
我们将 v l a s t v_{last} vlast 与 W v o c a b W_{vocab} Wvocab 相乘:
L o g i t s = v l a s t ⋅ W v o c a b Logits = v_{last} \cdot W_{vocab} Logits=vlast⋅Wvocab
你会得到一个长度为 100,000 的一维数组(Logits)。这里面的每一个数字,代表了词表中对应词的分数。最后通过一次 Softmax 运算,把这 100,000 个分数变成总和为 1 的概率分布。
我们查看到,索引号为 9527(对应单词 "AI")的概率高达 0.82,于是模型输出了 "AI"。
这就是大模型"预填充阶段 (Prefill)" 绝对完整的底层运行轨迹。没有任何跳步,没有任何黑盒。
自回归解码阶段 (Decoding Phase) 就是把"我"、"爱"、"学习"、"AI"这 4 个词重新送进网络预测下一个词。
但真正的挑战现在才开始。按照上面的公式,我们是不是要把这 4 个词的 Q , K , V Q, K, V Q,K,V 重新计算一遍?
那自回归解码阶段 (Decoding Phase) 是如何通过引入 KV Cache 机制来避免这种毁灭性的重复计算,并由此引出必须要解决的核心难题:边缘设备的内存和带宽为何会被 KV Cache 直接撑爆?