本文将手把手教你使用Pytorch搭建一个Seq2Seq模型来实现机器翻译的功能。所使用的数据集为torchtext自带的Multi30k翻译数据集,在没有使用注意力机制的前提下测试集ppl可以达到50.78。所有代码亲测可运行,码字不易,转载请注明出处,谢谢!
https://blog.csdn.net/weixin_43632501/article/details/98731800
简介
本文将使用PyTorch和TorchText构建一个深度学习模型,该模型是《Sequence to Sequence Learning with Neural Networks》这篇论文的Pytorch实现,作者将其应用于机器翻译问题。同时,Seq2Seq模型还可以应用于涉及从一个序列到另一个序列的任何问题,例如文本摘要、聊天机器人、图片描述等自然语言处理任务。
准备数据
除了PyTorch和TorchText,我们还将使用spaCy进行分词,spaCy是一个Python自然语言处理工具包,可是实现对文本做分词、词性分析、命名实体识别、依存分析等功能。
import torch
import torch.nn as nn
import torch.optim as optim
from torchtext.datasets import TranslationDataset, Multi30k
from torchtext.data import Field, BucketIterator
import spacy
import random
import math
import time
设置随机数种子
SEED = 1234
random.seed(SEED)
torch.manual_seed(SEED)
torch.backends.cudnn.deterministic = True
接下来,我们将使用spacy创建分词器(tokenizers),分词器的作用是将一个句子转换为组成该句子的单个符号列表,例如。“good morning!”变成了“good”,“morning”和“!”。在对某种语言进行分词前,spacy需要下载该语言的模型。
//分别下载英语和德语的模型
python -m spacy download en
python -m spacy download de
加载模型
spacy_de = spacy.load('de')
spacy_en = spacy.load('en')
创建分词器函数以便传递给TorchText 。在原论文中,作者发现颠倒源语言的输入的顺序可以取得不错的翻译效果,例如,一句话为“good morning!”,颠倒顺序分词后变为"!", “morning”, 和"good"。
def tokenize_de(text):
//将德语进行分词并颠倒顺序
return [tok.text for tok in spacy_de.tokenizer(text)][::-1]
def tokenize_en(text):
//将英语进行分词,不颠倒顺序
return [tok.text for tok in spacy_en.tokenizer(text)]
下面是TorchText出场,它主要包含以下组件:
Field对象 :指定要如何处理某个字段,比如指定分词方法,是否转成小写,起始字符,结束字符,补全字符以及词典等。
Dataset类 :用于加载数据,torchtext的Dataset是继承自pytorch的Dataset,提供了一个可以下载压缩数据并解压的方法(支持.zip, .gz, .tgz)。splits方法可以同时读取训练集,验证集,测试集。TabularDataset可以很方便的读取CSV, TSV, or JSON格式的文件
迭代器 : 返回模型所需要的处理后的数据。迭代器主要分为Iterator,BucketIterator,BPTTIterator三种。
- Iterator:标准迭代器
- BucketIterator:相比于标准迭代器,会将类似长度的样本当做一批来处理,因为在文本处理中经常会需要将每一批样本长度补齐为当前批中最长序列的长度,因此当样本长度差别较大时,使用BucketIerator可以带来填充效率的提高。除此之外,我们还可以在Field中通过fix_length参数来对样本进行截断补齐操作。
- BPTTIterator:基于BPTT(基于时间的反向传播算法)的迭代器,一般用于语言模型中。
其他 : torchtext提供常用文本数据集(datasets),并可以直接加载使用,现在包含的数据集包括:
- Sentiment analysis: SST and IMDb
- Question classification: TREC
- Entailment: SNLI
- Language modeling: WikiText-2
- Machine translation: Multi30k, IWSLT, WMT14
首先,我们创建SRC和TRG两个Field对象,tokenize为我们刚才定义的分词器函数,在每句话的开头加入字符SOS,结尾加入字符EOS,将所有单词转换为小写。
SRC = Field(tokenize = tokenize_de,
init_token = '<sos>',
eos_token = '<eos>',
lower = True)
TRG = Field(tokenize = tokenize_en,
init_token = '<sos>',
eos_token = '<eos>',
lower = True)
然后,我们加载训练集、验证集和测试集,生成dataset类。使用torchtext自带的Multi30k数据集,这是一个包含约30000个平行的英语、德语和法语句子的数据集,每个句子包含约12个单词。
// splits方法可以同时加载训练集,验证集和测试集,
//参数exts指定使用哪种语言作为源语言和目标语言,fileds指定定义好的Field类
train_data, valid_data, test_data = Multi30k.splits(exts = ('.de', '.en'),
fields = (SRC, TRG))
我们可以查看一下加载完的数据集
print(f"Number of training examples: {len(train_data.examples)}")
print(f"Number of validation examples: {len(valid_data.examples)}")
print(f"Number of testing examples: {len(test_data.examples)}")
Number of training examples: 29000
Number of validation examples: 1014
Number of testing examples: 1000
还可以看一下生成的第一个训练样本,可以看到源语言的顺序已经颠倒了。
print(vars(train_data.examples[0]))
{'src': ['.', 'büsche', 'vieler', 'nähe', 'der', 'in', 'freien', 'im', 'sind', 'männer', 'weiße', 'junge', 'zwei'], 'trg': ['two', 'young', ',', 'white', 'males', 'are', 'outside', 'near', 'many', 'bushes', '.']}
下一步我们将构建词表,所谓构建词表,即需要给每个单词编码,也就是用数字表示每个单词,这样才能传入模型。可以使用dataset类中的build_vocab()方法传入用于构建词表的数据集。注意,源语言和目标语言的词表是不同的,而且词表应该只从训练集构建,而不是验证/测试集,这可以防止“信息泄漏”到模型中。
// 设置最小词频为2,当一个单词在数据集中出现次数小于2时会被转换为<unk>字符。
SRC.build_vocab(train_data, min_freq = 2)
TRG.build_vocab(train_data, min_freq =