总结笔记: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) # (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 模型
model = TransformerModel(src_vocab, tgt_vocab, d_model, nhead, num_encoder_layers, num_decoder_layers, dim_feedforward, dropout)
三、其他上分技巧
1. 调参(Hyperparameter Tuning)
调参是提升模型性能的重要步骤。以下是一些常见的超参数及其调整建议:
- Epochs:增加训练轮数可以让模型有更多机会学习训练数据中的模式,但要注意防止过拟合。
- Batch Size:较大的批量大小可以加快训练速度,但需要更多的显存;较小的批量大小则可能提高模型的泛化能力。
- Learning Rate:合适的学习率对模型训练的收敛速度和最终效果有很大影响。可以尝试使用学习率调度器(如Noam Scheduler)来动态调整学习率。
- Dropout Rate:适当的dropout可以防止过拟合,提高模型的泛化能力。
- Head和Layer数量:增加多头注意力机制的头数和编码器、解码器的层数可以提高模型的表达能力,但也会增加计算复杂度。
2. 加入术语词典
在机器翻译中,术语词典可以帮助模型处理专业术语和固定表达:
- 输出替换:模型生成翻译结果后,将其中的术语替换为词典中的对应词语。
- 数据预处理:在数据预处理阶段,将训练数据中的术语按照词典进行替换,使模型在训练时就能学习到术语的正确翻译。
- 嵌入调整:在模型中加入专门处理术语的层,通过查找词典中的术语,为其生成专门的嵌入向量,并与常规词嵌入结合使用。
3. 数据清洗(Data Cleaning)
清洗数据可以提高模型训练的效果:
- 删除噪声数据:删除或修正训练数据中的拼写错误、语法错误和翻译错误。
- 标准化文本:统一文本格式,如大小写转换、标点符号处理等。
- 去重:删除重复的句对,以减少数据冗余。
4. 数据扩增(Data Augmentation)
数据扩增可以增加训练数据的多样性,提升模型的泛化能力:
- 回译(Back-Translation):将源语言文本翻译成目标语言,再将其翻译回源语言,生成的新文本作为额外的训练数据。
- 同义词替换:随机选择句子中的词,并用其同义词替换。
- 语法重构:使用句法分析和语义解析技术重新表述句子,保持原意不变。
- 多语言翻译:将文本翻译成多种语言后再翻译回原语言,以获得多样化翻译。
5. 学习率调度策略(Learning Rate Scheduling)
除了固定学习率,可以使用动态调整学习率的方法:
- Noam Scheduler:结合了warmup(预热)阶段和衰减阶段,适合Transformer模型。
- Step Decay:每隔一定数量的epoch,学习率按固定比例衰减。
- Cosine Annealing:学习率随周期性变化,通常从初始值下降到接近零,然后再逐渐上升。
6. 预训练模型(Pre-trained Models)
使用预训练模型可以提高模型的初始性能:
- 小型预训练模型:选择1B以下的小模型进行预训练,然后在目标任务上进行微调。预训练模型可以提供更好的初始权重,使模型更快收敛。
- 自定义预训练:在自己的大规模数据集上训练一个小型预训练模型,然后在目标任务上进行微调。
7. 细化训练集(Fine-tuning on Dev Set)
在开发集上进行微调,可以提高测试集上的表现:
- 开发集微调:使用开发集的数据对模型进行微调,因为开发集与测试集的分布比较相近,这样可以提高模型在测试集上的性能。
8. 语言模型筛选(Language Model Filtering)
在训练集上训练一个语言模型,用来筛选高质量的句子:
- 高分句子选择:在开发集和测试集上训练一个语言模型,用该模型给训练集中的句子打分,选择高分句子作为训练数据,提高模型的训练质量。
9. 集成学习(Ensemble Learning)
集成学习通过结合多个模型的预测结果,可以提高模型的稳定性和准确性:
- 多模型集成:训练多个不同初始化或架构的模型,并使用集成方法(如投票或平均)来产生最终翻译结果。这可以减少单一模型的过拟合风险,提高翻译的稳定性。
通过这些技巧的综合应用,可以显著提升Transformer模型在机器翻译任务中的表现。每一种技巧都可以根据具体情况进行调整和优化,以获得最佳效果。