一层 LLaMA 到底发生了什么?从 Attention 到 SwiGLU,手把手拆 Transformer Block
前言
在上一篇文章中,我们讲了 Tokenizer、BPE 和 SentencePiece。
若未看过,可顺道移步上篇:大模型不是直接读文字的:一文讲透 Tokenizer、BPE 和 SentencePiece
也就是说,我们已经知道了:
原始文本
↓
Tokenizer
↓
Token IDs
↓
Embedding
↓
向量表示
但是问题来了:
文本变成向量之后,LLaMA 到底是怎么处理这些向量的?
这篇文章就专门解决这个问题。
LLaMA 本质上是一个 Decoder-only Transformer。如果我们把它拆开看,会发现它不是一个神秘黑箱,而是由一层一层的 Transformer Block 堆起来的。
比如:
LLaMA-7B:32 层 Transformer Block
LLaMA-13B:40 层 Transformer Block
LLaMA-33B:60 层 Transformer Block
LLaMA-65B:80 层 Transformer Block
每一层 Transformer Block 主要做两件事:
第一步:Attention
让不同 token 之间交流信息。
第二步:FFN / MLP
让每个 token 对自己的特征进行加工。
如果用一句话概括:
Attention 负责“互相交流”,FFN 负责“独立思考”。
这一篇我们把每个模块拆开讲清楚。本文会重点讲:
- LLaMA 为什么是 Decoder-only Transformer?
- 一个 Transformer Block 的整体结构是什么?
- RMSNorm 是什么?为什么 Pre-Norm 更稳定?
- Q、K、V 是什么?Attention 到底怎么算?
- Causal Attention 为什么不能看未来?
- RoPE 为什么能表示相对位置?
- FFN / MLP 是什么?为什么要升维再降维?
- SwiGLU 门控机制是怎么工作的?
- 一个完整 LLaMA Block 的公式是什么?
- 这些结构和后续 VLM 有什么关系?
一、先看整体:一层 LLaMA Block 长什么样?
先不要急着看公式,我们先看一层 LLaMA Transformer Block 的整体流程。
输入 x
│
│
▼
RMSNorm
│
▼
Causal Multi-Head Self-Attention
│
▼
Residual Add
│
得到 h
│
│
▼
RMSNorm
│
▼
SwiGLU FFN / MLP
│
▼
Residual Add
│
得到 y
如果写成更直观的结构:
x
├─────────────── 残差连接 ───────────────┐
│ │
RMSNorm │
│ │
Causal Multi-Head Attention │
│ │
└────────────── Add ◄───────────────────┘
│
h
├─────────────── 残差连接 ───────────────┐
│ │
RMSNorm │
│ │
SwiGLU FFN │
│ │
└────────────── Add ◄───────────────────┘
│
y
这一层做完之后,输出 (y) 会进入下一层 Transformer Block。
所以 LLaMA 的完整模型可以理解成:
Token Embedding
↓
Transformer Block 1
↓
Transformer Block 2
↓
Transformer Block 3
↓
...
↓
Transformer Block N
↓
输出层
↓
预测下一个 token
二、LLaMA 是 Decoder-only Transformer
先解释这个名字。
2.1 Transformer 是什么?
Transformer 是一种基于 Attention 机制的神经网络结构。
它最核心的特点是:
不像 RNN 那样一个词一个词顺序处理,而是通过 Attention 直接建模 token 之间的关系。
比如一句话:
The cat sat on the mat.
Transformer 可以让 cat 直接和 sat、mat、The 等 token 建立联系。
对于该部分不熟悉的看客,可移步此篇博文:
结合 I love you 彻底读懂 Transformer:从输入表示到概率输出
2.2 Decoder-only 是什么?
Transformer 原始结构有两部分:
Encoder
Decoder
BERT 用的是 Encoder-only。
GPT、LLaMA 用的是 Decoder-only。
Decoder-only 的意思是:
模型只使用 Transformer Decoder 堆叠,通过自回归方式预测下一个 token。
这里的 自回归 Autoregressive,意思是:
预测当前位置时,只能依赖前面的 token,不能偷看后面的 token。
比如输入:
I love machine
模型要预测下一个 token:
learning
它只能看:
I love machine
不能提前看到真实答案 learning。
2.3 LLaMA 的训练目标是什么?
LLaMA 的训练目标是 next-token prediction,也就是预测下一个 token。
语言模型训练 loss 通常写成:
L L M = − ∑ t = 1 T log p θ ( x t ∣ x < t ) \mathcal{L}_{LM} = -\sum_{t=1}^{T}\log p_\theta(x_t \mid x_{<t}) LLM=−t=1∑Tlogpθ(xt∣x<t)
这个公式必须慢慢拆开。
其中:
- ( L L M \mathcal{L}_{LM} LLM):语言模型的损失函数,loss 越小越好;
- (T):当前训练序列的长度;
- (t):序列中的第 (t) 个位置;
- ( x t x_t xt):第 (t) 个真实 token;
- ( x < t x_{<t} x<t):第 (t) 个 token 前面的所有 token;
- ( p θ ( x t ∣ x < t ) p_\theta(x_t \mid x_{<t}) pθ(xt∣x<t)):模型在看到前文 (x_{<t}) 后,预测真实 token (x_t) 的概率;
- ( θ \theta θ):模型参数;
- ( log \log log):对概率取对数;
- 负号 (-):因为我们希望真实 token 的概率越大越好,而训练优化器通常是最小化 loss。
如果模型把真实 token 的概率预测得很高,那么:
p 越大
log p 越接近 0
-loss 越小
如果模型把真实 token 的概率预测得很低,那么:
p 越小
log p 是很大的负数
-loss 就很大
所以这个公式的直观含义是:
模型要尽可能提高真实下一个 token 的概率。
三、输入 x 是什么?
在进入 Transformer Block 之前,文本已经经过了 tokenizer 和 embedding。
假设输入句子是:
I love machine learning.
Tokenizer 得到 token IDs:
[306, 5360, 4933, 6509, 29889]
Embedding 层把每个 token ID 变成向量。
Embedding 公式是:
x i = E [ t i ] x_i = E[t_i] xi=E[ti]
其中:
- ( t i t_i ti):第 (i) 个 token 的 ID;
- ( E E E):embedding 矩阵,是模型的可学习参数;
- ( E [ t i ] E[t_i] E[ti]):从 embedding 矩阵中取出第 ( t i t_i ti) 行;
- ( x i x_i xi):第 (i) 个 token 对应的向量表示。
如果 hidden size 是 4096,那么每个 token 会变成一个 4096 维向量。
假设序列长度是 5,那么输入矩阵形状是:
[5, 4096]
一般写成:
X ∈ R^{seq_len × d_model}
其中:
- (X):整个序列的 hidden states;
- (seq_len):序列长度;
- ( d _ m o d e l d\_{model} d_model):模型 hidden dimension,比如 LLaMA-7B 是 4096;
- ( R { s e q _ l e n × d _ m o d e l } R^{}\{seq\_len \times d\_model\} R{seq_len×d_model}):表示这是一个形状为 ( s e q _ l e n × d _ m o d e l seq\_len \times d\_{model} seq_len×d_model) 的实数矩阵。
四、RMSNorm:进入子层之前先稳定一下
在 LLaMA 中,每个子模块前都会先做 RMSNorm。
也就是说,LLaMA 用的是 Pre-Norm 结构。
4.1 Norm 是什么?
Norm 是 normalization,中文叫 归一化。
它的作用是:
控制向量的数值尺度,让模型训练更稳定。
大模型有很多层。如果每一层输出的数值忽大忽小,后面的层会很难训练。
归一化就像是在提醒模型:
别让激活值太大,也别让它太小。
4.2 Pre-Norm 和 Post-Norm 的区别
原始 Transformer 更接近 Post-Norm:
x → Sublayer → Add → Norm
公式是:
y = Norm ( x + F ( x ) ) y = \text{Norm}(x + F(x)) y=Norm(x+F(x))
其中:
- (x):当前层输入;
- (F(x)):子层计算结果,可以是 Attention,也可以是 FFN;
- (x + F(x)):残差连接;
- ( Norm ( ⋅ ) \text{Norm}(\cdot) Norm(⋅)):归一化;
- (y):输出。
LLaMA 使用 Pre-Norm:
x → Norm → Sublayer → Add
公式是:
y = x + F ( Norm ( x ) ) y = x + F(\text{Norm}(x)) y=x+F(Norm(x))
其中:
- (x):当前层输入;
- ( Norm ( x ) \text{Norm}(x) Norm(x)):先对输入做归一化;
- ( F ( Norm ( x ) ) F(\text{Norm}(x)) F(Norm(x))):把归一化后的结果送入子层;
- ( x + F ( Norm ( x ) ) x + F(\text{Norm}(x)) x+F(Norm(x))):残差连接;
- (y):输出。
4.3 为什么 Pre-Norm 更稳定?
你可能会问:
Post-Norm 的输出不也是下一层的输入吗?为什么 Pre-Norm 更稳?
关键点不是“下一层有没有归一化”,而是:
反向传播时,梯度能不能沿着残差连接稳定传回去。
对于 Pre-Norm:
y = x + F ( Norm ( x ) ) y = x + F(\text{Norm}(x)) y=x+F(Norm(x))
这里有一条很直接的路径:
y → x
也就是说,哪怕 ( F ( Norm ( x ) ) F(\text{Norm}(x)) F(Norm(x))) 这个子层暂时学得不好,(x) 仍然可以通过残差连接直接传递下去。
这条路径很像高速公路。
而 Post-Norm:
y = Norm ( x + F ( x ) ) y = \text{Norm}(x + F(x)) y=Norm(x+F(x))
残差相加后还要经过 Norm。梯度回传时,这条残差路径被 Norm 包住了,不如 Pre-Norm 那么直接。
所以可以这样理解:
Post-Norm:残差路径上有一个 Norm 检查站。
Pre-Norm:残差路径更像一条直通高速路。
大模型层数很深,几十层甚至上百层,训练稳定性非常重要。 所以 LLaMA、GPT 类模型普遍更倾向 Pre-Norm。
4.4 RMSNorm 公式
LLaMA 使用的是 RMSNorm。
RMSNorm 全称是 Root Mean Square Layer Normalization,中文可以叫 均方根归一化。
公式是:
RMSNorm ( x i ) = γ i x i 1 d ∑ j = 1 d x j 2 + ϵ \text{RMSNorm}(x_i) = \gamma_i \frac{x_i}{\sqrt{\frac{1}{d}\sum_{j=1}^{d}x_j^2+\epsilon}} RMSNorm(xi)=γid1∑j=1dxj2+ϵxi
逐个解释:
- ( x = [ x 1 , x 2 , . . . , x d ] x = [x_1, x_2, ..., x_d] x=[x1,x2,...,xd]):一个 token 的 hidden vector;
- ( x i x_i xi):这个向量的第 (i) 个维度;
- ( d d d):hidden dimension,也就是向量维度;
- ( ∑ j = 1 d x j 2 \sum_{j=1}^{d}x_j^2 ∑j=1dxj2):把所有维度的平方加起来;
- ( 1 d ∑ j = 1 d x j 2 \frac{1}{d}\sum_{j=1}^{d}x_j^2 d1∑j=1dxj2):所有维度平方的平均值;
- ( 1 d ∑ j = 1 d x j 2 + ϵ \sqrt{\frac{1}{d}\sum_{j=1}^{d}x_j^2+\epsilon} d1∑j=1dxj2+ϵ):均方根 RMS,( ϵ \epsilon ϵ) 是防止除零的小常数;
- ( γ i \gamma_i γi):第 (i) 个维度的可学习缩放参数;
- ( RMSNorm ( x i ) \text{RMSNorm}(x_i) RMSNorm(xi)):归一化后的第 (i) 个维度。
一句话理解:
RMSNorm 不减均值,只按向量整体大小做缩放。
它和 LayerNorm 的区别是:
LayerNorm:减均值 + 除标准差
RMSNorm:不减均值,只除 RMS
为什么可以不减均值?
因为在很多大模型实践中,真正关键的是控制向量尺度。
只要每层输入的整体大小稳定,训练就已经稳定很多。
五、Self-Attention:token 之间如何交流?
接下来进入最核心的模块:Self-Attention。
5.1 Self-Attention 是什么?
Attention,中文叫 注意力机制。
它解决的问题是:
当前 token 应该关注序列中的哪些 token?
比如句子:
The animal didn't cross the street because it was too tired.
这里的 it 指的是谁?
很可能是:
animal
模型需要让 it 去关注前面的 animal,而不是只看自己。
Attention 就是做这件事的。
5.2 Q、K、V 是什么?
Self-Attention 里,每个 token 会生成三个向量:
Q:Query
K:Key
V:Value
先解释名字:
Query,查询向量:我想找什么信息?
Key,键向量:我有什么特征可以被别人匹配?
Value,值向量:如果别人关注我,我提供什么信息?
它们由输入 (X) 乘以三个可学习矩阵得到:
Q = X W Q Q = XW_Q Q=XWQ
K = X W K K = XW_K K=XWK
V = X W V V = XW_V V=XWV
逐个解释:
- (X):当前输入序列的 hidden states,形状通常是 ( [ s e q _ l e n , d _ m o d e l ] [seq\_len, d\_{model}] [seq_len,d_model]);
- ( W Q W_Q WQ):Query 投影矩阵,是可学习参数;
- ( W K W_K WK):Key 投影矩阵,是可学习参数;
- ( W V W_V WV):Value 投影矩阵,是可学习参数;
- (Q):所有 token 的 Query 向量;
- (K):所有 token 的 Key 向量;
- (V):所有 token 的 Value 向量。
注意这里有一个很容易混的点:
W Q 、 W K 、 W V W_Q、W_K、W_V WQ、WK、WV 是训练出来的参数。
Q、K、V 是根据当前输入动态算出来的结果。
推理时,参数矩阵固定。
但是不同输入会产生不同的 Q、K、V。
5.3 Attention 分数怎么算?
Attention 的核心是计算 Query 和 Key 的相似度。
公式是:
Score = Q K T \text{Score} = QK^T Score=QKT
解释:
- (Q):Query 矩阵;
- (K):Key 矩阵;
- ( K T K^T KT):Key 的转置;
- ( Q K T QK^T QKT):每个 query 和每个 key 的点积相似度;
- ( Score \text{Score} Score):注意力分数矩阵。
如果序列长度是 4,那么 (QK^T) 会得到一个 (4 \times 4) 的矩阵:
key1 key2 key3 key4
query1 . . . .
query2 . . . .
query3 . . . .
query4 . . . .
每个位置表示:
当前 token 对另一个 token 的关注程度
5.4 为什么要除以 ( d k \sqrt{d_k} dk)?
Attention 公式里不是直接用 ( Q K T QK^T QKT),而是:
Q K T d k \frac{QK^T}{\sqrt{d_k}} dkQKT
其中:
- ( d k d_k dk):Key/Query 向量的维度;
- ( d k \sqrt{d_k} dk):对 ( d k d_k dk) 开平方;
- 除以 ( d k \sqrt{d_k} dk):为了防止点积值太大。
为什么点积值会太大?
如果 Q 和 K 的维度很高,点积累加很多项,数值可能变大。
如果分数太大,softmax 会变得非常尖锐,导致梯度不稳定。
所以除以 ( d k \sqrt{d_k} dk) 是一种缩放。
这就是 Scaled Dot-Product Attention,缩放点积注意力。
5.5 Softmax 是什么?
Attention 分数还需要经过 softmax。
Softmax 的作用是:
把一组分数变成概率分布。
比如某个 token 对其他 token 的原始分数是:
[2.0, 1.0, 0.1]
softmax 后可能变成:
[0.66, 0.24, 0.10]
这些值加起来等于 1,表示关注比例。
Softmax 公式是:
softmax ( z i ) = e z i ∑ j = 1 n e z j \text{softmax}(z_i) = \frac{e^{z_i}}{\sum_{j=1}^{n}e^{z_j}} softmax(zi)=∑j=1nezjezi
逐个解释:
- ( z i z_i zi):第 (i) 个原始分数;
- ( e z i e^{z_i} ezi):对第 (i) 个分数取指数;
- ( n n n):这一组分数的数量;
- ( ∑ j = 1 n e z j \sum_{j=1}^{n}e^{z_j} ∑j=1nezj):所有分数指数值的总和;
- ( softmax ( z i ) \text{softmax}(z_i) softmax(zi)):第 (i) 个分数对应的归一化概率。
5.6 完整 Attention 公式
完整的 Attention 公式是:
A t t e n t i o n = softmax ( Q K T d k ) V Attention = \text{softmax} \left( \frac{QK^T}{\sqrt{d_k}} \right) V Attention=softmax(dkQKT)V
逐个解释:
- (Q):Query 矩阵,表示每个 token 想查询什么;
- (K):Key 矩阵,表示每个 token 有什么可被匹配的特征;
- (V):Value 矩阵,表示每个 token 真正提供的信息;
- ( K T K^T KT):Key 矩阵的转置;
- ( Q K T QK^T QKT):Query 和 Key 的相似度矩阵;
- ( d k d_k dk):Query/Key 的维度;
- ( d k \sqrt{d_k} dk):缩放因子,防止分数过大;
- ( softmax ( ⋅ ) \text{softmax}(\cdot) softmax(⋅)):把相似度分数转成注意力权重;
- 最后乘以 (V):根据注意力权重,对 Value 做加权求和。
一句话理解:
Attention 先算“我应该关注谁”,再根据关注权重把别人的信息汇总过来。
六、Causal Attention:为什么不能看未来?
LLaMA 是 Decoder-only 自回归模型,所以 Attention 必须是 causal 的。
Causal,中文叫 因果的。
意思是:
当前 token 只能看自己和前面的 token,不能看后面的 token。
6.1 为什么不能看未来?
因为训练任务是预测下一个 token。
假设训练序列是:
I love machine learning
模型在预测 machine 时,只能看:
I love
不能提前看到:
machine learning
如果它能看到未来,就相当于考试时偷看答案。
所以必须加 causal mask。
6.2 Causal Mask 长什么样?
假设序列有 4 个 token:
t1 t2 t3 t4
普通 self-attention 理论上每个 token 都能看所有 token:
t1 t2 t3 t4
t1 ✓ ✓ ✓ ✓
t2 ✓ ✓ ✓ ✓
t3 ✓ ✓ ✓ ✓
t4 ✓ ✓ ✓ ✓
但 causal attention 是:
t1 t2 t3 t4
t1 ✓ ✗ ✗ ✗
t2 ✓ ✓ ✗ ✗
t3 ✓ ✓ ✓ ✗
t4 ✓ ✓ ✓ ✓
只能看左边和自己,不能看右边。
这就是下三角矩阵。
6.3 Mask 在公式里怎么体现?
Attention 原始分数是:
S = Q K T d k S = \frac{QK^T}{\sqrt{d_k}} S=dkQKT
其中:
- (S):attention score matrix;
- ( Q K T QK^T QKT):Q 和 K 的点积相似度;
- ( d k d_k dk):Q/K 的维度。
加 causal mask 后:
S ′ = S + M S' = S + M S′=S+M
其中:
- (S’):加 mask 后的分数;
- (S):原始 attention 分数;
- (M):mask 矩阵。
对于允许看的位置,(M) 取 0。
对于不允许看的未来位置,(M) 取一个非常大的负数,比如 ( − ∞ -\infty −∞)。
也就是:
M i j = { 0 if j ≤ i − ∞ if j > i M_{ij} = \begin{cases} 0 & \text{if } j \leq i \\ -\infty & \text{if } j > i\end{cases} Mij={0−∞if j≤iif j>i
逐个解释:
- (i):当前 query 的位置;
- (j):被看的 key 的位置;
- ( j ≤ i j \leq i j≤i):说明被看的 token 在当前位置之前或就是自己,可以看;
- (j > i):说明被看的 token 在未来,不能看;
- ( M i j M_{ij} Mij):mask 矩阵第 (i,j) 个位置的值。
为什么用 ( − ∞ -\infty −∞)?
因为 softmax 之后:
e^{-∞} ≈ 0
也就是说,未来 token 的注意力权重会变成 0。
七、Multi-Head Attention:为什么要多头?
LLaMA 用的是 Multi-Head Attention,多头注意力。
先解释:
Head,可以理解成一组独立的注意力视角。
一个 head 可能关注语法关系。
一个 head 可能关注实体关系。
一个 head 可能关注长距离依赖。
一个 head 可能关注标点和格式。
当然,这只是帮助理解,不是每个 head 都能被严格解释成一个固定功能。
7.1 多头注意力公式
单个 head 的计算是:
head i = Attention ( Q i , K i , V i ) \text{head}_i = \text{Attention}(Q_i,K_i,V_i) headi=Attention(Qi,Ki,Vi)
其中:
- ( head i \text{head}_i headi):第 (i) 个注意力头的输出;
- ( Q i Q_i Qi):第 (i) 个头对应的 Query;
- ( K i K_i Ki):第 (i) 个头对应的 Key;
- ( V i V_i Vi):第 (i) 个头对应的 Value;
- ( Attention ( ⋅ ) \text{Attention}(\cdot) Attention(⋅)):缩放点积注意力计算。
多个 head 拼接起来:
MultiHead ( Q , K , V ) = Concat ( head 1 , head 2 , . . . , head h ) W O \text{MultiHead}(Q,K,V) = \text{Concat}(\text{head}_1,\text{head}_2,...,\text{head}_h)W_O MultiHead(Q,K,V)=Concat(head1,head2,...,headh)WO
逐个解释:
- (h):head 的数量;
- ( head 1 , head 2 , . . . , head h \text{head}_1,\text{head}_2,...,\text{head}_h head1,head2,...,headh):不同注意力头的输出;
- ( Concat ( ⋅ ) \text{Concat}(\cdot) Concat(⋅)):把多个 head 的输出在维度上拼接起来;
- ( W O W_O WO):输出投影矩阵,是可学习参数;
- ( MultiHead ( Q , K , V ) \text{MultiHead}(Q,K,V) MultiHead(Q,K,V)):多头注意力最终输出。
为什么最后要乘 ( W O W_O WO)?
因为多个 head 拼接后,需要再通过一个线性层融合不同 head 的信息,并投影回模型的 hidden dimension。
八、RoPE:旋转位置编码
Attention 本身有一个问题:
它不知道 token 的顺序。
如果不加位置信息,Transformer 很难区分:
我 爱 机器 学习
和:
学习 机器 爱 我
所以必须加入位置编码。
LLaMA 使用的是 RoPE。
8.1 RoPE 是什么?
RoPE 全称是 Rotary Position Embedding,中文叫 旋转位置编码。
它的核心思想是:
不直接把位置向量加到 token embedding 上,而是在计算 Attention 时,对 Q 和 K 做旋转,让 Q 和 K 的点积自然包含相对位置信息。
注意:
RoPE 作用在 Q 和 K 上
一般不作用在 V 上
8.2 二维旋转公式
先看最基础的二维旋转。
给定二维向量:
[ x 1 x 2 ] \begin{bmatrix} x_1 \ x_2 \end{bmatrix} [x1 x2]
旋转角度 (\theta) 后:
x 1 ′ , x 2 ′ = [ cos θ − sin θ sin θ cos θ ] [ x 1 x 2 ] x_1', x_2' = \begin{bmatrix} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{bmatrix} \begin{bmatrix} x_1\ x_2 \end{bmatrix} x1′,x2′=[cosθsinθ−sinθcosθ][x1 x2]
逐个解释:
- ( x 1 , x 2 x_1, x_2 x1,x2):旋转前二维向量的两个坐标;
- ( x 1 ′ , x 2 ′ x_1', x_2' x1′,x2′):旋转后的两个坐标;
- ( θ \theta θ):旋转角度;
- ( cos θ \cos\theta cosθ)、( sin θ \sin\theta sinθ):三角函数;
- 中间的 ( 2 × 2 2 \times 2 2×2) 矩阵:旋转矩阵。
这个公式表达的意思是:
向量长度不变,但方向发生旋转。
8.3 RoPE 如何作用在高维向量上?
真实模型里的 Q、K 不是二维,而是高维向量。
假设一个 head 的维度是 8:
[x1, x2, x3, x4, x5, x6, x7, x8]
RoPE 会把它两两分组:
(x1, x2)
(x3, x4)
(x5, x6)
(x7, x8)
每一组都当成一个二维向量进行旋转。
但是不同组使用不同频率。
位置为 (m) 的 token,第 (i) 组使用的旋转角度大致是:
m θ i m\theta_i mθi
其中:
- (m):token 的位置;
- (i):第 (i) 个二维维度组;
- ( θ i \theta_i θi):第 (i) 组对应的旋转频率。
通常:
θ i = 10000 − 2 i / d \theta_i = 10000^{-2i/d} θi=10000−2i/d
逐个解释:
- ( θ i \theta_i θi):第 (i) 个维度组的基础频率;
- (i):维度组编号;
- (d):当前 attention head 的维度;
- (10000):一个常用的频率基数;
- ( − 2 i / d -2i/d −2i/d):让不同维度组拥有不同频率。
简单理解:
有些维度转得快,用来感知短距离。
有些维度转得慢,用来感知长距离。
就像钟表:
秒针转得快
分针转得慢
时针转得更慢
多个频率组合起来,就能表达不同尺度的位置关系。
8.4 RoPE 为什么能表示相对位置?
Attention 分数本来是:
q m T k n q_m^T k_n qmTkn
其中:
- ( q m q_m qm):位置 (m) 的 Query;
- ( k n k_n kn):位置 (n) 的 Key;
- ( q m T k n q_m^T k_n qmTkn):二者的点积相似度。
RoPE 会先对它们按位置旋转:
q m ′ = R m q m q_m' = R_m q_m qm′=Rmqm
k n ′ = R n k n k_n' = R_n k_n kn′=Rnkn
其中:
- ( R m R_m Rm):位置 (m) 对应的旋转矩阵;
- ( R n R_n Rn):位置 (n) 对应的旋转矩阵;
- ( q m ′ q_m' qm′):旋转后的 Query;
- ( k n ′ k_n' kn′):旋转后的 Key。
然后计算:
( R m q m ) T ( R n k n ) (R_m q_m)^T(R_n k_n) (Rmqm)T(Rnkn)
旋转矩阵有一个重要性质:
( R m q m ) T ( R n k n ) = q m T R n − m k n (R_m q_m)^T(R_n k_n) =q_m^T R_{n-m} k_n (Rmqm)T(Rnkn)=qmTRn−mkn
这个公式很关键。
逐个解释:
- 左边 ( ( R m q m ) T ( R n k n ) (R_m q_m)^T(R_n k_n) (Rmqm)T(Rnkn)):位置 (m) 的 Query 和位置 (n) 的 Key 分别旋转后再点积;
- 右边 ( q m T R n − m k n q_m^T R_{n-m} k_n qmTRn−mkn):最终效果和 (n-m) 有关;
- (n-m):两个 token 的相对距离。
所以 RoPE 的核心优势是:
表面上给 Q、K 注入的是绝对位置,点积之后体现出来的是相对位置差。
这就是为什么 RoPE 很适合自回归语言模型。
8.5 旋转一圈会不会重合?
这个问题非常关键。
答案是:
单个频率确实可能重合,但 RoPE 使用多组不同频率,整体不容易在正常上下文长度内完全重合。
二维旋转有周期性:
cos ( θ ) = cos ( θ + 2 π ) \cos(\theta)=\cos(\theta+2\pi) cos(θ)=cos(θ+2π)
sin ( θ ) = sin ( θ + 2 π ) \sin(\theta)=\sin(\theta+2\pi) sin(θ)=sin(θ+2π)
这说明如果只看一个二维平面,旋转一圈确实会回到原方向。
但是 RoPE 不是只有一个二维平面。它是很多二维组,每组频率不同。
想让整个高维向量完全重合,需要所有频率同时回到相同状态。
这就像多个不同速度的指针同时回到同一个位置,周期会非常长。
所以可以这样记:
单个频率:会周期重复。
多个频率组合:整体重复周期大大拉长。
RoPE 主要服务于相对位置建模,而不是给每个位置发唯一身份证。
不过 RoPE 也不是完美的。
当序列长度远远超过训练长度时,RoPE 可能出现长上下文外推问题。后来的很多长上下文模型会使用 RoPE scaling、NTK-aware scaling、YaRN 等方法改进它。
这部分可以以后在长上下文模型里专门讲。
九、Residual Connection:残差连接
Attention 算完后,LLaMA 不会直接把 Attention 输出作为最终结果,而是会和原输入相加。
公式是:
h = x + Attention ( RMSNorm ( x ) ) h = x + \text{Attention}(\text{RMSNorm}(x)) h=x+Attention(RMSNorm(x))
逐个解释:
- (x):当前 Transformer Block 的输入;
- ( RMSNorm ( x ) \text{RMSNorm}(x) RMSNorm(x)):先对输入做 RMSNorm;
- ( Attention ( RMSNorm ( x ) ) \text{Attention}(\text{RMSNorm}(x)) Attention(RMSNorm(x))):Attention 子层的输出;
- ( x + Attention ( RMSNorm ( x ) ) x + \text{Attention}(\text{RMSNorm}(x)) x+Attention(RMSNorm(x))):残差连接;
- (h):Attention 子层之后的输出。
残差连接的作用是:
保留原始信息
稳定梯度传播
让深层网络更容易训练
可以简单理解为:
子层学到的是对原输入的“补充修改”,而不是完全重写原输入。
十、FFN / MLP:每个 token 自己的“思考”
Attention 之后,token 之间已经交流过信息。
接下来进入 FFN / MLP。
10.1 FFN 是什么?
FFN 全称是 Feed-Forward Network,中文叫 前馈神经网络。
在 Transformer 里,它通常指:
对每个 token 的 hidden vector 独立进行非线性加工的模块。
它不是单纯的激活层,而是一个完整的小网络。
普通 FFN 公式是:
FFN ( x ) = W 2 σ ( W 1 x ) \text{FFN}(x)=W_2\sigma(W_1x) FFN(x)=W2σ(W1x)
逐个解释:
- (x):某个 token 的 hidden vector;
- ( W 1 W_1 W1):第一层线性变换矩阵,通常负责升维;
- ( σ \sigma σ):激活函数,比如 ReLU、GELU、SiLU;
- ( W 2 W_2 W2):第二层线性变换矩阵,通常负责降维;
- ( FFN ( x ) \text{FFN}(x) FFN(x)):FFN 的输出。
流程是:
x
↓
Linear 升维
↓
Activation 非线性激活
↓
Linear 降维
↓
输出
10.2 MLP 是什么?
MLP 全称是 Multi-Layer Perceptron,中文叫 多层感知机。
它是最基础的神经网络结构:
Linear
↓
Activation
↓
Linear
在 Transformer 语境里,很多论文会把 FFN 和 MLP 混着叫。
更准确地说:
MLP:通用神经网络名字。
FFN:Transformer 里那个 position-wise 的 MLP 子层。
所以:
Transformer 里的 FFN 本质上就是一个对每个 token 独立应用的 MLP。
10.3 position-wise 是什么意思?
position-wise,可以翻译成 按位置独立处理。
这里的 position 指的是 token 在序列中的位置。
假设序列有 4 个 token:
t1, t2, t3, t4
它们的 hidden states 是:
x1, x2, x3, x4
FFN 做的是:
FFN(x1)
FFN(x2)
FFN(x3)
FFN(x4)
它不会在 FFN 里直接让 (x1) 和 (x3) 交流信息。
token 之间的交流主要发生在 Attention 里。
FFN 是对每个 token 自己的表示做加工。
但是注意:
每个位置使用的是同一套 FFN 参数。
不是:
x1 用 FFN1
x2 用 FFN2
x3 用 FFN3
而是:
x1、x2、x3、x4 都用同一个 FFN
所以 position-wise 的完整意思是:
每个 token 独立通过 FFN,但所有 token 共享同一套 FFN 参数。
10.4 为什么 FFN 要升维?
普通 Transformer FFN 会先升维,再降维。
比如:
4096 → 11008 → 4096
为什么要这样做?
因为如果只是:
4096 → 4096
表达能力有限。
升维可以理解为:
临时把 token 表示投影到更大的特征空间,让模型有更多维度去展开、组合、筛选信息。
类比一下:
原来只有 4096 个抽屉放信息。
升维后临时打开 11008 个抽屉。
模型可以把信息拆得更细、更丰富。
最后再压回 4096 维,继续进入下一层。
十一、SwiGLU:LLaMA 的门控 FFN
LLaMA 没有使用最普通的 ReLU FFN,而是使用了 SwiGLU。
11.1 Gate 是什么?
Gate,中文叫 门控机制。
它的核心思想是:
不是什么信息都无条件通过,而是让模型学习一个“门”,决定哪些特征通过,哪些特征被抑制。
举个简单例子:
候选信息 u = [10, 5, -3, 8]
门控值 g = [0.9, 0.1, 0.0, 0.7]
逐元素相乘:
u ⊙ g = [9, 0.5, 0, 5.6]
这表示:
第 1 个特征:强烈保留
第 2 个特征:大幅削弱
第 3 个特征:关掉
第 4 个特征:部分保留
11.2 GLU 是什么?
GLU 全称是 Gated Linear Unit,中文叫 门控线性单元。
普通 FFN 是一条路:
x → Linear → Activation → Linear
GLU 变成两条路:
一路生成候选信息
一路生成门控值
然后相乘
GLU 的经典公式是:
GLU ( x ) = ( x W a ) ⊙ σ ( x W b ) \text{GLU}(x) = (xW_a)\odot\sigma(xW_b) GLU(x)=(xWa)⊙σ(xWb)
逐个解释:
- (x):输入向量;
- ( W a W_a Wa):候选信息投影矩阵,是可学习参数;
- ( x W a xW_a xWa):候选信息;
- ( W b W_b Wb):门控投影矩阵,是可学习参数;
- ( x W b xW_b xWb):门控分支的线性输出;
- ( σ ( ⋅ ) \sigma(\cdot) σ(⋅)):sigmoid 函数,把值压到 0 到 1;
- ( ⊙ \odot ⊙):逐元素相乘;
- ( GLU ( x ) \text{GLU}(x) GLU(x)):门控后的输出。
这里的关键是:
W_a 和 W_b 是参数。
xW_a 和 σ(xW_b) 是根据当前输入算出来的动态结果。
11.3 SiLU / Swish 是什么?
SwiGLU 里的 “Swi” 来自 Swish,也常叫 SiLU。
SiLU 公式是:
SiLU ( z ) = z ⋅ σ ( z ) \text{SiLU}(z)=z\cdot\sigma(z) SiLU(z)=z⋅σ(z)
逐个解释:
- (z):输入值;
- ( σ ( z ) \sigma(z) σ(z)):sigmoid 函数;
- ( z ⋅ σ ( z ) z\cdot\sigma(z) z⋅σ(z)):输入 (z) 乘以一个 0 到 1 之间的门控值;
- ( SiLU ( z ) \text{SiLU}(z) SiLU(z)):SiLU 激活后的结果。
Sigmoid 公式是:
σ ( z ) = 1 1 + e − z \sigma(z)=\frac{1}{1+e^{-z}} σ(z)=1+e−z1
逐个解释:
- (z):输入值;
- ( e − z e^{-z} e−z):自然指数;
- ( 1 + e − z 1+e^{-z} 1+e−z):分母;
- ( σ ( z ) \sigma(z) σ(z)):输出范围在 0 到 1 之间。
SiLU 比 ReLU 更平滑。
ReLU 是:
ReLU ( z ) = max ( 0 , z ) \text{ReLU}(z)=\max(0,z) ReLU(z)=max(0,z)
其中:
- 如果 (z > 0),输出 (z);
- 如果 ( z ≤ 0 z \leq 0 z≤0),输出 0。
ReLU 很简单粗暴。
SiLU 则是平滑地控制信息通过。
11.4 SwiGLU 公式
LLaMA 中的 SwiGLU 可以写成:
SwiGLU ( x ) = W down ( SiLU ( W gate x ) ⊙ W up x ) \text{SwiGLU}(x)= W_{\text{down}} \left( \text{SiLU}(W_{\text{gate}}x) \odot W_{\text{up}}x \right) SwiGLU(x)=Wdown(SiLU(Wgatex)⊙Wupx)
逐个解释:
- (x):输入 token 的 hidden vector;
- ( W gate W_{\text{gate}} Wgate):门控矩阵,是可学习参数;
- ( W gate x W_{\text{gate}}x Wgatex):门控分支的线性输出;
- ( SiLU ( W gate x ) \text{SiLU}(W_{\text{gate}}x) SiLU(Wgatex)):经过 SiLU 激活后的门控值;
- ( W up W_{\text{up}} Wup):升维矩阵,是可学习参数;
- ( W up x W_{\text{up}}x Wupx):候选特征;
- ( ⊙ \odot ⊙):逐元素相乘;
- ( W down W_{\text{down}} Wdown):降维矩阵,是可学习参数;
- ( W down ( ⋅ ) W_{\text{down}}(\cdot) Wdown(⋅)):把中间高维特征投影回 hidden size;
- ( SwiGLU ( x ) \text{SwiGLU}(x) SwiGLU(x)):SwiGLU FFN 的输出。
用流程图表示:
输入 x
│
├── W_gate x → SiLU → 门控值 g
│
└── W_up x → 候选特征 u
│
▼
g ⊙ u
│
▼
W_down
│
▼
输出
11.5 门控矩阵是固定的吗?
训练时不是固定的,训练完后固定。
具体来说:
训练阶段:
W_gate、W_up、W_down 都是可学习参数,会被 optimizer 更新。
推理阶段:
W_gate、W_up、W_down 固定不变。
但是注意:
参数固定,不代表门控值固定。
门控值是:
g = SiLU ( W gate x ) g = \text{SiLU}(W_{\text{gate}}x) g=SiLU(Wgatex)
其中:
- ( W gate W_{\text{gate}} Wgate):训练好的固定参数;
- (x):当前输入 token 的 hidden vector;
- (g):根据当前输入动态算出来的门控值。
不同输入 (x) 不同,得到的 (g) 也不同。
所以:
W_gate 是固定参数。
gate value 是动态结果。
这点非常关键。
11.6 参数和语义是什么关系?
不能简单理解为:
某一个参数 = 某一个语义
比如不能说:
W_gate 里的某个数字就是“金融语义”
另一个数字就是“河岸语义”
更准确的理解是:
语义是分布在大量参数和特征维度组合中的。
这叫 Distributed Representation,分布式表示。
比如单词 bank 有两个意思:
银行
河岸
模型看到:
The bank approved the loan.
Attention 会让 bank 关注 loan、approved 等词。
这时 bank 的 hidden state 会更偏向金融语义。
模型看到:
The bank near the river collapsed.
Attention 会让 bank 关注 river、collapsed 等词。
这时 bank 的 hidden state 会更偏向河岸语义。
然后 FFN/SwiGLU 根据这个上下文表示 (x),动态计算门控值,决定哪些内部特征要保留,哪些要抑制。
所以可以这样理解:
Attention 先根据上下文调整 token 表示。
SwiGLU 再根据这个表示筛选内部特征。
十二、FFN 子层的残差连接
FFN 输出后,也会做残差连接。
公式是:
y = h + FFN ( RMSNorm ( h ) ) y = h + \text{FFN}(\text{RMSNorm}(h)) y=h+FFN(RMSNorm(h))
逐个解释:
- (h):Attention 子层之后的 hidden state;
- ( RMSNorm ( h ) \text{RMSNorm}(h) RMSNorm(h)):进入 FFN 前先做 RMSNorm;
- ( FFN ( RMSNorm ( h ) ) \text{FFN}(\text{RMSNorm}(h)) FFN(RMSNorm(h))):FFN/SwiGLU 子层输出;
- ( h + FFN ( RMSNorm ( h ) ) h + \text{FFN}(\text{RMSNorm}(h)) h+FFN(RMSNorm(h))):残差连接;
- (y):当前 Transformer Block 的最终输出。
这一步和 Attention 子层类似:
FFN 学到的是对 (h) 的补充加工,而不是完全替换 (h)。
十三、一个完整 LLaMA Block 的公式
现在我们把一层 LLaMA Block 串起来。
第一步,Attention 子层:
h = x + Attention ( RMSNorm ( x ) ) h=x+\text{Attention}(\text{RMSNorm}(x)) h=x+Attention(RMSNorm(x))
逐个解释:
- (x):当前 block 的输入;
- ( RMSNorm ( x ) \text{RMSNorm}(x) RMSNorm(x)):对子层输入做归一化;
- ( Attention ( RMSNorm ( x ) ) \text{Attention}(\text{RMSNorm}(x)) Attention(RMSNorm(x))):因果多头自注意力输出;
- ( x + Attention ( RMSNorm ( x ) ) x + \text{Attention}(\text{RMSNorm}(x)) x+Attention(RMSNorm(x))):残差连接;
- (h):Attention 子层后的中间结果。
第二步,FFN 子层:
y = h + FFN ( RMSNorm ( h ) ) y= h + \text{FFN}(\text{RMSNorm}(h)) y=h+FFN(RMSNorm(h))
逐个解释:
- (h):Attention 子层后的中间结果;
- ( RMSNorm ( h ) \text{RMSNorm}(h) RMSNorm(h)):进入 FFN 前先归一化;
- ( FFN ( RMSNorm ( h ) ) \text{FFN}(\text{RMSNorm}(h)) FFN(RMSNorm(h))):SwiGLU FFN 输出;
- ( h + FFN ( RMSNorm ( h ) ) h + \text{FFN}(\text{RMSNorm}(h)) h+FFN(RMSNorm(h))):残差连接;
- (y):当前 block 的最终输出。
所以完整流程就是:
输入 x
↓
RMSNorm(x)
↓
Causal Multi-Head Attention
↓
和 x 残差相加,得到 h
↓
RMSNorm(h)
↓
SwiGLU FFN
↓
和 h 残差相加,得到 y
一句话总结:
LLaMA Block = Pre-Norm Attention + Residual + Pre-Norm SwiGLU FFN + Residual。
十四、用代码伪代码理解一层 LLaMA
下面写一个简化伪代码,帮助理解。
def llama_block(x):
# 1. Attention 子层
norm_x = RMSNorm(x)
attn_out = CausalMultiHeadAttention(norm_x)
h = x + attn_out
# 2. FFN 子层
norm_h = RMSNorm(h)
ffn_out = SwiGLU_FFN(norm_h)
y = h + ffn_out
return y
如果把 Attention 展开一点:
def attention(x):
Q = x @ W_q
K = x @ W_k
V = x @ W_v
Q = RoPE(Q)
K = RoPE(K)
scores = Q @ K.T / sqrt(d_k)
scores = scores + causal_mask
weights = softmax(scores)
out = weights @ V
return out @ W_o
如果把 SwiGLU 展开一点:
def swiglu_ffn(x):
gate = SiLU(x @ W_gate)
up = x @ W_up
hidden = gate * up
out = hidden @ W_down
return out
注意这里的 @ 表示矩阵乘法,* 表示逐元素相乘。
十五、这些模块各自负责什么?
我们最后把所有模块放到一张表里。
| 模块 | 中文名 | 作用 |
|---|---|---|
| Tokenizer | 分词器 | 把文本变成 token ID |
| Embedding | 嵌入层 | 把 token ID 变成向量 |
| RMSNorm | 均方根归一化 | 控制向量尺度,稳定训练 |
| Pre-Norm | 前归一化 | 让残差路径更稳定 |
| Q/K/V | 查询/键/值 | Attention 中的信息匹配与汇总 |
| Causal Attention | 因果注意力 | 当前 token 只能看过去,不能看未来 |
| Multi-Head Attention | 多头注意力 | 从多个子空间建模 token 关系 |
| RoPE | 旋转位置编码 | 让 Attention 感知相对位置 |
| Residual | 残差连接 | 保留原信息,稳定梯度 |
| FFN / MLP | 前馈网络/多层感知机 | 对每个 token 的特征进行非线性加工 |
| SwiGLU | 门控 FFN | 用门控机制筛选特征 |
十六、新手常见误区
误区一:FFN 就是激活层
不对。
FFN 不是单纯激活层,而是:
Linear 升维 + 激活/门控 + Linear 降维
激活函数只是 FFN 里面的一步。
误区二:position-wise 表示每个位置有独立参数
不对。
position-wise 的意思是:
每个 token 独立处理
但所有 token 共享同一套 FFN 参数
误区三:门控矩阵推理时还会变
不对。
推理时门控矩阵参数固定。
但门控值会根据输入动态变化。
也就是:
W_gate 固定
SiLU(W_gate x) 动态变化
误区四:RoPE 是给每个位置一个绝对编号
不完全对。
RoPE 表面上根据绝对位置旋转 Q/K,
但在 Q/K 点积中体现为相对位置差。
它更重要的作用是:
让 attention score 感知 token 之间的距离
十七、总结
这一篇我们从一层 LLaMA Transformer Block 出发,拆解了它的核心组件。
LLaMA 的一层可以写成两个公式:
h = x + Attention ( RMSNorm ( x ) ) h= x + \text{Attention}(\text{RMSNorm}(x)) h=x+Attention(RMSNorm(x))
y = h + FFN ( RMSNorm ( h ) ) y= h + \text{FFN}(\text{RMSNorm}(h)) y=h+FFN(RMSNorm(h))
其中:
- (x):当前 block 输入;
- (h):Attention 后的中间结果;
- (y):当前 block 输出;
- ( RMSNorm \text{RMSNorm} RMSNorm):控制输入尺度;
- ( Attention \text{Attention} Attention):让 token 之间交流;
- ( FFN \text{FFN} FFN):对每个 token 的特征进行加工;
- 残差连接:保留原始信息,稳定训练。
更具体地说:
RMSNorm:稳定训练
Attention:token 间交流
Causal Mask:防止偷看未来
RoPE:注入相对位置信息
Residual:保留原信息
FFN/MLP:单 token 特征加工
SwiGLU:门控筛选特征
一句话总结:
LLaMA 的 Transformer Block 并不神秘,它就是让 token 先通过 Attention 和上下文交流,再通过 SwiGLU FFN 对自身表示进行加工,最后通过残差连接稳定地传递到下一层。
到这里,我们已经理解了 LLaMA 的结构基础。
下一篇我们继续讲训练部分:
LLaMA 为什么小而强?从 Chinchilla Scaling Law 到 AdamW,一文讲清大模型训练配方。
更多推荐



所有评论(0)