当前位置:  首页>> 技术小册>> NLP入门到实战精讲(中)

61 | Transformer代码实现剖析

在自然语言处理(NLP)领域,Transformer模型自2017年由Vaswani等人提出以来,凭借其强大的序列建模能力迅速成为了众多任务的基石,包括但不限于机器翻译、文本生成、文本分类等。本章将深入剖析Transformer模型的代码实现,从架构概览到关键组件的详细解析,帮助读者理解并亲手实现这一革命性的模型。

61.1 Transformer模型概述

Transformer模型是一种基于自注意力(Self-Attention)机制的深度神经网络,它彻底摒弃了传统循环神经网络(RNN)和卷积神经网络(CNN)在处理序列数据时的固有局限,通过并行计算大幅度提高了处理效率,同时能够捕捉到序列中任意位置之间的依赖关系。

Transformer模型主要由编码器(Encoder)和解码器(Decoder)两部分组成,每部分由多个相同的层堆叠而成。每层包含两个子层:第一个是多头自注意力(Multi-Head Attention)机制,用于处理输入序列的上下文信息;第二个是前馈神经网络(Feed Forward Neural Network),用于非线性变换和特征提取。此外,每个子层后都接有残差连接(Residual Connection)和层归一化(Layer Normalization),以增强模型的稳定性和训练效率。

61.2 Transformer代码结构概览

在实现Transformer模型之前,我们首先需要定义一些基础的函数和类,包括但不限于:

  • 位置编码(Positional Encoding):由于Transformer模型本身不包含循环或卷积结构,无法直接获取序列中元素的相对或绝对位置信息,因此需要通过位置编码为模型提供位置信息。
  • 掩码(Masking):在训练过程中,解码器需要避免看到未来的信息,因此需要通过掩码机制来阻止自注意力机制访问未来的序列元素。
  • 多头自注意力(Multi-Head Attention):将输入序列分割成多个“头”,每个头独立进行自注意力计算,然后将结果拼接起来,通过线性变换得到最终输出。
  • 前馈神经网络(Feed Forward Network):通常是一个两层全连接网络,用于引入非线性变换。

61.3 关键组件详解与代码实现

61.3.1 位置编码(Positional Encoding)

位置编码是一种将位置信息添加到序列元素中的方法,通常使用正弦和余弦函数来实现。代码示例(Python + PyTorch)如下:

  1. import torch
  2. import math
  3. def positional_encoding(position, d_model):
  4. """
  5. 生成位置编码。
  6. :param position: 序列中元素的位置(0, 1, ..., n-1)
  7. :param d_model: 嵌入向量的维度
  8. :return: 形状为 (1, position, d_model) 的位置编码张量
  9. """
  10. pe = torch.zeros(position, d_model)
  11. position = torch.arange(0, position, dtype=torch.float).unsqueeze(1)
  12. div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
  13. pe[:, 0::2] = torch.sin(position * div_term)
  14. pe[:, 1::2] = torch.cos(position * div_term)
  15. pe = pe.unsqueeze(0).transpose(0, 1)
  16. return pe
61.3.2 多头自注意力(Multi-Head Attention)

多头自注意力是Transformer模型的核心部分,它通过并行处理多个自注意力“头”来增强模型的表示能力。代码实现涉及矩阵运算,较为复杂,以下是简化版的框架示意:

  1. class MultiHeadAttention(torch.nn.Module):
  2. def __init__(self, d_model, n_heads):
  3. super(MultiHeadAttention, self).__init__()
  4. self.d_model = d_model
  5. self.n_heads = n_heads
  6. self.d_k = d_model // n_heads
  7. self.wq, self.wk, self.wv = self._weights_init()
  8. def forward(self, q, k, v, mask=None):
  9. # 分割输入到不同的头
  10. q, k, v = self._split_heads(q, k, v)
  11. # 计算自注意力分数
  12. scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.d_k)
  13. # 应用掩码
  14. if mask is not None:
  15. scores = scores.masked_fill(mask == 0, float('-1e20'))
  16. # softmax和缩放
  17. attention_weights = torch.nn.functional.softmax(scores, dim=-1)
  18. # 注意力加权
  19. out = torch.matmul(attention_weights, v)
  20. # 合并头并返回输出
  21. out = self._combine_heads(out)
  22. return out
  23. # 省略了_weights_init, _split_heads, _combine_heads的具体实现
61.3.3 前馈神经网络(Feed Forward Network)

前馈神经网络相对简单,通常是一个两层全连接网络,第一层进行线性变换,第二层进行非线性激活(如ReLU):

  1. class PositionwiseFeedForward(torch.nn.Module):
  2. def __init__(self, d_model, d_ff, dropout=0.1):
  3. super(PositionwiseFeedForward, self).__init__()
  4. self.w1 = torch.nn.Linear(d_model, d_ff)
  5. self.w2 = torch.nn.Linear(d_ff, d_model)
  6. self.dropout = torch.nn.Dropout(dropout)
  7. def forward(self, x):
  8. return self.w2(self.dropout(torch.nn.functional.relu(self.w1(x))))
61.3.4 编码器与解码器层

编码器和解码器层是Transformer模型的基本构建块,它们各自包含多个子层(如多头自注意力、前馈神经网络)以及必要的残差连接和层归一化。

  1. class EncoderLayer(torch.nn.Module):
  2. # 省略具体实现,包括多头自注意力和前馈神经网络的封装
  3. class DecoderLayer(torch.nn.Module):
  4. # 省略具体实现,除了上述子层外,还需处理编码器-解码器注意力
  5. class TransformerModel(torch.nn.Module):
  6. def __init__(self, n_encoder_layers, n_decoder_layers, d_model, n_heads, d_ff, max_seq_length):
  7. # 初始化编码器和解码器层
  8. # ...
  9. def forward(self, src, tgt, src_mask, tgt_mask, src_padding_mask, tgt_padding_mask, memory_key_padding_mask):
  10. # 前向传播逻辑,包括编码器和解码器的处理
  11. # ...

61.4 总结

本章通过详细解析Transformer模型的各个关键组件,包括位置编码、多头自注意力、前馈神经网络以及编码器和解码器层的实现,为读者提供了从理论到实践的全面指导。Transformer模型的强大之处在于其自注意力机制能够捕捉序列中的长距离依赖关系,而多头机制则进一步增强了模型的表示能力。通过深入理解并亲手实现Transformer模型,读者将能够更好地掌握这一前沿技术,并在NLP领域的应用中发挥其巨大潜力。