总结笔记:Task3:基于Transformer解决机器翻译任务
一、Transformer 介绍
1. 传统序列到序列建模方法的局限性
- 卷积神经网络(CNN):由于受限的上下文窗口,CNN在建模长文本方面存在天然不足,描述长距离依赖需要多层卷积操作,不同层之间信息传递可能有损失。
- 循环神经网络(RNN):上下文的语义依赖通过维护循环单元中的隐状态实现,序列早期的上下文信息可能被逐渐遗忘。注意力机制在一定程度上缓解了这个问题,但RNN在编码效率方面仍存在不足。
2. Transformer 模型
- 提出背景:为了更好地描述文字序列,谷歌研究人员在2017年提出了Transformer模型,该模型完全通过注意力机制完成对源语言序列和目标语言序列全局依赖的建模。
- 核心思想:通过自注意力机制(self-attention)衡量上下文中每一个单词对当前单词的重要程度,摒弃循环结构,使得模型运行变得高效。
3. Transformer 主要组件
- 编码器(Encoder):由多个相同的层叠加而成,每个层包含两个子层:多头自注意力汇聚和基于位置的前馈网络。
- 解码器(Decoder):结构与编码器类似,但额外包含一个多头注意力模块,用于接收编码器的输出。
- 注意力层:核心是利用多头自注意力机制,使每个位置的表示不仅依赖于当前位置,还能够直接获取其他位置的表示。
4. 关键模块
- 嵌入表示层:通过输入嵌入层将每个单词转换为其相对应的向量表示。序列中每一个单词所在的位置都对应一个向量,这些向量会与单词表示相加送入后续模块。
- 位置编码:使用不同频率的正余弦函数计算位置编码,解决了序列中单词之间的相对位置关系问题。
- 自注意力机制:用于计算上下文单词的权重得分,衡量上下文中每一个单词对当前单词的重要程度。
- 前馈层:通过一个带有Relu激活函数的两层全连接网络对输入进行非线性变换。
- 残差连接与层归一化:提升训练的稳定性,缓解优化过程中潜在的不稳定、收敛速度慢等问题。
二、基于 task2 的 baseline 修改代码
- 位置编码
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)
- Transformer 模型
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 = src.transpose(0, 1)
tgt = tgt.transpose(0, 1)
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 模型
model = TransformerModel(src_vocab, tgt_vocab, d_model, nhead, num_encoder_layers, num_decoder_layers, dim_feedforward, dropout)
三、其他上分技巧
- 调参:增加 epochs,使用全部训练集,调整模型参数(如 head 和 layers)。
- 加入术语词典:通过替换术语、数据预处理和动态调整术语嵌入向量等方法提升翻译效果。
- 数据清洗:去除脏数据,提高训练质量。
- 数据扩增:使用回译、同义词替换、重新表述句子和多语言翻译等方法扩增数据。
- 学习率调度策略:使用 Noam Scheduler、Step Decay、Cosine Annealing 等策略调整学习率。
- 训练小预训练模型:对于资源充足的情况下,训练小型预训练模型。
- 开发集上 finetune:在开发集上 finetune 训练集上的模型。
- 使用语言模型:在开发集和测试集上训一个语言模型,用语言模型对训练集中的句子打分,选出高分句子。
- 集成学习:训练多个不同初始化或架构的模型,使用集成方法产生最终翻译。