每周 6-10 小时
本周目标
深入理解 Transformer 架构——当前 NLP 和多模态 AI 的基石。掌握 Self-Attention 机制(模型如何"关注"输入中的不同部分)、 Multi-Head Attention(并行关注多种信息)、位置编码(弥补序列位置信息的缺失)。
学习内容
9.1 序列建模基础
9.1.1 序列任务类型:分类、生成、翻译
序列数据在现实世界中无处不在,从文本、语音到时间序列数据,都需要特殊的建模方法。根据任务目标的不同,序列任务可以分为以下三类:
序列分类任务旨在将整个输入序列映射到一个类别标签。典型的应用包括情感分析(判断一段文本的情感倾向)、文本分类(将文档归类到不同主题)和命名实体识别(识别文本中的实体类型)。这类任务的特点是输入为变长序列,输出为固定维度的类别分布。
序列生成任务要求模型根据给定的上下文或条件生成新的序列。语言建模(预测下一个词)、文本摘要(生成简洁的摘要)和对话生成都属于此类任务。生成任务通常采用自回归方式,逐个生成序列中的元素。
序列到序列(Seq2Seq)任务是最具挑战性的序列任务类型,需要将输入序列转换为输出序列,且两者长度可能不同。机器翻译是最典型的 Seq2Seq 任务,例如将英文句子"Hello world"翻译为中文"你好世界"。其他应用包括语音识别(音频到文本)和代码生成(自然语言到程序代码)。
9.1.2 编码器-解码器框架:Seq2Seq 架构
编码器-解码器(Encoder-Decoder)框架是解决 Seq2Seq 问题的经典架构。该框架由两个主要组件构成:
**编码器(Encoder)**负责将变长的输入序列压缩为固定长度的上下文向量(Context Vector)。编码器通常采用循环神经网络(RNN)或其变体(LSTM、GRU),逐词处理输入序列,最终状态编码了整个输入序列的信息。
**解码器(Decoder)**则基于编码器产生的上下文向量生成输出序列。解码器同样采用 RNN 结构,在每个时间步生成一个输出词,并将该词作为下一个时间步的输入,直到生成结束标记。
编码器-解码器框架的计算流程可以形式化表示为:
给定输入序列 ,编码器计算:
其中 是 RNN 单元的非线性变换函数, 是第 步的隐藏状态。编码器最终状态 作为上下文向量 。
解码器基于上下文向量生成输出序列 :
其中 是解码器在第 步的隐藏状态, 是解码器 RNN 单元。
9.1.3 序列到序列学习:对齐问题
传统编码器-解码器架构存在一个根本性缺陷:信息瓶颈。无论输入序列多长,所有信息都被压缩到一个固定维度的上下文向量中。这导致长序列的信息丢失,模型难以准确翻译或生成输出。
对齐问题是 Seq2Seq 学习的核心挑战。在机器翻译中,源语言句子的不同部分与目标语言句子的不同部分存在对应关系。例如,在英译中时,英文的主语通常对应中文的主语,但词序可能不同。传统方法无法显式建模这种软对齐关系。
Bahdanau 等人提出的注意力机制为解决对齐问题提供了有效方案。注意力机制允许解码器在生成每个输出词时"关注"输入序列的不同部分,动态地选择相关信息,而非仅依赖固定的上下文向量。这一思想为后续 Transformer 架构的诞生奠定了基础。
9.2 注意力机制
9.2.1 注意力原理与直观理解:Query-Key-Value
注意力机制的核心思想源于人类视觉注意力:当处理复杂信息时,我们会选择性地关注最相关的部分,而非同时处理所有信息。在神经网络中,注意力机制通过计算查询(Query)与键(Key)之间的相似度,确定应该关注哪些值(Value)。
Query-Key-Value(QKV)框架是注意力机制的数学基础。可以将这一框架类比为数据库查询:
- Query(查询):表示当前需要关注什么信息
- Key(键):表示存储信息的位置标识
- Value(值):表示实际存储的信息内容
注意力计算过程分为三个步骤:
- 相似度计算:计算 Query 与每个 Key 的相似度分数
- 权重归一化:使用 softmax 函数将分数转换为概率分布
- 加权求和:根据权重对 Value 进行加权求和,得到注意力输出
数学上,给定 Query 向量 、Key 矩阵 和 Value 矩阵 ,注意力输出为:
其中注意力权重 计算为:
常用的相似度评分函数包括:
- 点积注意力:
- 缩放点积注意力:
- 加性注意力:
9.2.2 自注意力机制:Self-Attention 计算
**自注意力(Self-Attention)**是注意力机制的重要变体,它计算序列中每个位置与其他所有位置的注意力关系。与标准注意力不同,自注意力的 Query、Key、Value 都来自同一个序列,使模型能够捕捉序列内部的依赖关系。
自注意力的计算过程如下:
给定输入序列的嵌入表示 ,首先通过线性变换得到 Q、K、V 矩阵:
其中 是可学习的投影矩阵。
然后计算缩放点积注意力:
缩放点积注意力中的缩放因子 具有重要作用。当 较大时,点积的数值会变得很大,导致 softmax 函数的梯度变得非常小。缩放操作可以稳定梯度,改善训练效果。
下图展示了自注意力权重矩阵的可视化结果:

图 9-1:句子"The cat sat on the mat"的自注意力权重矩阵。每个单元格表示 Query 位置(行)对 Key 位置(列)的注意力权重。对角线值较高表明词语主要关注自身及其相邻词。
从图中可以观察到以下模式:
- 对角线元素值较高,表明每个词主要关注自身
- 相邻词之间存在较强的注意力连接
- "mat"与"on"、"the"之间存在较强的语义关联
import torch
import torch.nn as nn
import torch.nn.functional as F
import math
class SelfAttention(nn.Module):
def __init__(self, d_model, n_heads):
super().__init__()
self.d_model = d_model
self.n_heads = n_heads
self.d_k = d_model // n_heads # 每个头的维度
# Q、K、V 投影矩阵
self.W_q = nn.Linear(d_model, d_model)
self.W_k = nn.Linear(d_model, d_model)
self.W_v = nn.Linear(d_model, d_model)
self.W_o = nn.Linear(d_model, d_model)
def forward(self, x, mask=None):
B, L, D = x.shape # batch, seq_len, d_model
# 投影并分头
Q = self.W_q(x).view(B, L, self.n_heads, self.d_k).transpose(1, 2)
K = self.W_k(x).view(B, L, self.n_heads, self.d_k).transpose(1, 2)
V = self.W_v(x).view(B, L, self.n_heads, self.d_k).transpose(1, 2)
# Q, K, V shape: (B, n_heads, L, d_k)
# 计算注意力分数
scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k)
# scores shape: (B, n_heads, L, L)
if mask is not None:
scores = scores.masked_fill(mask == 0, float("-inf"))
attn_weights = F.softmax(scores, dim=-1)
# 加权求和
output = torch.matmul(attn_weights, V)
# output shape: (B, n_heads, L, d_k)
# 拼接多头并投影
output = output.transpose(1, 2).contiguous().view(B, L, D)
return self.W_o(output)
# ─── 直觉理解 ───
# "我喜欢吃苹果" 中,"苹果"会关注"吃"(动词-宾语关系)
# "苹果公司发布了新手机" 中,"苹果"会关注"公司"、"发布"(主语-谓语关系)
# 同一个词在不同上下文中,关注的词不同,表示也不同
9.2.3 多头注意力:并行注意力头
**多头注意力(Multi-Head Attention)**是 Transformer 的核心创新之一。其核心思想是:不同的注意力头可以学习不同类型的依赖关系,多个头并行工作可以捕捉更丰富的语义信息。
多头注意力的计算过程如下:
首先,将 Q、K、V 分别投影到 个不同的子空间:
其中 ,,通常 。
然后,在每个子空间独立计算注意力:
最后,将所有头的输出拼接并投影:
其中 是输出投影矩阵。
下图展示了 8 个注意力头的权重分布:

图 9-2:多头注意力可视化。不同注意力头学习不同的关注模式:Head 1 主要关注相邻词(局部注意力),Head 2 关注句首词,Head 3 关注名词,Head 4 关注动词,其余头学习更复杂的依赖关系。
多头注意力的优势在于:
- 表达能力增强:不同头可以专注于不同的 linguistic phenomena(句法、语义、共指等)
- 并行计算:所有头可以并行计算,充分利用 GPU 加速
- 稳定性提升:多个头的集成提高了模型的鲁棒性
9.2.4 注意力可视化:注意力权重分析
注意力权重可视化是理解 Transformer 模型行为的重要工具。通过分析注意力矩阵,我们可以洞察模型如何建立词与词之间的关联。
注意力模式类型:
- 对角线注意力:词语主要关注自身和相邻词,反映局部依赖
- 垂直注意力:多个 Query 关注同一个 Key,表明该 Key 包含重要信息
- 稀疏注意力:注意力分布稀疏,表明模型关注少数关键位置
- 均匀注意力:注意力分布均匀,表明所有位置信息都相关
注意力分析的应用:
- 模型可解释性:理解模型的决策依据
- 错误分析:定位模型关注错误的原因
- 知识蒸馏:提取注意力模式指导小模型训练
- 多语言分析:比较不同语言的注意力模式差异
9.3 Transformer 架构
9.3.1 编码器结构与组件:多头注意力、FFN、LayerNorm
2017 年,Vaswani 等人提出的 Transformer 架构完全摒弃了循环和卷积结构,完全基于注意力机制构建。Transformer 编码器由 个相同的层堆叠而成,每层包含两个子层:
多头自注意力子层:
位置前馈网络(Position-wise Feed-Forward Network, FFN):
FFN 对每个位置独立应用相同的变换,将输入从 维映射到 维再映射回 维。通常 。
层归一化(Layer Normalization)和残差连接:
每个子层都使用残差连接和层归一化:
层归一化对每个样本的所有特征进行归一化:
其中 和 是特征的均值和方差, 和 是可学习的缩放和平移参数。
残差连接的作用包括:
- 缓解梯度消失问题,使深层网络可训练
- 允许信息直接传递,保留低层特征
- 加速收敛,改善优化效果
9.3.2 解码器结构与组件:掩码注意力、交叉注意力
Transformer 解码器同样由 个相同的层堆叠而成,但每层包含三个子层:
掩码多头自注意力(Masked Multi-Head Self-Attention):
解码器的自注意力需要防止关注未来位置,确保生成第 个词时只能使用已生成的 个词信息。这通过掩码实现:
其中掩码矩阵 满足:
这种掩码方式称为因果掩码(Causal Masking)或下三角掩码,确保模型只能关注当前位置及之前的位置。
交叉注意力(Cross-Attention):
交叉注意力层允许解码器关注编码器的输出。与自注意力不同,交叉注意力的 Query 来自解码器,而 Key 和 Value 来自编码器:
交叉注意力是 Seq2Seq 任务的关键,它建立了源序列和目标序列之间的对齐关系。
位置前馈网络:与编码器相同。
9.3.3 位置编码与嵌入:正弦位置编码、可学习位置编码
由于 Transformer 没有循环或卷积结构,它本身无法感知序列中词的位置信息。为了引入位置信息,需要将位置编码添加到词嵌入中。
**正弦位置编码(Sinusoidal Position Encoding)**是 Transformer 原论文采用的方法。对于位置 和维度 ,位置编码定义为:
正弦位置编码的优势:
- 可以外推到训练时未见过的更长序列
- 每个位置的编码是唯一的
- 相对位置可以通过线性变换得到: 可以用 线性表示
下图展示了正弦位置编码的可视化:

图 9-3:正弦位置编码可视化。左图显示位置编码矩阵的热图,不同维度以不同频率的正弦/余弦函数编码位置信息。右图展示几个代表性维度的波形,低维度(如 Dim 0)变化缓慢,高维度(如 Dim 31)变化快速。
**可学习位置编码(Learned Position Embedding)**是另一种常用方法,直接将位置编码作为可学习的参数。BERT 等模型采用这种方法。可学习位置编码的优势是灵活性高,可以适应特定任务,但无法外推到更长序列。
最终输入表示为词嵌入与位置编码之和:
class PositionalEncoding(nn.Module):
def __init__(self, d_model, max_len=5000):
super().__init__()
pe = torch.zeros(max_len, d_model)
position = torch.arange(0, max_len).unsqueeze(1).float()
div_term = torch.exp(
torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model)
)
pe[:, 0::2] = torch.sin(position * div_term) # 偶数维度用 sin
pe[:, 1::2] = torch.cos(position * div_term) # 奇数维度用 cos
self.register_buffer("pe", pe.unsqueeze(0))
def forward(self, x):
# x shape: (B, L, D)
return x + self.pe[:, :x.size(1)]
# ─── 为什么用三角函数?───
# 1. 不同维度有不同的"频率",低频捕捉长距离,高频捕捉短距离
# 2. PE(pos+k) 可以表示为 PE(pos) 的线性函数,模型能学到相对位置
# 3. 不需要额外学习参数
9.3.4 完整 Transformer 模型:架构整体理解
完整的 Transformer 模型将编码器和解码器组合在一起,形成端到端的 Seq2Seq 架构。
Transformer 工作流程:
- 输入嵌入:源序列和目标序列分别通过嵌入层和位置编码
- 编码器处理:源序列通过编码器,生成上下文表示
- 解码器处理:目标序列通过解码器,交叉注意力层关注编码器输出
- 输出生成:解码器输出通过线性层和 softmax,预测下一个词的概率分布
下表对比了 RNN、LSTM 和 Transformer 三种架构的关键特性:
| 特性 | RNN | LSTM | Transformer |
|---|---|---|---|
| 核心机制 | 循环连接 | 门控循环 | 自注意力 |
| 并行性 | 顺序计算 | 顺序计算 | 完全并行 |
| 长距离依赖 | 困难 | 改善 | 直接建模 |
| 训练速度 | 慢 | 慢 | 快 |
| 位置信息 | 隐式 | 隐式 | 显式编码 |
| 典型应用 | 短序列 | 中等序列 | 长序列 |

图 9-4:RNN、LSTM、Transformer 三种架构的性能对比。左图显示随着序列长度增加,Transformer 保持稳定的性能,而 RNN/LSTM 性能显著下降。中图显示 Transformer 的训练时间随序列长度增长较慢。右图显示 Transformer 在长距离依赖捕获方面具有明显优势。
Transformer 架构的革命性意义在于:
- 完全并行化:摒弃循环结构,实现完全并行计算
- 长距离依赖:注意力机制直接建模任意距离的关系
- 可扩展性:架构简洁,易于扩展到更大规模
class TransformerEncoderLayer(nn.Module):
def __init__(self, d_model, n_heads, d_ff, dropout=0.1):
super().__init__()
self.attention = SelfAttention(d_model, n_heads)
self.ffn = nn.Sequential(
nn.Linear(d_model, d_ff),
nn.GELU(),
nn.Dropout(dropout),
nn.Linear(d_ff, d_model),
nn.Dropout(dropout)
)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.dropout = nn.Dropout(dropout)
def forward(self, x, mask=None):
# 残差连接 + LayerNorm(Pre-Norm 风格)
x = x + self.dropout(self.attention(self.norm1(x), mask))
x = x + self.ffn(self.norm2(x))
return x
时间分配建议
- 3h Self-Attention:Q/K/V、注意力计算、可视化注意力权重
- 2h Multi-Head Attention + 位置编码
- 2h 手写完整 Transformer Encoder 层
- 1h 阅读 "Attention Is All You Need" 论文核心部分
里程碑(第 9-10 周联合)
完成一个文本分类小项目。可以选择情感分析、新闻分类或意图识别任务, 使用预训练的 BERT 模型微调,理解迁移学习的威力。
学完你应该能...
- 从零实现 Self-Attention 计算过程
- 解释 Multi-Head Attention 的作用和优势
- 理解位置编码的数学原理和必要性
- 描述 Transformer 编码器的完整结构