0


【Datawhale AI夏令营】 Task3 学习笔记

学习目标:进阶Transformer模型完成任务,入门深度学习

一、概念梳理

1、深度学习

深度学习(Deep Learning)是一种机器学习方法,基于人工神经网络进行数据分析和模式识别。它通过模拟人脑的神经元结构和功能,利用多层神经网络(通常包括多个隐藏层)来处理复杂的输入数据,从中提取特征并进行预测或分类。以下是深度学习的一些关键概念和特点:

  1. 神经网络:深度学习模型由多个层组成,每一层包含若干个神经元。神经元通过权重连接,形成一个复杂的网络结构。

  2. 多层结构:与传统的神经网络不同,深度学习模型通常具有多个隐藏层,这使得它们能够提取更高级别的特征。

  3. 激活函数:每个神经元接收输入信号,并通过激活函数(如ReLU、Sigmoid等)进行非线性变换,输出结果传递到下一层。

  4. 训练过程:深度学习模型通过反向传播算法(Backpropagation)进行训练,调整各个连接的权重,以最小化预测误差。

  5. 大数据和高性能计算:深度学习需要大量的数据和计算资源,通常使用GPU或TPU等高性能硬件来加速训练过程。

  6. 应用领域:深度学习在图像识别、自然语言处理、语音识别、自动驾驶、推荐系统等领域取得了显著成果。

    深度学习的发展得益于大数据和计算能力的提升,以及新算法和新模型的不断创新,如卷积神经网络(CNN)、循环神经网络(RNN)和生成对抗网络(GAN)等。通过深度学习,机器能够自动学习和提取数据中的特征,进行复杂任务的预测和决策。

2、Transformer模型

Transformer模型是一种用于自然语言处理(NLP)的神经网络架构,由Vaswani等人在2017年的论文《Attention is All You Need》中提出。与传统的循环神经网络(RNN)和长短期记忆网络(LSTM)不同,Transformer模型完全依赖于注意力机制(Attention Mechanism)来处理输入数据,而不使用循环结构。以下是Transformer模型的关键概念和特点:
  1. 注意力机制(Attention Mechanism):Transformer模型的核心是自注意力机制(Self-Attention),它允许模型在处理每个单词时,能够关注输入序列中的所有其他单词。这种机制使得模型可以捕捉到长距离的依赖关系。

  2. 编码器-解码器架构(Encoder-Decoder Architecture):Transformer模型通常由编码器和解码器两部分组成。编码器将输入序列转换为一组隐藏状态,解码器根据这些隐藏状态生成输出序列。在一些应用中,如机器翻译,编码器-解码器架构尤其有效。

  3. 多头注意力机制(Multi-Head Attention):通过多个注意力头(Attention Heads),模型可以从不同的子空间中学习表示,增强了模型的表达能力和捕捉复杂模式的能力。

  4. 位置编码(Positional Encoding):由于Transformer模型不使用循环结构,它需要一种方式来捕捉输入序列中的位置信息。位置编码通过将位置信息添加到输入嵌入向量中,帮助模型识别序列中的顺序关系。

  5. 层归一化(Layer Normalization)和残差连接(Residual Connections):这些技术帮助训练更深的模型,防止梯度消失或爆炸,并加速收敛。

  6. 前馈神经网络(Feed-Forward Neural Networks):在每个编码器和解码器层中,除了注意力机制外,还有一个前馈神经网络,用于进一步处理特征表示。

    总结而言,Transformer模型通过注意力机制和并行计算显著提升了NLP任务的性能和效率,成为当前深度学习研究和应用中的一个重要工具。

二、Baseline精解与实操

由于Task3的baseline主要是在Task2的baseline基础上改进了模型,其余变动较少,我们着重看一下与Task2不同的部分,相同的部分不再赘述。

1、模型改进

# 位置编码
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和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)

上述代码实现了基于PyTorch的Transformer模型。代码包含了:

(1)位置编码(Positional Encoding):

** **1.位置编码的初始化:构造一个位置编码矩阵

pe

,其形状为(max_len, d_model)。使用正弦和余弦函数根据位置和频率生成编码。

** **2.位置编码的计算:对于每个位置

position

,计算其在不同维度的编码值,奇数维度使用正弦函数,偶数维度使用余弦函数。

** **3.位置编码的应用:在前向传播时,将位置编码添加到输入

x

中,并应用dropout。

(2)Transformer模型的定义:
  1. 初始化:- Transformer模型:创建一个PyTorch的Transformer模型实例,指定模型的参数如嵌入维度d_model、注意力头数nhead、编码器和解码器的层数、前馈网络维度、dropout率等。- 嵌入层:定义源语言和目标语言的嵌入层,将词汇表中的每个词映射到一个d_model维度的向量。- 位置编码:实例化位置编码模块。- 输出层:定义一个线性层,将Transformer的输出转换为目标语言词汇表中的词的概率分布。
  2. 前向传播:- 调整维度:将输入序列srctgt的维度从(batch_size, seq_len)调整为(seq_len, batch_size)以适应PyTorch的Transformer模块。- 生成掩码:创建用于掩盖序列中无效位置的掩码src_masktgt_mask,以及用于处理填充标记的掩码src_padding_masktgt_padding_mask。- 嵌入和位置编码:对源序列和目标序列进行嵌入,并添加位置编码。- Transformer计算:将嵌入后的源序列和目标序列、掩码传入Transformer模型,计算输出。- 输出转换:通过线性层将Transformer的输出转换为目标语言的词汇表大小的概率分布。

2、调整参数

  1. 学习率调度(Learning Rate Scheduling): 尝试使用学习率调度器,如Warmup和Cosine Annealing,可以帮助模型更好地收敛。

  2. 批量大小(Batch Size): 尝试适当增加批量大小可以提高训练稳定性和收敛速度,但要根据硬件资源调整。

  3. 正则化(Regularization): 尝试使用L2正则化、Label Smoothing等技术可以防止模型过拟合。

  4. 轮次(Epochs)

      更多的epoch可以让模型在训练数据上进行更多的迭代,从而更好地拟合数据,提高训练准确率,这里尝试从五轮增加到十轮,跑分确实有所提高。
    

3、清洗训练样本

    尝试使用正则加手动清洗训练样本中的脏数据,如“Joey. (掌声) (掌声) 乔伊”、“Thank you. (马嘶声) 谢谢你们”等这种声音词,跑分有所提高

4、术语词典

def load_dictionary(dict_path):
    term_dict = {}
    with open(dict_path, 'r', encoding='utf-8') as f:
        data = f.read()
    data = data.strip().split('\n')
    source_term = [line.split('\t')[0] for line in data]
    target_term = [line.split('\t')[1] for line in data]
    for i in range(len(source_term)):
        term_dict[source_term[i]] = target_term[i]
    return term_dict

def post_process_translation(translation, term_dict):
    """ 使用术语词典进行后处理 """
    translated_words = [term_dict.get(word, word) for word in translation]
    return "".join(translated_words)
  load_dictionary

函数负责读取术语词典文件并构建一个源术语到目标术语的映射字典。

  post_process_translation

函数使用术语词典对翻译结果进行后处理,根据词典替换翻译结果中的术语。

if __name__ == "__main__":
    dict_path = '../dataset/en-zh.dic'  # 这应该是你的术语词典文件路径
    term_dict = load_dictionary(dict_path)
    
    save_dir = '../results/submit_add_dict.txt'
    with open(save_dir, 'w') as f:
        translated_sentences = []
        for batch in test_loader:  # 遍历所有数据
            src, _ = batch
            src = src.to(DEVICE)
            translated = translate_sentence(src[0], en_vocab, zh_vocab, model, DEVICE)  # 翻译结果
            results = post_process_translation(translated, term_dict)
            results = "".join(results)
            f.write(results + '\n')  # 将结果写入文件
            break
    print(f"翻译完成,结果已保存到{save_dir}")
主程序,主要功能是:
  1. 加载术语词典:从指定路径加载术语词典文件。

  2. 翻译测试数据:从测试数据加载器 test_loader 中获取源语言句子并使用翻译模型进行翻译。

  3. 后处理翻译结果:使用术语词典对翻译结果进行后处理,确保翻译结果中的术语一致性。

  4. 保存翻译结果:将处理后的翻译结果写入指定的文件中。

    引入术语词典后,准确率也有所提高。

5、总结

由于Task3是在Task2基础上做的改进,实操起来感觉比较简单,难度主要在于对Transformer的理解,以及在基础参数上自己尝试做出优化以抵达更高的上限。 

本文转载自: https://blog.csdn.net/weixin_69831681/article/details/140526665
版权归原作者 土295 所有, 如有侵权,请联系我们删除。

“【Datawhale AI夏令营】 Task3 学习笔记”的评论:

还没有评论