目录
- 一、先从一张经典结构图开始
- [二、Transformer 不是一个模块,而是一组模块的组合](#二、Transformer 不是一个模块,而是一组模块的组合)
- [三、什么是 Encoder?](#三、什么是 Encoder?)
- [四、Encoder Block:Transformer 的基本积木](#四、Encoder Block:Transformer 的基本积木)
- [五、第一部分:Multi-Head Self-Attention](#五、第一部分:Multi-Head Self-Attention)
- [六、第二部分:为什么 Attention 后面还需要 Add?](#六、第二部分:为什么 Attention 后面还需要 Add?)
- [七、第三部分:Norm 又是什么?](#七、第三部分:Norm 又是什么?)
- [八、第四部分:Attention 之后,为什么还需要 FFN?](#八、第四部分:Attention 之后,为什么还需要 FFN?)
- [九、现在,我们终于可以看懂 Encoder Block 了](#九、现在,我们终于可以看懂 Encoder Block 了)
- [十、Encoder 到底输出了什么?](#十、Encoder 到底输出了什么?)
- [十一、接下来轮到 Decoder](#十一、接下来轮到 Decoder)
- [十二、Decoder Block 为什么有两个 Attention?](#十二、Decoder Block 为什么有两个 Attention?)
- [十三、Encoder 和 Decoder 到底有什么区别?](#十三、Encoder 和 Decoder 到底有什么区别?)
- [十四、Transformer 到底是如何完成一次机器翻译的?](#十四、Transformer 到底是如何完成一次机器翻译的?)
- [十五、等等,还有一个重要问题:Transformer 怎么知道顺序?](#十五、等等,还有一个重要问题:Transformer 怎么知道顺序?)
- [十六、Transformer 的完整数据流](#十六、Transformer 的完整数据流)
- [十七、一个容易产生的误解:现在的 GPT 还是完整 Transformer 吗?](#十七、一个容易产生的误解:现在的 GPT 还是完整 Transformer 吗?)
- [十八、现在,我们对 Transformer 应该有什么整体认识?](#十八、现在,我们对 Transformer 应该有什么整体认识?)
- 十九、小结
- [下一篇:Self-Attention 到底是什么?](#下一篇:Self-Attention 到底是什么?)
《深入理解 Transformer:Transformer 究竟是什么?》
前言
在上一篇文章中,我们暂时没有急着讨论 Q、K、V,也没有直接推导 Attention 的计算公式。
我们先回答了一个更基础的问题:
为什么需要 Transformer?
在 Transformer 出现之前,处理文本等序列数据的主流方案是 RNN、LSTM 和 GRU。
它们都有一个非常明显的共同特点:
text
x₁ → x₂ → x₃ → x₄ → ···
也就是说,模型通常需要按照序列的顺序,一步一步地处理数据。
这种方式虽然符合人类阅读文本的直觉,但也带来了两个非常关键的问题:
- 计算过程存在顺序依赖,难以充分利用并行计算能力;
- 距离较远的信息需要经过多次传递,长距离依赖难以处理。
随后,Attention 的出现改变了这种思路。
我们不再要求信息必须:
text
Token 1
↓
Token 2
↓
Token 3
↓
Token 4
一级一级地传递。
而是可以让当前信息直接去关注序列中的其他信息:
text
Token 1 ─────────────┐
Token 2 ─────────────┤
Token 3 ─────────────┼──→ 当前 Token
Token 4 ─────────────┤
Token 5 ─────────────┘
于是,一个新的问题出现了:
既然 Attention 已经这么强大,那么 Transformer 到底是什么?
很多刚开始学习 Transformer 的人都会有一种错觉:
Transformer 不就是 Attention 吗?
这个理解不能说完全错误,但显然还不够完整。
Attention 是 Transformer 最核心的思想之一,但一个完整的 Transformer 并不是只有 Attention。
在原始的 Transformer 中,我们还会看到:
text
Encoder
Decoder
Multi-Head Attention
Feed Forward Network
Residual Connection
Layer Normalization
Positional Encoding
这些模块共同组成了 Transformer。
因此,这一篇文章我们的目标并不是立刻钻进某一个公式,而是先完成一件更加重要的事情:
先把 Transformer 的整体结构看明白。
只有先知道:
text
Transformer 由哪些部分组成?
每一部分负责什么?
数据在模型中如何流动?
后面学习 Self-Attention、Multi-Head Attention 等内容时,才不会只看到一堆零散的公式。
一、先从一张经典结构图开始
Transformer 最早由论文《Attention Is All You Need》提出。
原始 Transformer 是一个典型的:
Encoder-Decoder 架构。
我们可以先把它最外层的结构理解成:
text
输入序列
↓
Encoder
↓
中间表示
↓
Decoder
↓
输出序列
例如机器翻译:
text
I love artificial intelligence.
↓
Encoder
↓
理解输入信息
↓
Decoder
↓
我喜欢人工智能。
这里需要注意:
Encoder 和 Decoder 并不是 Transformer 随便划分出来的两个部分,它们承担着不同的职责。
简单来说:
- Encoder:负责理解输入;
- Decoder:负责根据已有信息生成输出。
我们可以先把它们理解成:
text
Transformer
┌────────────┴────────────┐
│ │
↓ ↓
Encoder Decoder
│ │
理解输入 生成输出
但是,真实的 Transformer 并不是只有一个 Encoder 和一个 Decoder。
它们内部实际上都由多个相同的 Block 堆叠而成。
下面是 Transformer 最外层的整体架构图:
#mermaid-svg-yfssAKUcSuKFkME0{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-yfssAKUcSuKFkME0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-yfssAKUcSuKFkME0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-yfssAKUcSuKFkME0 .error-icon{fill:#552222;}#mermaid-svg-yfssAKUcSuKFkME0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-yfssAKUcSuKFkME0 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-yfssAKUcSuKFkME0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-yfssAKUcSuKFkME0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-yfssAKUcSuKFkME0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-yfssAKUcSuKFkME0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-yfssAKUcSuKFkME0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-yfssAKUcSuKFkME0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-yfssAKUcSuKFkME0 .marker.cross{stroke:#333333;}#mermaid-svg-yfssAKUcSuKFkME0 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-yfssAKUcSuKFkME0 p{margin:0;}#mermaid-svg-yfssAKUcSuKFkME0 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-yfssAKUcSuKFkME0 .cluster-label text{fill:#333;}#mermaid-svg-yfssAKUcSuKFkME0 .cluster-label span{color:#333;}#mermaid-svg-yfssAKUcSuKFkME0 .cluster-label span p{background-color:transparent;}#mermaid-svg-yfssAKUcSuKFkME0 .label text,#mermaid-svg-yfssAKUcSuKFkME0 span{fill:#333;color:#333;}#mermaid-svg-yfssAKUcSuKFkME0 .node rect,#mermaid-svg-yfssAKUcSuKFkME0 .node circle,#mermaid-svg-yfssAKUcSuKFkME0 .node ellipse,#mermaid-svg-yfssAKUcSuKFkME0 .node polygon,#mermaid-svg-yfssAKUcSuKFkME0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-yfssAKUcSuKFkME0 .rough-node .label text,#mermaid-svg-yfssAKUcSuKFkME0 .node .label text,#mermaid-svg-yfssAKUcSuKFkME0 .image-shape .label,#mermaid-svg-yfssAKUcSuKFkME0 .icon-shape .label{text-anchor:middle;}#mermaid-svg-yfssAKUcSuKFkME0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-yfssAKUcSuKFkME0 .rough-node .label,#mermaid-svg-yfssAKUcSuKFkME0 .node .label,#mermaid-svg-yfssAKUcSuKFkME0 .image-shape .label,#mermaid-svg-yfssAKUcSuKFkME0 .icon-shape .label{text-align:center;}#mermaid-svg-yfssAKUcSuKFkME0 .node.clickable{cursor:pointer;}#mermaid-svg-yfssAKUcSuKFkME0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-yfssAKUcSuKFkME0 .arrowheadPath{fill:#333333;}#mermaid-svg-yfssAKUcSuKFkME0 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-yfssAKUcSuKFkME0 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-yfssAKUcSuKFkME0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yfssAKUcSuKFkME0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-yfssAKUcSuKFkME0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yfssAKUcSuKFkME0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-yfssAKUcSuKFkME0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-yfssAKUcSuKFkME0 .cluster text{fill:#333;}#mermaid-svg-yfssAKUcSuKFkME0 .cluster span{color:#333;}#mermaid-svg-yfssAKUcSuKFkME0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-yfssAKUcSuKFkME0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-yfssAKUcSuKFkME0 rect.text{fill:none;stroke-width:0;}#mermaid-svg-yfssAKUcSuKFkME0 .icon-shape,#mermaid-svg-yfssAKUcSuKFkME0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yfssAKUcSuKFkME0 .icon-shape p,#mermaid-svg-yfssAKUcSuKFkME0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-yfssAKUcSuKFkME0 .icon-shape .label rect,#mermaid-svg-yfssAKUcSuKFkME0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yfssAKUcSuKFkME0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-yfssAKUcSuKFkME0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-yfssAKUcSuKFkME0 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 输入序列
Encoder
中间表示
Decoder
输出序列
二、Transformer 不是一个模块,而是一组模块的组合
假设我们把 Transformer 看成一栋建筑。
那么:
text
Encoder
和:
text
Decoder
更像是两栋主要建筑。
而真正组成这两栋建筑的,是一个个重复出现的模块。
原始 Transformer 的结构可以简单表示为:
text
Input
↓
Embedding
↓
Positional Encoding
↓
┌─────────────────┐
│ Encoder Block │
└─────────────────┘
↓
┌─────────────────┐
│ Encoder Block │
└─────────────────┘
↓
···
↓
Encoder Output
Decoder 也是类似的结构:
text
Output
↓
Embedding
↓
Positional Encoding
↓
┌─────────────────┐
│ Decoder Block │
└─────────────────┘
↓
┌─────────────────┐
│ Decoder Block │
└─────────────────┘
↓
···
↓
Linear
↓
Softmax
↓
预测下一个 Token
所以理解 Transformer 时,实际上可以分成三个层次:
text
第一层:整体架构
↓
Encoder + Decoder
第二层:Block
↓
多个相同模块重复堆叠
第三层:具体组件
↓
Attention
FFN
Residual
LayerNorm
...
我们这一篇主要解决前两个层次的问题。
至于最底层的 Attention 是怎么计算的,会在后面的文章中逐步展开。
下面是 Transformer 三个理解层次的 Mermaid 图:
#mermaid-svg-2DAOTt9PxWdA6wru{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2DAOTt9PxWdA6wru .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2DAOTt9PxWdA6wru .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2DAOTt9PxWdA6wru .error-icon{fill:#552222;}#mermaid-svg-2DAOTt9PxWdA6wru .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2DAOTt9PxWdA6wru .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2DAOTt9PxWdA6wru .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2DAOTt9PxWdA6wru .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2DAOTt9PxWdA6wru .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2DAOTt9PxWdA6wru .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2DAOTt9PxWdA6wru .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2DAOTt9PxWdA6wru .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2DAOTt9PxWdA6wru .marker.cross{stroke:#333333;}#mermaid-svg-2DAOTt9PxWdA6wru svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2DAOTt9PxWdA6wru p{margin:0;}#mermaid-svg-2DAOTt9PxWdA6wru .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2DAOTt9PxWdA6wru .cluster-label text{fill:#333;}#mermaid-svg-2DAOTt9PxWdA6wru .cluster-label span{color:#333;}#mermaid-svg-2DAOTt9PxWdA6wru .cluster-label span p{background-color:transparent;}#mermaid-svg-2DAOTt9PxWdA6wru .label text,#mermaid-svg-2DAOTt9PxWdA6wru span{fill:#333;color:#333;}#mermaid-svg-2DAOTt9PxWdA6wru .node rect,#mermaid-svg-2DAOTt9PxWdA6wru .node circle,#mermaid-svg-2DAOTt9PxWdA6wru .node ellipse,#mermaid-svg-2DAOTt9PxWdA6wru .node polygon,#mermaid-svg-2DAOTt9PxWdA6wru .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2DAOTt9PxWdA6wru .rough-node .label text,#mermaid-svg-2DAOTt9PxWdA6wru .node .label text,#mermaid-svg-2DAOTt9PxWdA6wru .image-shape .label,#mermaid-svg-2DAOTt9PxWdA6wru .icon-shape .label{text-anchor:middle;}#mermaid-svg-2DAOTt9PxWdA6wru .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2DAOTt9PxWdA6wru .rough-node .label,#mermaid-svg-2DAOTt9PxWdA6wru .node .label,#mermaid-svg-2DAOTt9PxWdA6wru .image-shape .label,#mermaid-svg-2DAOTt9PxWdA6wru .icon-shape .label{text-align:center;}#mermaid-svg-2DAOTt9PxWdA6wru .node.clickable{cursor:pointer;}#mermaid-svg-2DAOTt9PxWdA6wru .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2DAOTt9PxWdA6wru .arrowheadPath{fill:#333333;}#mermaid-svg-2DAOTt9PxWdA6wru .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2DAOTt9PxWdA6wru .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2DAOTt9PxWdA6wru .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2DAOTt9PxWdA6wru .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2DAOTt9PxWdA6wru .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2DAOTt9PxWdA6wru .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2DAOTt9PxWdA6wru .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2DAOTt9PxWdA6wru .cluster text{fill:#333;}#mermaid-svg-2DAOTt9PxWdA6wru .cluster span{color:#333;}#mermaid-svg-2DAOTt9PxWdA6wru div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-2DAOTt9PxWdA6wru .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2DAOTt9PxWdA6wru rect.text{fill:none;stroke-width:0;}#mermaid-svg-2DAOTt9PxWdA6wru .icon-shape,#mermaid-svg-2DAOTt9PxWdA6wru .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2DAOTt9PxWdA6wru .icon-shape p,#mermaid-svg-2DAOTt9PxWdA6wru .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2DAOTt9PxWdA6wru .icon-shape .label rect,#mermaid-svg-2DAOTt9PxWdA6wru .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2DAOTt9PxWdA6wru .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2DAOTt9PxWdA6wru .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2DAOTt9PxWdA6wru :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 第三层:具体组件
Attention
FFN
Residual
LayerNorm
第二层:Block
多个相同模块重复堆叠
第一层:整体架构
Encoder + Decoder
三、什么是 Encoder?
先来看 Transformer 左边的 Encoder。
假设输入一句英文:
text
I love artificial intelligence.
计算机首先并不能直接理解:
text
I
love
artificial
intelligence
这些单词。
因为对于神经网络来说,真正能够参与计算的是数字。
所以,输入首先需要经过一个过程:
text
文本
↓
Tokenization
↓
Token
↓
Token ID
↓
Embedding
↓
向量
例如:
text
"I" → [0.12, -0.35, 0.71, ...]
"love" → [0.43, 0.18, -0.22, ...]
"artificial" → [-0.61, 0.29, 0.44, ...]
"intelligence" → [0.07, -0.82, 0.15, ...]
这里得到的向量,就是后续 Transformer 真正处理的数据。
Encoder 的任务,就是不断处理这些 Token 对应的表示,并让模型逐渐理解:
这些 Token 分别是什么,以及它们之间存在什么关系。
例如:
text
I love artificial intelligence.
经过 Encoder 之后,我们希望模型不再只是孤立地看:
text
I
love
artificial
intelligence
而是能够建立这样的联系:
text
I ───────→ love
│
↓
artificial intelligence
也就是说:
Encoder 的核心任务,是让每一个 Token 都获得"结合上下文之后"的表示。
这句话非常重要。
在输入 Transformer 之前:
text
love
可能只是"love"这个词本身的向量。
但是经过 Encoder 的多层处理之后:
text
love
所对应的向量,已经不再只包含"love"自身的信息。
它还会结合:
text
谁在 love?
love 什么?
周围还有哪些词?
这些词之间是什么关系?
于是,Token 的表示就从:
孤立的词表示
逐渐变成:
包含上下文信息的表示。
这就是 Encoder 的核心价值。
四、Encoder Block:Transformer 的基本积木
一个完整的 Encoder 并不是一次计算就结束。
它由多个 Encoder Block 叠加而成。
原始 Transformer 论文中使用了:
text
N × Encoder Block
也就是说:
text
Input
↓
Encoder Block 1
↓
Encoder Block 2
↓
Encoder Block 3
↓
···
↓
Encoder Block N
为什么需要多层?
我们可以把它理解成:
第一层建立比较基础的关系,后面的层继续在前一层结果的基础上进行更加复杂的特征提取。
例如:
text
第一层:
可能关注相邻 Token 之间的关系
第二层:
开始建立更复杂的语义关系
第三层:
进一步组合这些信息
......
最后:
形成更加丰富的上下文表示
当然,真实模型内部究竟学到了什么关系,并不是人为提前规定好的。
这里的"第一层学语法、第二层学语义"只能帮助我们建立直觉。
真正的情况是:
模型通过训练自己学习应该如何利用每一层的参数。
那么,一个 Encoder Block 内部到底有什么?
我们先看一个简化结构:
text
Input
↓
Multi-Head Self-Attention
↓
Add & Norm
↓
Feed Forward Network
↓
Add & Norm
↓
Output
这里出现了几个重要模块:
- Multi-Head Self-Attention;
- Residual Connection;
- Layer Normalization;
- Feed Forward Network。
接下来,我们先从整体职责理解它们。
下面是 Encoder Block 内部结构的 Mermaid 图:
#mermaid-svg-pXXaup0xEa4pMhAp{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-pXXaup0xEa4pMhAp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-pXXaup0xEa4pMhAp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-pXXaup0xEa4pMhAp .error-icon{fill:#552222;}#mermaid-svg-pXXaup0xEa4pMhAp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-pXXaup0xEa4pMhAp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-pXXaup0xEa4pMhAp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-pXXaup0xEa4pMhAp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-pXXaup0xEa4pMhAp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-pXXaup0xEa4pMhAp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-pXXaup0xEa4pMhAp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-pXXaup0xEa4pMhAp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-pXXaup0xEa4pMhAp .marker.cross{stroke:#333333;}#mermaid-svg-pXXaup0xEa4pMhAp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-pXXaup0xEa4pMhAp p{margin:0;}#mermaid-svg-pXXaup0xEa4pMhAp .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-pXXaup0xEa4pMhAp .cluster-label text{fill:#333;}#mermaid-svg-pXXaup0xEa4pMhAp .cluster-label span{color:#333;}#mermaid-svg-pXXaup0xEa4pMhAp .cluster-label span p{background-color:transparent;}#mermaid-svg-pXXaup0xEa4pMhAp .label text,#mermaid-svg-pXXaup0xEa4pMhAp span{fill:#333;color:#333;}#mermaid-svg-pXXaup0xEa4pMhAp .node rect,#mermaid-svg-pXXaup0xEa4pMhAp .node circle,#mermaid-svg-pXXaup0xEa4pMhAp .node ellipse,#mermaid-svg-pXXaup0xEa4pMhAp .node polygon,#mermaid-svg-pXXaup0xEa4pMhAp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-pXXaup0xEa4pMhAp .rough-node .label text,#mermaid-svg-pXXaup0xEa4pMhAp .node .label text,#mermaid-svg-pXXaup0xEa4pMhAp .image-shape .label,#mermaid-svg-pXXaup0xEa4pMhAp .icon-shape .label{text-anchor:middle;}#mermaid-svg-pXXaup0xEa4pMhAp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-pXXaup0xEa4pMhAp .rough-node .label,#mermaid-svg-pXXaup0xEa4pMhAp .node .label,#mermaid-svg-pXXaup0xEa4pMhAp .image-shape .label,#mermaid-svg-pXXaup0xEa4pMhAp .icon-shape .label{text-align:center;}#mermaid-svg-pXXaup0xEa4pMhAp .node.clickable{cursor:pointer;}#mermaid-svg-pXXaup0xEa4pMhAp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-pXXaup0xEa4pMhAp .arrowheadPath{fill:#333333;}#mermaid-svg-pXXaup0xEa4pMhAp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-pXXaup0xEa4pMhAp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-pXXaup0xEa4pMhAp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pXXaup0xEa4pMhAp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-pXXaup0xEa4pMhAp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pXXaup0xEa4pMhAp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-pXXaup0xEa4pMhAp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-pXXaup0xEa4pMhAp .cluster text{fill:#333;}#mermaid-svg-pXXaup0xEa4pMhAp .cluster span{color:#333;}#mermaid-svg-pXXaup0xEa4pMhAp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-pXXaup0xEa4pMhAp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-pXXaup0xEa4pMhAp rect.text{fill:none;stroke-width:0;}#mermaid-svg-pXXaup0xEa4pMhAp .icon-shape,#mermaid-svg-pXXaup0xEa4pMhAp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pXXaup0xEa4pMhAp .icon-shape p,#mermaid-svg-pXXaup0xEa4pMhAp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-pXXaup0xEa4pMhAp .icon-shape .label rect,#mermaid-svg-pXXaup0xEa4pMhAp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pXXaup0xEa4pMhAp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-pXXaup0xEa4pMhAp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-pXXaup0xEa4pMhAp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Input
Multi-Head Self-Attention
Add & Norm
Feed Forward Network
Add & Norm
Output
五、第一部分:Multi-Head Self-Attention
这是 Transformer 中最核心的模块之一。
虽然具体计算过程会在后面的文章中详细展开,但现在我们先理解:
它在整个 Transformer 中负责什么?
答案是:
让 Token 之间进行信息交互。
假设有一句话:
text
我 喜欢 学习 人工智能
如果每个 Token 都只是独立存在:
text
我
喜欢
学习
人工智能
那么模型并不知道:
text
谁喜欢?
喜欢什么?
学习什么?
于是 Self-Attention 会让每个 Token 都有机会去关注其他 Token。
例如:
text
我
↑
│
喜欢 ← 学习 → 人工智能
更准确地说:
对于当前 Token,模型会计算序列中其他 Token 对它的重要程度。
因此:
text
Self-Attention
解决的是:
Token 与 Token 之间如何交换信息。
这里的:
text
Self
也非常关键。
它表示:
Attention 的信息来源和查询对象来自同一个序列。
例如:
text
我 喜欢 学习 人工智能
序列中的每一个 Token 都可以关注:
text
我
喜欢
学习
人工智能
包括自己。
因此:
text
Self-Attention
可以简单理解成:
序列内部自己关注自己。
至于为什么还要:
text
Multi-Head
也就是"多头",我们暂时先留一个问题:
如果一个 Attention 能够建立关系,为什么还需要多个 Attention Head?
这个问题会在后面的文章中专门展开。
六、第二部分:为什么 Attention 后面还需要 Add?
在 Transformer 的结构图中,你会经常看到:
text
Add & Norm
其中:
text
Add
指的就是:
Residual Connection,残差连接。
它的结构可以简单理解为:
text
x
├───────────────┐
│ │
↓ │
Sublayer(x) │
│ │
└─────── + ─────┘
↓
Output
数学上可以写成:
y = x + F ( x ) y = x + F(x) y=x+F(x)
其中:
- x x x 是原始输入;
- F ( x ) F(x) F(x) 是某个子层处理之后的结果;
- y y y 是最终输出。
例如:
text
Input
↓
Attention
↓
处理结果
同时:
Input ────────────────┐
↓
Attention Output + Input
↓
新输出
那么问题来了:
为什么不直接使用 Attention 的输出,而要把原始输入再加回来?
最直观的理解是:
让网络在学习新信息的同时,不轻易丢失原始信息。
假设某个模块处理之后产生:
F ( x ) F(x) F(x)
如果我们直接输出:
y = F ( x ) y = F(x) y=F(x)
那么下一层看到的只有:
text
处理之后的信息
而加入残差连接之后:
y = x + F ( x ) y = x + F(x) y=x+F(x)
下一层同时保留:
text
原始信息 + 新学习的信息
从深度网络训练的角度来看,Residual Connection 还有一个非常重要的作用:
帮助信息和梯度在深层网络中传播。
Transformer 往往会堆叠很多层。
如果信息必须经过:
text
Layer 1
↓
Layer 2
↓
Layer 3
↓
......
↓
Layer N
每一层都进行复杂变换,那么网络越深,训练就可能越困难。
Residual Connection 相当于提供了一条更加直接的信息通道。
所以:
text
Add
并不是一个可有可无的小操作。
它是深层 Transformer 能够稳定训练的重要设计之一。
下面是残差连接(Residual Connection)的 Mermaid 图:
#mermaid-svg-Bj3ezxrbPLqSCgpu{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Bj3ezxrbPLqSCgpu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Bj3ezxrbPLqSCgpu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Bj3ezxrbPLqSCgpu .error-icon{fill:#552222;}#mermaid-svg-Bj3ezxrbPLqSCgpu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Bj3ezxrbPLqSCgpu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Bj3ezxrbPLqSCgpu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Bj3ezxrbPLqSCgpu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Bj3ezxrbPLqSCgpu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Bj3ezxrbPLqSCgpu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Bj3ezxrbPLqSCgpu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Bj3ezxrbPLqSCgpu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Bj3ezxrbPLqSCgpu .marker.cross{stroke:#333333;}#mermaid-svg-Bj3ezxrbPLqSCgpu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Bj3ezxrbPLqSCgpu p{margin:0;}#mermaid-svg-Bj3ezxrbPLqSCgpu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Bj3ezxrbPLqSCgpu .cluster-label text{fill:#333;}#mermaid-svg-Bj3ezxrbPLqSCgpu .cluster-label span{color:#333;}#mermaid-svg-Bj3ezxrbPLqSCgpu .cluster-label span p{background-color:transparent;}#mermaid-svg-Bj3ezxrbPLqSCgpu .label text,#mermaid-svg-Bj3ezxrbPLqSCgpu span{fill:#333;color:#333;}#mermaid-svg-Bj3ezxrbPLqSCgpu .node rect,#mermaid-svg-Bj3ezxrbPLqSCgpu .node circle,#mermaid-svg-Bj3ezxrbPLqSCgpu .node ellipse,#mermaid-svg-Bj3ezxrbPLqSCgpu .node polygon,#mermaid-svg-Bj3ezxrbPLqSCgpu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Bj3ezxrbPLqSCgpu .rough-node .label text,#mermaid-svg-Bj3ezxrbPLqSCgpu .node .label text,#mermaid-svg-Bj3ezxrbPLqSCgpu .image-shape .label,#mermaid-svg-Bj3ezxrbPLqSCgpu .icon-shape .label{text-anchor:middle;}#mermaid-svg-Bj3ezxrbPLqSCgpu .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Bj3ezxrbPLqSCgpu .rough-node .label,#mermaid-svg-Bj3ezxrbPLqSCgpu .node .label,#mermaid-svg-Bj3ezxrbPLqSCgpu .image-shape .label,#mermaid-svg-Bj3ezxrbPLqSCgpu .icon-shape .label{text-align:center;}#mermaid-svg-Bj3ezxrbPLqSCgpu .node.clickable{cursor:pointer;}#mermaid-svg-Bj3ezxrbPLqSCgpu .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Bj3ezxrbPLqSCgpu .arrowheadPath{fill:#333333;}#mermaid-svg-Bj3ezxrbPLqSCgpu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Bj3ezxrbPLqSCgpu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Bj3ezxrbPLqSCgpu .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Bj3ezxrbPLqSCgpu .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Bj3ezxrbPLqSCgpu .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Bj3ezxrbPLqSCgpu .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Bj3ezxrbPLqSCgpu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Bj3ezxrbPLqSCgpu .cluster text{fill:#333;}#mermaid-svg-Bj3ezxrbPLqSCgpu .cluster span{color:#333;}#mermaid-svg-Bj3ezxrbPLqSCgpu div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Bj3ezxrbPLqSCgpu .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Bj3ezxrbPLqSCgpu rect.text{fill:none;stroke-width:0;}#mermaid-svg-Bj3ezxrbPLqSCgpu .icon-shape,#mermaid-svg-Bj3ezxrbPLqSCgpu .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Bj3ezxrbPLqSCgpu .icon-shape p,#mermaid-svg-Bj3ezxrbPLqSCgpu .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Bj3ezxrbPLqSCgpu .icon-shape .label rect,#mermaid-svg-Bj3ezxrbPLqSCgpu .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Bj3ezxrbPLqSCgpu .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Bj3ezxrbPLqSCgpu .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Bj3ezxrbPLqSCgpu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} x
Sublayer(x)
+
y = x + F(x)
七、第三部分:Norm 又是什么?
Add 后面紧接着:
text
Norm
也就是:
Normalization,归一化。
在 Transformer 中,经典设计使用的是:
Layer Normalization,LayerNorm。
我们先不急着推导它的全部细节,只理解它的作用。
假设某一层输出的数据分布非常不稳定:
text
某些数值非常大
某些数值非常小
某些数据分布变化很明显
那么后面的网络就需要不断适应这些变化。
这会增加训练难度。
于是,我们希望对数据进行一定程度的标准化处理,让后续网络能够在更加稳定的数据分布上进行训练。
LayerNorm 的基本思想是:
对单个样本的特征维度进行归一化。
对于一个输入向量:
x = x 1 , x 2 , ... , x d x = x_1, x_2, \\dots, x_d x=x1,x2,...,xd
首先计算均值:
μ = 1 d ∑ i = 1 d x i \mu = \frac{1}{d}\sum_{i=1}^{d}x_i μ=d1i=1∑dxi
然后计算方差:
σ 2 = 1 d ∑ i = 1 d ( x i − μ ) 2 \sigma^2 = \frac{1}{d}\sum_{i=1}^{d}(x_i-\mu)^2 σ2=d1i=1∑d(xi−μ)2
最后进行归一化:
x ^ i = x i − μ σ 2 + ϵ \hat{x}_i = \frac{x_i-\mu}{\sqrt{\sigma^2+\epsilon}} x^i=σ2+ϵ xi−μ
随后再通过可学习参数进行缩放和平移:
y i = γ x ^ i + β y_i = \gamma \hat{x}_i + \beta yi=γx^i+β
其中:
- γ \gamma γ:缩放参数;
- β \beta β:平移参数;
- ϵ \epsilon ϵ:防止分母为 0 0 0 的一个很小的常数。
现在不需要强行记住这些公式。
这一阶段更重要的是知道:
LayerNorm 的作用,是帮助网络保持更加稳定的数值分布,从而让深层网络训练得更加稳定。
因此:
text
Attention
↓
Add
↓
LayerNorm
共同构成了 Transformer 中非常重要的一部分。
八、第四部分:Attention 之后,为什么还需要 FFN?
很多刚学习 Transformer 的人,会把注意力全部放在 Attention 上。
但实际上,一个完整的 Transformer Block 中还有一个非常重要的模块:
Feed Forward Network,前馈神经网络。
通常简称:
text
FFN
它的结构其实并不复杂。
可以理解成:
text
输入
↓
Linear
↓
Activation
↓
Linear
↓
输出
原始 Transformer 中可以写成:
$$
\operatorname{FFN}(x)
\max(0, xW_1+b_1)W_2+b_2
$$
其中:
- 第一层线性层通常将维度扩大;
- 中间经过非线性激活函数;
- 第二层再将维度映射回来。
例如:
text
d_model
↓
Linear
↓
d_ff
↓
Activation
↓
Linear
↓
d_model
那么问题来了:
Attention 不是已经处理完信息了吗?为什么还需要 FFN?
这里可以从职责上理解。
Attention 更擅长做:
Token 与 Token 之间的信息交互。
例如:
text
Token A
↔
Token B
↔
Token C
而 FFN 则更像是在:
每个 Token 获得上下文信息之后,进一步对自己的表示进行非线性变换。
我们可以简单理解为:
text
Attention:
"我应该从其他 Token 获得哪些信息?"
FFN:
"获得这些信息之后,我应该如何进一步处理自己?"
因此,一个 Transformer Block 实际上包含两类重要计算:
text
Attention
↓
Token 之间交换信息
FFN
↓
每个 Token 自己进行进一步变换
两者缺一不可。
九、现在,我们终于可以看懂 Encoder Block 了
经过前面的拆解,一个 Encoder Block 可以重新画成:
text
Input
│
├──────────────────────────────┐
↓ │
Multi-Head Self-Attention │
↓ │
Add ◄────────────────────┘
↓
LayerNorm
│
├──────────────────────────────┐
↓ │
Feed Forward Network │
↓ │
Add ◄────────────────────┘
↓
LayerNorm
↓
Output
现在,我们可以给每个模块一句话的定位:
Multi-Head Self-Attention
让不同 Token 之间交换信息。
Residual Connection
保留原始信息,并帮助深层网络训练。
LayerNorm
稳定数据分布,帮助训练过程更加稳定。
Feed Forward Network
对每个 Token 的表示进一步进行非线性变换。
于是,一个 Encoder Block 的完整工作流程可以理解成:
text
原始 Token 表示
↓
先看看其他 Token
↓
获得上下文信息
↓
保留原始信息
↓
稳定数据分布
↓
进一步进行非线性处理
↓
再次保留原始信息
↓
输出新的 Token 表示
这就是 Encoder Block 的基本思想。
下面是 Encoder Block 完整工作流程的 Mermaid 图:
#mermaid-svg-ZjLv1EgqBsceOf01{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZjLv1EgqBsceOf01 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZjLv1EgqBsceOf01 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZjLv1EgqBsceOf01 .error-icon{fill:#552222;}#mermaid-svg-ZjLv1EgqBsceOf01 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZjLv1EgqBsceOf01 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZjLv1EgqBsceOf01 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZjLv1EgqBsceOf01 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZjLv1EgqBsceOf01 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZjLv1EgqBsceOf01 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZjLv1EgqBsceOf01 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZjLv1EgqBsceOf01 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZjLv1EgqBsceOf01 .marker.cross{stroke:#333333;}#mermaid-svg-ZjLv1EgqBsceOf01 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZjLv1EgqBsceOf01 p{margin:0;}#mermaid-svg-ZjLv1EgqBsceOf01 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZjLv1EgqBsceOf01 .cluster-label text{fill:#333;}#mermaid-svg-ZjLv1EgqBsceOf01 .cluster-label span{color:#333;}#mermaid-svg-ZjLv1EgqBsceOf01 .cluster-label span p{background-color:transparent;}#mermaid-svg-ZjLv1EgqBsceOf01 .label text,#mermaid-svg-ZjLv1EgqBsceOf01 span{fill:#333;color:#333;}#mermaid-svg-ZjLv1EgqBsceOf01 .node rect,#mermaid-svg-ZjLv1EgqBsceOf01 .node circle,#mermaid-svg-ZjLv1EgqBsceOf01 .node ellipse,#mermaid-svg-ZjLv1EgqBsceOf01 .node polygon,#mermaid-svg-ZjLv1EgqBsceOf01 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZjLv1EgqBsceOf01 .rough-node .label text,#mermaid-svg-ZjLv1EgqBsceOf01 .node .label text,#mermaid-svg-ZjLv1EgqBsceOf01 .image-shape .label,#mermaid-svg-ZjLv1EgqBsceOf01 .icon-shape .label{text-anchor:middle;}#mermaid-svg-ZjLv1EgqBsceOf01 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ZjLv1EgqBsceOf01 .rough-node .label,#mermaid-svg-ZjLv1EgqBsceOf01 .node .label,#mermaid-svg-ZjLv1EgqBsceOf01 .image-shape .label,#mermaid-svg-ZjLv1EgqBsceOf01 .icon-shape .label{text-align:center;}#mermaid-svg-ZjLv1EgqBsceOf01 .node.clickable{cursor:pointer;}#mermaid-svg-ZjLv1EgqBsceOf01 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ZjLv1EgqBsceOf01 .arrowheadPath{fill:#333333;}#mermaid-svg-ZjLv1EgqBsceOf01 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZjLv1EgqBsceOf01 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZjLv1EgqBsceOf01 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZjLv1EgqBsceOf01 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ZjLv1EgqBsceOf01 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZjLv1EgqBsceOf01 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ZjLv1EgqBsceOf01 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZjLv1EgqBsceOf01 .cluster text{fill:#333;}#mermaid-svg-ZjLv1EgqBsceOf01 .cluster span{color:#333;}#mermaid-svg-ZjLv1EgqBsceOf01 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ZjLv1EgqBsceOf01 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ZjLv1EgqBsceOf01 rect.text{fill:none;stroke-width:0;}#mermaid-svg-ZjLv1EgqBsceOf01 .icon-shape,#mermaid-svg-ZjLv1EgqBsceOf01 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZjLv1EgqBsceOf01 .icon-shape p,#mermaid-svg-ZjLv1EgqBsceOf01 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ZjLv1EgqBsceOf01 .icon-shape .label rect,#mermaid-svg-ZjLv1EgqBsceOf01 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZjLv1EgqBsceOf01 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ZjLv1EgqBsceOf01 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ZjLv1EgqBsceOf01 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 原始 Token 表示
先看看其他 Token
获得上下文信息
保留原始信息
稳定数据分布
进一步进行非线性处理
再次保留原始信息
输出新的 Token 表示
十、Encoder 到底输出了什么?
Encoder 处理完成之后,会输出什么?
答案不是:
text
一个句子
也不是:
text
最终分类结果
而是:
经过上下文编码之后的一组 Token 表示。
例如输入:
text
I love artificial intelligence.
最开始:
text
I
love
artificial
intelligence
每个 Token 都有自己的初始 Embedding。
经过多个 Encoder Block 之后:
text
I' ← 结合上下文之后的新表示
love' ← 结合上下文之后的新表示
artificial' ← 结合上下文之后的新表示
intelligence' ← 结合上下文之后的新表示
这里的:
text
I'
love'
...
并不是新的单词。
而是表示:
每个 Token 对应的向量已经发生了变化。
例如:
text
love
经过 Encoder 之后,它所对应的向量不再只表示:
"love 这个词本身"。
它可能已经包含:
text
谁在 love?
love 的对象是什么?
这句话整体表达什么?
这样的上下文信息。
所以 Encoder 的输出可以理解为:
一组上下文化(Contextualized)的 Token 表示。
十一、接下来轮到 Decoder
如果说 Encoder 负责:
理解输入。
那么 Decoder 负责的就是:
生成输出。
例如机器翻译:
text
输入:
I love artificial intelligence.
输出:
我喜欢人工智能。
Decoder 并不是一次性把整句话全部生成出来。
它通常按照自回归的方式:
text
开始
↓
我
↓
我 喜欢
↓
我 喜欢 人工智能
↓
结束
也就是说:
生成当前 Token 时,需要参考之前已经生成的 Token。
因此 Decoder 面临两个任务:
- 关注自己之前生成的内容;
- 关注 Encoder 对输入序列的理解。
所以,Decoder 的结构会比 Encoder 更复杂一些。
十二、Decoder Block 为什么有两个 Attention?
一个典型的 Decoder Block 可以简化为:
text
Input
↓
Masked Multi-Head Self-Attention
↓
Add & Norm
↓
Encoder-Decoder Attention
↓
Add & Norm
↓
Feed Forward Network
↓
Add & Norm
↓
Output
你会发现:
Decoder 里面有两个 Attention。
为什么?
因为它需要解决两个不同的问题。
12.1 第一个 Attention:看自己已经生成的内容
假设 Decoder 正在生成:
text
我 喜欢 人工智能
当它准备预测下一个 Token 时,它需要参考:
text
我
喜欢
人工智能
这些已经出现的信息。
因此,第一个 Attention 负责:
让 Decoder 内部的 Token 彼此建立关系。
但是,这里有一个非常重要的限制。
假设我们正在预测:
text
Token 3
那么模型不能提前偷看:
text
Token 4
Token 5
Token 6
否则训练和实际生成时就会出现问题。
因此,Decoder 使用的是:
Masked Self-Attention。
所谓 Mask,可以简单理解成:
把未来的信息遮住。
例如:
text
Token 1 可以看:
Token 1
Token 2 可以看:
Token 1、Token 2
Token 3 可以看:
Token 1、Token 2、Token 3
Token 4 可以看:
Token 1、Token 2、Token 3、Token 4
但是:
text
Token 2
不能偷看:
text
Token 3、Token 4......
所以注意力矩阵会形成一种类似:
text
✓ × × ×
✓ ✓ × ×
✓ ✓ ✓ ×
✓ ✓ ✓ ✓
的结构。
这就是所谓的:
Causal Mask,因果掩码。
它保证:
当前 Token 只能看到自己以及过去的信息,不能看到未来。
这也是后面理解 GPT 的一个非常重要的基础。
12.2 第二个 Attention:去看 Encoder
Decoder 只看自己还不够。
例如我们现在正在进行翻译:
text
输入:
I love artificial intelligence.
Decoder 正在生成:
text
我 喜欢 ...
那么当 Decoder 想继续生成时,它还需要知道:
输入的英文到底是什么?
所以,它需要去关注 Encoder 的输出。
于是出现第二个 Attention:
Encoder-Decoder Attention。
我们可以理解成:
text
Decoder 当前状态
↓
"我现在需要什么信息?"
↓
Query
Encoder 输出
↓
"输入中有哪些信息?"
↓
Key / Value
因此,这个 Attention 的核心作用是:
让 Decoder 在生成输出时,能够参考 Encoder 对输入序列的理解。
例如:
text
Encoder:
理解英文输入
Decoder:
生成中文输出
Decoder ─────→ 需要时关注 Encoder
所以原始 Transformer 的 Decoder 实际上同时处理:
text
过去生成的信息
+
输入序列的信息
这也是为什么它需要两个不同的 Attention 模块。
十三、Encoder 和 Decoder 到底有什么区别?
现在我们可以把两者放在一起比较。
Encoder
text
Input
↓
Self-Attention
↓
Add & Norm
↓
FFN
↓
Add & Norm
核心目标:
理解整个输入序列。
因为 Encoder 在处理输入时,通常可以同时看到整个输入:
text
Token 1
Token 2
Token 3
...
Token N
因此 Encoder 的 Self-Attention 可以建立:
text
任意 Token ↔ 任意 Token
之间的关系。
Decoder
text
Output
↓
Masked Self-Attention
↓
Add & Norm
↓
Encoder-Decoder Attention
↓
Add & Norm
↓
FFN
↓
Add & Norm
核心目标:
根据已有信息逐步生成输出。
因此 Decoder:
- 需要关注已经生成的 Token;
- 不能偷看未来 Token;
- 还需要关注 Encoder 输出。
所以 Decoder 比 Encoder 多了:
text
Masked Self-Attention
和:
text
Encoder-Decoder Attention
这样的结构。
十四、Transformer 到底是如何完成一次机器翻译的?
现在我们把整个过程串起来。
假设:
text
输入:
I love artificial intelligence.
目标:
text
我喜欢人工智能。
整个流程可以简单理解成:
text
英文输入
↓
Tokenization
↓
Token Embedding
↓
Positional Encoding
↓
Encoder
↓
Encoder Output
↓
Decoder
↓
预测"我"
↓
Decoder
↓
预测"喜欢"
↓
Decoder
↓
预测"人工智能"
↓
Decoder
↓
结束
展开一点:
text
┌──────────────────────────────┐
│ Encoder │
│ │
│ I love artificial intelligence │
│ ↓ │
│ 上下文信息理解 │
└──────────────┬───────────────┘
│
│ Encoder Output
↓
┌──────────────────────────────┐
│ Decoder │
│ │
│ <START> │
│ ↓ │
│ 我 │
│ ↓ │
│ 我 喜欢 │
│ ↓ │
│ 我 喜欢 人工智能 │
└──────────────────────────────┘
这就是原始 Transformer Encoder-Decoder 架构的整体工作方式。
十五、等等,还有一个重要问题:Transformer 怎么知道顺序?
现在我们已经知道:
text
Attention
可以让不同 Token 建立联系。
但这里还有一个问题。
假设输入:
text
我 喜欢 你
和:
text
你 喜欢 我
这两句话使用的 Token 完全一样:
text
我
喜欢
你
但是顺序不同,含义也完全不同。
然而 Attention 本身只是在计算 Token 之间的关系。
那么:
Transformer 怎么知道哪个 Token 在前,哪个 Token 在后?
这就是:
Positional Encoding,位置编码。
Transformer 不像 RNN。
RNN 天然按照:
text
第一个
↓
第二个
↓
第三个
的顺序进行计算。
因此:
顺序本身就包含在计算过程中。
但是 Transformer 可以并行处理:
text
Token 1
Token 2
Token 3
Token 4
这意味着:
模型本身并不知道这些 Token 的先后顺序。
所以必须额外把位置信息加入进去:
text
Token Embedding
+
Position Information
↓
Transformer Input
原始 Transformer 使用的是:
Sinusoidal Positional Encoding,正弦余弦位置编码。
位置编码公式如下:
P E ( p o s , 2 i ) = sin ( p o s 10000 2 i d model ) PE(pos, 2i) = \sin\left(\frac{pos}{10000^{\frac{2i}{d_{\text{model}}}}}\right) PE(pos,2i)=sin(10000dmodel2ipos)
P E ( p o s , 2 i + 1 ) = cos ( p o s 10000 2 i d model ) PE(pos, 2i+1) = \cos\left(\frac{pos}{10000^{\frac{2i}{d_{\text{model}}}}}\right) PE(pos,2i+1)=cos(10000dmodel2ipos)
其中 p o s pos pos 为位置索引, i i i 为维度索引, d model d_{\text{model}} dmodel 为模型维度。
现在不需要强行理解:
为什么是 10000?
为什么使用 sin?
为什么使用 cos?
这一篇只需要先建立一个概念:
Attention 本身不包含顺序信息,所以 Transformer 必须额外告诉模型每个 Token 的位置。
后面我们会专门讨论 Position Encoding。
十六、Transformer 的完整数据流
到这里,我们已经可以把原始 Transformer 的整体流程串起来了。
text
输入序列
↓
Token Embedding
↓
Positional Encoding
↓
┌────────────────┐
│ Encoder Block │
└────────────────┘
↓
┌────────────────┐
│ Encoder Block │
└────────────────┘
↓
...
↓
Encoder Output
│
│
↓
输出序列 → Token Embedding
↓
Positional Encoding
↓
Masked Self-Attention
↓
Add & Norm
↓
Encoder-Decoder Attention
↓
Add & Norm
↓
Feed Forward Network
↓
Add & Norm
↓
Linear
↓
Softmax
↓
下一个 Token
从职责上,我们也可以把 Transformer 简化成:
text
Embedding
↓
把 Token 转换成向量
Positional Encoding
↓
告诉模型 Token 的位置
Attention
↓
建立 Token 之间的关系
FFN
↓
进一步处理 Token 表示
Residual + LayerNorm
↓
帮助深层网络稳定训练
Encoder
↓
理解输入
Decoder
↓
生成输出
十七、一个容易产生的误解:现在的 GPT 还是完整 Transformer 吗?
看到这里,可能会有一个问题:
我们现在使用的大语言模型,例如 GPT,真的还是上面这种完整的 Encoder-Decoder Transformer 吗?
答案是:
不完全是。
Transformer 最初是一种完整的 Encoder-Decoder 架构。
但后来研究人员发现,不同任务可以使用 Transformer 的不同部分。
于是逐渐出现了三种非常重要的架构:
text
Encoder-only
Decoder-only
Encoder-Decoder
例如:
Encoder-only
典型代表:
text
BERT
主要使用 Transformer Encoder。
更擅长:
text
文本理解
文本分类
命名实体识别
Decoder-only
典型代表:
text
GPT
主要使用 Transformer Decoder 中适合自回归生成的结构。
更擅长:
text
文本生成
对话
代码生成
今天的大语言模型,大量采用的就是这种:
Decoder-only Transformer。
Encoder-Decoder
典型代表:
text
T5
更适合:
text
机器翻译
文本摘要
输入序列 → 输出序列
所以:
Transformer 并不是只有一种固定形态。
它更像是一套非常成功的基础架构思想。
不同模型可以根据任务:
text
只使用 Encoder
只使用 Decoder
或者同时使用 Encoder + Decoder
这也是 Transformer 后来能够广泛发展的一个重要原因。
十八、现在,我们对 Transformer 应该有什么整体认识?
经过这一篇文章,我们暂时还没有深入:
text
Q、K、V
也没有真正计算:
text
Attention Score
但现在,我们至少应该建立起这样一张地图:
text
Transformer
│
┌───────────────┴───────────────┐
│ │
↓ ↓
Encoder Decoder
│ │
│ Masked Self-Attention
│ │
Self-Attention ↓
│ Encoder-Decoder Attention
↓ │
Add & Norm ↓
↓ FFN
FFN │
↓ ↓
Add & Norm Add & Norm
并且知道每个模块的大致职责:
| 模块 | 核心作用 |
|---|---|
| Embedding | 将 Token 转换为向量 |
| Positional Encoding | 提供位置信息 |
| Self-Attention | 建立 Token 之间的关系 |
| Multi-Head Attention | 从多个角度建立关系 |
| FFN | 对 Token 表示进行非线性变换 |
| Residual Connection | 保留信息,帮助深层训练 |
| LayerNorm | 稳定数据分布 |
| Encoder | 理解输入 |
| Decoder | 逐步生成输出 |
如果能够建立起这张地图,那么后面学习 Transformer 的细节就会轻松很多。
十九、小结
这一篇,我们主要完成了一件事情:
从整体上认识 Transformer 到底是什么。
Transformer 最初采用的是:
Encoder-Decoder 架构。
其中:
text
Encoder
负责:
理解输入序列,并生成包含上下文信息的表示。
而:
text
Decoder
负责:
在参考已有输出和 Encoder 信息的基础上,逐步生成新的 Token。
一个典型的 Encoder Block 包含:
text
Multi-Head Self-Attention
↓
Add & Norm
↓
Feed Forward Network
↓
Add & Norm
而 Decoder Block 则比 Encoder 多出了:
text
Masked Self-Attention
以及:
text
Encoder-Decoder Attention
同时,因为 Transformer 不像 RNN 一样天然按照顺序处理数据,所以还需要:
Positional Encoding。
最终,整个 Transformer 可以理解成:
text
Token
↓
Embedding
↓
加入位置信息
↓
Attention 建立关系
↓
FFN 进一步处理
↓
多层 Block 不断提取信息
↓
完成理解或生成任务
下一篇:Self-Attention 到底是什么?
现在,我们已经看清了 Transformer 的整体结构。
但是整套架构中最核心的问题仍然没有真正解决:
Attention 到底是怎么工作的?
为什么 Transformer 会把输入转换成:
text
Q
K
V
为什么要计算:
Q K T QK^T QKT
为什么还要进行:
softmax ( Q K T d k ) \operatorname{softmax} \left( \frac{QK^T}{\sqrt{d_k}} \right) softmax(dk QKT)
这些操作究竟在做什么?
下一篇,我们将正式进入 Transformer 最核心的部分:
Self-Attention。
我们会从一个具体的句子开始,一步一步理解:
text
Q 到底是什么?
K 到底是什么?
V 又是什么?
然后亲手走完整个 Attention 的计算过程。
只有真正理解了 Self-Attention,Transformer 的核心才算真正开始展开。