从baseline代码详解入门深度学习心得——Datawhale AI 夏令营

        当前机器翻译任务的主流解决方案是基于神经网络进行建模,通常我们基于神经网络解决机器翻译任务的流程如下:

1.配置环境

  • torchtext :是一个用于自然语言处理(NLP)任务的库,它提供了丰富的功能,包括数据预处理、词汇构建、序列化和批处理等,特别适合于文本分类、情感分析、机器翻译等任务

  • jieba是一个中文分词库,用于将中文文本切分成有意义的词语

  • sacrebleu:用于评估机器翻译质量的工具,主要通过计算BLEU(Bilingual Evaluation Understudy)得分来衡量生成文本与参考译文之间的相似度

  • spacy:是一个强大的自然语言处理库,支持70+语言的分词与训练

  • !pip install torchtext    
    !pip install jieba
    !pip install sacrebleu

 安装spacy用于英文的tokenizer:

!pip install -U pip setuptools wheel -i https://pypi.tuna.tsinghua.edu.cn/simple
!pip install -U 'spacy[cuda12x]' -i https://pypi.tuna.tsinghua.edu.cn/simple
!pip install ../dataset/en_core_web_trf-3.7.3-py3-none-any.whl
!python -m spacy download en_core_web_sm

2.数据预处理

        对英文和中文数据进行预处理,并将处理后的数据以元组列表的形式返回。每个元组包含两个列表:一个是英文文本分词en_data: List[str]后的结果(限制在最大长度MAX_LENGTH内),另一个是中文文本分词zh_data: List[str]后的结果(同样限制在最大长度内)。

       定义一个 collate_fn  函数用于在数据加载过程中处理一个批次的数据,特别是将英文和中文的文本序列转换为张量,并对其进行填充以确保它们具有相同的长度。这个函数通常与 PyTorch 的 DataLoader 一起使用,以自定义批次数据的处理方式。

  1. 使用了 en_vocab['<pad>'] 和 zh_vocab['<pad>'] 作为填充值。

  2. 处理空序列:处理批次中单个序列为空的情况,通过 if not en_batch or not zh_batch: 检查了这一点,并返回了空的张量。

    if not en_batch or not zh_batch:  # 如果整个批次为空,返回空张量
            return torch.tensor([]), torch.tensor([])
  3. 注释和调试:打印语句来调试(例如 "存在为空")。

    if en_item and zh_item:  # 确保两个序列都不为空
                # print("都不为空")
                en_batch.append(torch.tensor(en_item))
                zh_batch.append(torch.tensor(zh_item))
            else:
                print("存在为空")

       构建一个load_data函数,用于加载并预处理训练集、开发集和测试集,并构建相应的数据加载器。

# preprocess_dataset: 处理单个数据集的读取、分词/标记化、截断等  
# build_vocab: 根据处理后的数据集构建词汇表  
# TranslationDataset: 封装处理后的数据和词汇表的类  
# DataLoader: PyTorch中的数据加载器类  
# collate_fn: 一个可选的函数,用于在DataLoader中合并样本列表  
# read_data: 读取文件内容的函数(在preprocess_dataset中隐含地使用)  
# en_tokenizer 和 zh_tokenizer 是分词器或标记化器的实例,用于处理英文和中文文本

3.模型构建

       定义一个基于GRU(门控循环单元)的编码器类 Encoder,它是PyTorch中nn.Module的一个子类。这个编码器通常用于序列到序列(Seq2Seq)模型,如机器翻译、文本摘要等任务中,用于编码输入序列。

初始化函数 __init__:

  • input_dim: 输入的词汇表大小,即嵌入层需要处理的不同的单词数量。
  • emb_dim: 嵌入层的维度,即每个单词被嵌入后的向量大小。
  • hid_dim: GRU隐藏层的维度。
  • n_layers: GRU的层数,即堆叠的GRU单元的数量。
  • dropout: 在GRU层之间应用的dropout比率,用于防止过拟合。

       在初始化函数中,首先通过super().__init__()调用父类nn.Module的初始化方法。然后,定义了几个关键的层:

  • self.embedding: 嵌入层,将输入的单词索引(整数)转换为固定大小的密集向量。
  • self.gru: GRU层,用于处理序列数据。这里的batch_first=True表示输入张量的第一个维度是批次大小,这符合PyTorch的常规约定。
  • self.dropout: Dropout层,用于在训练过程中随机丢弃一部分神经元的输出,以减少过拟合。

前向传播函数 forward

forward方法中,定义了数据通过编码器时的处理流程:

  1. 首先,将输入src(一个包含单词索引的二维张量,形状为[batch size, src len])通过嵌入层转换为嵌入向量embedded。然后,应用dropout以减少过拟合。

  2. 接着,将嵌入向量embedded传递给GRU层。GRU层会按照时间步(即序列中的每个单词)处理这些嵌入向量,并输出每个时间步的隐藏状态outputs以及最后一个时间步的隐藏状态(对于所有层)hiddenoutputs的形状为[batch size, src len, hid dim * n directions],但由于我们使用的是单向GRU,所以n directions为1,这部分通常可以省略。hidden的形状为[n layers * n directions, batch size, hid dim],同样地,由于单向性,n directions为1。

  3. 最后,forward方法返回outputshiddenoutputs可以用于某些任务(如注意力机制),而hidden通常用作解码器的初始隐藏状态。

       在Decoderforward方法中,我们首先处理输入(通常是前一个时间步的预测或真实的输出单词的索引),然后将其嵌入到高维空间中。接着,我们使用注意力机制来计算编码器输出的加权和,这个加权和将作为GRU单元的额外输入。最后,GRU的输出被用来预测下一个单词。

def forward(self, src):
        embedded = self.dropout(self.embedding(src))
        outputs, hidden = self.gru(embedded)
 def forward(self, hidden, encoder_outputs):
        batch_size = encoder_outputs.shape[0]
        src_len = encoder_outputs.shape[1]        
        hidden = hidden.repeat(src_len, 1, 1).transpose(0, 1)
        energy = torch.tanh(self.attn(torch.cat((hidden, encoder_outputs), dim=2)))
        attention = self.v(energy).squeeze(2)
        return F.softmax(attention, dim=1)

4.模型训练

       定义一个训练模型的函数 train_model,该函数接收多个参数,包括模型 (model)、训练数据迭代器 (train_iterator)、验证数据迭代器 (valid_iterator)、优化器 (optimizer)、损失函数 (criterion)、训练轮次 (N_EPOCHS)、以及梯度裁剪的阈值 (CLIP)。主要目的是在指定的训练轮次内,使用训练数据训练模型,并在每个轮次结束时使用验证数据评估模型的表现。如果模型在验证集上的表现有所改善(即验证损失降低),则保存当前的模型状态。

def train_model(model, train_iterator, valid_iterator, optimizer, criterion, N_EPOCHS=10, CLIP=1):
    best_valid_loss = float('inf')

      定义几个关键的常量,包括最大句子长度 MAX_LENGTH、批量大小 BATCH_SIZE、设备 DEVICE(用于指定是使用 CPU 还是 GPU),以及采样训练集的数量 N(尽管在后续代码中并未直接使用 N)。指定数据集文件的路径,并调用一个假设存在的 load_data 函数来加载数据,该函数返回训练集、开发集、测试集的加载器(DataLoader),以及英文和中文的词汇表。

MAX_LENGTH = 100  # 最大句子长度
BATCH_SIZE = 32
DEVICE = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
N = 100   # 采样训练集的数量

train_path = '../dataset/train.txt'
dev_en_path = '../dataset/dev_en.txt'
dev_zh_path = '../dataset/dev_zh.txt'
test_en_path = '../dataset/test_en.txt'

train_loader, dev_loader, test_loader, en_vocab, zh_vocab = load_data(
    train_path, dev_en_path, dev_zh_path, test_en_path
)

         训练模型:

train_model(model, train_loader, dev_loader, optimizer, criterion, N_EPOCHS, CLIP)

 5.翻译质量评价

 6.个人心得

       在深入探索深度学习的广阔领域时,从baseline代码详解入手无疑是一条高效且扎实的路径。这段经历不仅让我对深度学习的基本原理有了更深刻的理解,还让我在实际操作中逐步掌握了构建、训练和评估模型的关键技能。

一、理论与实践的结合

       首先,通过阅读代码,我深刻体会到理论知识与实践操作之间的紧密联系。理论是构建模型的基石,而代码则是将这些理论转化为实际应用的桥梁。通过逐行阅读代码,我能够直观地看到理论概念如何在代码中实现,比如前向传播forward等。这种理论与实践相结合的方式,极大地加深了我对深度学习核心概念的理解。

二、细节决定成败

       在深入运行过程中,我逐渐意识到细节的重要性。从数据预处理、模型架构的设计、超参数的调整到训练过程中的优化策略,每一个细节都可能对模型的性能产生重大影响。通过仔细研究学习代码中的每一个步骤,我初步学会了如何优化这些细节,以提高模型的准确性和效率。

三、调试与反思

       在尝试复现过程中,我不可避免地遇到了各种bug和性能瓶颈。通过不断地调试和反思,我学会了如何有效地定位问题、分析原因并寻找解决方案。这个过程虽然充满挑战,但也极大地锻炼了我的问题解决能力和代码调试能力。

四、持续学习与探索

       深度学习是一个快速发展的领域,新的算法、模型和技术层出不穷。通过本次夏令营的学习,我意识到持续学习和探索的重要性。只有不断学习最新的研究成果和技术趋势,才能保持自己在这个领域的竞争力。

 这个夏令营不简单

#AI夏令营

#Datawhale #夏令营

  • 14
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值