DL:Transformer 的基本原理与 PyTorch 实现
Transformer 是深度学习中最重要的模型结构之一。它最初用于自然语言处理任务后来逐渐扩展到图像、语音、多模态、代码生成、推荐系统和强化学习等领域成为现代大模型的核心基础结构。传统循环神经网络RNN按时间步依次处理序列能够利用上下文信息但难以完全并行计算也容易受到长距离依赖问题影响。Transformer 不再依赖循环结构而是使用注意力机制Attention Mechanism让序列中的每个位置都可以直接关注其他位置从而更高效地建模上下文关系。图 1从 RNN 到 Transformer可以简单理解为• RNN按顺序逐步读• Transformer同时看全局关系Transformer 的核心思想是让模型根据任务需要自动判断序列中哪些位置更重要并把相关信息加权汇总起来。一、为什么需要 Transformer序列数据中元素之间往往存在上下文关系。例如在句子这部电影虽然前半段节奏很慢但结尾非常精彩。如果只看“很慢”模型可能认为这是负面评价如果结合后面的“但结尾非常精彩”模型就需要理解转折关系并综合判断整体语义。RNN 通过隐藏状态逐步传递历史信息其中• xₜ 表示第 t 个时间步输入• hₜ₋₁ 表示上一时间步隐藏状态• hₜ 表示当前时间步隐藏状态• f 表示循环单元的状态更新函数这种结构的问题在于• 序列必须按时间顺序逐步处理难以充分并行• 远距离信息需要经过多个时间步传递• 长序列中容易出现梯度消失或信息衰减• 每个位置对其他位置的依赖关系不够直接Transformer 采用了不同思路。它让每个位置直接和序列中所有位置建立联系再根据相关性大小分配注意力权重。例如在句子中这部电影虽然前半段节奏很慢但结尾非常精彩。模型在理解“精彩”时可以直接关注“结尾”“但”“电影”等相关词而不必像 RNN 那样一步步传递信息。图 2RNN 与 Transformer 处理序列方式的对比因此Transformer 的优势主要体现在• 能直接建模长距离依赖• 更适合并行计算• 能根据上下文动态分配注意力• 适合大规模数据和大模型训练• 可扩展到文本、图像、语音和多模态任务二、Transformer 的整体结构Transformer 最初采用编码器—解码器结构Encoder–Decoder Architecture用于机器翻译等序列到序列任务。整体结构可以概括为输入序列 → 编码器 → 上下文表示 → 解码器 → 输出序列其中• 编码器负责理解输入序列• 解码器负责根据已生成内容和编码器输出生成目标序列• 注意力机制负责建模不同位置之间的依赖关系图 3Transformer 的编码器—解码器结构1、编码器编码器Encoder接收输入序列并把每个位置转换成包含上下文信息的表示。一个 Transformer 编码器通常由多个相同结构的编码器层堆叠而成。每个编码器层主要包含两部分多头自注意力层 → 前馈神经网络层并配合• 残差连接• 层归一化• Dropout可以写成输入表示→ 多头自注意力→ 残差连接与层归一化→ 前馈神经网络→ 残差连接与层归一化→ 输出表示编码器的目标是让每个位置的表示都融合整个输入序列中的相关信息。2、解码器解码器Decoder用于生成输出序列。它通常也由多个相同结构的解码器层堆叠而成。每个解码器层主要包含三部分掩码多头自注意力层 → 编码器—解码器注意力层 → 前馈神经网络层其中• 掩码自注意力用于防止模型看到未来位置• 编码器—解码器注意力用于关注输入序列信息• 前馈神经网络用于进一步变换特征在文本生成任务中解码器必须按顺序生成 token。生成第 t 个 token 时模型只能使用前面已经生成的 token而不能提前看到未来答案。因此解码器中的自注意力通常需要使用因果掩码Causal Mask。3、Transformer 的三种常见变体虽然原始 Transformer 是编码器—解码器结构但现代模型常见三种形式• Encoder-only只使用编码器• Decoder-only只使用解码器• Encoder–Decoder编码器和解码器同时使用图 4Transformer 的三种常见结构形式它们适合不同任务• Encoder-only适合理解类任务如文本分类、句子匹配、命名实体识别• Decoder-only适合生成类任务如语言模型、代码生成、对话生成• Encoder–Decoder适合输入到输出的转换任务如机器翻译、摘要生成例如• BERT 属于典型 Encoder-only 架构• GPT 属于典型 Decoder-only 架构• T5 属于典型 Encoder–Decoder 架构三、自注意力机制的基本思想自注意力机制Self-Attention是 Transformer 的核心。它的目标是让序列中的每个位置根据需要关注序列中的其他位置并把相关信息加权汇总起来。假设输入序列为我 喜欢 深度 学习当模型处理“学习”这个词时它可能需要关注“深度”当处理“喜欢”时它可能需要关注“我”和后面的内容。自注意力机制不是固定写死哪些词重要而是通过训练自动学习注意力权重。可以简单理解为自注意力 对序列内部不同位置之间的相关性进行建模1、Query、Key 和 Value自注意力机制中每个输入位置都会生成三个向量• 查询向量QueryQ• 键向量KeyK• 值向量ValueV可以理解为• Query当前位置想找什么信息• Key每个位置提供什么检索线索• Value每个位置真正提供的信息内容对于输入表示 X可以通过线性变换得到其中• X 表示输入序列表示矩阵• W_Q 表示生成 Query 的权重矩阵• W_K 表示生成 Key 的权重矩阵• W_V 表示生成 Value 的权重矩阵• Q、K、V 分别表示查询、键和值矩阵图 5自注意力机制中的 Query、Key、Value从直观角度看• Q 和 K 用来计算“相关性”• V 用来提供“被汇总的信息”2、缩放点积注意力Transformer 中最常用的是缩放点积注意力Scaled Dot-Product Attention。它的计算过程可以分为三步计算相关性得分 → Softmax 转成权重 → 对 Value 加权求和公式为其中• Q 表示查询矩阵• K 表示键矩阵• V 表示值矩阵• Kᵀ 表示 K 的转置• dₖ 表示 Key 向量维度• √dₖ 用于缩放点积结果• softmax 用于把相关性得分转换为注意力权重为什么要除以 √dₖ当向量维度 dₖ 较大时QKᵀ 的数值可能变大导致 softmax 进入饱和区使梯度变小。除以 √dₖ 可以缓解这个问题使训练更稳定。四、多头注意力机制单个注意力头可以学习一种关系模式但序列中的关系往往不止一种。例如在一句话中模型可能需要同时关注• 主语和谓语之间的关系• 修饰词和中心词之间的关系• 代词和指代对象之间的关系• 远距离语义依赖• 局部相邻词关系多头注意力Multi-Head Attention通过多个注意力头并行计算让模型从不同子空间学习不同类型的关系。图 6多头注意力机制每个注意力头可以写为其中• headᵢ 表示第 i 个注意力头• WᵢQ、WᵢK、WᵢV 表示第 i 个头对应的投影矩阵• Q、K、V 表示原始查询、键和值矩阵多个注意力头的结果会拼接起来再经过一次线性变换其中• h 表示注意力头数量• Concat 表示拼接操作• Wᴼ 表示输出投影矩阵可以简单理解为• 单头注意力从一个角度看序列关系• 多头注意力从多个角度同时看序列关系五、位置编码让模型知道顺序Transformer 的自注意力机制本身并不按顺序处理 token。它可以同时看到整个序列但这也带来一个问题如果不额外提供位置信息模型并不知道哪个词在前哪个词在后。因此Transformer 需要位置编码Positional Encoding来注入顺序信息。图 7位置编码如何注入顺序信息最经典的位置编码使用正弦和余弦函数其中• pos 表示 token 在序列中的位置• i 表示维度索引• d_model 表示模型隐藏维度• PE 表示位置编码位置编码会与 token embedding 相加其中• E 表示 token embedding• PE 表示位置编码• X 表示加入位置信息后的输入表示从直观角度看• Token Embedding 告诉模型“这个词是什么”• Position Encoding 告诉模型“这个词在哪里”现代 Transformer 也常使用可学习位置编码、相对位置编码、旋转位置编码等变体但基本目标相同让模型能够感知顺序。六、Transformer 层的组成一个标准 Transformer 编码器层通常由两大模块组成• 多头自注意力模块• 前馈神经网络模块并配合残差连接和层归一化。图 8Transformer 编码器层结构1、多头自注意力模块多头自注意力负责让每个位置与其他位置交互信息。输入为 X输出为其中• X 同时作为 Q、K、V• Z 表示自注意力输出• MultiHeadAttention 表示多头自注意力计算这里写作 (X,X,X)是因为自注意力中 Query、Key、Value 都来自同一个序列。2、残差连接与层归一化为了稳定训练Transformer 会使用残差连接Residual Connection和层归一化Layer Normalization。简化写法为其中• X 表示模块输入• Z 表示注意力模块输出• X Z 表示残差连接• LayerNorm 表示层归一化• X₁ 表示归一化后的输出残差连接可以帮助信息和梯度更顺畅地传递层归一化可以让训练更加稳定。3、前馈神经网络模块Transformer 中的前馈神经网络通常是逐位置应用的多层感知机。可以写为其中• x 表示某个位置的表示• W₁、W₂ 表示两层线性变换的权重• b₁、b₂ 表示偏置• σ 表示非线性激活函数例如 ReLU 或 GELU• FFN 表示前馈神经网络模块注意FFN 是对每个位置独立应用的。它不会直接混合不同位置的信息不同位置之间的信息交互主要由注意力机制完成。4、完整编码器层一个简化的 Transformer 编码器层可以写成其中• X 表示输入表示• X₁ 表示经过注意力模块后的中间表示• Y 表示编码器层输出• MultiHeadAttention 表示多头自注意力• FFN 表示前馈神经网络模块多个编码器层堆叠后就形成了 Transformer 编码器。七、PyTorch 实现使用 Transformer 做文本分类下面使用 PyTorch 构建一个简化版 Transformer 文本分类模型。为了突出 Transformer 的基本结构示例使用小型中文样例数据集按字符级别建模完成正面 / 负面情感分类。图 9Transformer 文本分类的训练与预测流程这个示例的目标不是训练高性能中文情感分析模型而是帮助理解文本编号 → 嵌入表示 → 位置编码 → Transformer 编码器 → 分类输出1、导入库import mathimport torchimport torch.nn as nn from torch.utils.data import Dataset, DataLoader这里使用• math 用于计算位置编码• torch 构建张量与训练流程• torch.nn 定义神经网络层• Dataset 和 DataLoader 构建数据集与批量读取2、准备样例数据samples [ (这部电影很好看, 1), (演员表演非常精彩, 1), (剧情紧凑我很喜欢, 1), (这家餐厅味道不错, 1), (服务态度很好, 1), (这部电影太难看了, 0), (剧情拖沓让人失望, 0), (演员表演很糟糕, 0), (这家餐厅味道很差, 0), (服务态度不好, 0),]其中• 标签 1 表示正面情感• 标签 0 表示负面情感为了简化实现这里按字符构建词表。真实任务中通常会使用分词器、子词编码、预训练词向量或预训练大模型。3、构建字符表与编码函数# 构建字符表将所有文本中的字符去重并排序chars sorted(set(.join(text for text, _ in samples))) # 创建字符到ID的映射从1开始0留给填充符char_to_id {char: idx 1 for idx, char in enumerate(chars)} # 填充符ID为0pad_id 0 # 编码函数将文本字符串转换为对应ID列表def encode_text(text): return [char_to_id[char] for char in text]其中• char_to_id 用于把字符映射为整数编号• pad_id0 表示填充符• encode_text() 用于把文本转换为编号序列4、定义数据集与批处理函数# 定义情感分析数据集类class SentimentDataset(Dataset): def __init__(self, samples): self.samples samples # 存储 (文本, 标签) 样本列表 def __len__(self): return len(self.samples) # 返回数据集大小 def __getitem__(self, index): text, label self.samples[index] # 获取样本 ids encode_text(text) # 文本转ID序列 return ( torch.tensor(ids, dtypetorch.long), # ID序列张量 torch.tensor(label, dtypetorch.long) # 标签张量 ) # 自定义批处理函数将同一batch中的序列填充到相同长度def collate_fn(batch): sequences, labels zip(*batch) # 分离ID序列和标签 lengths [len(seq) for seq in sequences] max_len max(lengths) # 当前batch中最大序列长度 # 创建填充后的张量初始全为pad_id padded torch.full( size(len(sequences), max_len), fill_valuepad_id, dtypetorch.long ) # 将每个序列填充到相应位置 for i, seq in enumerate(sequences): padded[i, :len(seq)] seq labels torch.stack(labels) # 堆叠标签为张量 return padded, labels其中• SentimentDataset 定义单个样本的读取方式• collate_fn 用于把不同长度句子填充到同一长度• padded 的形状为 batch_size × sequence_length• labels 的形状为 batch_size构建 DataLoader# 创建情感分析数据集实例dataset SentimentDataset(samples) # 创建数据加载器每批4个样本打乱顺序使用自定义的填充函数train_loader DataLoader( dataset, batch_size4, shuffleTrue, collate_fncollate_fn)5、定义位置编码下面实现经典正弦—余弦位置编码。# 位置编码Transformer 中用于注入序列位置信息class PositionalEncoding(nn.Module): def __init__(self, d_model, max_len100): super().__init__() # 初始化位置编码矩阵 (max_len, d_model) pe torch.zeros(max_len, d_model) # 位置索引 (0,1,2,...,max_len-1)形状 (max_len,1) position torch.arange(0, max_len, dtypetorch.float32).unsqueeze(1) # 计算分母项10000^(2i/d_model) 的倒数即 exp(-(2i/d_model)*ln(10000)) # 对偶数维使用 sin奇数维使用 cos div_term torch.exp( torch.arange(0, d_model, 2, dtypetorch.float32) * (-math.log(10000.0) / d_model) ) # 填充偶数索引列0,2,4,...用 sin pe[:, 0::2] torch.sin(position * div_term) # 填充奇数索引列1,3,5,...用 cos pe[:, 1::2] torch.cos(position * div_term) # 增加 batch 维度形状 (1, max_len, d_model) pe pe.unsqueeze(0) # 注册为缓冲区不参与梯度更新但随模型保存和移动 self.register_buffer(pe, pe) def forward(self, x): # x: (batch, seq_len, d_model) seq_len x.size(1) # 添加位置编码截取前 seq_len 个位置 return x self.pe[:, :seq_len, :]其中• d_model 表示 embedding 维度• max_len 表示最大序列长度• pe 保存不同位置的位置编码• register_buffer() 表示 pe 不是可训练参数但会随模型一起保存和移动设备• x 的形状为 batch_size × sequence_length × d_model6、定义 Transformer 文本分类模型# 基于 Transformer 编码器的文本分类器class TransformerTextClassifier(nn.Module): def __init__( self, vocab_size, # 词汇表大小 d_model32, # 嵌入和 Transformer 隐藏维度 nhead4, # 多头注意力头数 num_layers2, # 编码器层数 dim_feedforward64, # 前馈网络维度 num_classes2, # 分类类别数 max_len100 # 最大序列长度位置编码 ): super().__init__() # 词嵌入层padding_idx0 的 token 向量不参与训练 self.embedding nn.Embedding( num_embeddingsvocab_size, embedding_dimd_model, padding_idxpad_id ) # 位置编码层 self.pos_encoding PositionalEncoding(d_modeld_model, max_lenmax_len) # Transformer 编码器层配置 encoder_layer nn.TransformerEncoderLayer( d_modeld_model, nheadnhead, dim_feedforwarddim_feedforward, batch_firstTrue # 输入形状为 (batch, seq, feature) ) # 堆叠多层编码器 self.encoder nn.TransformerEncoder( encoder_layerencoder_layer, num_layersnum_layers ) # 分类头将池化后的向量映射到类别 logits self.classifier nn.Linear(d_model, num_classes) def forward(self, x): # x: (batch, seq_len) token ID 序列 padding_mask (x pad_id) # 标记 padding 位置为 True # 词嵌入 位置编码 x self.embedding(x) # (batch, seq_len, d_model) x self.pos_encoding(x) # Transformer 编码器自动使用 padding_mask 忽略填充位置 encoded self.encoder( x, src_key_padding_maskpadding_mask # 形状 (batch, seq_len) ) # 输出 (batch, seq_len, d_model) # 计算有效 token 的平均值忽略 padding valid_mask ~padding_mask # (batch, seq_len) valid_mask valid_mask.unsqueeze(-1) # (batch, seq_len, 1) pooled (encoded * valid_mask).sum(dim1) / valid_mask.sum(dim1) # (batch, d_model) # 分类输出 logits self.classifier(pooled) # (batch, num_classes) return logits这个模型结构可以概括为字符编号→ Embedding→ 位置编码→ TransformerEncoder→ 平均池化→ Linear 分类其中• Embedding 把字符编号转换为向量• 位置编码提供顺序信息• TransformerEncoder 建模上下文关系• padding_mask 避免模型把填充符当作有效字符• 平均池化把序列表示汇总为句子表示• Linear 输出分类 logits需要注意这里是 Encoder-only 结构适合文本分类这类理解任务。7、创建模型、损失函数和优化器# 词汇表大小 字符映射数 1因为 char_to_id 从 1 开始pad_id0vocab_size len(char_to_id) 1 # 实例化 Transformer 文本分类器模型model TransformerTextClassifier( vocab_sizevocab_size, # 词汇表大小 d_model32, # 嵌入和 Transformer 隐藏维度 nhead4, # 注意力头数 num_layers2, # Transformer 编码器层数 dim_feedforward64, # 前馈网络内部维度 num_classes2, # 输出类别数正面/负面 max_len100 # 最大序列长度位置编码) # 交叉熵损失函数适用于多分类criterion nn.CrossEntropyLoss()# Adam 优化器学习率 0.001优化模型所有参数optimizer torch.optim.Adam(model.parameters(), lr0.001)其中• vocab_size 表示词表大小• d_model32 表示向量维度• nhead4 表示使用 4 个注意力头• num_layers2 表示堆叠 2 个编码器层• num_classes2 表示二分类• CrossEntropyLoss 用于分类任务• Adam 用于参数更新模型最后输出 logits不需要手动添加 Softmax。8、训练模型num_epochs 30 # 训练轮数 for epoch in range(num_epochs): model.train() # 设置为训练模式 total_loss 0.0 # 累计损失 # 遍历训练数据加载器 for batch_x, batch_y in train_loader: logits model(batch_x) # 前向传播得到预测 logits loss criterion(logits, batch_y) # 计算交叉熵损失 optimizer.zero_grad() # 清空旧梯度 loss.backward() # 反向传播 optimizer.step() # 更新参数 total_loss loss.item() * batch_x.size(0) # 累加本批次损失加权 # 计算本轮平均损失 avg_loss total_loss / len(dataset) # 每 10 轮输出一次平均损失 if (epoch 1) % 10 0: print(fEpoch [{epoch 1}/{num_epochs}], Loss: {avg_loss:.4f})训练过程仍然遵循深度学习的标准闭环前向传播 → 计算损失 → 清空旧梯度 → 反向传播 → 参数更新其中• model(batch_x) 表示前向传播• criterion(logits,batch_y) 表示计算分类损失• optimizer.zero_grad() 清空旧梯度• loss.backward() 计算梯度• optimizer.step() 更新参数9、预测新句子def predict_sentiment(text): 预测单条文本的情感正面/负面 model.eval() # 切换到评估模式 # 将文本编码为ID张量并添加batch维度形状: 1, seq_len ids torch.tensor( encode_text(text), dtypetorch.long ).unsqueeze(0) with torch.no_grad(): # 禁用梯度计算 logits model(ids) # 模型前向传播得到logits pred logits.argmax(dim1).item() # 取概率最大类别的索引 return 正面 if pred 1 else 负面 # 1-正面, 0-负面测试预测print(predict_sentiment(电影非常精彩))print(predict_sentiment(服务太差了))可能输出正面负面由于这里的数据集很小模型只是演示 Transformer 的基本流程不能代表真实中文情感分析系统的效果。真实任务通常需要更大规模数据、更稳定的训练策略以及预训练模型。10、查看张量形状可以打印一个批次的形状帮助理解 Transformer 的输入输出。# 取一个批次数据用于观察中间张量形状batch_x, batch_y next(iter(train_loader)) # 通过模型的嵌入层embedded model.embedding(batch_x) # (batch, seq_len, d_model)# 添加位置编码encoded_input model.pos_encoding(embedded) # 形状不变 print(输入编号形状, batch_x.shape) # (batch, seq_len)print(嵌入后形状, embedded.shape) # (batch, seq_len, d_model)print(加入位置编码后形状, encoded_input.shape) # 与 embedding 形状相同 # 直接通过完整模型前向传播得到 logitswith torch.no_grad(): logits model(batch_x) # (batch, num_classes) print(输出 logits 形状, logits.shape) # (batch, 2)可能看到类似结果输入编号形状 torch.Size([4, 8])嵌入后形状 torch.Size([4, 8, 32])加入位置编码后形状 torch.Size([4, 8, 32])输出 logits 形状 torch.Size([4, 2])其中• 4 表示 batch_size• 8 表示当前批次填充后的序列长度• 32 表示 d_model• 2 表示两个类别的 logits八、Transformer 的适用场景、局限与扩展方向Transformer 是当前深度学习中最重要的通用结构之一。它不只用于自然语言处理也广泛应用于视觉、多模态、语音、代码和科学计算等领域。图 10Transformer 的适用场景、局限与扩展方向1、适用场景Transformer 常用于• 文本分类• 机器翻译• 文本摘要• 对话生成• 代码生成• 图像分类• 图像生成• 语音识别• 多模态理解与生成其中大语言模型和多模态模型大多以 Transformer 或其变体为基础。2、主要优势Transformer 的主要优势包括• 能直接建模长距离依赖• 更适合并行计算• 注意力机制可动态选择重要信息• 可扩展到大规模数据和模型• 适合文本、图像、语音和多模态任务• 已成为现代大模型的重要基础结构与 RNN 相比Transformer 不需要逐时间步递归计算因此在大规模训练中更具优势。3、主要局限Transformer 也有明显局限• 自注意力计算成本随序列长度快速增长• 长文本处理需要较大显存和计算资源• 大模型训练成本很高• 对数据规模和工程系统依赖较强• 注意力权重并不总能直接解释模型决策• 生成模型可能出现幻觉、偏见和安全问题标准自注意力的计算复杂度通常与序列长度平方相关。若序列长度为 n注意力矩阵大小约为因此长序列建模是 Transformer 的重要挑战之一。4、扩展方向从基础 Transformer 出发可以继续学习• BERT编码器结构适合理解任务• GPT解码器结构适合生成任务• T5编码器—解码器结构适合文本到文本任务• Vision Transformer将 Transformer 用于图像任务• Swin Transformer使用窗口注意力改进视觉建模• Longformer面向长文本的稀疏注意力结构• Transformer-XL改进长距离上下文建模• MoE Transformer使用专家混合机制扩展模型容量这些结构虽然形式不同但都围绕一个核心问题展开如何更高效、更稳定、更可控地建模大规模上下文关系 小结Transformer 通过自注意力机制直接建模序列中不同位置之间的关系并通过多头注意力、位置编码、前馈网络、残差连接和层归一化构成可扩展的深度网络结构。它突破了 RNN 顺序计算的限制成为大语言模型、多模态模型和现代深度学习系统的重要基础。“点赞有美意赞赏是鼓励”