Transformer 技术架构与源码分析

Transformer 技术架构与源码分析

一句话概括:Transformer 不是神经网络架构的版本号递进,而是一场彻底的"范式革命"------以纯注意力机制彻底取代了统治序列建模数十年的 RNN 和 CNN,将序列建模从"顺序计算"的桎梏中解放出来,让并行训练成为可能;它以"编码器-解码器"为骨架、以多头自注意力为核心、以位置编码为序、以残差连接与层归一化为盾,用一套简洁而统一的原语回答了一个根本问题: 如果模型可以一次性"看见"整个序列中的所有位置,为什么还要一步步地"走过"它们?****

一、引言

如果你用 PyTorch 写过序列到序列模型,你一定写过类似这样的代码:

python 复制代码
import torch
import torch.nn as nn

transformer = nn.Transformer(
    d_model=512,
    nhead=8,
    num_encoder_layers=6,
    num_decoder_layers=6,
)
src = torch.rand((10, 32, 512))  # (seq_len, batch, feature)
tgt = torch.rand((20, 32, 512))
out = transformer(src, tgt)       # (tgt_seq_len, batch, feature)

十几行代码,一个能处理序列到序列任务的 Transformer 就跑起来了。看起来很轻松,对吧?

但在 2017 年之前,事情远没有这么简单。如果你想处理机器翻译------把一句英语翻译成德语------你几乎绕不开 RNN(循环神经网络)或 LSTM。RNN 的致命缺陷是什么?顺序计算。t 时刻的隐藏状态必须等 t-1 时刻算完才能开始。这意味着:你无法在训练时并行处理一个句子中的不同位置。序列越长,训练越慢。更糟的是,RNN 在处理长距离依赖时,信息会在逐层传递中衰减------句子开头的词要"翻山越岭"才能影响到末尾的词。

你可能会问:CNN 呢?卷积总可以并行吧? 确实可以,但 CNN 的视野是局部的------一个卷积核只能看到它窗口大小内的内容。要捕捉长距离依赖,你得堆很多层,或者用很大的卷积核,而这又带来了新的计算开销。

正是在这个背景下,2017 年 6 月,Google Brain 的 Ashish Vaswani 等八位作者发表了一篇论文,标题掷地有声:《Attention Is All You Need》 。这篇论文提出了一个完全基于注意力机制的网络架构------Transformer,彻底抛弃了循环和卷积。

你可以把 RNN 想象成一个人逐字阅读------必须读完上一个字才能看下一个;而 Transformer 就像一个人同时扫视整页纸,所有词一次性映入眼帘,然后通过注意力机制判断哪些词之间关系最紧密。

那么,这个"完全基于注意力"的架构到底长什么样?为什么它能以更少的训练时间达到更高的翻译质量?我们从论文和 PyTorch 源码出发,一步步拆解。

二、整体架构与设计哲学

2.1 架构总览:编码器-解码器

Transformer 采用经典的编码器-解码器(Encoder-Decoder) 架构。整体结构可以用下图概括:

复制代码
                    输出概率
                       ↑
              ┌─────────────────┐
              │   Softmax       │
              └────────┬────────┘
                       ↑
              ┌─────────────────┐
              │   线性层        │
              └────────┬────────┘
                       ↑
         ┌─────────────┴─────────────┐
         │       解码器(×6)         │
         │  ┌───────────────────┐   │
         │  │ 掩码多头自注意力   │   │
         │  └────────┬──────────┘   │
         │           ↓              │
         │  ┌───────────────────┐   │
         │  │ 编码器-解码器注意力│   │
         │  └────────┬──────────┘   │
         │           ↓              │
         │  ┌───────────────────┐   │
         │  │  前馈网络         │   │
         │  └───────────────────┘   │
         └─────────────┬─────────────┘
                       ↑
              ┌────────┴────────┐
              │  编码器(×6)    │
              │  ┌────────────┐ │
              │  │多头自注意力 │ │
              │  └─────┬──────┘ │
              │        ↓        │
              │  ┌────────────┐ │
              │  │  前馈网络  │ │
              │  └────────────┘ │
              └────────┬────────┘
                       ↑
              ┌────────┴────────┐
              │  输入嵌入 + 位置编码│
              └─────────────────┘

图:Transformer 整体架构。左侧编码器(Encoder)由 N=6 个相同层堆叠,右侧解码器(Decoder)同样由 N=6 个相同层堆叠。编码器的输出通过"编码器-解码器注意力"传递给解码器的每一层。

这里最关键的设计决策是:编码器和解码器都不包含任何循环或卷积层------整个网络完全由注意力机制和前馈网络组成。

2.2 设计哲学:为什么是 Attention?

Transformer 的设计围绕一个核心判断展开:RNN 的顺序计算是根本性的瓶颈

论文在引言中明确指出:循环模型沿输入和输出序列的符号位置进行因子化计算------将位置对齐到计算时间步,生成隐藏状态序列,作为前一个隐藏状态和当前位置输入的函数。这种固有的顺序性排除了训练样本内的并行化,在长序列情况下尤其致命。

而注意力机制的本质是什么?它允许模型在建模依赖关系时,不关心它们在输入或输出序列中的距离。一个词可以直接"注意"到千里之外的另一个词,无需经过中间词的层层传递。

"我们提出 Transformer,一种摒弃循环、完全依赖注意力机制来捕捉输入输出之间全局依赖关系的模型架构。"

这就是"Attention Is All You Need"的真正含义------注意力不仅是组件,而是全部

2.3 模型配置与规模

论文中报告的基准模型配置如下:

参数
词嵌入维度(d_model 512
注意力头数(nhead 8
编码器层数 6
解码器层数 6
前馈网络维度(dim_feedforward 2048
Dropout 0.1
层归一化 eps 1e-5

在 WMT 2014 英德翻译任务上,Transformer 取得了 28.4 BLEU 的成绩,比当时包括集成模型在内的最佳结果提升了超过 2 个 BLEU。在英法翻译任务上,仅用 8 块 GPU 训练 3.5 天就达到了 41.8 BLEU 的单模型 SOTA------而当时最佳模型的训练成本远高于此。

三、核心抽象与编程模型

3.1 缩放点积注意力------一切的基础

缩放点积注意力(Scaled Dot-Product Attention)是 Transformer 中最基础的运算单元------所有多头注意力、编码器-解码器注意力都建立在它之上。

公式如下:

Attention(Q,K,V)=softmax(QKTdk)V\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)VAttention(Q,K,V)=softmax(dk QKT)V

其中 Q(查询)、K(键)、V(值)都是矩阵。除以 dk\sqrt{d_k}dk 是为了防止点积过大导致 softmax 进入梯度极小的区域。

python 复制代码
# 文件路径:llm_internals/appendix/a2_pytorch_examples.md

import torch
import torch.nn.functional as F
import math

def scaled_dot_product_attention(Q, K, V, mask=None):
    """缩放点积注意力"""
    d_k = Q.size(-1)
    scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(d_k)
    
    if mask is not None:
        # 将 mask 中为 0 的位置设为负无穷,softmax 后权重为 0
        scores = scores.masked_fill(mask == 0, torch.finfo(scores.dtype).min)
    
    attn_weights = F.softmax(scores, dim=-1)
    output = torch.matmul(attn_weights, V)
    return output, attn_weights

这段代码实现了什么? 给定一个查询 Q,计算它与所有键 K 的相似度(点积),缩放后用 softmax 得到注意力权重,最后用权重对值 V 加权求和。

设计模式解读 :这是策略模式(Strategy Pattern) 的体现------缩放点积注意力是一个可插拔的"注意力策略",不同的注意力变体(如多头注意力、掩码注意力)可以复用这个核心函数,只需改变 Q、K、V 的来源或添加不同的 mask。

设计权衡分析

  • 收益 :① 点积计算可以高度并行化(矩阵乘法);② 计算复杂度为 O(n²·d),对于中等长度序列非常高效;③ 缩放因子 dk\sqrt{d_k}dk 保证了梯度的稳定性。
  • 代价:① O(n²) 的空间/时间复杂度意味着超长序列(如 10k+ tokens)会面临内存和计算瓶颈;② 没有内置的位置感知能力------需要额外引入位置编码。
  • 适用场景:因此,在处理中等长度序列(如机器翻译、文本摘要)时,缩放点积注意力是首选;而在处理超长文档时,需要引入稀疏注意力或线性注意力等变体。

3.2 多头注意力------从"一个视角"到"多个视角"

如果说缩放点积注意力是 Transformer 的"眼睛",那么多头注意力就是让这双眼睛同时拥有多个"视角"

python 复制代码
# 文件路径:llm_internals/appendix/a2_pytorch_examples.md

import torch.nn as nn

class MultiHeadAttention(nn.Module):
    def __init__(self, d_model, n_heads):
        super().__init__()
        if d_model % n_heads != 0:
            raise ValueError("d_model must be divisible by n_heads")
        
        self.d_model = d_model
        self.n_heads = n_heads
        self.d_k = d_model // n_heads
        
        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, Q, K, V, mask=None):
        batch_size = Q.size(0)
        
        # 1. 线性投影并拆分为多个头
        Q = self.W_q(Q).view(batch_size, -1, self.n_heads, self.d_k).transpose(1, 2)
        K = self.W_k(K).view(batch_size, -1, self.n_heads, self.d_k).transpose(1, 2)
        V = self.W_v(V).view(batch_size, -1, self.n_heads, self.d_k).transpose(1, 2)
        
        # 2. 缩放点积注意力(每个头独立计算)
        # attn_output: (batch, n_heads, seq_len, d_k)
        attn_output, _ = scaled_dot_product_attention(Q, K, V, mask)
        
        # 3. 合并多头
        attn_output = attn_output.transpose(1, 2).contiguous().view(
            batch_size, -1, self.d_model
        )
        
        # 4. 最终线性投影
        output = self.W_o(attn_output)
        return output

这段代码实现了什么? 多头注意力将 d_model 维的 Q、K、V 分别投影到 n_heads 个子空间,在每个子空间中独立计算缩放点积注意力,最后将各头的结果拼接并通过一个线性层输出。

设计模式解读 :这是组合模式(Composite Pattern) 的体现------多个注意力头并行计算,然后将结果组合成一个统一的输出。每个头是一个独立的注意力"专家",关注序列中不同方面的关系。

设计权衡分析

  • 收益 :① 多个头可以关注不同的语义关系(如语法、语义、指代等);② 每个头的 dk=dmodel/nheadsd_k = d_{model} / n_{heads}dk=dmodel/nheads,总计算量与单头注意力相当;③ 多头机制为模型提供了更强的表示能力。
  • 代价 :① 需要额外的线性投影层(Wq,Wk,Wv,WoW_q, W_k, W_v, W_oWq,Wk,Wv,Wo),增加了参数量;② 各头独立计算,缺乏头间的信息交互机制。
  • 适用场景:因此,在绝大多数序列建模任务中,多头注意力都是标准配置;头数通常取 8 或 16,需要在表示能力和计算效率之间取得平衡。

3.3 位置编码------给"无顺序"的模型注入顺序

Transformer 本身对位置信息完全不敏感------如果输入是"我爱你"和"你爱我",在不加位置信息的情况下,模型看到的是完全相同的词集合,无法区分顺序。

为了解决这个问题,Transformer 在输入嵌入中加入位置编码(Positional Encoding)

论文使用正弦和余弦函数生成位置编码:

PE(pos,2i)=sin⁡(pos/100002i/dmodel)PE_{(pos, 2i)} = \sin(pos / 10000^{2i/d_{model}})PE(pos,2i)=sin(pos/100002i/dmodel)

PE(pos,2i+1)=cos⁡(pos/100002i/dmodel)PE_{(pos, 2i+1)} = \cos(pos / 10000^{2i/d_{model}})PE(pos,2i+1)=cos(pos/100002i/dmodel)

python 复制代码
# 文件路径:示例代码 ------ PositionalEncoding 实现

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, dtype=torch.float).unsqueeze(1)
        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)
        pe[:, 1::2] = torch.cos(position * div_term)
        pe = pe.unsqueeze(0)  # (1, max_len, d_model)
        self.register_buffer('pe', pe)
    
    def forward(self, x):
        return x + self.pe[:, :x.size(1), :]

这段代码实现了什么? 为每个位置 pos 生成一个 d_model 维的编码向量,然后直接加到输入嵌入上。

设计权衡分析

  • 收益 :① 正弦/余弦编码是确定性的,无需额外学习参数;② PEpos+kPE_{pos+k}PEpos+k 可以表示为 PEposPE_{pos}PEpos 的线性组合,模型可以轻松学习相对位置关系;③ 可以泛化到比训练时更长的序列。
  • 代价:① 绝对位置编码是固定的,无法针对特定任务自适应调整;② 在深层网络中,位置信息可能被稀释------但残差连接保证了初始位置信息始终存在。
  • 适用场景:因此,在标准 Transformer 中正弦位置编码是首选;但在某些需要可学习位置嵌入的任务中(如 BERT),也可以使用可学习的 Positional Embedding。

四、核心模块源码解析

4.1 前馈网络------每个位置的"独立思考"

除了注意力子层,Transformer 的每个编码器和解码器层还包含一个前馈网络(Feed-Forward Network, FFN)

FFN(x)=max⁡(0,xW1+b1)W2+b2FFN(x) = \max(0, xW_1 + b_1)W_2 + b_2FFN(x)=max(0,xW1+b1)W2+b2

这是一个两层的全连接网络,中间使用 ReLU 激活。

python 复制代码
# 文件路径:示例代码 ------ FeedForwardNetwork 实现

class FeedForwardNetwork(nn.Module):
    def __init__(self, hidden_size, filter_size, dropout_rate):
        super().__init__()
        self.layer1 = nn.Linear(hidden_size, filter_size)
        self.relu = nn.ReLU()
        self.dropout = nn.Dropout(dropout_rate)
        self.layer2 = nn.Linear(filter_size, hidden_size)
    
    def forward(self, x):
        x = self.layer1(x)
        x = self.relu(x)
        x = self.dropout(x)
        x = self.layer2(x)
        return x

这段代码实现了什么? 对序列中的每个位置独立地应用相同的两层全连接网络。注意"位置独立"------这意味着不同位置共享相同的参数,但计算互不影响,因此可以高度并行化。

设计模式解读 :这是策略模式(Strategy Pattern) 的体现------FFN 是一个可替换的"位置级变换策略"。在 Transformer 中它与注意力子层交替出现,形成"全局交互(注意力)+ 局部变换(FFN)"的组合。

设计权衡分析

  • 收益 :① 位置独立计算,可以完全并行化;② 两层结构(扩展再压缩)赋予了模型非线性变换能力;③ 中间维度 dff=2048d_{ff}=2048dff=2048 远大于 dmodel=512d_{model}=512dmodel=512,为模型提供了充足的表示容量。
  • 代价:① FFN 参数量巨大(约占总参数的 2/3);② 每个位置都要过一遍大矩阵,计算开销不可忽视。
  • 适用场景:因此,FFN 是 Transformer 不可或缺的组成部分;在效率优化场景中,可以用 Gated Linear Unit (GLU) 或 SwiGLU 等变体替代标准 FFN。

4.2 编码器层------堆叠出深度理解

编码器(Encoder)由 N 个相同的编码器层堆叠而成,每个编码器层包含两个子层:

  1. 多头自注意力子层:让每个位置关注输入序列中的所有位置
  2. 前馈网络子层:对每个位置独立进行非线性变换

每个子层都配有残差连接层归一化

output=LayerNorm(x+Sublayer(x))\text{output} = \text{LayerNorm}(x + \text{Sublayer}(x))output=LayerNorm(x+Sublayer(x))

python 复制代码
# 文件路径:torch/nn/modules/transformer.py(PyTorch 官方实现结构示意)

class TransformerEncoderLayer(Module):
    def __init__(self, d_model, nhead, dim_feedforward=2048, 
                 dropout=0.1, activation="relu"):
        super().__init__()
        self.self_attn = MultiheadAttention(d_model, nhead, dropout=dropout)
        # Implementation of Feedforward model
        self.linear1 = Linear(d_model, dim_feedforward)
        self.dropout = Dropout(dropout)
        self.linear2 = Linear(dim_feedforward, d_model)
        
        self.norm1 = LayerNorm(d_model, eps=1e-5)
        self.norm2 = LayerNorm(d_model, eps=1e-5)
        self.dropout1 = Dropout(dropout)
        self.dropout2 = Dropout(dropout)
        self.activation = _get_activation_fn(activation)
    
    def forward(self, src, src_mask=None, src_key_padding_mask=None):
        # 子层1:多头自注意力 + 残差连接 + 层归一化
        src2 = self.self_attn(src, src, src, attn_mask=src_mask,
                              key_padding_mask=src_key_padding_mask)[0]
        src = src + self.dropout1(src2)
        src = self.norm1(src)
        
        # 子层2:前馈网络 + 残差连接 + 层归一化
        src2 = self.linear2(self.dropout(self.activation(self.linear1(src))))
        src = src + self.dropout2(src2)
        src = self.norm2(src)
        return src

这段代码实现了什么? 一个标准的 Transformer 编码器层:先做多头自注意力(让序列内部充分交互),加残差和层归一化;再做前馈网络(每个位置独立变换),再加残差和层归一化。

设计模式解读 :这是模板方法模式(Template Method Pattern) 的体现------编码器层定义了一个固定的"处理流程"(自注意力 → 残差+归一化 → 前馈 → 残差+归一化),但具体的子层实现(如注意力头数、FFN 维度、激活函数)可以灵活配置。

设计权衡分析

  • 收益:① 残差连接让梯度可以直传,支持训练深层网络;② 层归一化稳定了训练过程;③ 每个子层独立配置,灵活性高。
  • 代价:① 残差连接和层归一化增加了前向/反向计算量;② 标准化操作引入了额外的均值和方差计算。
  • 适用场景:因此,这种"子层 + 残差 + 归一化"的三段式结构已成为 Transformer 及其变体的标准设计模式。

4.3 解码器层------掩码让"自回归"成为可能

解码器(Decoder)与编码器结构相似,但多了一个"编码器-解码器注意力"子层 ,并且第一个自注意力子层使用了掩码(Masking)

python 复制代码
# 文件路径:torch/nn/modules/transformer.py(PyTorch 官方实现结构示意)

class TransformerDecoderLayer(Module):
    def __init__(self, d_model, nhead, dim_feedforward=2048, 
                 dropout=0.1, activation="relu"):
        super().__init__()
        self.self_attn = MultiheadAttention(d_model, nhead, dropout=dropout)
        self.multihead_attn = MultiheadAttention(d_model, nhead, dropout=dropout)
        # Feedforward 部分与编码器相同
        # ...
    
    def forward(self, tgt, memory, tgt_mask=None, memory_mask=None,
                tgt_key_padding_mask=None, memory_key_padding_mask=None):
        # 子层1:掩码多头自注意力(防止看到未来位置)
        tgt2 = self.self_attn(tgt, tgt, tgt, attn_mask=tgt_mask,
                              key_padding_mask=tgt_key_padding_mask)[0]
        tgt = tgt + self.dropout1(tgt2)
        tgt = self.norm1(tgt)
        
        # 子层2:编码器-解码器注意力(Q 来自解码器,K/V 来自编码器)
        tgt2 = self.multihead_attn(tgt, memory, memory, attn_mask=memory_mask,
                                   key_padding_mask=memory_key_padding_mask)[0]
        tgt = tgt + self.dropout2(tgt2)
        tgt = self.norm2(tgt)
        
        # 子层3:前馈网络
        tgt2 = self.linear2(self.dropout(self.activation(self.linear1(tgt))))
        tgt = tgt + self.dropout3(tgt2)
        tgt = self.norm3(tgt)
        return tgt

这段代码实现了什么? 解码器层有三个子层:① 掩码自注意力------确保生成当前位置时只能看到已生成的位置(不能"偷看"未来);② 编码器-解码器注意力------让解码器关注编码器输出的相关信息;③ 前馈网络。

掩码通过一个上三角矩阵实现,将未来位置的注意力分数设为 −∞-\infty−∞:

python 复制代码
# 文件路径:torch/nn/modules/transformer.py

def _generate_square_subsequent_mask(sz, device, dtype):
    """生成上三角因果掩码"""
    return torch.triu(
        torch.full((sz, sz), float('-inf'), dtype=dtype, device=device),
        diagonal=1,
    )

设计权衡分析

  • 收益:① 掩码机制保证了自回归生成的因果性------第 t 步只能看到前 t-1 个位置;② 编码器-解码器注意力让解码器可以"查阅"源序列的完整信息。
  • 代价:① 推理时是自回归的(逐个生成 token),无法像编码器那样完全并行;② 掩码引入了额外的矩阵操作开销。
  • 适用场景:因此,解码器架构适合生成式任务(翻译、摘要、对话生成);对于纯理解任务(如 BERT),只需编码器即可。

五、核心执行流程与运行时机制

5.1 完整前向传播------从输入到输出

当你向 Transformer 输入一个源序列(如英语句子)和目标序列(如德语句子前缀)时,完整的前向传播如下:

复制代码
┌─────────────────────────────────────────────────────────────────┐
│ 1. 输入嵌入 + 位置编码                                          │
│    源序列 → Input Embedding → + Positional Encoding            │
│    目标序列 → Input Embedding → + Positional Encoding          │
│    ↓                                                           │
│ 2. 编码器(6层堆叠)                                           │
│    每层:多头自注意力 → 残差+归一化 → FFN → 残差+归一化        │
│    输出:编码器记忆(Encoder Memory)                          │
│    ↓                                                           │
│ 3. 解码器(6层堆叠)                                           │
│    每层:                                                     │
│    ├── 掩码多头自注意力(防止看到未来)                        │
│    ├── 残差+归一化                                            │
│    ├── 编码器-解码器注意力(Q=解码器输出,K/V=编码器记忆)    │
│    ├── 残差+归一化                                            │
│    └── FFN → 残差+归一化                                      │
│    ↓                                                           │
│ 4. 输出层                                                      │
│    线性层 → Softmax → 词汇概率分布                            │
└─────────────────────────────────────────────────────────────────┘

关键洞察 :编码器可以完全并行 处理整个源序列------所有位置同时计算。而解码器在训练时也可以并行 (因为目标序列已知,掩码保证了因果性),但在推理时只能自回归地逐个生成 token。

5.2 三种注意力各司其职

Transformer 中出现了三种注意力,它们的 Q、K、V 来源各不相同:

注意力类型 Q(查询) K(键) V(值) 作用
编码器自注意力 编码器输入 编码器输入 编码器输入 捕获源序列内部依赖
解码器自注意力 解码器输入 解码器输入 解码器输入 捕获目标序列内部依赖(掩码)
编码器-解码器注意力 解码器输出 编码器输出 编码器输出 让解码器关注源序列

这种设计让三种注意力各司其职,互不干扰。

5.3 运行时关键决策的权衡分析

决策 方案 收益 代价
位置编码 正弦/余弦函数 无需学习参数,可泛化到更长序列 无法针对任务自适应调整
多头数量 8 个头 每个头关注不同语义关系 头数增加带来额外计算
层数 6 层编码器 + 6 层解码器 足够深的表示能力 层数增加带来训练难度
残差连接 每子层后加残差 梯度直传,支持深层训练 轻微增加计算量

六、工程化实践

理论说完了,接下来聊聊实战------用 PyTorch 搭建 Transformer 时,你最关心的几个问题。

6.1 PyTorch 官方实现

PyTorch 提供了完整的 Transformer 模块:

python 复制代码
import torch
import torch.nn as nn

# 创建 Transformer 模型
transformer = nn.Transformer(
    d_model=512,              # 词嵌入维度
    nhead=8,                  # 注意力头数
    num_encoder_layers=6,     # 编码器层数
    num_decoder_layers=6,     # 解码器层数
    dim_feedforward=2048,     # FFN 中间维度
    dropout=0.1,              # Dropout 比率
    activation='relu',        # 激活函数
    batch_first=False,        # (seq, batch, feature) 格式
)

# 前向传播
src = torch.rand((10, 32, 512))   # (src_len, batch, d_model)
tgt = torch.rand((20, 32, 512))   # (tgt_len, batch, d_model)
out = transformer(src, tgt)       # (tgt_len, batch, d_model)

6.2 掩码的正确使用

在训练时,你需要传入两个掩码:

python 复制代码
# 生成目标序列的因果掩码(防止看到未来)
tgt_mask = nn.Transformer.generate_square_subsequent_mask(tgt_len)

# 生成填充掩码(忽略 padding 位置)
src_padding_mask = (src == pad_idx)
tgt_padding_mask = (tgt == pad_idx)

out = transformer(src, tgt, tgt_mask=tgt_mask,
                  src_key_padding_mask=src_padding_mask,
                  tgt_key_padding_mask=tgt_padding_mask)

6.3 常见工程陷阱与解决方案

陷阱 1:维度不匹配

现象d_model 不能被 nhead 整除时报错。

解决方案 :确保 d_model % nhead == 0

陷阱 2:掩码形状错误

现象:注意力计算时报 shape mismatch。

解决方案

  • tgt_mask:形状 (tgt_len, tgt_len) 的方阵
  • src_key_padding_mask:形状 (batch, src_len) 的布尔张量,True 表示需要被遮蔽的位置
陷阱 3:训练与推理的差异

现象:训练时效果很好,推理时效果差。

解决方案 :训练时使用 teacher forcing(目标序列全部已知),推理时需要自回归生成。确保推理时:

  • 每次只传入已生成的部分目标序列
  • 使用 tgt_mask 防止看到"未来"

七、总结与展望

7.1 关键里程碑

事件 时间 意义
《Attention Is All You Need》发表 2017 年 6 月 12 日 Transformer 架构诞生
WMT 2014 英德翻译 2017 28.4 BLEU,超越最佳集成模型
WMT 2014 英法翻译 2017 41.8 BLEU,单模型 SOTA
BERT(2018) 2018 仅用编码器的 Transformer
GPT(2018-) 2018 起 仅用解码器的 Transformer
大语言模型时代 2020 起 Transformer 成为所有 LLM 的基石

7.2 设计哲学提炼

Transformer 的设计哲学可以提炼为三个关键词:

  1. 注意力即一切:完全摒弃循环和卷积,仅靠注意力机制捕捉全局依赖

  2. 并行即效率:摆脱顺序计算的束缚,让训练并行化成为可能

  3. 简单即强大:用一套统一的原语(多头注意力 + 前馈网络 + 残差连接)构建了整个架构

7.3 核心架构亮点

亮点 说明
纯注意力架构 完全摒弃 RNN/CNN,开创序列建模新范式
多头注意力 多个头并行捕获不同语义关系
位置编码 正弦/余弦函数注入顺序信息
残差连接 + 层归一化 支持训练深层网络
编码器-解码器注意力 让解码器"查阅"源序列信息

7.4 对开发者的启示

Transformer 的本质不是一种神经网络层,而是一种全新的序列建模范式------用"全局注意力"取代"局部递推",用"并行计算"取代"顺序计算"。它回答了一个根本问题:在处理序列时,我们真的需要一步步地"走"过每一个位置吗?答案是不需要------让所有位置同时"看见"彼此,反而更好

适用场景

  • 机器翻译、文本摘要、对话生成等序列到序列任务
  • 需要捕捉长距离依赖的 NLP 任务
  • 作为 BERT、GPT 等大语言模型的基础架构

不适用场景

  • 对 O(n²) 复杂度敏感的超长序列(>10k tokens)------需使用稀疏注意力或线性注意力变体
  • 对实时性要求极高、无法接受自回归推理延迟的场景

本文数据来源:Vaswani et al., "Attention Is All You Need" (arXiv:1706.03762, 2017)、PyTorch 官方文档及源码(torch.nn.Transformer)、各社区实现解析(截至 2026 年 8 月)

如您所在的企业正面临数字化难题,或有 AI 落地、系统集成相关需求,欢迎进一步沟通。我们可提供针对贵企业具体场景的定制化方案和现场调研服务。

相关推荐
官乐1 小时前
AI面试指南(多agent开发流程)
人工智能·面试·职场和发展
学习zhao极致it1 小时前
AI量化交易训练营(完结)
人工智能
程序员三藏1 小时前
自动化测试用例编写详解
自动化测试·软件测试·python·功能测试·测试工具·职场和发展·测试用例
RAOY的AI笔记1 小时前
ChatGPT账号安全设置教程:MFA、活跃会话、数据导出与异常登录处理
人工智能·安全·chatgpt
水如烟1 小时前
孤能子视角:华夏“科学”回望·04兵法与治理–––“处”的关系场感知
人工智能
招财小梗1 小时前
AI矩阵获客,品牌连锁落地方案揭秘
大数据·人工智能·矩阵
m4Rk_2 小时前
【论文阅读】Agent 记忆机制(47):Nemori——用“预测误差”判断什么经验值得被记住
论文阅读·人工智能·学习·开源·github
230万光年的思念2 小时前
分子动力学模拟参考文献
python
源码学社2 小时前
股市复盘Skills全流程资料包:从手工复盘到AI赋能的进阶指南
人工智能·skills·股市复盘·股市复盘skills