目录

前言

1.模型结构

1.1 encoder

1.2 decoder

2.数据处理

2.1 数据集

2.2 字典构建

2.3 特殊符号

3.参数加载

3.1 库的导入

3.2 词典导入

4.准备训练集

4.1 导入原始训练数据

4.2词典修改

4.3 加载数据集

5.Encoder

6.Decoder

7.模型训练

7.1输入格式

7.2 线性映射层

7.3超参数设置

7.4具体训练

8.模型预测

9.图形交互界面


前言

前段时间老师推荐过进行LSTM方向的研究,且笔者考虑到transformer在NLP领域的火热现状,于是选择了seq2seq作为切入点。seq2seq具有和transformer一样的encoder和decoder结构,但缺少attention部分,同时seq2seq模型中的encoder和decoder一般采用LSTM进行实现,因此笔者认为学习seq2seq模型能同时有利于对LSTM和Transformer的理解和学习。本文对基于seq2seq的机器翻译系统从原理和代码上进行了分析和实现,并在代码部分进行了详尽的注释和解析,方便读者理解。


1.模型结构

seq2seq模型由encoder和decoder两部分构成,以下分别对两部分进行模型原理的说明。

1.1 encoder

如下图所示,对于encoder层,假设想要翻译的英文句子为“Hi.”,其对应的汉语意思为"你好。"。

encoder的输入中,将英文句子中的每个符号作为一次输入,利用LSTM模型提取英文句子的语义,即图中最后一个hidden层。

1.2 decoder

训练时:

将encoder层提取的语义作为decoder层的初始hidden层输入,此时在训练中会在汉语译文中增加BOS和EOS用以训练。decoder层的训练输入为BOS+汉语译文各个字,对应的标签数据为汉语译文各个字+EOS。

也就是用红框中的内容作为decoder层的输入,绿框部分作为Label进行训练。注意在测试集上的预测中,decoder层的输入只有BOS,因为实际使用中预测的就是汉语译文,自然不会有汉语译文供参考了。

2.数据处理

2.1 数据集

本系统采用的数据集是含有两万多条的中英句子,具体形式如下图所示:

2.2 字典构建

依据数据集,分别提取汉字,英文符号去重后分别构造词典(字符到数字索引的键值对)en_word_2_index,ch_word_2_index,以及en_index_2_word,ch_index_2_word(数字索引到字符的列表,因为列表排序序号可以替代数字,因此用列表表示即可)。

2.3 特殊符号

系统中增加了PAD,BOS,EOS分别进行数据填充和译文修饰。

3.参数加载

3.1 库的导入

2.2中提到过对原始数据进行去重并构造字典,这里提供处理完成后的pickle类型文件直接供使用。对于pickle文件的提取也需要在之后 import pickle。

import torch
import torch.nn as nn
import pandas as pd
from torch.utils.data import Dataset,DataLoader
import pickle

3.2 词典导入

#读取提前构建好的双语字符与数字索引的字典
with open("datas\\ch.vec","rb") as f1:_, ch_word_2_index,ch_index_2_word = pickle.load(f1)with open("datas\\en.vec","rb") as f2:_, en_word_2_index, en_index_2_word = pickle.load(f2)

word_2_index,index_2_word的类型分别是字典和列表,后者是列表因为其默认序号可以替代数字,因而省略了数字部分。提取出的四个序列如下所示,此处并不是构造one-hot向量,而是作为数字索引,在之后直接构造对应的embbding。两个pickle文件中各自有三列数据,第一列是字向量,因此只需要提取后两列数据即可。

4.准备训练集

4.1 导入原始训练数据

从csv文件中分别取出训练集数据,并将该过程封装为一个函数,同时增加了nums参数用来选择读取的数据样本数量。考虑到读者的机器性能,本系统对nums进行了较小的取值,因此预测准确率会受到相应的影响,读者可根据自己机器的性能对nums进行扩值或者忽略(默认是导入所有数据样本进行训练)。

def get_datas(file = "datas\\translate.csv",nums = None):all_datas = pd.read_csv(file)# 从csv文件中分别读取英语和汉语译文,并分别列表化en_datas = list(all_datas["english"])ch_datas = list(all_datas["chinese"])# 因为总数据比较多,因此在初期做测试的时候设置nums参数,限制取出的数据数量,取少了会影响训练的效果if nums == None:return en_datas,ch_dataselse:return en_datas[:nums],ch_datas[:nums]

提取出的ch_datas,en_datas如下所示:

4.2词典修改

在中英文词典中增加PAD,BOS,EOS三个符号,因为最开始的导入文件中没有这三种符号,也可以直接修改文件,省略这一部分。

# 在字典中添加PAD,BOS,EOS三个新字符,也可以对字典进行处理,就不用再添加了
ch_corpus_len = len(ch_word_2_index)
en_corpus_len = len(en_word_2_index)ch_word_2_index.update({"<PAD>":ch_corpus_len,"<BOS>":ch_corpus_len + 1 , "<EOS>":ch_corpus_len+2})
en_word_2_index.update({"<PAD>":en_corpus_len})ch_index_2_word += ["<PAD>","<BOS>","<EOS>"]
en_index_2_word += ["<PAD>"]#直接增加长度或者自增len的长度都可以
ch_corpus_len += 3
en_corpus_len = len(en_word_2_index)

4.3 加载数据集

①使用pytorch提供的Dataloader加载数据集。在代码中的“return en_index,ch_index”部分,pytorch会自动将同一batch的结果进行自动组合,因此要通过自定义 vatch_data_process函数来完成对各结果的padding填充,BOS,EOS的增加等自定义操作。

通过修改Dataloader里对应的collate_fn参数,实现自动组合前对各结果的自定义修改功能。

②batch_data_process函数有两个作用。第一,实现对encoder层输入的padding,否则一条一条输入太慢,将一个batch的数据作为矩阵输入更高效。这里的padding对象也是同一batch各样本的输入,因为考虑所有样本时,句子长度差异较大,效果不好。参数中的batch_datas也就是上文en_index,ch_index的batch-size个组合成的迭代器。

③第二,实现在训练集上decoder层输入的修改,即输入层的句子前加上BOS标志开始,标签最后加上EOS标志结束,参考1.2中的图示。加上BOS是因为预测时不知道中文,如果不加上BOS,则输入为空;加上EOS则是标志着预测过程的停止。

④结果:下两图分别代表经处理后返回的encoder,decoder层输入。

假设batch为3,则英语句子对应索引经padding后,长度相等,且padding体现为77。

汉语译文经处理后,最开始都是表示BOS的3590,末尾是padding3589和表示EOS的3591。

总代码如下:

#对数据进行处理,①将每个英语和汉语句子中的字符对应通过字典对应为数字索引,②对各组数字索引进行pad处理,③对汉字句子进行BOS和EOS处理
class MyDataset(Dataset):def __init__(self,en_data,ch_data,en_word_2_index,ch_word_2_index):# 导入双语原始句子self.en_data = en_dataself.ch_data = ch_data# 导入双语字符与数字索引的字典self.en_word_2_index = en_word_2_indexself.ch_word_2_index = ch_word_2_index# 将一对双语句子从字符状态转换为数字索引状态,形式是将一个句子中的各个字符转换为数字索引的列表def __getitem__(self,index):en = self.en_data[index]ch = self.ch_data[index]en_index = [self.en_word_2_index[i] for i in en]ch_index = [self.ch_word_2_index[i] for i in ch]return en_index,ch_index# pytorch会自动地把batch-size个双语句子组合在一起,参数中的batch_datas也就是上文en_index,ch_index的batch-size个组合成的迭代器# 此处添加的函数作为Dataloader的一个参数,意在对数字索引格式进行再处理,也就是pad,EOS,BOS这些def batch_data_process(self,batch_datas):global deviceen_index , ch_index = [],[]en_len , ch_len = [],[]for en,ch in batch_datas:en_index.append(en)ch_index.append(ch)en_len.append(len(en))ch_len.append(len(ch))max_en_len = max(en_len)max_ch_len = max(ch_len)# 在每一句英文句子的数字索引列表后添加PAD统一大小,在每一句汉语句子的数字索引列表前加BOS,后加EOS,再添加PAD统一大小en_index = [ i + [self.en_word_2_index["<PAD>"]] * (max_en_len - len(i))   for i in en_index]ch_index = [[self.ch_word_2_index["<BOS>"]]+ i + [self.ch_word_2_index["<EOS>"]] + [self.ch_word_2_index["<PAD>"]] * (max_ch_len - len(i))   for i in ch_index]#tensor化并且布置到GPU上训练en_index = torch.tensor(en_index,device = device)ch_index = torch.tensor(ch_index,device = device)#返回的是一个batch的经过padding,BOS,EOS修改的句子中各字符对应的数字索引构成的列表return en_index,ch_index# 判断提取的双语句子的数量是否相等,不相等肯定是提取出问题了def __len__(self):assert len(self.en_data) == len(self.ch_data)return len(self.ch_data)

5.Encoder

利用pytorch构造英文字符对应的词向量embbding,然后将输入句子的数字索引形式转化为对应的词向量,以词向量的形式输入,词向量的维度由超参数指定。

#对数字索引形式的句子进行Embedding处理和基于LSTM的语义提取
class Encoder(nn.Module):def __init__(self,encoder_embedding_num,encoder_hidden_num,en_corpus_len):super().__init__()# 基于英文语料库建立Embeddingself.embedding = nn.Embedding(en_corpus_len,encoder_embedding_num)# batch-first设置为True是因为torch中dataset处理后的batch批次的样本,其向量维度是batch,input-size,hidden-size,因此不符合默认的input-size,batch,hidden-size# 顺序,通过该参数的设置实现参数的匹配self.lstm = nn.LSTM(encoder_embedding_num,encoder_hidden_num,batch_first=True)def forward(self,en_index):# 将输入的句子进行Embbdeing处理,同时得到Encoder层的输出:encoder_hiddenen_embedding = self.embedding(en_index)_,encoder_hidden =self.lstm(en_embedding)return encoder_hidden

6.Decoder

这部分和Encoder的思路基本相同,主要区别在于decoder部分的输入依据当前是训练还是测试有所不同,上文已经提及,此处不再赘述。

class Decoder(nn.Module):def __init__(self,decoder_embedding_num,decoder_hidden_num,ch_corpus_len):super().__init__()self.embedding = nn.Embedding(ch_corpus_len,decoder_embedding_num)self.lstm = nn.LSTM(decoder_embedding_num,decoder_hidden_num,batch_first=True)# decoder_input和encoder部分那个en_index是一个意思,都是数字索引格式的句子,# 但因为汉语句子进行了BOS和EOS处理,我们要对Decoder的输入额外地进行去除末尾EOS的处理def forward(self,decoder_input,hidden):embedding = self.embedding(decoder_input)decoder_output,decoder_hidden = self.lstm(embedding,hidden)return decoder_output,decoder_hidden

7.模型训练

将超参数传入Encoder和Decoder,构造损失函数,优化器,调用两层得到输出,并对输出进行线性变换得到预测结果,与实际标签比较。

7.1输入格式

注意到在decoder层的输入前,将汉语译文作了两次处理,分别是保留BOS和保留EOS,因为之前增加这两个符号时没有区别输入和标签,在此处进行分类。

7.2 线性映射层

增加classifier,实现将decoder层输出的维度变换。

7.3超参数设置

encoder_embedding_num和decoder_embedding_num依据英汉词典的大小设置,encoder_hidden_num = 100和decoder_hidden_num = 100则是对LSTM隐层维度的设置,应保持两者一致,否则应另增加一层线性变换用以修改维度。

# 超参数的设置
en_datas,ch_datas = get_datas(nums=200)
encoder_embedding_num = 50
encoder_hidden_num = 100
decoder_embedding_num = 107
decoder_hidden_num = 100batch_size = 2
epoch = 40
lr = 0.001

7.4具体训练

在loss函数求值过程中,en_index,ch_index的维度分别为3和2,因此将en_index的前两维拉成一维,ch_index同理,从而求loss,否则对一个batch的数据单独求loss会相对更加麻烦。

for e in range(epoch):for en_index,ch_index  in dataloader:loss = model(en_index,ch_index)loss.backward()opt.step()opt.zero_grad()print(f"loss:{loss:.3f}")

总代码如下:

class Seq2Seq(nn.Module):def __init__(self,encoder_embedding_num,encoder_hidden_num,en_corpus_len,decoder_embedding_num,decoder_hidden_num,ch_corpus_len):super().__init__()self.encoder = Encoder(encoder_embedding_num,encoder_hidden_num,en_corpus_len)self.decoder = Decoder(decoder_embedding_num,decoder_hidden_num,ch_corpus_len)# 将Decoder的输出从Embedding维度映射到汉字词典的维度,匹配对应的汉字self.classifier = nn.Linear(decoder_hidden_num,ch_corpus_len)self.cross_loss = nn.CrossEntropyLoss()def forward(self,en_index,ch_index):# 将汉语句子分别去首尾的BOS和EOS作为Decoder层的输入和标签decoder_input = ch_index[:,:-1]label = ch_index[:,1:]encoder_hidden = self.encoder(en_index)# 预测模型只需要各cell的hidden,不需要最后cell的hiddendecoder_output,_ = self.decoder(decoder_input,encoder_hidden)pre = self.classifier(decoder_output)# label是[batch,len],pre是[batch,len,Embedding],因此要进行维度处理,此处是把一个batch里的句子都放在一个维度loss = self.cross_loss(pre.reshape(-1,pre.shape[-1]),label.reshape(-1))return loss

8.模型预测

应注意预测阶段中decoder的输入只有BOS符号

#供用户使用的API接口
def translate(sentence):global en_word_2_index,model,device,ch_word_2_index,ch_index_2_word# 将英文句子中的字母转换为数字索引,此处进行维度增加的处理并tensor化,方便传参en_index = torch.tensor([[en_word_2_index[i] for i in sentence]],device=device)result = []# 与训练时不同,预测时decoder的输入是“空”,能利用的只有encoder传入的语义hidden,因此之前会设置汉语句子首位为BOS,此处直接将BOS作为初始输入,并将cell的预测结果作为下一个cell# 的输入,实现汉语语义的预测encoder_hidden = model.encoder(en_index)decoder_input = torch.tensor([[ch_word_2_index["<BOS>"]]],device=device)# 将encoder层的输入作为decoder层的初始hiddendecoder_hidden = encoder_hiddenwhile True:decoder_output,decoder_hidden = model.decoder(decoder_input,decoder_hidden)pre = model.classifier(decoder_output)w_index = int(torch.argmax(pre,dim=-1))word = ch_index_2_word[w_index]if word == "<EOS>" or len(result) > 50:breakresult.append(word)# 将cell的预测结果作为下一个cell的输入,实现汉语语义的预测decoder_input = torch.tensor([[w_index]],device=device)hanyu="".join(result)return hanyu# print(hanyu)# print("*****")# print("译文: ","".join(result))

9.图形交互界面

本系统的图形交互界面采用PySimpleGUI实现,该库的具体使用参考另一篇博客即可,最终的实现效果如下:

总代码如下:

from seq2seq import translate
import PySimpleGUI as sglayout = [[sg.Text('输入你想翻译的英文:'), sg.Text(size=(15,1), key='-OUTPUT-')],[sg.Input(key='-IN-')],[sg.Button('Show'), sg.Button('Exit')],[sg.Text('注:预测的准确率与训练样本数量有关,本实验为了方便展示')],[sg.Text('注:只选择了少量样本进行训练,如要提高预测率,可增加训练样本数量')]]
#第一行留有一个用以更新的变量区域,键名自定义设置,此处设置为-OUTPUT-window = sg.Window('基于seq2seq的机器翻译系统', layout)while True:  # Event Loopevent, values = window.read()print(event, values)if event in  (None, 'Exit'):breakif event == 'Show':text_input = values['-IN-']hanju = translate(text_input)# Update the "output" text element to be the value of "input" elementwindow['-OUTPUT-'].update(hanju)#将输入的值更新在OUTPUT区域window.close()

基于seq2seq的机器翻译系统相关推荐

  1. EMNLP'21 | 基于相似样本检索的在线更新机器翻译系统

    点击上方"AI遇见机器学习",选择"星标"公众号 重磅干货,第一时间送达 来自:AI科技评论 机器翻译指的是使用机器将一种语言的文本翻译成另一种语言的文本.机器 ...

  2. 基于PaddlePaddle的机器翻译教程 | 深度学习基础任务系列

    https://www.toutiao.com/a6699979989980283404/ 本文转载自PaddlePaddle 量子位 编辑 | 公众号 QbitAI 机器翻译(machine tra ...

  3. NLP——8.基于统计的翻译系统

    基于统计的机器翻译:mosesdecoder作为比对翻译效果的baseline,如果不如这个的效果,就说明测试系统效果不算好. 首先看看一共需要以下三个模型: 语言模型:用来评估这句话的通畅程度. 1 ...

  4. 一个基于Tensorflow的神经网络机器翻译系统

    一个基于Tensorflow的神经网络机器翻译系统 Github地址:https://github.com/zhaocq-nlp/NJUNMT-tf 系统完全基于Tensorflow最基本的array ...

  5. 神经机器翻译系统资料

    作者:zhbzz2007 出处:http://www.cnblogs.com/zhbzz2007 欢迎转载,也请保留这段声明.谢谢! 1 简介 自2013年提出了神经机器翻译系统之后,神经机器翻译系统 ...

  6. 基于Seq2Seq模型的简易中文聊天机器人

    临近毕业季,又想起了做过的简易聊天机器人chartbot毕业设计,因为算是自己第一次接触这个智能问答领域吧,所以到现在还觉得特别有意思且难忘.我是个行动派,觉得有意思的东西,肯定就要记录下来了.下面我 ...

  7. 【深度学习机器翻译】GNMT:Google 的的神经机器翻译系统

    Google's Neural Machine Translation System: Bridging the Gap between Human and Machine Translation 1 ...

  8. Seq2Seq实战——机器翻译

    基于seq2seq做一个机器翻译 我们将使用PyTorch和TorchText构建一个机器学习模型,从一个序列到另一个序列. 将德语到英语翻译成英语 该模型是<Sequence to Seque ...

  9. 微软机器翻译系统:中-英翻译水平可“与人类媲美”

    本文经授权转载自公众号「微软研究院AI头条」. 继在语音识别和机器阅读领域取得的"过人"成绩,由微软亚洲研究院与雷德蒙研究院的研究人员组成的团队宣布,其研发的机器翻译系统在通用新闻 ...

最新文章

  1. 企业如何培养出得力的下属?
  2. 电脑技巧:Win10自带存储感知功能给电脑磁盘瘦身
  3. linux开机启动详细流程图
  4. mysql之index
  5. python 图片相似度算法比较_python 比较2张图片的相似度的方法示例
  6. stm32 薄膜键盘原理_雷蛇发布第二代轻机械键盘,你了解什么是轻机械键盘吗?...
  7. 时间戳转为时间友好显示
  8. 2013-2018卷积神经网络中十个最重要的概念与创新
  9. php安全拦截,php类中的各种拦截器用法分析
  10. Request的getParameter和getAttribute方法的区别
  11. ART-Pi 实现音乐播放器 --播放《天空之城》
  12. Java用ListArray以人名的姓氏排队
  13. html表格字符分散,如何实现Word表格文字分散对齐?
  14. 垂直网站之路:金融风暴加速向电子商务转型
  15. UltraEdit mac版
  16. 树莓派(USB麦克风和麦克风阵列) 录音和播放
  17. electron开发问题记录
  18. 求学者们论文的引用次数(中等难度C++)
  19. 如何做一个网页送给女朋友做生日礼物
  20. tbody、thead

热门文章

  1. 提高电脑运行速度---清理内存工具
  2. 各个架构下的linux启动流程-从linux被加载到start_kernel
  3. [python]动态规划
  4. 首行缩进2字符,CSS文本外观之文本缩进
  5. 码分复用 = 频分复用 + 时分复用 ?
  6. 记录aui的toast使用
  7. 双肩包、电梯、椅子、保温杯的测试用例
  8. 诚之和:Java基础知识枚举Enum类介绍以及案例使用详解
  9. 怎样合理创建es索引_如何通过Elasticsearch创建索引库?
  10. 现在学3D建模入行是不是晚了?学好了还能找到高薪的好工作吗