第二章:Transformer架构总览:Encoder-Decoder结构,核心组件概览

好,咱们进入正题。上一章我们聊了注意力机制为什么重要,这一章我带你看看Transformer这个大家伙到底长什么样。说实话,我第一次看Transformer论文的时候,也被那个复杂的架构图吓了一跳。但别怕,拆开来看,其实就两大块:Encoder和Decoder。

2.1 整体架构:一个“编码-解码”的流水线

Transformer的设计思路,说白了就是一个“编码-解码”的流水线。你想想看,我们做翻译任务时,先把中文句子“编码”成一个中间表示,再从这个中间表示“解码”成英文。Transformer就是这么干的。

我个人习惯把Encoder想象成一个“阅读理解”模块,它把输入序列读进去,提取出丰富的语义信息。Decoder则像一个“写作”模块,它根据Encoder提供的语义信息,一步步生成输出序列。

核心流程:

  1. 输入序列经过Embedding和位置编码
  2. 进入N层Encoder堆叠(通常是6层)
  3. Encoder输出一个“语义丰富的上下文表示”
  4. Decoder接收这个上下文表示,并结合自身已生成的内容
  5. 逐词生成输出序列

嗯,这里要注意:Encoder和Decoder不是简单的串联。Decoder在生成每个词时,都会“回头看”Encoder的输出。这就是所谓的交叉注意力机制。

2.2 Encoder:信息的“深度加工厂”

每个Encoder层由两个核心子层组成:

  • 多头自注意力层(Multi-Head Self-Attention):让每个词都能看到序列中的所有其他词
  • 前馈神经网络层(Feed-Forward Network):对每个位置的信息进行非线性变换

每个子层后面都跟着一个残差连接和层归一化。这个设计很巧妙,残差连接解决了深层网络的梯度消失问题,层归一化则让训练更稳定。

我在项目中遇到过一个问题:当序列长度超过512时,标准Transformer的性能会急剧下降。为什么?因为自注意力的计算复杂度是O(n²),序列越长,计算量越大。后来我们用了Longformer的稀疏注意力才解决。

我的小技巧:如果你在处理长文本,可以考虑使用滑动窗口注意力或者局部敏感哈希注意力。这些变体在保持效果的同时,能大幅降低计算量。

2.3 Decoder:生成的“精雕细琢”

Decoder的结构和Encoder类似,但多了一个子层:

  • 掩码多头自注意力层(Masked Multi-Head Self-Attention):防止Decoder看到未来的信息
  • 交叉注意力层(Cross-Attention):让Decoder关注Encoder的输出
  • 前馈神经网络层:和Encoder一样

掩码自注意力是Decoder的关键。你想想看,在生成第t个词时,模型不应该看到第t+1个词。所以我们会用一个上三角矩阵把未来的位置遮住。我曾经在这个地方踩过坑——忘记加掩码,结果模型在训练时“作弊”,生成效果一塌糊涂。

避坑指南:我曾经在实现Decoder时,把掩码矩阵的维度搞错了。注意:掩码矩阵的形状应该是 [batch_size, seq_len, seq_len],并且要确保在softmax之前应用掩码,否则注意力权重会泄露未来信息。

2.4 核心组件速览

Transformer的核心组件其实不多,我整理了一个表格,方便你对照:

组件 作用 我的理解
位置编码 给序列中的每个位置添加位置信息 没有它,模型会把“我爱你”和“你爱我”当成一回事
多头注意力 从多个角度捕捉序列中的依赖关系 相当于多个“专家”同时看问题
残差连接 缓解梯度消失,让深层网络可训练 给梯度开了一条“高速公路”
层归一化 稳定训练过程,加速收敛 让每一层的输出分布都“规规矩矩”
前馈网络 对每个位置进行非线性变换 相当于一个“特征精炼”步骤

2.5 一个简单的代码示例

光说不练假把式。我写了一个极简的Transformer Encoder层实现,帮你理解核心逻辑:

import torch
import torch.nn as nn

class SimpleEncoderLayer(nn.Module):
    def __init__(self, d_model, n_heads, d_ff):
        super().__init__()
        # 多头自注意力
        self.self_attn = nn.MultiheadAttention(d_model, n_heads)
        # 前馈网络
        self.ffn = nn.Sequential(
            nn.Linear(d_model, d_ff),
            nn.ReLU(),
            nn.Linear(d_ff, d_model)
        )
        # 层归一化
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        
    def forward(self, x):
        # 自注意力 + 残差连接
        attn_out, _ = self.self_attn(x, x, x)
        x = self.norm1(x + attn_out)
        
        # 前馈网络 + 残差连接
        ffn_out = self.ffn(x)
        x = self.norm2(x + ffn_out)
        
        return x

你看,核心代码就这么几行。但别小看它,正是这些简单的组件组合在一起,才造就了Transformer的强大能力。

2.6 为什么Encoder-Decoder结构这么重要?

说白了,这种结构让模型能够处理“序列到序列”的任务。机器翻译、文本摘要、语音识别……这些任务本质上都是把一个序列映射到另一个序列。

Encoder负责理解输入,Decoder负责生成输出。两者通过交叉注意力机制紧密协作。我个人觉得,这种“分工明确”的设计是Transformer成功的关键之一。

你想想看,如果没有Encoder-Decoder结构,我们怎么让模型知道“I love you”对应的是“我爱你”而不是“你爱我”?正是Encoder把源语言的信息编码成上下文向量,Decoder才能根据这个向量生成正确的目标语言。

总结一下:

  • Transformer = Encoder + Decoder
  • Encoder:理解输入,提取语义
  • Decoder:生成输出,逐词预测
  • 核心组件:位置编码、多头注意力、残差连接、层归一化、前馈网络
  • 掩码机制:防止Decoder看到未来信息

下一章,我会深入讲解多头注意力机制的具体实现。到时候咱们再细聊Q、K、V这些“三剑客”是怎么工作的。