Transformer架构
Transformer是一种由Vaswani等人在2017年提出的神经网络架构,最初用于机器翻译任务,但现在已广泛应用于各种自然语言处理(NLP)任务,如文本生成、问答系统和文本分类等。与传统的循环神经网络(RNN)和长短期记忆(LSTM)网络不同,Transformer完全基于注意力机制,不使用任何循环或卷积操作,这使得它在处理长距离依赖关系时更为高效。
Transformer模型主要由编码器(Encoder)和解码器(Decoder)两个部分组成,每个部分又由多个堆叠的相同层(Layers)构成。
编码器(Encoder)
编码器的每一层包含两个子层:
- 多头自注意力机制(Multi-Head Self-Attention Mechanism)
- 输入的一组向量通过多个不同的注意力头进行计算,每个头独立地关注不同的上下文部分。
- 这些多个注意力头的输出被连接起来,再通过线性变换得到最终的输出。
- 位置前馈网络(Position-wise Feed-Forward Network)
- 一个简单的全连接前馈网络,应用于每个位置的向量。
- 后接ReLU激活函数和另一个线性变换。
注意力机制(Attention Mechanism)
注意力机制是Transformer的核心组件,用于计算输入序列中不同词之间的相关性。具体步骤如下:
- 计算Query、Key和Value:
- 对输入向量进行线性变换得到查询(Q)、键(K)和值(V)向量。
- 计算注意力权重:
- 通过点积计算查询向量与键向量之间的相似性,并应用缩放因子和softmax函数得到注意力权重。
- 加权求和得到输出:
- 使用注意力权重对值向量进行加权求和,得到最终的注意力输出。
多头注意力(Multi-Head Attention)
多头注意力机制通过并行多个独立的注意力头来扩展模型的能力,使其能够在不同的子空间中关注不同的上下文信息。每个头独立计算注意力,然后将所有头的输出连接在一起,再进行线性变换。
优点
- 并行计算:由于没有循环操作,Transformer可以并行处理输入序列中的所有词,极大地提高了计算效率。
- 捕捉长距离依赖:注意力机制使得模型能够有效捕捉输入序列中的长距离依赖关系。
- 灵活性强:可以用于多种NLP任务,并在许多任务上取得了显著的效果。
应用
Transformer模型已经成为NLP领域的标准架构,并被广泛应用于以下任务:
- 机器翻译(如Google的翻译系统)
- 文本生成(如GPT系列模型)
- 问答系统(如BERT)
- 文本分类(如RoBERTa)
- 信息抽取(如T5)
代码解析
以Task2给出的代码为基础,主要修改模型构建部分:
class PositionalEncoding(nn.Module):
def __init__(self, d_model, dropout=0.1, max_len=5000):
super(PositionalEncoding, self).__init__()
self.dropout = nn.Dropout(p=dropout)
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).transpose(0, 1)
self.register_buffer('pe', pe)
def forward(self, x):
x = x + self.pe[:x.size(0), :]
return self.dropout(x)
class TransformerModel(nn.Module):
def __init__(self, src_vocab, tgt_vocab, d_model, nhead, num_encoder_layers, num_decoder_layers, dim_feedforward, dropout):
super(TransformerModel, self).__init__()
self.transformer = nn.Transformer(d_model, nhead, num_encoder_layers, num_decoder_layers, dim_feedforward, dropout)
self.src_embedding = nn.Embedding(len(src_vocab), d_model)
self.tgt_embedding = nn.Embedding(len(tgt_vocab), d_model)
self.positional_encoding = PositionalEncoding(d_model, dropout)
self.fc_out = nn.Linear(d_model, len(tgt_vocab))
self.src_vocab = src_vocab
self.tgt_vocab = tgt_vocab
self.d_model = d_model
def forward(self, src, tgt):
# 调整src和tgt的维度
src = src.transpose(0, 1) # (seq_len, batch_size)
tgt = tgt.transpose(0, 1) # (seq_len, batch_size)
src_mask = self.transformer.generate_square_subsequent_mask(src.size(0)).to(src.device)
tgt_mask = self.transformer.generate_square_subsequent_mask(tgt.size(0)).to(tgt.device)
src_padding_mask = (src == self.src_vocab['<pad>']).transpose(0, 1)
tgt_padding_mask = (tgt == self.tgt_vocab['<pad>']).transpose(0, 1)
src_embedded = self.positional_encoding(self.src_embedding(src) * math.sqrt(self.d_model))
tgt_embedded = self.positional_encoding(self.tgt_embedding(tgt) * math.sqrt(self.d_model))
output = self.transformer(src_embedded, tgt_embedded,
src_mask, tgt_mask, None, src_padding_mask, tgt_padding_mask, src_padding_mask)
return self.fc_out(output).transpose(0, 1)
这段代码实现了一个基于Transformer模型的序列到序列(sequence-to-sequence)模型,用于例如机器翻译任务。代码主要包括两个类:PositionalEncoding
和TransformerModel
。
PositionalEncoding类
PositionalEncoding
类实现了位置编码,用于向输入的词向量添加位置信息,使模型能够感知序列中每个词的位置。具体来说:
-
__init__
方法:d_model
:模型的维度。dropout
:应用于位置编码后的dropout概率。max_len
:预计算的位置编码的最大长度。- 通过计算位置编码矩阵
pe
,并保存它以供后续使用。 register_buffer
用于保存pe
,这样在模型保存和加载时可以自动处理这个参数。
-
forward
方法:- 输入
x
:形状为(seq_len, batch_size, d_model)的张量。 - 将位置编码添加到输入
x
上并应用dropout。
- 输入
TransformerModel类
TransformerModel
类实现了一个基于PyTorch的完整Transformer模型结构,包括词嵌入层、位置编码层、Transformer编码器-解码器及输出层。
-
__init__
方法:- 初始化各种层,包括词嵌入层、位置编码层、Transformer层和最终的全连接层。
src_vocab
和tgt_vocab
是源语言和目标语言的词汇表,用于词嵌入的初始化。d_model
:词嵌入和模型的维度。nhead
:多头注意力机制中的头数。num_encoder_layers
和num_decoder_layers
:编码器和解码器的层数。dim_feedforward
:前馈神经网络的维度。dropout
:dropout概率。
-
forward
方法:- 输入
src
和tgt
:源语言和目标语言的输入序列,形状为(batch_size, seq_len)。 - 调整
src
和tgt
的维度,变为(seq_len, batch_size)以符合Transformer输入要求。 - 生成源和目标序列的自注意力mask(掩码)。
- 生成源和目标序列的padding mask,避免在计算中考虑填充位置。
- 对源和目标序列进行词嵌入并添加位置编码。
- 通过Transformer层计算输出。
- 通过全连接层将模型输出转换为目标词汇表的大小。
- 输入
代码流程
- 位置编码(PositionalEncoding):为输入序列添加位置信息。
- 词嵌入(Embedding):将输入序列的索引转换为词向量。
- Transformer层:通过多头注意力机制和前馈神经网络层处理输入序列。
- 全连接层(fc_out):将Transformer的输出转换为目标词汇表的概率分布。
- 掩码(Masking):应用于自注意力机制中,避免在计算中考虑填充位置。
运行结果
在魔搭平台运行代码,并将得到的submit_task3.txt文件提交至讯飞平台,分数相比于task1的0.6571分已经有了很大的提升,达到了11.5902分
提分技巧
调参
- 修改N的大小,使用全部训练集;
- 修改N_EPOCHS大小,适当增大训练轮数。
加入术语词典
这是在此竞赛中比较有效的方法,加入术语词典的方法策略也有很多,如:
- 在模型生成的翻译输出中替换术语,这是最简单的方法
- 整合到数据预处理流程,确保它们在翻译中保持一致
- 在模型内部动态地调整术语的嵌入,这涉及到在模型中加入一个额外的层,该层负责查找术语词典中的术语,并为其生成专门的嵌入向量,然后将这些向量与常规的词嵌入结合使用
数据清洗
将训练集中的脏数据手动清除,或使用正则匹配等方式清除。
数据扩增
-
回译(back-translation):将源语言文本先翻译成目标语言,再将目标语言文本翻译回源语言,生成的新文本作为额外的训练数据
-
同义词替换:随机选择句子中的词,并用其同义词替换
-
使用句法分析和语义解析技术重新表述句子,保持原意不变
-
将文本翻译成多种语言后再翻译回原语言,以获得多样化翻译
采用更精细的学习率调度策略
-
Noam Scheduler:结合了warmup(预热)阶段和衰减阶段
-
Step Decay:最简单的一种学习率衰减策略,每隔一定数量的epoch,学习率按固定比例衰减
-
Cosine Annealing:学习率随周期性变化,通常从初始值下降到接近零,然后再逐渐上升