最近在折腾项目的时候碰到了这个知识点,查了不少资料,索性整理出来分享给大家。
目录
1.1 Encoder 和 Decoder 最初分别负责什么? 二、什么是 Masked Self-Attention? 三、Causal Mask:把未来的信息遮住 四、Mask 到底是怎么加进 Attention 里的?五、为什么一定要使用 -∞?六、为什么这种 Mask 是一个三角矩阵?七、Decoder 的 Self-Attention 到底解决了什么坑?八、什么是 Autoregressive Generation?九、训练时和生成时有什么区别? 十、Decoder 中为什么还有 Cross-Attention?十一、Cross-Attention 和 Self-Attention 有什么区别?十二、GPT 为什么没有 Encoder 和 Cross-Attention?十三、从输入到下一个 token:完整过程十四、为什么模型输出的不是“一个答案”,而是一组概率?十五、Greedy、Sampling:下一个 token 到底怎么选?十六、一个完整的自回归生成例子十七、训练语言模型时,模型到底在学习什么?十八、为什么 Causal Mask 对训练也非常重要?十九、现在重新理解 Decoder二十、Encoder-Decoder Transformer 与 GPT 的区别二十一、从“Transformer Block”到“语言模型”二十二、这一篇真正需要记住什么?二十三、下一篇:从 Transformer 到 GPT,大语言模型是怎么来的?前言
前六篇文章,我们已经沿着 Transformer 的核心结构一步一步地走了下来。
第一篇,我们从 RNN、LSTM 的局限出发,理解了为什么需要 Transformer。
第二篇,我们从整体结构上认识了 Encoder、Decoder,以及 Transformer 的模块化设计。
第三篇,我们深入 Self-Attention,理解了 Query、Key、Value,以及注意力分数是如何计算出来的。
第四篇,我们进一步理解 Multi-Head Attention,了解了为什么 Transformer 不只使用一个注意力头。
第五篇,我们解决了 Self-Attention 无法天然感知顺序的坑,认识了 Positional Encoding,以及现代模型中常见的其他位置表示方法。
第六篇,我们又把 Transformer Block 内部剩下的重要组件补齐了:FFN、Residual Connection 和 LayerNorm。
到这里,我们已经了解一个 Transformer Block 是如何工作的:
输入
↓
Multi-Head Attention
↓
Residual + LayerNorm
↓
FFN
↓
Residual + LayerNorm
↓
输出
但,这还没有回答一个最关键的坑:
Transformer 是如何真正生成一段文本的?
我们平时使用大语言模型时,输入:
今天天气
模型并不是一次性把完整答案全部计算出来。
它更像是在不断重复这样的过程:
今天天气
↓
预测下一个 token
↓
今天天气很好
↓
再次预测下一个 token
↓
今天天气很好。
↓
再次预测下一个 token
↓
...
也就是说,大语言模型生成文本时,本质上是在不断预测:
下一个 token 是什么?
但这里立刻出现了一个非常严重的问题。
假设训练数据中有一句:
我喜欢吃苹果
当模型正在预测“苹果”时,它理应只能看到:
我喜欢吃
而不能提前看到:
苹果
否则模型就相当于已经了解了答案。
于是 Decoder 需要一种特殊机制:
让当前位置只能关注自己以及自己之前的 token,而不能看到未来的信息。
这就是本篇文章的核心:
Decoder
Masked Self-Attention
Causal Mask
Autoregressive Generation
理解了这些东西,我们才能真正理解 GPT 这一类大语言模型为什么能够一个 token 一个 token 地生成文本。
下面是本篇文章的整体脉络图:
前六篇:理解 Transformer Block
核心问题:如何生成文本?
Decoder 需要遮挡未来信息
Masked Self-Attention
Causal Mask(三角矩阵)
Autoregressive Generation
训练 vs 生成
Cross-Attention(Encoder-Decoder)
GPT:Decoder-only
从输入到下一个 token 的完整过程
一、为什么 Transformer 需要 Decoder?
1.1 Encoder 和 Decoder 最初分别负责什么?
在最初的 Transformer 结构中,Transformer 并不是专门为了聊天或者文本续写设计的。
原始 Transformer 主要用于:
输入序列
↓
Encoder
↓
理解输入
↓
Decoder
↓
生成输出序列
例如机器翻译:
英语:
I love apples.
↓ Encoder
内部表示
↓ Decoder
中文:
我喜欢苹果。
Encoder 更像是在负责:
理解输入序列。
Decoder 更像是在负责:
根据已有的信息,一步一步生成输出序列。
这两个部分虽然都使用 Transformer Block,但它们的任务并不完全一样。
1.2 Decoder 为什么不能像 Encoder 一样直接看完整序列?
假设我们现在要生成:
我 喜欢 吃 苹果
如果是 Encoder,那么看到完整句子没有问题。
因为 Encoder 的任务主要是理解:
我
喜欢
吃
苹果
这些 token 之间的关系。
但 Decoder 不一样。
Decoder 的任务是:
根据已经生成的东西,预测下一个 token。
例如:
输入:我
预测:喜欢
然后:
输入:我 喜欢
预测:吃
然后:
输入:我 喜欢 吃
预测:苹果
于是 Decoder 在预测当前位置时,不能看到未来的 token。
否则:
我 喜欢 吃 苹果
↑
已经知道答案
模型就不需要真正学习预测了。
于是 Decoder 必须具备一种“遮挡未来信息”的能力。
这就是 Masked Self-Attention。
二、什么是 Masked Self-Attention?
2.1 先回顾普通 Self-Attention
在第三篇文章中,我们已经知道 Self-Attention 的核心公式:
Attention
(
Q
,
K
,
V
)
=
softmax
(
Q
K
T
d
k
)
V
\operatorname{Attention}(Q,K,V)= \operatorname{softmax} \left( \frac{QK^T}{\sqrt{d_k}} \right)V
Attention(Q,K,V)=softmax(dk
QKT)V
其中:
- Q
Q 表示 Query
- K
K
K 表示 Key
- V
V
V 表示 Value
首先计算:
Q
K
T
QK^T
QKT
获得不同 token 之间的相关性分数。
然后除以:
d
k
\sqrt{d_k}
dk
再通过 Softmax 转换成注意力权重。
最后对
V
V
V 进行加权求和。
问题在于:
普通 Self-Attention 默认允许每一个 token 关注所有 token。
例如:
我 喜欢 吃 苹果
对于“吃”这个 token,普通 Self-Attention 理论上可以同时看到:
我
喜欢
吃
苹果
也就是说,它甚至可以看到未来的“苹果”。
这对于理解整个句子没有问题。
但对于“预测下一个 token”的任务,这是不允许的。
三、Causal Mask:把未来的信息遮住
3.1 用一个简单例子理解 Mask
假设我们有四个 token:
我 喜欢 吃 苹果
我们希望:
我
只能看到自己。
我 喜欢
中的“喜欢”可以看到:
我
喜欢
而不能看到:
吃
苹果
于是就可以画成一个注意力矩阵:
我 喜欢 吃 苹果
我 ✓ × × ×
喜欢 ✓ ✓ × ×
吃 ✓ ✓ ✓ ×
苹果 ✓ ✓ ✓ ✓
这里:
✓ = 可以关注
× = 不允许关注
可以发现,这个矩阵具有一个非常明显的特点:
只能看到当前位置左边以及当前位置本身。
于是它也经常被称为:
Causal Attention
或者:
Causal Self-Attention
其中 Causal 可以理解成“因果”。
因为在生成过程中:
当前位置只能依赖已经出现的信息,而不能依赖未来的信息。
四、Mask 到底是怎么加进 Attention 里的?
现在我们已经知道需要遮住未来 token。
但模型究竟是怎么做到的?
关键就在 Attention 分数计算完成以后。
普通 Attention 会先计算:
S
=
Q
K
T
d
k
S=\frac{QK^T}{\sqrt{d_k}}
S=dk
QKT
这里的
S
S
S 就是 Attention Score,也就是注意力分数矩阵。
例如可能获得:
S
=
[
2.1
1.2
0.7
1.5
0.8
2.4
1.1
1.7
1.2
0.9
2.8
1.6
1.4
1.8
0.7
2.5
]
S= \begin{bmatrix} 2.1 & 1.2 & 0.7 & 1.5\\ 0.8 & 2.4 & 1.1 & 1.7\\ 1.2 & 0.9 & 2.8 & 1.6\\ 1.4 & 1.8 & 0.7 & 2.5 \end{bmatrix}
S=
2.10.81.21.41.22.40.91.80.71.12.80.71.51.71.62.5
但是我们不希望第一行看到第二、第三、第四个 token。
所以可以构造一个 Mask:
M
=
[
0
−
∞
−
∞
−
∞
0
0
−
∞
−
∞
0
0
0
−
∞
0
0
0
0
]
M= \begin{bmatrix} 0 & -\infty & -\infty & -\infty\\ 0 & 0 & -\infty & -\infty\\ 0 & 0 & 0 & -\infty\\ 0 & 0 & 0 & 0 \end{bmatrix}
M=
0000−∞000−∞−∞00−∞−∞−∞0
然后把它加到原来的分数矩阵:
S
′
=
S
+
M
S'=S+M
S′=S+M
于是:
S
′
=
[
2.1
−
∞
−
∞
−
∞
0.8
2.4
−
∞
−
∞
1.2
0.9
2.8
−
∞
1.4
1.8
0.7
2.5
]
S'= \begin{bmatrix} 2.1 & -\infty & -\infty & -\infty\\ 0.8 & 2.4 & -\infty & -\infty\\ 1.2 & 0.9 & 2.8 & -\infty\\ 1.4 & 1.8 & 0.7 & 2.5 \end{bmatrix}
S′=
2.10.81.21.4−∞2.40.91.8−∞−∞2.80.7−∞−∞−∞2.5
接下来再进行 Softmax:
A
=
softmax
(
S
′
)
A=\operatorname{softmax}(S')
A=softmax(S′)
这里最关键的地方就是:
e
−
∞
=
0
e^{-\infty}=0
e−∞=0
于是那些被 Mask 的位置经过 Softmax 后,注意力权重就会变成 0。
也就是说:
未来 token
↓
Mask
↓
分数变成 -∞
↓
Softmax
↓
权重变成 0
↓
无法获得未来 token 的信息
这就是 Causal Mask 最核心的实现思想。
下面是 Mask 加入 Attention 的完整流程:
计算注意力分数 S = QK^T / √d_k
构造 Causal Mask M
S' = S + M
Softmax(S')
未来位置权重变为 0
无法获得未来 token 的信息
五、为什么一定要使用 -∞?
这里有一个很容易产生疑问的问题:
为什么 Mask 使用的是:
−
∞
-\infty
−∞
而不是简单地写成:
0
或者:
-1
原因就在 Softmax。
Softmax 的公式是:
$$ \operatorname{softmax}(x_i)
\frac{e^{x_i}}
{\sum_j e^{x_j}}
$$
如果我们希望某个位置的最终权重严格变成 0,那么最直接的方法就是让:
e
x
i
=
0
e^{x_i}=0
exi=0
而:
e
−
∞
=
0
e^{-\infty}=0
e−∞=0
所以把对应位置的分数设置成:
−
∞
-\infty
−∞
Softmax 之后,这个位置就不会获得注意力权重。
于是 Mask 并不是把未来的信息“删除”。
更准确地说:
Mask 让模型在计算注意力权重时忽略未来位置。
六、为什么这种 Mask 是一个三角矩阵?
把刚才的 Mask 单独拿出来:
M
=
[
0
−
∞
−
∞
−
∞
0
0
−
∞
−
∞
0
0
0
−
∞
0
0
0
0
]
M= \begin{bmatrix} 0 & -\infty & -\infty & -\infty\\ 0 & 0 & -\infty & -\infty\\ 0 & 0 & 0 & -\infty\\ 0 & 0 & 0 & 0 \end{bmatrix}
M=
0000−∞000−∞−∞00−∞−∞−∞0
可以发现:
0 -∞ -∞ -∞
0 0 -∞ -∞
0 0 0 -∞
0 0 0 0
它实际上是一个下三角矩阵。
所以这种 Mask 经常被称为:
Causal Mask / Lower Triangular Mask
如果序列长度是
n
n
n,那么可以把它抽象成:
M
i
j
=
{
0
,
j
≤
i
−
∞
,
j
>
i
M_{ij}= \begin{cases} 0, & j\leq i\\ -\infty, & j>i \end{cases}
Mij={0,−∞,j≤ij>i
这里:
- i
i 表示当前 token 的位置
- j
j
j 表示它正在关注的位置
当:
j
≤
i
j\leq i
j≤i
说明目标位置没有超过当前位置,可以访问。
当:
j
>
i
j>i
j>i
说明目标位置属于未来,因此必须屏蔽。
七、Decoder 的 Self-Attention 到底解决了什么问题?
到这里,我们就可以重新理解 Decoder 中的 Self-Attention。
普通 Self-Attention:
每个 token
↓
可以关注所有 token
Masked Self-Attention:
每个 token
↓
只能关注自己和过去的 token
所以:
Encoder Self-Attention
和:
Decoder Masked Self-Attention
最大的区别之一,就是是否需要阻止未来信息泄露。
可以简单总结成:
模块能看到哪些 tokenEncoder Self-Attention整个输入序列Decoder Masked Self-Attention当前 token 以及之前的 token
这也是 Decoder 能够用于自回归生成的重要基础。
八、什么是 Autoregressive Generation?
现在我们终于可以进入大语言模型生成文本的核心机制:
Autoregressive Generation,自回归生成。
“自回归”这个词看起来比较复杂,但核心思想其实很简单:
根据已经生成的东西,预测下一个 token,然后把这个 token 加入输入,再继续预测下一个 token。
例如我们想生成:
我喜欢吃苹果
模型可能经历:
输入:
我
预测:
喜欢
然后:
输入:
我 喜欢
预测:
吃
然后:
输入:
我 喜欢 吃
预测:
苹果
最后:
输入:
我 喜欢 吃 苹果
预测:
这里:
可以理解成:
End Of Sequence
也就是告诉模型:
这段文本生成收尾了。
因此整个过程可以抽象成:
已有 token
↓
Transformer
↓
预测下一个 token
↓
把新 token 加入序列
↓
再次输入 Transformer
↓
继续预测
↓
...
这就是大语言模型最基本的文本生成过程。
下面是自回归生成的完整流程:
已有 token
Transformer
预测下一个 token
把新 token 加入序列
再次输入 Transformer
遇到 EOS 收尾
九、训练时和生成时有什么区别?
这里是理解 GPT 类模型时非常重要的一点。
虽然训练和真正生成文本时都在做:
预测下一个 token
但是两者的执行方式其实并不完全一样。
9.1 训练阶段:一次计算挺多位置
假设训练数据是:
我 喜欢 吃 苹果
训练时可以把整个序列一次送进模型。
但是通过 Causal Mask:
我 → 只能看我
喜欢 → 只能看我、喜欢
吃 → 只能看我、喜欢、吃
苹果 → 只能看我、喜欢、吃、苹果
于是模型可以同时计算多个位置的预测任务。
例如:
输入位置:
我
喜欢
吃
目标:
喜欢
吃
苹果
可以理解成:
我 → 预测 喜欢
喜欢 → 预测 吃
吃 → 预测 苹果
虽然这些预测在逻辑上具有先后关系,但由于 Causal Mask 已经保证了每个位置看不到未来信息,因此训练时可以把多个位置放到一次矩阵计算中并行处理。
这也是 Transformer 相对于传统 RNN 的一个重要优势:
训练阶段可以高度并行化。
9.2 为什么生成阶段不能像训练一样一次预测全部?
因为生成阶段的未来 token 根本还不存在。
例如现在只有:
我 喜欢
模型要预测:
吃
只有模型生成“吃”以后,下一次输入才变成:
我 喜欢 吃
然后才能预测:
苹果
所以生成过程天然具有:
一步
↓
下一步
↓
再下一步
↓
再下一步
这样的顺序依赖。
因此可以形成一个非常重要的对比:
训练:
大量 token
↓
Causal Mask
↓
并行计算多个位置
生成:
已有 token
↓
预测一个 token
↓
加入新 token
↓
继续预测
所以:
Transformer 在训练阶段可以高度并行,而在自回归生成阶段,token 的产生仍然具有天然的顺序性。
这也是为什么大语言模型生成文本时,我们可以明显看到:
一个 token
一个 token
一个 token
不断出现。
下面是训练与生成阶段的对比图:
生成阶段
已有 token
预测一个 token
加入新 token
继续预测
训练阶段
大量 token
Causal Mask
并行计算多个位置
十、Decoder 中为什么还有 Cross-Attention?
到这里我们主要讲的是 Decoder 的 Masked Self-Attention。
但是原始 Transformer Decoder 还有一个非常重要的组件:
Cross-Attention。
这也是 Encoder-Decoder Transformer 和 GPT 这类 Decoder-only 模型之间非常重要的区别之一。
先看原始 Transformer。
整体结构:
输入
↓
Encoder
↓
Encoder Output
↓
Decoder
↓
输出
Decoder 需要同时获取两类信息。
第一类:
已经生成的内容。
这通过:
Masked Self-Attention
完成。
第二类:
Encoder 对输入序列提取出来的信息。
这通过:
Cross-Attention
完成。
十一、Cross-Attention 和 Self-Attention 有什么区别?
我们之前讲过 Self-Attention。
Self-Attention 的特点是:
Q、K、V
来自同一个序列
而 Cross-Attention 则不同。
它通常是:
Q
来自 Decoder
K、V
来自 Encoder
可以简单画成:
Decoder 当前表示
│
↓
Q
│
↓
Cross-Attention
↑
│
K、V
│
↑
Encoder 输出
所以 Cross-Attention 的作用可以理解成:
Decoder 在生成内容时,主动去查询 Encoder 对输入内容的理解。
例如机器翻译:
英文输入:
I love apples.
↓
Encoder
↓
内部表示
↓
Decoder
Decoder 在生成:
我
之后,又需要生成:
喜欢
它就可以通过 Cross-Attention 去关注 Encoder 中与当前生成内容相关的部分。
因此:
Masked Self-Attention
解决的是:
我已经生成了什么?
而:
Cross-Attention
解决的是:
输入内容中哪些信息对我现在生成最有帮助?
十二、GPT 为什么没有 Encoder 和 Cross-Attention?
这里就可以自然过渡到现代大语言模型。
像 GPT 这一类模型,并不是完整使用原始 Transformer 的 Encoder-Decoder 结构。
它采用的是:
Decoder-only Transformer
也就是说:
只有 Decoder
没有 Encoder
那么既然没有 Encoder:
Cross-Attention
自然也就没有必要存在。
GPT 类模型的核心结构更加接近:
Token
↓
Embedding
↓
Transformer Block
↓
Transformer Block
↓
Transformer Block
↓
...
↓
Logits
↓
Next Token
其中 Transformer Block 主要使用:
Causal / Masked Self-Attention
+
FFN
+
Residual
+
LayerNorm
所以我们现在终于可以把前面几篇文章和 GPT 联系起来。
十三、从输入到下一个 token:完整过程
假设用户输入:
今天天气
首先需要把文本转换成 token。
实际模型中的 token 划分会根据具体 Tokenizer 而不同,这里只是为了理解流程。
然后:
Token
↓
Token Embedding
↓
Position Information
↓
Transformer Block 1
↓
Transformer Block 2
↓
...
↓
Transformer Block N
经过挺多层 Transformer Block 后,模型会获得最后一个位置的隐藏表示。
假设最后一个 token 是:
气
模型需要根据它以及前面的上下文预测下一个 token。
最终通过一个输出层,把隐藏表示转换成整个词表上的分数。
假设词表中有:
很好
不错
晴朗
阴天
...
模型可能得到:
z
=
[
2.1
1.5
0.8
0.2
⋮
]
z= \begin{bmatrix} 2.1\\ 1.5\\ 0.8\\ 0.2\\ \vdots \end{bmatrix}
z=
2.11.50.80.2⋮
这里的每一个数都可以理解为对应 token 的预测分数,也就是 Logit。
然后通过 Softmax 转换成概率:
P
(
y
i
)
=
e
z
i
∑
j
e
z
j
P(y_i)= \frac{e^{z_i}} {\sum_j e^{z_j}}
P(yi)=∑jezjezi
于是就得到:
很好 0.42
不错 0.27
晴朗 0.18
阴天 0.05
...
然后模型根据某种生成策略选择一个 token。
例如选择:
很好
于是文本变成:
今天天气很好
然后继续进行下一轮预测。
下面是整个语言模型推理流程的完整图:
文本
Tokenization
Token Embedding
Position Information
Transformer Blocks
Hidden Representation
Linear
Logits
Softmax
下一个 Token
加入上下文
十四、为什么模型输出的不是“一个答案”,而是一组概率?
这是理解语言模型非常关键的一步。
模型本质上并不是直接输出:
答案 = 很好
而是输出:
词表中每一个 token 的分数。
例如:
很好 2.8
不错 2.1
晴朗 1.7
阴天 0.9
下雨 0.4
...
这些分数经过 Softmax 后,就变成概率分布。
所以模型实际上是在回答:
根据当前上下文,下一个 token 分别有多大概率出现?
因此:
Transformer
↓
Hidden State
↓
Linear
↓
Logits
↓
Softmax
↓
Probability Distribution
↓
选择下一个 Token
这一步就把前面学习的 Attention、FFN 等内部计算,连接到了真正的“文本生成”。
十五、Greedy、Sampling:下一个 token 到底怎么选?
得到概率以后,还有一个问题:
到底选择哪个 token?
最简单的方法叫做:
Greedy Decoding
也就是每次都选择概率最大的 token。
例如:
很好 0.45
不错 0.30
晴朗 0.15
阴天 0.10
那么直接选择:
很好
但是,如果每次都选择概率最高的 token,生成结果可能比较固定。
因此实际大语言模型中还可以使用各种 Sampling 方法。
例如:
Temperature
Top-K
Top-P
它们的作用,本质上都是对最终的概率分布进行调整,影响模型如何选择下一个 token。
这里先不展开,因为这些内容涉及生成策略本身,后面的文章还会再次用到。
目前只需要记住:
Transformer 最终给出的不是一句完整答案,而是对“下一个 token”的概率分布。
十六、一个完整的自回归生成例子
现在把整个过程完整走一遍。
假设用户输入:
今天
第一轮:
输入:
今天
模型预测:
天气
于是:
今天 天气
第二轮:
输入:
今天 天气
模型预测:
很好
于是:
今天 天气 很好
第三轮:
输入:
今天 天气 很好
模型预测:
。
于是:
今天 天气 很好 。
第四轮:
输入:
今天 天气 很好 。
模型预测:
生成收尾。
整个过程可以表示成:
x1
↓
预测 x2
↓
x1 x2
↓
预测 x3
↓
x1 x2 x3
↓
预测 x4
↓
...
如果用数学形式表示,假设已经生成了:
x
1
,
x
2
,
⋯
,
x
t
x_1,x_2,\cdots,x_t
x1,x2,⋯,xt
那么模型要预测的就是:
P
(
x
t
+
1
∣
x
1
,
x
2
,
⋯
,
x
t
)
P(x_{t+1}\mid x_1,x_2,\cdots,x_t)
P(xt+1∣x1,x2,⋯,xt)
也就是说:
下一个 token 的概率,取决于之前已经出现的所有 token。
生成过程不断重复:
x
t
+
1
∼
P
(
x
t
+
1
∣
x
1
,
⋯
,
x
t
)
x_{t+1} \sim P(x_{t+1}\mid x_1,\cdots,x_t)
xt+1∼P(xt+1∣x1,⋯,xt)
然后把新生成的
x
t
+
1
x_{t+1}
xt+1 加入上下文,继续预测:
x
t
+
2
∼
P
(
x
t
+
2
∣
x
1
,
⋯
,
x
t
,
x
t
+
1
)
x_{t+2} \sim P(x_{t+2}\mid x_1,\cdots,x_t,x_{t+1})
xt+2∼P(xt+2∣x1,⋯,xt,xt+1)
这就是自回归生成。
十七、训练语言模型时,模型到底在学习什么?
现在我们已经知道模型如何生成文本。
那么训练阶段到底在学什么?
最核心的目标其实非常简单:
根据前面的 token,预测下一个 token。
假设训练文本是:
我 喜欢 吃 苹果
那么可以构造:
输入:
我
目标:
喜欢
然后:
输入:
我 喜欢
目标:
吃
再然后:
输入:
我 喜欢 吃
目标:
苹果
因此训练目标可以写成:
P
(
x
t
∣
x
1
,
x
2
,
⋯
,
x
t
−
1
)
P(x_t\mid x_1,x_2,\cdots,x_{t-1})
P(xt∣x1,x2,⋯,xt−1)
模型希望让正确答案的概率尽可能大。
通常会使用交叉熵损失。
对于一个位置:
L
=
−
log
P
(
x
t
∣
x
1
,
⋯
,
x
t
−
1
)
L=-\log P(x_t\mid x_1,\cdots,x_{t-1})
L=−logP(xt∣x1,⋯,xt−1)
如果模型正确 token 的概率很高,那么损失就会比较小。
如果模型给正确 token 的概率非常低,那么损失就会变大。
所以训练过程中,模型不断调整自己的参数,使得:
在给定前文的情况下,正确的下一个 token 更容易获得较高概率。
当这种学习在大量文本上进行之后,模型就逐渐获得了语言建模能力。
十八、为什么 Causal Mask 对训练也非常重要?
现在可以重新理解 Causal Mask 的意义。
假设训练文本:
我 喜欢 吃 苹果
我们希望模型学习:
我
↓
喜欢
我 喜欢
↓
吃
我 喜欢 吃
↓
苹果
如果没有 Mask,那么在预测“吃”的时候,模型可能直接看到“苹果”。
那么它学习到的就不是:
根据过去预测未来
而变成:
根据完整答案预测答案
这显然没有意义。
所以 Causal Mask 保证了训练过程遵守一个非常重要的规则:
预测当前位置时,只允许使用过去的信息。
因此:
Causal Mask
↓
阻止未来信息泄露
↓
保证训练目标正确
↓
模型学习“根据过去预测未来”
这也是为什么 GPT 类模型的核心通常会被称为:
Causal Language Modeling
也就是因果语言建模。
十九、现在重新理解 Decoder
到了这里,我们已经可以完整理解 Decoder 的作用。
原始 Transformer Decoder 中,可以把核心结构概括成:
输入已经生成的 token
↓
Masked Self-Attention
↓
Residual + LayerNorm
↓
Cross-Attention
↓
Residual + LayerNorm
↓
FFN
↓
Residual + LayerNorm
↓
输出
其中:
Masked Self-Attention
负责:
让当前 token 关注已经出现的 token,同时阻止未来信息进入。
Cross-Attention
负责:
让 Decoder 关注 Encoder 对输入序列提取出的信息。
FFN
负责:
对隐藏表示进行进一步的非线性变换。
Residual + LayerNorm
负责:
帮助信息传播并保持隐藏表示稳定。
这几个组件组合起来,就构成了完整的 Decoder Block。
二十、Encoder-Decoder Transformer 与 GPT 的区别
现在可以把两种结构放在一起看。
原始 Transformer:
输入
↓
Encoder
↓
Encoder Output
↓
Decoder
↓
输出
Decoder 中包含:
Masked Self-Attention
+
Cross-Attention
+
FFN
而 GPT 类模型:
输入
↓
Decoder-only Transformer
↓
输出下一个 token 的概率
其中主要使用:
Causal Self-Attention
+
FFN
+
Residual
+
LayerNorm
因为没有 Encoder,所以也不需要 Cross-Attention。
这就是为什么当我们后面讨论 GPT 时,经常会说:
GPT 是 Decoder-only Transformer。
理解这一点以后,前面几篇文章中的很多知识就真正串起来了。
二十一、从“Transformer Block”到“语言模型”
到这里,我们终于完成了一个非常重要的连接。
前面第六篇,我们学习的是:
Transformer Block
它解决的是:
如何处理隐藏表示?
这一篇,我们进一步加入:
Causal Mask
Decoder
Autoregressive Generation
于是 Transformer 开始具备:
根据前文预测后文的能力。
整个过程可以概括成:
文本
↓
Tokenization
↓
Token Embedding
↓
Position Information
↓
Transformer Blocks
↓
Hidden Representation
↓
Linear
↓
Logits
↓
Softmax
↓
下一个 Token
↓
加入上下文
↓
再次计算
↓
继续生成
这已经非常接近我们今天所说的“大语言模型”的基本推理流程了。
当然,现代大语言模型还加入了很多更加复杂的技术,例如:
KV Cache
RoPE
RMSNorm
SwiGLU
FlashAttention
MoE
各种并行训练技术
这些内容会在后面的文章中逐渐展开。
二十二、这一篇真正需要记住什么?
如果只保留这一篇最重要的内容,可以记住下面几个核心概念。
第一,Decoder 的任务是根据已经出现的内容生成后续内容。
因此它不能像普通 Encoder 一样无条件看到完整序列。
第二,Masked Self-Attention 会阻止当前位置看到未来 token。
核心 Mask 可以表示为:
M
i
j
=
{
0
,
j
≤
i
−
∞
,
j
>
i
M_{ij}= \begin{cases} 0, & j\leq i\\ -\infty, & j>i \end{cases}
Mij={0,−∞,j≤ij>i
然后:
Attention
(
Q
,
K
,
V
)
=
softmax
(
Q
K
T
d
k
+
M
)
V
\operatorname{Attention}(Q,K,V)= \operatorname{softmax} \left( \frac{QK^T}{\sqrt{d_k}}+M \right)V
Attention(Q,K,V)=softmax(dk
QKT+M)V
这样就可以保证未来位置的注意力权重为 0。
第三,自回归生成就是不断预测下一个 token。
数学上可以表示为:
P
(
x
t
+
1
∣
x
1
,
⋯
,
x
t
)
P(x_{t+1}\mid x_1,\cdots,x_t)
P(xt+1∣x1,⋯,xt)
模型根据已经出现的 token,预测下一个 token,然后把预测结果加入上下文。
第四,训练阶段和生成阶段的执行方式不同。
训练阶段可以利用 Causal Mask 一次并行计算多个位置。
生成阶段则因为未来 token 尚不存在,需要一个 token 一个 token 地向前生成。
第五,GPT 属于 Decoder-only Transformer。
它主要依靠:
Causal Self-Attention
+
FFN
+
Residual
+
LayerNorm
完成对上下文的建模和下一个 token 的预测。
二十三、下一篇:从 Transformer 到 GPT,大语言模型是怎么来的?
到这里,我们已经真正走到了 Transformer 和大语言模型之间的连接处。
前面的文章主要是在回答:
Transformer 是怎么工作的?
而这一篇开始,我们已经回答了:
Transformer 是怎么生成文本的?
但是还有一个更大的问题:
为什么一个原本用于序列建模的 Transformer,最后能够发展成今天的 GPT 和各种大语言模型?
我们已经知道 GPT 使用:
Decoder-only Transformer
也知道它通过:
Next Token Prediction
不断学习语言。
但仅仅知道这些还不够。
我们还需要继续回答:
为什么 GPT 只需要 Decoder?
为什么预测下一个 token 就能学到如此丰富的语言能力?
预训练到底在做什么?
为什么模型越大通常需要越多的数据和计算?
Transformer 是如何从一个架构,逐渐发展成今天的大语言模型的?
这些问题会把我们从:
Transformer
真正带到:
GPT
以及:
Large Language Model
下一篇,我们就正式从 Transformer 走向 GPT。
第八篇:从 Transformer 到 GPT——大语言模型是怎么来的?
暂时整理到这里。以上都是个人理解,可能有疏漏,欢迎指正。
评论 (0)
暂无评论