从零开始构建 Transformer:完整代码、原理解析:《Attention Is All You Need》

近年来,自从 2017 年 Google 提出的论文《Attention Is All You Need》发布以来,Transformer 模型迅速成为自然语言处理(NLP)领域的主流模型。与传统的循环神经网络(RNN)和卷积神经网络(CNN)相比,Transformer 模型依靠注意力机制实现了并行化处理,能够有效捕捉长距离依赖关系,并在多个任务上取得了出色的性能。本文将详细讲解 Transformer 模型的设计思想、各模块实现原理、代码实现细节以及优化方法,希望能帮助读者深入理解 Transformer 模型的内部机制。


1. Transformer 模型背景

在深度学习的早期,RNN 及其变种 LSTM、GRU 一直是处理序列数据的主要模型。然而,由于序列数据的并行计算受限以及梯度消失、梯度爆炸等问题,RNN 在长序列建模上存在不少局限性。CNN 在捕获局部特征方面效果较好,但在全局依赖建模上又有所不足。

Transformer 模型正是在这种背景下应运而生。Transformer 模型摒弃了传统的循环结构,完全依赖注意力机制来捕捉序列中各位置之间的依赖关系。这种基于注意力的架构具有以下优点:

并行化计算:所有单词的表示可以同时计算,大大提高训练速度。

捕捉长距离依赖:通过注意力机制,模型能够直接关注序列中任意两个位置的信息。

结构简单:相对于 RNN,Transformer 模型的结构更简单,易于扩展和修改。


2. Transformer 模型架构概述

Transformer 模型由编码器(Encoder)和解码器(Decoder)两部分组成。整个模型的输入是一个源语言序列,输出是一个目标语言序列。

其基本结构图如下:

                   输入嵌入 + 位置编码
                             │
                      ┌────Encoder────┐
                      │   层叠 N 次   │
                      └──────┬──────┘
                             │  memory
                             ▼
              ┌───Decoder───┬────────┐
              │ 层叠 N 次   │   输出层 Generator
              └────────────┴────────┘
                             │
                             ▼
                       输出对数概率分布

 

编码器主要由若干个 EncoderLayer 组成,每一层由两部分构成:多头自注意力层(Multi-Head Self-Attention)和前馈全连接层(Feed-Forward Network),两部分之间采用残差连接(Residual Connection)和层归一化(Layer Normalization)。

解码器的结构与编码器类似,但增加了编码器-解码器注意力层(Encoder-Decoder Attention),用于在生成过程中结合编码器输出的上下文信息,同时使用下三角掩码(subsequent mask)防止模型在解码时利用未来信息。


3. Transformer 模型的核心技术

3.1 注意力机制

注意力机制(Attention Mechanism)是 Transformer 模型的核心。简单来说,注意力机制允许模型在计算每个位置的表示时,根据所有位置的输入数据给予不同的权重。数学上,对于查询(Query)、键(Key)和值(Value),注意力计算公式为:

3.1.1 Scaled Dot-Product Attention

3.1.2 掩码机制

在实际任务中,注意力机制往往需要使用掩码来屏蔽掉某些不需要的信息。例如:

填充掩码(Padding Mask):当输入序列中存在填充(padding)时,模型需要忽略这些填充值对注意力计算的影响。

后续掩码(Subsequent Mask 或 Look-Ahead Mask):在解码器中,为防止模型在生成当前词时利用未来词的信息,需要对注意力分布进行屏蔽,使得每个位置只能看到当前位置及之前的部分。

我们可以使用如下函数自动生成这两类掩码:

def subsequent_mask(size: int) -> torch.Tensor:
    """
    生成下三角掩码,用于解码器的目标序列,确保每个位置只能关注当前及之前的词(防止信息泄露)。
    
    返回的掩码形状为 [1, size, size],其中 True 表示允许访问,False 表示屏蔽。
    """
    subsequent_mask = torch.triu(torch.ones(1, size, size, dtype=torch.uint8), diagonal=1)
    return subsequent_mask == 0

def create_pad_mask(seq: torch.Tensor, pad_token: int = 0) -> torch.Tensor:
    """
    生成填充掩码,用于屏蔽输入序列中 pad 的位置。
    
    :param seq: 输入的词索引张量,形状为 [batch_size, seq_len]
    :param pad_token: 表示 pad 的 token 值,默认是0
    :return: 掩码张量,形状为 [batch_size, 1, seq_len],其中 True 表示非pad位置
    """
    return (seq != pad_token).unsqueeze(1)

这两个函数分别用于生成后续掩码和填充掩码,最终可将二者进行逻辑与运算,生成最终目标序列的掩码。

3.2 多头注意力

单头注意力虽然有效,但在实际应用中,不同的注意力头能够捕获输入数据的不同特征。多头注意力机制通过将输入向量分成多个子空间,在每个子空间上独立计算注意力,然后将各个头的结果拼接起来,经过线性变换得到最终输出。

其计算过程可以描述为:

3.3 前馈全连接层

在 Transformer 中,每个编码器层和解码器层都包含一个前馈全连接层。该层由两个线性层构成,中间通过 ReLU 激活和 dropout 进行非线性转换。

其作用在于为模型提供更强的表达能力,使得单层的注意力机制能够捕捉复杂的模式。

公式如下:

3.4 层归一化与残差连接

在 Transformer 模型中,每个子层(如注意力层、前馈网络)都包裹着残差连接(Residual Connection)和层归一化(LayerNorm)。

这种设计可以使得梯度传递更加顺畅,缓解深层网络中的梯度消失问题,同时有助于模型快速收敛。

残差连接的实现方式为:

这种设计将原始输入与子层的输出相加,起到“跳跃连接”的作用。


4. Transformer 模型的代码实现


下面给出一个基于 PyTorch 的 Transformer 模型的完整实现,包括词嵌入、位置编码、多头注意力、前馈全连接层、层归一化、编码器和解码器层、以及整体 EncoderDecoder 模块。代码实现遵循官方论文《Attention Is All You Need》的设计思想。

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

# -------------------------------
# 辅助函数:深度复制模块
def clones(module: nn.Module, N: int) -> nn.ModuleList:
    """返回包含 N 个深拷贝模块的 ModuleList"""
    return nn.ModuleList([copy.deepcopy(module) for _ in range(N)])

# -------------------------------
# 词嵌入层
class Embeddings(nn.Module):
    def __init__(self, d_model: int, vocab: int):
        super(Embeddings, self).__init__()
        self.lut = nn.Embedding(vocab, d_model)
        self.d_model = d_model

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        # 将词索引映射到向量,并乘以 sqrt(d_model) 来缩放
        return self.lut(x) * math.sqrt(self.d_model)

# -------------------------------
# 位置编码器
class PositionalEncoding(nn.Module):
    def __init__(self, d_model: int, dropout: float, max_len: int = 5000):
        super(PositionalEncoding, self).__init__()
        self.dropout = nn.Dropout(p=dropout)

        # 创建一个 [max_len, d_model] 的位置编码矩阵
        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)
        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: torch.Tensor) -> torch.Tensor:
        # x: [batch_size, seq_len, d_model]
        x = x + self.pe[:, :x.size(1)]
        return self.dropout(x)

# -------------------------------
# 注意力函数(Scaled Dot-Product Attention)
def attention(query: torch.Tensor, key: torch.Tensor, value: torch.Tensor,
              mask: torch.Tensor = None, dropout: nn.Dropout = None) -> (torch.Tensor, torch.Tensor):
    d_k = query.size(-1)
    # 计算 QK^T 并缩放
    scores = torch.matmul(query, key.transpose(-2, -1)) / math.sqrt(d_k)
    if mask is not None:
        # mask 期望形状为 [batch_size, 1, seq_len, seq_len] 或可广播至该形状
        scores = scores.masked_fill(mask == 0, -1e9)
    p_attn = F.softmax(scores, dim=-1)
    if dropout is not None:
        p_attn = dropout(p_attn)
    return torch.matmul(p_attn, value), p_attn

# -------------------------------
# 多头注意力层
class MultiHeadedAttention(nn.Module):
    def __init__(self, head: int, embedding_dim: int, dropout: float = 0.1):
        super(MultiHeadedAttention, self).__init__()
        assert embedding_dim % head == 0, "embedding_dim 必须能被 head 整除"
        self.d_k = embedding_dim // head
        self.head = head
        self.linears = clones(nn.Linear(embedding_dim, embedding_dim), 4)
        self.attn = None
        self.dropout = nn.Dropout(p=dropout)

    def forward(self, query: torch.Tensor, key: torch.Tensor, value: torch.Tensor,
                mask: torch.Tensor = None) -> torch.Tensor:
        batch_size = query.size(0)
        # 对输入分别做线性变换,再将维度重排为 [batch_size, head, seq_len, d_k]
        query, key, value = [
            lin(x).view(batch_size, -1, self.head, self.d_k).transpose(1, 2)
            for lin, x in zip(self.linears, (query, key, value))
        ]
        # 调用注意力函数;注意:mask 需要形状为 [batch_size, 1, seq_len, seq_len],
        # 如果传入的 mask 形状不对,可以在外部提前调整。
        x, self.attn = attention(query, key, value, mask=mask, dropout=self.dropout)
        # 将输出重排为 [batch_size, seq_len, embedding_dim]
        x = x.transpose(1, 2).contiguous().view(batch_size, -1, self.head * self.d_k)
        return self.linears[-1](x)

# -------------------------------
# 前馈全连接层
class PositionwiseFeedForward(nn.Module):
    def __init__(self, d_model: int, d_ff: int, dropout: float = 0.1):
        super(PositionwiseFeedForward, self).__init__()
        self.w1 = nn.Linear(d_model, d_ff)
        self.w2 = nn.Linear(d_ff, d_model)
        self.dropout = nn.Dropout(p=dropout)

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        return self.w2(self.dropout(F.relu(self.w1(x))))

# -------------------------------
# 层归一化
class LayerNorm(nn.Module):
    def __init__(self, features: int, eps: float = 1e-6):
        super(LayerNorm, self).__init__()
        self.a2 = nn.Parameter(torch.ones(features))
        self.b2 = nn.Parameter(torch.zeros(features))
        self.eps = eps

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        mean = x.mean(-1, keepdim=True)
        std = x.std(-1, keepdim=True)
        return self.a2 * (x - mean) / (std + self.eps) + self.b2

# -------------------------------
# 残差连接与子层连接结构
class SublayerConnection(nn.Module):
    def __init__(self, size: int, dropout: float = 0.1):
        super(SublayerConnection, self).__init__()
        self.norm = LayerNorm(size)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x: torch.Tensor, sublayer) -> torch.Tensor:
        # 规范化后传入子层,最后与输入进行残差连接
        return x + self.dropout(sublayer(self.norm(x)))

# -------------------------------
# 编码器层
class EncoderLayer(nn.Module):
    def __init__(self, size: int, self_attn: nn.Module, feed_forward: nn.Module, dropout: float):
        super(EncoderLayer, self).__init__()
        self.self_attn = self_attn
        self.feed_forward = feed_forward
        self.size = size
        self.sublayer = clones(SublayerConnection(size, dropout), 2)

    def forward(self, x: torch.Tensor, mask: torch.Tensor) -> torch.Tensor:
        x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, mask))
        x = self.sublayer[1](x, self.feed_forward)
        return x

# -------------------------------
# 编码器:堆叠多个 EncoderLayer
class Encoder(nn.Module):
    def __init__(self, layer: nn.Module, N: int):
        super(Encoder, self).__init__()
        self.layers = clones(layer, N)
        self.norm = LayerNorm(layer.size)

    def forward(self, x: torch.Tensor, mask: torch.Tensor) -> torch.Tensor:
        for layer in self.layers:
            x = layer(x, mask)
        return self.norm(x)

# -------------------------------
# 解码器层
class DecoderLayer(nn.Module):
    def __init__(self, d_model: int, self_attn: nn.Module, src_attn: nn.Module,
                 feed_forward: nn.Module, dropout: float):
        super(DecoderLayer, self).__init__()
        self.self_attn = self_attn
        self.src_attn = src_attn
        self.feed_forward = feed_forward
        self.sublayer = clones(SublayerConnection(d_model, dropout), 3)

    def forward(self, x: torch.Tensor, memory: torch.Tensor,
                source_mask: torch.Tensor, target_mask: torch.Tensor) -> torch.Tensor:
        m = memory
        x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, target_mask))
        x = self.sublayer[1](x, lambda x: self.src_attn(x, m, m, source_mask))
        x = self.sublayer[2](x, self.feed_forward)
        return x

# -------------------------------
# 解码器:堆叠多个 DecoderLayer
class Decoder(nn.Module):
    def __init__(self, d_model: int, layer: nn.Module, N: int):
        super(Decoder, self).__init__()
        self.layers = clones(layer, N)
        self.norm = LayerNorm(d_model)

    def forward(self, x: torch.Tensor, memory: torch.Tensor,
                source_mask: torch.Tensor, target_mask: torch.Tensor) -> torch.Tensor:
        for layer in self.layers:
            x = layer(x, memory, source_mask, target_mask)
        return self.norm(x)

# -------------------------------
# 输出生成器:将 Transformer 输出映射到词汇表上
class Generator(nn.Module):
    def __init__(self, d_model: int, vocab_size: int):
        super(Generator, self).__init__()
        self.project = nn.Linear(d_model, vocab_size)

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        return F.log_softmax(self.project(x), dim=-1)

# -------------------------------
# EncoderDecoder 模块:整体 Transformer 模型
class EncoderDecoder(nn.Module):
    def __init__(self, encoder: nn.Module, decoder: nn.Module,
                 source_embed: nn.Module, target_embed: nn.Module,
                 generator: nn.Module):
        super(EncoderDecoder, self).__init__()
        self.encoder = encoder
        self.decoder = decoder
        self.src_embed = source_embed
        self.tgt_embed = target_embed
        self.generator = generator

    def forward(self, source: torch.Tensor, target: torch.Tensor,
                source_mask: torch.Tensor, target_mask: torch.Tensor) -> torch.Tensor:
        memory = self.encode(source, source_mask)
        decoded = self.decode(memory, source_mask, target, target_mask)
        return self.generator(decoded)

    def encode(self, source: torch.Tensor, source_mask: torch.Tensor) -> torch.Tensor:
        return self.encoder(self.src_embed(source), source_mask)

    def decode(self, memory: torch.Tensor, source_mask: torch.Tensor,
               target: torch.Tensor, target_mask: torch.Tensor) -> torch.Tensor:
        return self.decoder(self.tgt_embed(target), memory, source_mask, target_mask)

# -------------------------------
# 掩码辅助函数
def create_pad_mask(seq: torch.Tensor, pad_token: int = 0) -> torch.Tensor:
    """
    生成填充掩码,返回形状 [batch_size, 1, 1, seq_len],后续会广播到注意力得分上
    """
    return (seq != pad_token).unsqueeze(1).unsqueeze(2)

def subsequent_mask(size: int) -> torch.Tensor:
    """
    生成下三角掩码,返回形状 [1, size, size],用于解码器防止利用未来信息
    """
    attn_shape = (1, size, size)
    subsequent_mask = torch.triu(torch.ones(attn_shape, dtype=torch.uint8), diagonal=1)
    return subsequent_mask == 0

# -------------------------------
# 构建 Transformer 模型的函数
def make_model(source_vocab: int, target_vocab: int, N: int = 6,
               d_model: int = 512, d_ff: int = 2048, head: int = 8,
               dropout: float = 0.1) -> nn.Module:
    c = copy.deepcopy
    attn = MultiHeadedAttention(head, d_model, dropout)
    ff = PositionwiseFeedForward(d_model, d_ff, dropout)
    position = PositionalEncoding(d_model, dropout)
    model = EncoderDecoder(
        Encoder(EncoderLayer(d_model, c(attn), c(ff), dropout), N),
        Decoder(d_model, DecoderLayer(d_model, c(attn), c(attn), c(ff), dropout), N),
        nn.Sequential(Embeddings(d_model, source_vocab), c(position)),
        nn.Sequential(Embeddings(d_model, target_vocab), c(position)),
        Generator(d_model, target_vocab)
    )
    # 参数初始化(Xavier 均匀初始化)
    for p in model.parameters():
        if p.dim() > 1:
            nn.init.xavier_uniform_(p)
    return model

# -------------------------------
# 测试 Transformer 模型
def dm_test_make_model():
    source_vocab = 500
    target_vocab = 1000
    N = 6
    model = make_model(source_vocab, target_vocab, N=N, d_model=512, d_ff=2048, head=8, dropout=0.1)
    print("模型结构:\n", model)

    # 构造测试输入,形状 [batch_size, seq_len]
    source = torch.LongTensor([[1, 2, 3, 8], [3, 4, 1, 8]])
    target = torch.LongTensor([[1, 2, 3, 8], [3, 4, 1, 8]])

    # 自动生成掩码:pad 掩码(此处假设 pad token 为 0)和后续掩码(下三角 mask)
    source_mask = create_pad_mask(source, pad_token=0)  # [batch_size, 1, 1, src_seq_len]
    # 解码器的掩码同时考虑 pad 掩码与后续 mask
    tgt_pad_mask = create_pad_mask(target, pad_token=0)   # [batch_size, 1, 1, tgt_seq_len]
    tgt_sub_mask = subsequent_mask(target.size(1)).to(target.device)  # [1, tgt_seq_len, tgt_seq_len]
    # 广播后得到的 target_mask 形状为 [batch_size, 1, tgt_seq_len, tgt_seq_len],再 squeeze第一维即可用于注意力计算
    target_mask = tgt_pad_mask & tgt_sub_mask.unsqueeze(1)

    output = model(source, target, source_mask, target_mask)
    print("输出结果:", output)
    print("输出形状:", output.shape)

if __name__ == '__main__':
    dm_test_make_model()

输出:

代码说明

本文介绍了基于 PyTorch 实现 Transformer 模型的完整代码,包括以下主要部分:

1. 基础模块与辅助函数

• 使用 clones 函数实现深度复制。

• Embeddings 和 PositionalEncoding 实现了输入数据的嵌入和位置编码。

2. 注意力机制

• attention 函数实现了 Scaled Dot-Product Attention,并支持掩码操作。

• MultiHeadedAttention 通过多个线性层和维度重排实现多头注意力。

3. 前馈全连接层与层归一化

• PositionwiseFeedForward 由两个线性层、ReLU 和 dropout 组成。

• LayerNorm 对输入的最后一维进行归一化。

4. 残差连接与子层连接

• SublayerConnection 先对输入进行归一化,再传入子层,并加上原始输入形成残差连接。

5. 编码器与解码器

• EncoderLayer 和 DecoderLayer 分别构成编码器和解码器的基本单元,堆叠后构成完整的编码器和解码器。

• DecoderLayer 中还包含编码器-解码器注意力层。

6. 输出生成器与整体模型

• Generator 将解码器输出映射到目标词汇表上,并通过 log_softmax 得到对数概率分布。

• EncoderDecoder 模块将所有部分组合成完整的 Transformer 模型。

7. 掩码生成

• 自动生成 pad 掩码和下三角掩码,确保模型在训练和推理过程中不会利用无效信息。

8. 参数初始化

• 使用 Xavier 均匀初始化方法初始化模型参数。

这套代码实现遵循了论文《Attention Is All You Need》的设计思想,可直接用于构建 Transformer 模型,并根据具体任务进行扩展与优化。


4.1 辅助函数与基础模块

首先是一些辅助函数,例如深度复制模块(clones)以及 Embeddings、PositionalEncoding 等基础模块。

 

位置编码的计算公式: 

 

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

def clones(module: nn.Module, N: int) -> nn.ModuleList:
    """返回包含 N 个深拷贝模块的 ModuleList"""
    return nn.ModuleList([copy.deepcopy(module) for _ in range(N)])

class Embeddings(nn.Module):
    def __init__(self, d_model: int, vocab: int):
        super(Embeddings, self).__init__()
        self.d_model = d_model
        self.lut = nn.Embedding(vocab, d_model)
    def forward(self, x: torch.Tensor) -> torch.Tensor:
        return self.lut(x) * math.sqrt(self.d_model)

class PositionalEncoding(nn.Module):
    def __init__(self, d_model: int, dropout: float, max_len: int = 5000):
        super(PositionalEncoding, self).__init__()
        self.dropout = nn.Dropout(p=dropout)
        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)
        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: torch.Tensor) -> torch.Tensor:
        x = x + self.pe[:, :x.size(1)]
        return self.dropout(x)

4.2 注意力机制与多头注意力层

接下来实现注意力函数和多头注意力层。其中注意力函数计算 Scaled Dot-Product Attention,并支持掩码操作(支持填充掩码与后续掩码)。

def attention(query: torch.Tensor, key: torch.Tensor, value: torch.Tensor,
              mask: torch.Tensor = None, dropout: nn.Dropout = None) -> (torch.Tensor, torch.Tensor):
    d_k = query.size()[-1]
    scores = torch.matmul(query, key.transpose(-2, -1)) / math.sqrt(d_k)
    if mask is not None:
        # 如果 mask 的形状为 [batch_size, seq_len, seq_len],扩展到 [batch_size, 1, seq_len, seq_len]
        if mask.dim() == 3:
            mask = mask.unsqueeze(1)
        elif mask.size(0) != query.size(0):
            raise ValueError("mask 的第一维必须与 batch_size 相等")
        scores = scores.masked_fill(mask == 0, -1e9)
    p_attn = F.softmax(scores, dim=-1)
    if dropout is not None:
        p_attn = dropout(p_attn)
    x = torch.matmul(p_attn, value)
    return x, p_attn

class MultiHeadedAttention(nn.Module):
    def __init__(self, head: int, embedding_dim: int, dropout: float = 0.1):
        super(MultiHeadedAttention, self).__init__()
        assert embedding_dim % head == 0, "embedding_dim 必须能被 head 整除"
        self.d_k = embedding_dim // head
        self.head = head
        self.linears = clones(nn.Linear(embedding_dim, embedding_dim), 4)
        self.attn = None
        self.dropout = nn.Dropout(p=dropout)
    def forward(self, query: torch.Tensor, key: torch.Tensor, value: torch.Tensor,
                mask: torch.Tensor = None) -> torch.Tensor:
        batch_size = query.size(0)
        query, key, value = [
            model(x).view(batch_size, -1, self.head, self.d_k).transpose(1, 2)
            for model, x in zip(self.linears, (query, key, value))
        ]
        x, self.attn = attention(query, key, value, mask=mask, dropout=self.dropout)
        x = x.transpose(1, 2).contiguous().view(batch_size, -1, self.head * self.d_k)
        return self.linears[-1](x)

4.3 前馈全连接层与层归一化

前馈全连接层和层归一化层在 Transformer 模型中同样扮演重要角色。前馈层增加了网络的非线性能力,而层归一化有助于稳定训练过程。

class PositionwiseFeedForward(nn.Module):
    def __init__(self, d_model: int, d_ff: int, dropout: float = 0.1):
        super(PositionwiseFeedForward, self).__init__()
        self.w1 = nn.Linear(d_model, d_ff)
        self.w2 = nn.Linear(d_ff, d_model)
        self.dropout = nn.Dropout(p=dropout)
    def forward(self, x: torch.Tensor) -> torch.Tensor:
        return self.w2(self.dropout(F.relu(self.w1(x))))

class LayerNorm(nn.Module):
    def __init__(self, features: int, eps: float = 1e-6):
        super(LayerNorm, self).__init__()
        self.a2 = nn.Parameter(torch.ones(features))
        self.b2 = nn.Parameter(torch.zeros(features))
        self.eps = eps
    def forward(self, x: torch.Tensor) -> torch.Tensor:
        mean = x.mean(-1, keepdim=True)
        std = x.std(-1, keepdim=True)
        return self.a2 * (x - mean) / (std + self.eps) + self.b2

4.4 残差连接与子层连接结构

残差连接与子层连接(Sublayer Connection)在 Transformer 中用于缓解梯度消失问题,并加快收敛速度。代码如下:

class SublayerConnection(nn.Module):
    def __init__(self, size: int, dropout: float = 0.1):
        super(SublayerConnection, self).__init__()
        self.norm = LayerNorm(size)
        self.dropout = nn.Dropout(dropout)
    def forward(self, x: torch.Tensor, sublayer) -> torch.Tensor:
        return x + self.dropout(sublayer(self.norm(x)))

4.5 编码器层、编码器和解码器层

编码器层(EncoderLayer)和解码器层(DecoderLayer)是 Transformer 的基本组成单元。编码器层由自注意力层和前馈全连接层组成;解码器层除了自注意力层和前馈层外,还包括编码器-解码器注意力层。

class EncoderLayer(nn.Module):
    def __init__(self, size: int, self_attn: nn.Module, feed_forward: nn.Module, dropout: float):
        super(EncoderLayer, self).__init__()
        self.self_attn = self_attn
        self.feed_forward = feed_forward
        self.size = size
        self.sublayer = clones(SublayerConnection(size, dropout), 2)
    def forward(self, x: torch.Tensor, mask: torch.Tensor) -> torch.Tensor:
        x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, mask))
        x = self.sublayer[1](x, self.feed_forward)
        return x

class Encoder(nn.Module):
    def __init__(self, layer: nn.Module, N: int):
        super(Encoder, self).__init__()
        self.layers = clones(layer, N)
        self.norm = LayerNorm(layer.size)
    def forward(self, x: torch.Tensor, mask: torch.Tensor) -> torch.Tensor:
        for layer in self.layers:
            x = layer(x, mask)
        return self.norm(x)

class DecoderLayer(nn.Module):
    def __init__(self, d_model: int, self_attn: nn.Module, src_attn: nn.Module,
                 feed_forward: nn.Module, dropout: float):
        super(DecoderLayer, self).__init__()
        self.d_model = d_model
        self.self_attn = self_attn
        self.src_attn = src_attn
        self.feed_forward = feed_forward
        self.sublayer = clones(SublayerConnection(d_model, dropout), 3)
    def forward(self, x: torch.Tensor, memory: torch.Tensor,
                source_mask: torch.Tensor, target_mask: torch.Tensor) -> torch.Tensor:
        m = memory
        x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, target_mask))
        x = self.sublayer[1](x, lambda x: self.src_attn(x, m, m, source_mask))
        x = self.sublayer[2](x, self.feed_forward)
        return x

class Decoder(nn.Module):
    def __init__(self, d_model: int, layer: nn.Module, N: int):
        super(Decoder, self).__init__()
        self.layers = clones(layer, N)
        self.norm = LayerNorm(d_model)
    def forward(self, x: torch.Tensor, memory: torch.Tensor,
                source_mask: torch.Tensor, target_mask: torch.Tensor) -> torch.Tensor:
        for layer in self.layers:
            x = layer(x, memory, source_mask, target_mask)
        return self.norm(x)

4.6 输出生成器与整体 Transformer 模型

输出生成器(Generator)负责将解码器输出映射到目标词汇表上,并通过 log_softmax 生成对数概率。整体 Transformer 模型封装了编码器、解码器、嵌入层和输出层。

class Generator(nn.Module):
    def __init__(self, d_model: int, vocab_size: int):
        super(Generator, self).__init__()
        self.project = nn.Linear(d_model, vocab_size)
    def forward(self, x: torch.Tensor) -> torch.Tensor:
        return F.log_softmax(self.project(x), dim=-1)

class EncoderDecoder(nn.Module):
    def __init__(self, encoder: nn.Module, decoder: nn.Module,
                 source_embed: nn.Module, target_embed: nn.Module,
                 generator: nn.Module):
        super(EncoderDecoder, self).__init__()
        self.encoder = encoder
        self.decoder = decoder
        self.src_embed = source_embed
        self.tgt_embed = target_embed
        self.generator = generator
    def forward(self, source: torch.Tensor, target: torch.Tensor,
                source_mask: torch.Tensor, target_mask: torch.Tensor) -> torch.Tensor:
        memory = self.encode(source, source_mask)
        decoded = self.decode(memory, source_mask, target, target_mask)
        return self.generator(decoded)
    def encode(self, source: torch.Tensor, source_mask: torch.Tensor) -> torch.Tensor:
        return self.encoder(self.src_embed(source), source_mask)
    def decode(self, memory: torch.Tensor, source_mask: torch.Tensor,
               target: torch.Tensor, target_mask: torch.Tensor) -> torch.Tensor:
        return self.decoder(self.tgt_embed(target), memory, source_mask, target_mask)

4.7 构建 Transformer 模型函数

最后,我们封装一个函数 make_model 来构建整个 Transformer 模型。该函数根据给定参数初始化各个模块,完成模型参数初始化(使用 Xavier 均匀初始化),并返回最终的 EncoderDecoder 模型。

def make_model(source_vocab: int, target_vocab: int, N: int = 6,
               d_model: int = 512, d_ff: int = 2048, head: int = 8,
               dropout: float = 0.1) -> nn.Module:
    c = copy.deepcopy
    attn = MultiHeadedAttention(head=head, embedding_dim=d_model, dropout=dropout)
    ff = PositionwiseFeedForward(d_model=d_model, d_ff=d_ff, dropout=dropout)
    position = PositionalEncoding(d_model=d_model, dropout=dropout)
    model = EncoderDecoder(
        Encoder(EncoderLayer(d_model, c(attn), c(ff), dropout), N),
        Decoder(d_model, DecoderLayer(d_model, c(attn), c(attn), c(ff), dropout), N),
        nn.Sequential(Embeddings(d_model, source_vocab), c(position)),
        nn.Sequential(Embeddings(d_model, target_vocab), c(position)),
        Generator(d_model, target_vocab)
    )
    for p in model.parameters():
        if p.dim() > 1:
            nn.init.xavier_uniform_(p)
    return model

4.8 测试 Transformer 模型

以下是一个测试函数 dm_test_make_model,用于构造测试输入、生成掩码,并通过 Transformer 模型前向传播得到输出。注意,这里掩码部分我们实现了动态生成 pad 掩码和后续掩码,这样用户不需要手动提供全0的掩码。

def dm_test_make_model():
    source_vocab = 500
    target_vocab = 1000
    N = 6
    model = make_model(source_vocab, target_vocab, N=N, d_model=512, d_ff=2048, head=8, dropout=0.1)
    print(model)  # 打印模型结构

    # 构造测试输入:假设源数据和目标数据相同,形状为 [batch_size, seq_len]
    source = torch.LongTensor([[1, 2, 3, 8], [3, 4, 1, 8]])
    target = torch.LongTensor([[1, 2, 3, 8], [3, 4, 1, 8]])

    # 自动生成掩码:
    # 假设 pad token 为 0,则生成 pad 掩码;此处示例中输入不含 pad,可省略。
    def create_pad_mask(seq: torch.Tensor, pad_token: int = 0) -> torch.Tensor:
        return (seq != pad_token).unsqueeze(1)
    source_mask = create_pad_mask(source, pad_token=0)  # [batch_size, 1, src_seq_len]

    # 生成后续掩码(下三角掩码)用于目标序列
    def subsequent_mask(size: int) -> torch.Tensor:
        subsequent_mask = torch.triu(torch.ones(1, size, size, dtype=torch.uint8), diagonal=1)
        return subsequent_mask == 0
    target_mask = subsequent_mask(target.size(1)).to(source_mask.device)  # [1, tgt_seq_len, tgt_seq_len]
    # 如果需要同时屏蔽 pad,可以将 pad 掩码和 subsequent mask 结合
    pad_target_mask = create_pad_mask(target, pad_token=0)  # [batch_size, 1, tgt_seq_len]
    target_mask = pad_target_mask & target_mask  # 广播与结合

    output = model(source, target, source_mask, target_mask)
    print("输出结果:", output)
    print("输出形状:", output.shape)

if __name__ == '__main__':
    dm_test_make_model()

5. Transformer 的优化与扩展

Transformer 模型自发布以来,衍生出很多优化变体和扩展模型,例如 BERT、GPT、T5 等。下面介绍几种常见的优化思路:

5.1 模型参数初始化

在 Transformer 模型中,参数初始化非常关键。原论文中推荐使用 Xavier 均匀初始化(也称 Glorot 初始化),这种方法能够保持各层输出的方差稳定,从而有助于梯度传递。代码中我们已经在 make_model 函数中对所有参数使用了 nn.init.xavier_uniform_ 进行初始化。

5.2 掩码生成的改进

如前所述,掩码在 Transformer 中扮演了至关重要的角色。实际任务中,我们通常不希望用户手动提供全 0 掩码,而是自动生成 pad 掩码和后续掩码。

• 对于 pad 掩码,可以直接根据输入序列中 pad token 的位置生成。

• 对于后续掩码(look-ahead mask),生成下三角矩阵即可。

这两者可以通过逻辑与运算生成最终的目标掩码。

5.3 多头注意力的改进

原始 Transformer 中的多头注意力对每个头使用相同的计算流程,研究者后来提出了不同的变种,例如分离式注意力(separable attention)、改进的查询键分离策略等。

此外,如何高效实现大规模注意力计算也是一个热点问题,如 Linformer、Reformer 等模型提出了低秩近似或局部敏感哈希等方法来降低计算复杂度。

5.4 位置编码的改进

位置编码在 Transformer 中用于引入序列顺序信息,原始版本使用固定的正弦/余弦编码。后续工作中,BERT、RoBERTa 等预训练模型通常采用可学习的位置编码。此外,也有研究尝试利用相对位置编码(Relative Position Encoding)以更好地捕捉相对距离信息。

5.5 模型剪枝与加速

Transformer 模型参数量庞大,为了在移动设备或实时系统中应用,模型剪枝(Pruning)、量化(Quantization)以及知识蒸馏(Knowledge Distillation)等方法被广泛研究,以减小模型体积和加速推理过程。


6. 总结

Transformer 模型彻底改变了自然语言处理的研究方向,其基于注意力机制的设计突破了传统 RNN 的局限,实现了高效的并行计算和长距离依赖建模。本文详细介绍了 Transformer 模型的各个组成部分,包括:

1. 词嵌入与位置编码:将离散的词索引转换为连续的向量表示,并加入位置信息以捕捉序列顺序。

2. 注意力机制:核心的 Scaled Dot-Product Attention 及其多头变体,使模型可以灵活地捕获全局信息。

3. 前馈全连接层:增加模型的非线性表达能力,提高模型的拟合能力。

4. 层归一化与残差连接:缓解深层网络训练时的梯度消失问题,加快模型收敛速度。

5. 编码器和解码器结构:采用堆叠的 EncoderLayer 和 DecoderLayer 构建深层网络,实现源语言与目标语言之间的信息转换。

6. 掩码机制:自动生成 pad 掩码和后续掩码,确保模型在训练和推理过程中不会利用不应看到的信息。

此外,我们还讨论了 Transformer 模型的优化方法和扩展方向,从参数初始化、注意力计算优化到模型剪枝与量化,每个环节都是提升模型性能和效率的重要手段。

Transformer 模型以其高效并行、灵活捕获长距离依赖的优势,迅速成为 NLP 领域的核心架构。本文详细讲解了 Transformer 模型的各个模块——从嵌入层、位置编码、注意力机制、多头注意力、前馈网络、层归一化、残差连接到编码器和解码器的堆叠构造,以及如何利用自动生成的掩码确保模型正确工作。

通过完整的代码实现和详细解释,希望本文能帮助读者从理论到实践全面掌握 Transformer 模型,并为进一步优化与扩展提供参考。无论是用于机器翻译、文本生成还是其他 NLP 任务,Transformer 都为我们提供了强大的工具。未来,随着模型剪枝、量化和新型注意力机制的发展,Transformer 的应用前景将更加广阔。


参考资料

Attention Is All You Need (论文原文)

The Annotated Transformer

PyTorch 官方文档

• Transformer 论文(Attention Is All You Need):[1706.03762] Attention Is All You Need

• The Annotated Transformer(Harvard NLP):The Annotated Transformer


点赞、转发与反馈

如果你觉得本文对你理解 Transformer 模型有帮助,请点个赞、转发分享给更多的小伙伴,也欢迎留言讨论、指出不足,共同进步!

(完)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值