在深度学习席卷自然语言处理的历程中, 2017 年可能是最具转折意义的一年。 Google 的论文《 Attention Is All You Need 》提出了 Transformer 架构,彻底改变了 NLP 的游戏规则。在此之前, RNN 和 LSTM 统治了序列建模领域,但它们的顺序处理特性限制了并行化能力,长距离依赖问题也始终困扰着研究者。 Transformer 通过完全抛弃循环结构,仅依靠注意力机制就实现了更好的性能和更高的训练效率。
今天, BERT 、 GPT 、 T5 等模型的成功都建立在 Transformer 的基础上。理解 Transformer 不仅是理解现代 NLP 的关键,更是深入大语言模型的必经之路。本文将从 Seq2Seq 的局限性讲起,逐步推导出注意力机制的必要性,然后深入剖析 Transformer 的每个组件,最后通过 PyTorch 代码实现一个完整的 Transformer 模型。
Seq2Seq 的局限性:信息瓶颈问题
在注意力机制出现之前, Seq2Seq 模型是处理序列到序列任务(如机器翻译)的主流方法。典型的 Seq2Seq 架构包含两个部分:
- 编码器( Encoder):将输入序列
编码为固定长度的上下文向量 - 解码器( Decoder):基于上下文向量
生成输出序列
这个架构的核心问题在于信息瓶颈:无论输入序列有多长,编码器都必须将所有信息压缩到一个固定维度的向量
信息瓶颈的直观理解
想象你在翻译一篇 100 个词的文章。传统 Seq2Seq 要求你先读完整篇文章,然后在脑海中只记住一个"总结性的想法"(上下文向量),接着仅凭这个总结来翻译每一个词。这个总结无论多么精炼,也不可能完美保留原文的所有细节。
更具体地说,假设我们翻译句子:"The animal didn't cross the street because it was too tired."。当翻译到 "it" 时,需要知道 "it" 指的是 "animal" 而非 "street"。如果编码器的上下文向量丢失了这个关联信息,解码器就无法正确翻译。
数学上的表达
在标准 Seq2Seq 中,编码器的最后隐状态作为上下文向量:
解码器在时刻
注意这里的
注意力机制的诞生背景
2014 年, Bahdanau 等人在论文《 Neural Machine Translation by Jointly Learning to Align and Translate 》中首次提出了注意力机制。他们的核心洞察是:与其让编码器生成一个固定的上下文向量,不如让解码器在每个时刻动态地"查看"输入序列的不同部分。
这个想法非常符合人类翻译的过程:当我们翻译一个长句子时,会不断地回头看原文的不同片段,将注意力聚焦在当前翻译所需的关键信息上。
注意力的直观定义
注意力机制的本质是一个加权求和过程:
- 编码器为输入序列的每个位置生成一个隐状态
- 解码器在时刻
计算每个编码器隐状态的重要性权重 - 用这些权重对编码器隐状态进行加权求和,得到当前时刻的上下文向量
数学表达为:
其中权重
核心是:不同时刻的
Bahdanau 注意力(加性注意力)
Bahdanau 注意力是最早提出的注意力机制,也称为加性注意力( Additive Attention)或拼接注意力( Concat Attention)。
计算流程
给定解码器在时刻
步骤 1:计算对齐分数( Alignment Scores)
对于每个编码器位置
这里: -
这个公式的含义是:将解码器状态
步骤 2:归一化为注意力权重
使用 Softmax 将分数归一化为概率分布:
步骤 3:计算上下文向量
用注意力权重对编码器隐状态加权求和:
步骤 4:生成输出
将上下文向量与解码器状态结合,生成当前时刻的输出:
为什么叫"加性"注意力?
名字来源于分数计算公式中的加法操作:
可视化理解
假设我们翻译 "I love deep learning" 到法语。当解码器生成 "apprentissage" (learning) 时,注意力权重可能是:
| 输入词 | I | love | deep | learning |
|---|---|---|---|---|
| 权重 | 0.05 | 0.10 | 0.20 | 0.65 |
权重 0.65 表示解码器当前高度关注输入的 "learning",这符合翻译的对应关系。
Luong 注意力(点积注意力)
2015 年, Luong 等人提出了几种更简洁的注意力变体。其中核心:点积注意力( Dot-Product Attention),它极大简化了分数计算。
三种变体
Luong 提出了三种计算对齐分数的方法:
1. 点积( Dot)
直接计算解码器状态与编码器状态的点积。要求两者维度相同。
2. 通用( General)
引入一个权重矩阵
3. 拼接( Concat)
与 Bahdanau 类似,但使用拼接而非求和。
点积注意力的优势
点积注意力相比 Bahdanau 注意力有明显优势:
- 计算更简单:只需矩阵乘法,无需复杂的神经网络层
- 速度更快:可以高度并行化,利用现代 GPU 的矩阵运算优化
- 参数更少:不需要额外的
、 、 等参数
这些优势使得点积注意力成为 Transformer 的基础。
Bahdanau vs Luong:关键区别
| 特性 | Bahdanau 注意力 | Luong 注意力 |
|---|---|---|
| 提出时间 | 2014 | 2015 |
| 分数计算 | 加性(拼接 + 前馈网络) | 点积 / 通用 / 拼接 |
| 使用的解码器状态 | ||
| 计算复杂度 | 较高 | 较低(特别是点积) |
| 参数量 | 较多 | 较少(特别是点积) |
Self-Attention:自注意力详解
前面介绍的注意力机制都是编码器-解码器注意力( Encoder-Decoder Attention),即解码器关注编码器的输出。 Transformer 引入了一个更强大的概念:自注意力( Self-Attention)。
自注意力的核心思想
自注意力允许序列中的每个位置关注同一序列中的其他所有位置。这让模型能够捕捉序列内部的依赖关系,而无需依赖循环结构。
举个例子,考虑句子:"The animal didn't cross the street because it was too tired."
- 当处理 "it" 时,自注意力可以计算 "it" 与 "animal"、"street" 的关联程度
- 模型会学习到 "it" 与 "animal" 的关联更强,从而正确理解指代关系
Query 、 Key 、 Value:注意力的三要素
自注意力引入了三个核心概念:Query(查询)、Key(键)、Value(值)。这个命名来源于信息检索系统的类比。
直觉: Q/K/V 的信息检索类比
想象你在图书馆查资料:
- Query(查询):你的搜索关键词,比如"机器学习基础"
- Key(索引):每本书的关键词标签(目录卡片上的主题词)
- Value(内容):书架上书的实际内容
查找过程:
- 匹配阶段:用你的 Query 和所有 Key 对比,计算相关性分数(哪些书最匹配你的需求?)
- 加权阶段:用 Softmax 将分数转换为权重,决定每本书的重要性(最相关的书权重最高)
- 提取阶段:按权重提取 Value,相关性高的书贡献更多内容
在 Transformer 中:
- Query:当前位置想要获取什么信息("我需要什么?")
- Key:每个位置能提供什么信息("我有什么?")
- Value:每个位置的实际内容("我的内容是什么?")
为什么要分开 Q 、 K 、 V?
如果直接用同一个向量表示,就无法区分"需求"和"供给"。分开后:
- Query 表达"需求":当前位置需要什么类型的信息
- Key 表达"供给":其他位置能提供什么类型的信息
- Value 表达"内容":实际要传递的信息是什么
这种设计让模型可以学习到:哪些位置( Key)与当前需求( Query)匹配,然后提取对应的内容( Value)。
数学形式化
给定输入序列的嵌入
符号说明:
-$X ^{n d_{model}}
直觉理解:
- 每一行
分别是位置 的查询、键、值向量 - 是可学习的,训练过程中自动学会如何分解输入为 Q 、 K 、 V - 不同的投影矩阵让模型可以从同一个输入中提取出不同角度的信息
注意力计算的直观理解
自注意力的计算可以分解为三步:
步骤 1:计算相似度
对于位置
点积越大,表示
步骤 2:归一化
使用 Softmax 归一化为权重:
步骤 3:加权求和
用权重对值向量加权求和,得到位置
矩阵形式的简洁表达
上述过程可以用矩阵形式一次性处理所有位置:
这里: -
为什么自注意力如此强大?
- 并行化:所有位置可以同时计算,无需像 RNN 那样顺序处理
- 长距离依赖:任意两个位置都可以直接交互,无论距离多远
- 灵活性:通过学习
,模型可以自适应地捕捉不同类型的关系
Scaled Dot-Product Attention:缩放点积注意力
Transformer 使用的注意力机制称为 Scaled Dot-Product
Attention(缩放点积注意力)。相比普通点积注意力,它引入了一个缩放因子
为什么需要缩放?(公式动机)
问题 1:点积数值爆炸
当
数学推导:
假设
点积计算为:
由于
直觉:点积是
问题 2: Softmax 饱和与梯度消失
当点积值过大时(如
此时:
- 梯度消失: Softmax 在饱和区的梯度接近 0,反向传播信号微弱
- 分布极端化:所有权重几乎都集中在最大值上,模型只关注一个位置,丧失了分布式注意力的优势
缩放因子的作用
通过除以
方差推导:
完整公式:
符号说明:
-
-
:计算所有 Query-Key 对的相似度,形成 的矩阵 - 除以
:归一化,使相似度的数值范围适合 Softmax(通常在 之间) - Softmax 按行:每一行是一个 Query 对所有 Key 的注意力分布,和为 1
- 乘以
:加权求和,每个 Query 获得所有 Value 的加权平均
实际效果对比(数值示例)
场景:
缩放后:
关键结论: 缩放因子
Mask 机制
在某些场景下,需要阻止某些位置之间的注意力。例如:
- Padding Mask:忽略填充位置
- Look-Ahead Mask:在解码器中,阻止当前位置关注未来位置
实现方式是在 Softmax 之前,将需要屏蔽的位置的分数设为
其中
Multi-Head Attention:多头注意力
单个注意力头只能学习一种类型的关系。为了让模型同时关注不同的表示子空间, Transformer 使用了多头注意力( Multi-Head Attention)。
核心思想
多头注意力将 Q 、 K 、 V 分别线性投影到
数学定义
其中每个注意力头的计算为:
参数维度: -
为什么需要多头?
直观类比:多头注意力就像用多双眼睛同时观察一个物体。每个头可以学习不同类型的关系:
- Head 1:可能学习句法关系(如主谓宾)
- Head 2:可能学习语义关系(如同义词)
- Head 3:可能学习位置关系(如相邻词)
实际配置
在原始 Transformer 论文中: -
多头的优势
- 表示能力:不同子空间可以捕捉不同方面的信息
- 鲁棒性:即使某些头学习失败,其他头仍能提供有用信息
- 可解释性:可以可视化不同头学到的注意力模式
Transformer 完整架构
现在可以将所有组件组合起来,构建完整的 Transformer 架构。 Transformer 采用经典的编码器-解码器结构,但完全基于注意力机制,没有任何循环或卷积层。
整体结构
Transformer 由以下部分组成:
- 编码器( Encoder):
个相同的层堆叠(原论文 ) - 解码器( Decoder):
个相同的层堆叠 - 输入嵌入( Input Embedding):将词转换为向量
- 位置编码( Positional Encoding):添加位置信息
- 输出层:线性层 + Softmax,生成词表上的概率分布
编码器层的结构
每个编码器层包含两个子层:
子层 1:多头自注意力
输入序列自己关注自己。
子层 2:前馈网络
这是一个两层全连接网络,中间使用 ReLU 激活。通常中间层维度是
残差连接与层归一化
每个子层都使用残差连接和层归一化:
完整的编码器层可以表示为:
解码器层的结构
每个解码器层包含三个子层:
子层 1:掩码多头自注意力
使用 Look-Ahead Mask 防止关注未来位置。
子层 2:编码器-解码器注意力
子层 3:前馈网络
与编码器相同。
完整的解码器层:
架构图(文本表示)
1 | 输入序列 (源语言) 目标序列 (目标语言) |
编码器-解码器结构详解
Transformer 的编码器-解码器结构有明确的分工:
编码器的职责
编码器的目标是将输入序列转换为一系列连续表示,这些表示捕捉了输入的语义信息。
关键特性:
- 双向注意力:每个位置可以关注整个输入序列(包括前后)
- 并行处理:所有位置同时计算,无需等待前一个位置
- 多层抽象:通过堆叠多层,逐步抽象出高层语义
信息流动:
- 输入:词嵌入 + 位置编码
- 第 1 层:学习局部依赖(如短语结构)
- 第 2-3 层:学习句法关系(如主谓宾)
- 第 4-6 层:学习语义关系(如指代、推理)
解码器的职责
解码器基于编码器的表示,自回归地生成输出序列。
关键特性:
- 单向注意力:只能关注已生成的位置(通过 Look-Ahead Mask)
- 自回归生成:每个位置依赖前面位置的输出
- 跨序列注意力:通过编码器-解码器注意力访问输入信息
信息流动:
- 已生成的输出 + 位置编码 → Masked Self-Attention
- 结合编码器表示 → Cross-Attention
- 前馈网络进一步处理
- 输出层生成下一个词的概率
训练 vs 推理
训练阶段( Teacher Forcing):
解码器的输入是真实的目标序列(向右平移一位)。通过 Look-Ahead Mask,每个位置只能看到前面的真实标签,模拟自回归生成。
推理阶段(自回归解码):
解码器的输入是自己生成的序列。每次生成一个词,然后将其添加到输入序列,继续生成下一个词,直到生成结束符。
位置编码详解
由于 Transformer 完全抛弃了循环结构,它本身无法感知序列的顺序信息。为了解决这个问题,需要显式地添加位置编码( Positional Encoding)。
为什么需要位置编码?
什么是 Embedding?
在深入位置编码之前,先理解什么是 Embedding(嵌入)。
通俗理解: Embedding 就是"用向量表示对象"——把离散的符号(如词、 ID)转换成连续的数字向量。
为什么需要 Embedding?
计算机只认识数字,不认识"苹果"、"香蕉"这些词。我们需要把它们转换成数字向量,同时保留语义信息(相似的词有相似的向量)。
例子:
传统的独热编码( One-Hot):
- 苹果 =
- 香蕉 =
- 橙子 =
- 汽车 =
问题:
- 维度太高(词表有 10 万个词就需要 10 万维)
- 无法表达相似性(苹果和香蕉的相似度 = 苹果和汽车的相似度 = 0)
- 稀疏表示,浪费空间
Embedding 改进:
- 苹果 =
(稠密向量,如 256 维) - 香蕉 =
(与苹果相似) - 橙子 =
- 汽车 =
(与水果差异大)
优点:
- 维度低(通常 128-512 维)
- 相似的对象向量接近:
3. 稠密表示,信息丰富
核心思想: 用低维稠密向量表示高维稀疏对象,同时保留语义相似性。
位置编码的必要性
由于 Transformer 完全抛弃了循环结构,它本身无法感知序列的顺序信息。为了解决这个问题,需要显式地添加位置编码( Positional Encoding)。
考虑两个句子:
- "The cat chased the mouse."(猫追老鼠)
- "The mouse chased the cat."(老鼠追猫)
词汇完全相同,但顺序不同导致语义相反。如果没有位置信息,自注意力机制会给出相同的输出,因为它只是一个加权求和,与顺序无关。
数学上的问题:
自注意力的计算
如果对输入序列重新排列,注意力输出也会按相同方式重新排列,但每个位置的输出本身不变。这意味着模型无法区分"猫追老鼠"和"老鼠追猫"。
正弦/余弦位置编码
原始 Transformer 使用固定的正弦和余弦函数生成位置编码:
其中: -
正弦位置编码的优点
1. 唯一性
每个位置的编码都是唯一的,不会重复。
2. 外推性
模型可以处理比训练时更长的序列。由于编码是通过数学公式计算的,任意位置都能生成编码,即使该位置在训练数据中从未出现。
3. 相对位置信息
对于固定的偏移量
证明:利用三角恒等式
可以将
可学习位置编码
另一种方法是将位置编码作为可学习参数:
其中
优点: - 模型可以根据数据自适应学习最优的位置表示 - 在某些任务上性能略优于正弦编码
缺点: - 无法外推到更长序列(超过
实践选择
- BERT 、 GPT 系列:使用可学习位置编码
- 原始 Transformer:使用正弦位置编码
- T5 、 DeBERTa:使用相对位置编码(更复杂的变体)
现代实践中,可学习位置编码更常见,因为序列长度通常有上限(如 512 或 1024),而可学习编码的性能通常更好。
位置编码的添加方式
位置编码直接加到输入嵌入上:
这里没有使用拼接,因为加法保持了维度不变,且在实验中效果很好。直观理解是:嵌入提供内容信息,位置编码提供顺序信息,两者相加形成完整表示。
Layer Normalization vs Batch Normalization
Transformer 使用 Layer Normalization(层归一化) 而非深度学习中更常见的 Batch Normalization(批归一化)。理解它们的区别对于理解 Transformer 的训练稳定性至关重要。
Batch Normalization 回顾
批归一化在批次维度上归一化:
其中
问题:
- 依赖批次大小:小批次时统计量不稳定
- 序列长度不一致: NLP 任务中,不同样本的序列长度不同,批归一化会受到填充影响
- 训练/推理不一致:推理时需要使用训练时累积的统计量
Layer Normalization
层归一化在特征维度上归一化:
其中
对于形状为
Layer Norm 的优势
1. 独立于批次大小
每个样本独立归一化,不受批次大小影响。这对于 NLP 任务很重要,因为内存限制常导致小批次。
2. 适合序列数据
不同位置的归一化参数相同,不会受到序列长度变化的影响。
3. 训练/推理一致
无需维护移动平均统计量,训练和推理使用相同的计算。
Layer Norm 的位置
Transformer 中有两种 Layer Norm 的放置方式:
Post-LN(原始论文):
归一化在残差连接之后。
Pre-LN(更稳定):
归一化在子层之前。现代实现(如 GPT-2 、 GPT-3)通常使用 Pre-LN,因为它训练更稳定,梯度流动更好。
可学习的仿射变换
Layer Norm 之后通常添加可学习的缩放和偏移:
其中
残差连接的作用
Transformer 的每个子层都使用残差连接( Residual
Connection):
残差连接最初由 ResNet 提出,在 Transformer 中同样至关重要。
为什么需要残差连接?
梯度消失的直观理解
在深入残差连接之前,先理解什么是梯度消失。
信号衰减类比:
想象你在山谷中大喊一声,声音经过多次反弹传播:
- 第 1 次反弹:声音清晰,能量 100%
- 第 5 次反弹:声音变弱,能量约 30%
- 第 10 次反弹:几乎听不见,能量约 1%
- 第 20 次反弹:完全听不见,能量接近 0
梯度在深层网络中的传播类似:
- 每经过一层,梯度乘以一个数(如层的权重导数,通常
) - 经过多层后:梯度 =
- 如果每层的权重导数都是 0.5,经过 20 层:
(几乎为 0) - 结果:早期层的参数几乎不更新,只有后面几层在学习
数学证明( RNN 的例子):
假设 RNN 的权重矩阵
当
为什么 LSTM 能缓解梯度消失?
LSTM 通过门控机制,让梯度有"高速公路"( highway):
- 遗忘门
时:信息几乎无损传播
- 加法操作:
(加法不衰减梯度) - 类似给信号加了"中继站",防止衰减
但 LSTM 仍然是顺序结构,有两个问题:
- 无法并行化(必须等前一步计算完)
- 长距离依赖仍然困难(虽然比 RNN 好)
Transformer 的残差连接提供了类似但更强大的解决方案。
1. 缓解梯度消失
在深层网络中,梯度在反向传播时会逐层衰减。残差连接提供了一条从输出直接到输入的"快捷路径"( shortcut),梯度可以不经过中间层直接传播。
数学上,反向传播时:
关键点: 即使
直觉: 残差连接相当于在网络中铺设了一条"梯度高速公路",绕过可能衰减梯度的复杂层。
2. 简化训练
残差连接让网络更容易学习恒等映射。如果某一层对当前任务没有帮助,它只需将输出设为
0,整体就退化为恒等映射
对比:
- 无残差连接:层需要学习完整的变换
,即使目标是恒等映射也很难学 - 有残差连接:层只需学习残差
(差值),学习恒等映射时只需 ,非常简单
实际意义: 训练初期,大部分层可能还不知道该学什么,残差连接让它们可以先"什么都不做"(输出接近 0),不会破坏已有信息,随着训练推进再逐渐学习有用的变换。
3. 允许更深的网络
原始 Transformer 使用 6 层,但残差连接使得堆叠 12 层( BERT-base)、 24 层( BERT-large)、甚至 96 层( GPT-3)成为可能。
历史对比:
- ResNet 之前:网络超过 20 层就很难训练,性能反而下降(退化问题)
- ResNet 之后: 152 层网络轻松训练,性能随深度提升
Transformer 借鉴了这一设计,让大模型成为可能。
残差连接与 Layer Norm 的协同
残差连接与 Layer Norm 的组合特别强大:
这种组合是 Transformer 训练稳定性的关键。
实际效果
在 Transformer 论文中,作者发现: - 没有残差连接, 6 层模型难以收敛 - 有残差连接,可以轻松训练更深的模型
现代大模型(如 GPT-3 的 96 层)都严重依赖残差连接。
实战:从零实现 Transformer( PyTorch)
现在我们用 PyTorch 从零实现一个完整的 Transformer 模型。这个实现将包含所有核心组件,并附有详细注释。
缩放点积注意力
实现目的
实现注意力机制的核心计算:给定 Query 、 Key 、 Value,计算 Query 对 Value 的加权平均。
实现思路
- 计算 Query 和 Key 的点积相似度(
) - 缩放:除以
,防止数值过大 - 应用 Mask(可选):屏蔽不需要关注的位置
- Softmax 归一化:转换为概率分布
- 加权求和:用权重对 Value 进行加权平均
代码实现
1 | import torch |
代码解读
为什么 transpose(-2, -1)?
因为 matmul 要求最后两维可以相乘:
-
为什么 dim=-1?
对每个 Query,计算它对所有 Key 的注意力分布,所以在最后一维( seq_len 维度)做 softmax 。
数值稳定性
PyTorch 的 softmax 已经内置了数值稳定技巧(先减去最大值再计算),所以不需要手动处理。
注意事项
内存开销:注意力权重的形状是
- 序列长度 512 时,单个样本需要约 1MB 内存- 序列长度 2048 时,单个样本需要约 16MB 内存
- 长序列时内存开销巨大!
- 序列长度 2048 时,单个样本需要约 16MB 内存
梯度检查点:对于长序列,可以使用 PyTorch 的 gradient checkpointing 节省内存:
1
2from torch.utils.checkpoint import checkpoint
output = checkpoint(self.attention_layer, q, k, v)Mask 处理:实际使用时需要区分不同类型的 mask:
- Padding mask: 屏蔽填充位置(值为 0)
- Causal mask: 屏蔽未来位置(自回归模型)
- 两种 mask 可以组合使用(取交集)
多头注意力
1 | class MultiHeadAttention(nn.Module): |
前馈网络
1 | class PositionwiseFeedForward(nn.Module): |
位置编码
1 | class PositionalEncoding(nn.Module): |
编码器层
1 | class EncoderLayer(nn.Module): |
解码器层
1 | class DecoderLayer(nn.Module): |
完整 Transformer
1 | class Transformer(nn.Module): |
使用示例
1 | # 创建模型 |
训练循环示例
1 | import torch.optim as optim |
实战:使用 HuggingFace Transformers
虽然从零实现有助于理解原理,但在实际项目中,我们通常使用成熟的库。 HuggingFace Transformers 提供了预训练模型和便捷的 API 。
安装
1 | pip install transformers torch |
机器翻译示例
1 | from transformers import MarianMTModel, MarianTokenizer |
使用 T5 进行多种任务
T5( Text-to-Text Transfer Transformer)将所有 NLP 任务都统一为文本到文本的格式。
1 | from transformers import T5Tokenizer, T5ForConditionalGeneration |
微调预训练模型
1 | from transformers import Trainer, TrainingArguments |
自定义 Transformer 配置
1 | from transformers import BertConfig, BertModel |
可视化注意力权重
1 | from transformers import BertTokenizer, BertModel |
❓ Q&A: Transformer 常见问题
Q1: 为什么 Transformer 比 RNN 快?
A: 主要原因是并行化。 RNN
必须顺序处理序列(
- 训练阶段:所有时刻的输出可以一次性并行计算
- 推理阶段:编码器仍然并行;解码器仍需自回归,但单步计算更快
此外,自注意力的计算是高度优化的矩阵乘法,可以充分利用 GPU 的并行计算能力。
Q2: Transformer 的复杂度是多少?
A: 对于序列长度
- 自注意力层:
- 计算 需要 - 计算 需要 - 前馈层:
当 很大时(如长文档),自注意力的 复杂度成为瓶颈。这催生了许多高效 Transformer 变体: - Longformer:使用局部注意力和稀疏注意力,复杂度降为
( 是窗口大小) - Linformer:使用低秩近似,复杂度降为
( 是投影维度) - Performer:使用核方法近似注意力,复杂度降为
Q3: Transformer 如何处理变长序列?
A: 通过填充( Padding)和掩码( Masking):
- 填充:将所有序列填充到批次中的最大长度,使用特殊的 PAD token
- Padding Mask:在注意力计算时,将对应 PAD
位置的分数设为
,使其 Softmax 输出为 0
示例:
1 | def create_padding_mask(seq, pad_idx=0): |
Q4: 为什么需要 Warmup 和特殊的学习率调度?
A: Transformer
对学习率非常敏感。原论文使用的调度策略是:
这个策略的特点: 1. Warmup 阶段(前 4000 步):线性增加学习率 2. 衰减阶段:学习率按步数的平方根倒数衰减
原因: - Warmup 避免了训练初期梯度过大导致的不稳定 - 逐步衰减 帮助模型在后期精细调整
现代实践中,常用的还有余弦退火( Cosine Annealing)等策略。
Q5: Encoder-only 、 Decoder-only 、 Encoder-Decoder 有什么区别?
A: 这是三种不同的 Transformer 架构变体:
| 架构 | 结构 | 典型模型 | 适用任务 |
|---|---|---|---|
| Encoder-only | 只有编码器 | BERT, RoBERTa | 分类、 NER 、问答(理解) |
| Decoder-only | 只有解码器 | GPT, GPT-2/3 | 生成、续写、对话 |
| Encoder-Decoder | 完整编码器+解码器 | T5, BART, mT5 | 翻译、摘要、问答生成 |
核心区别: - Encoder-only:使用双向注意力,擅长理解和表示 - Decoder-only:使用单向注意力( Look-Ahead Mask),擅长生成 - Encoder-Decoder:结合两者优势,编码器理解输入,解码器生成输出
Q6: 什么是 Teacher Forcing?
A: Teacher Forcing 是训练 Seq2Seq 模型(包括 Transformer 解码器)的常用技术:
- 训练时:解码器的输入是真实的目标序列(向右平移一位)
- 推理时:解码器的输入是自己生成的序列
示例:
训练时翻译 "I love AI" → "J'aime l'IA"
| 时刻 | 解码器输入 | 目标输出 |
|---|---|---|
| 1 | <bos> |
J' |
| 2 | J' | aime |
| 3 | aime | l' |
| 4 | l' | IA |
| 5 | IA | <eos> |
即使时刻 2 的预测错误,时刻 3 仍使用真实的 "aime" 而非错误预测。
优点: 训练快速稳定,梯度信号清晰 缺点: 训练和推理的分布不一致( Exposure Bias)
Q7: Transformer 如何处理很长的序列?
A: 标准 Transformer 受限于
1. 分段处理( Sliding Window)
将长序列切分成多个固定长度的段,分别处理后合并。
2. 稀疏注意力
只关注局部窗口或特定模式的位置,如: - Longformer:局部窗口 + 全局注意力 - BigBird:随机注意力 + 窗口注意力 + 全局注意力
3. 分层注意力
先在局部块内做自注意力,再在块之间做自注意力。
4. 记忆机制
如 Transformer-XL,使用额外的记忆存储历史信息,避免重复计算。
5. 高效 Transformer
- Linformer:低秩近似
- Performer:核方法
- FNet:用傅里叶变换替代注意力
Q8: 为什么 BERT 用 Encoder-only, GPT 用 Decoder-only?
A: 这与它们的预训练任务相关:
BERT( Encoder-only): - 预训练任务:Masked Language Modeling( MLM) - 需要双向上下文来预测被遮盖的词 - 适合理解任务(分类、 NER 、问答)
GPT( Decoder-only): - 预训练任务:Causal Language Modeling( CLM) - 根据前文预测下一个词,只能看到左侧上下文 - 适合生成任务(续写、对话、代码生成)
设计哲学: - 如果任务需要理解双向上下文 → 用 Encoder - 如果任务需要自回归生成 → 用 Decoder - 如果任务是序列到序列转换 → 用 Encoder-Decoder
Q9: Transformer 能否处理其他模态(如图像、音频)?
A: 完全可以! Transformer 的核心是自注意力机制,不限于文本。
图像: - Vision Transformer (ViT):将图像切分成固定大小的 patch(如 16x16),每个 patch 展平成向量,加上位置编码,输入 Transformer - 效果:在大规模数据上, ViT 的性能超越 CNN
音频: - Whisper( OpenAI):用 Transformer 处理音频的梅尔频谱图,实现语音识别和翻译 - Wav2Vec 2.0:直接从原始音频波形学习表示
多模态: - CLIP:同时处理图像和文本,学习统一的嵌入空间 - Flamingo:结合视觉和语言 Transformer,实现视觉问答
关键技巧: 1. 将其他模态转换为序列表示( patch 、 token) 2. 设计合适的位置编码(如 2D 位置编码用于图像) 3. 根据模态特性调整模型结构
Q10: Transformer 的局限性是什么?
A: 尽管 Transformer 非常强大,但仍有局限:
1. 复杂度
2. 缺乏归纳偏置
RNN 有时序偏置, CNN 有局部性偏置。 Transformer 缺乏这些先验,需要更多数据和计算才能学到这些模式。
3. 位置编码的局限
固定的正弦位置编码无法泛化到极长序列;可学习位置编码无法外推。
4. 过拟合小数据
由于参数量大,在小数据集上容易过拟合,需要大量预训练或正则化。
5. 可解释性
虽然可以可视化注意力权重,但多层多头的复杂交互仍难以完全解释。
6. 能耗
训练大型 Transformer(如 GPT-3 的 1750 亿参数)需要巨大的计算资源,环境成本高。
未来方向: - 高效 Transformer 变体(线性复杂度) - 更好的位置编码(如相对位置编码) - 与其他架构的混合(如 Transformer + CNN) - 蒸馏和量化技术降低推理成本
总结
Transformer 的出现是 NLP 历史上的里程碑。通过完全基于注意力机制的架构,它解决了 RNN 的顺序处理瓶颈和长距离依赖问题,开启了预训练大模型的时代。
核心要点回顾:
- 注意力机制解决了 Seq2Seq 的信息瓶颈,允许模型动态关注输入的不同部分
- Self-Attention 通过 Query 、 Key 、 Value 三元组,让序列内部位置直接交互
- Scaled Dot-Product Attention 通过缩放因子
稳定训练 - Multi-Head Attention 让模型同时学习多种类型的关系
- 位置编码为无序的注意力机制注入顺序信息
- 残差连接和 Layer Norm 保证深层网络的训练稳定性
- Encoder-Decoder 结构分工明确:编码器理解输入,解码器生成输出
Transformer 不仅统治了 NLP 领域( BERT 、 GPT 、 T5),还扩展到了计算机视觉( ViT 、 DINO)、语音( Whisper 、 Wav2Vec)、多模态( CLIP 、 Flamingo)等多个领域。理解 Transformer 的原理和实现,是深入现代深度学习的必修课。
随着技术的发展,高效 Transformer 、长上下文模型、多模态模型等方向正在快速演进。但无论如何变化, Transformer 的核心思想——让数据自己说话,通过注意力机制学习关系——将继续指引未来的研究方向。
- 本文标题:自然语言处理(四)—— 注意力机制与 Transformer
- 本文作者:Chen Kai
- 创建时间:2024-02-20 15:45:00
- 本文链接:https://www.chenk.top/%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%A4%84%E7%90%86%EF%BC%88%E5%9B%9B%EF%BC%89%E2%80%94%E2%80%94-%E6%B3%A8%E6%84%8F%E5%8A%9B%E6%9C%BA%E5%88%B6%E4%B8%8ETransformer/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!