目录
一、前言
Seq2Seq模型把英语翻译成德语
我们可以注意到机器翻译是一个多对多的问题,输出长度和输入长度且不固定
做机器翻译的第一步都是处理数据,我们首先来对数据进行处理,把这些句子用矩阵,还有例如把大写字母变为小写字母,去掉标点符号等等,预处理之后,进行tokenization,把一句话进行变成很多个单词或者很多个字符,做tokenizer时候我们需要两个不同的tokenizer,英语用一个,德语用一个,tokenizer后我们需要建立两个字典,一个英语字典一个德语字典,tokenization可以是char-level也可是word-level,char-level会把一句话分解成很多字符,word-level会把一句话分割成很多单词。
在本次例子中我们使用char-level,但是实际中,大多都是使用word-level,因为他们的数据集足够大。
在上文中我们提到我们需要两个字典的原因是,在字符层面上,不同的语言通常有不同的alphabets字母表,英语有26个拉丁字母,如果区分大小写,那么就会有52个,德语也有26个拉丁字母,但是还有4个不常使用的字母。中文有几千个汉字。两种语言的字符通常是不一样的。所以应该用两个不同的tokenizer,各有各的字母表,如果是做word-level,更应该用不同的tokenizer和不同的字典,英语和德语的词汇完全不一样,绝大多数的德语在英语中是找不到的,并且不同的语言的有不同的分词方法,汉语、英语和欧洲语言的分词方法便不一样。
我们使用库作为tokenization,结束之后自动生成字典,左边是英语字典,一共有27个字符,26个字母,还有一共空格符号,27个字符分别对应27个数字,右边是德语字典,我们删除不常用的字符删除,只保留26个字母和一个空格符号
任务是把英语翻译成德语,英语是原语言,德语是目标语言,要往目标语言德语中加入两个符号,一个是起始符号,一个终止符号,那什么字符做起始符号和终止符号都可以,只要不要和已有字符冲突即可。
Tokenization结束后,每句话都变成了一个字符的列表,并且生成了一个英语字典和一个德语字典,使用这个字典便可以把每个字符映射成为一个整数。
这样一来,一句英语就变成了一串seqs,每个元素是一个整数,对于德语也是完全一样的,一句话先变成一个字符列表,然后用德语字典把每个字符映射成为一个数字,这样一来一句德语就变成了一句sequences,可以进一步把一个数字使用one-hot向量表示。
做完One-hot Encoding,每个字符用一个向量表示,每一句话就用一个矩阵表示,这个矩阵便是RNN的输入,数据便准备好了。
二、Seq2Seq模型的搭建
接下来我们搭建一个seq2seq模型,并且训练这个模型。
Seq2seq模型有一个编码器和一个解码器。
Encoding是LSTM或者其他RNN模型,用来从输入的英语句子中提取特征,encoding最后一个状态就是从输入的句子中提取的特征,包含这句话的信息,encoder其余的状态没有用,都被丢掉了,encoder的输出是LSTM最后的状态(h和最后的传输带c(c是中间状态,可以认为是encoder信息全部传给了c))
Decoder解码器用来生成德语,这个decoder其实就是文本生成器,只不过和文本生成器的初始状态不一样,初始状态一般文本生成器是全0向量,这个decoder的初始状态是encoder的最后一个状态,encoder最后一个状态h和c是从输入的英语句子中提取特征向量,概括了输入的英语句子,decoder通过这个状态来知道输入的句子是go away,decoder会输出一个概率分布,记作向量P
起始符后边这句德语,第一个字母是起始符,把起始符做One-Hot encoding,作为标签y,用标签y和预测Cross Entropy作为损失函数,我们期望预测的p尽量接近标签y,所以损失函数越小越好,有了损失函数,就可以反向传播计算梯度,梯度会从损失函数传到decoder ,然后再从decoder一直传到encoder,然后用梯度下降来更新decoder和encoder的模型参数,让损失函数减小
然后输入是两个字符,起始符和字母m,decoder会输出对下一个字母的预测,记作向量P,输出的下一个字符的字母是a,把a做One-hot encoding作为标签y,损失函数是标签y与预测P的CrossEntropy,然后用反向传播来计算梯度,更新decoder和encoder
再下一个输入是三个字符,起始符字母m和字母a,LSTM输出对下一个字符的预测,记为P向量,真实的下一个字母是向量c,所以标签y是字母c的One-hot向量,同样用反向传播来计算梯度,然后用梯度下降来计算decoder和encoder,不断重复这一过程,直到德语的最后一个字符
最后一轮,把整句德语作为decoder输入,所以用停止符的One-Hot向量作为标签y,希望输出的预测尽量接近标签,也就是停止符,然后再做一次反向传播,更新一次模型参数,不断重复这个训练过程,拿所有的英语、德语 二元组来训练decoder和encoder。
我们来搭建一个Seq2seq时,我们需要这样做。Encoder网络的输入是英语句子的One-Hot encoding用一个矩阵表示,encoding网络有一层或者多层LSTM,用来从英文句子中提取特征,LSTM的输出是最后一个状态h和最终的传输带c,decoder的初始状态是encoder的h和c,这样可以让decoder和encoder连接起来,在反向传播的时候,梯度可以顺着这条线,从decoder传播到encoder
decoder网络的输入是德语的上半句话,decoder输出当前状态h,然后全连接层输出对下一个字符的预测。
三、Seq2Seq模型的预测
训练好Seq2Seq模型,可以拿它把英语翻译成德语
把英语的每一个字符输入encoder,encoder会在状态h和c中,积累这句话的信息,encoder输出最后的状态,记作H0和C0,他们是从这句话里提取的特征,H0和C0被送到了decoder,encoder的输出H0和C0作为decoder的初始状态,这样一来,decoder就知道输入的英语句子是go away。
现在decoder就跟上节课讲的文本生成器一样工作
首先把起始符输入decoder,有了新的输入,decoder就会更新状态h和传输带c,并且预测下一个字符,decoder输出的预测是每个字符的概率值,根据概率值我们选取每个字符,我们可以选择概率最大的字符,也可以根据概率值来做随机抽样,可能会得到字符m,于是我们把字符m记录下来,现在LSTM的decoder状态变成了h1和c1,把新生成的字符m作为decoder的输入,让LSTM来预测下一个字符,基于状态h1和c1,以及新的输入m,LSTM会更新状态为h2和c2,并且输出一个概率分布,根据概率分布来抽样,我们可能得到字符a,把字符a记录下来,现在状态更新为h2和c2,拿新生成的字符a,作为输入让LSTM来做预测,基于状态h2和c2,以及新的输入字符a,LSTM会更新状态h3和c3,并且输出一个概率分布,根据概率分布来做抽样,得到字符c,然后把c记录,不断重复这个过程,更新状态,并且生成新的字符,然后不断用新生成的字符来做下一个输入。
【前边刚说拿句子做预测,现在为什么又说拿字符?因为刚才是在训练,现在是在推理,训练的时候输入是前面所有字符串,推理的时候decoder输入只有一个字符】
运行14轮只有,现在的装填是h14和c14,我们根据状态h14和c14,以及上一轮生成的字母e,拿e作为输入,根据decoder输出的概率分布做抽样,我们可能碰巧抽出终止符,一旦抽出终止符,便终止文本生成,并返回记录下的字符串,这个字符串便是模型翻译得到的德语。
四、Seq2Seq的改进
我们设计了一个Seq2Seq模型,他们的decoder和encoder都是LSTM
来思考一下,我们该如何改进这个seq2seq模型
Seq2Seq原理是encoder处理输入的英语句子,把信息都压缩在状态向量里,encoder最后一个状态是整句话的一个概要,理想情况下,encoder最后一个状态包含整句英语完整的信息,当然只是理想状态,假如英语句子很长,LSTM就会遗忘,假如英文句子有些信息被遗忘,那么decoder便不会有英语句子的完整信息,那么decoder生成的德语就会有所遗漏
一种显然的改进就是双向LSTM来代替单向LSTM,双向LSTM运行两条链,一个从左到右,一个从右到左,两条链独立运行,分别从输入中提取特征,从左到右的最后一个状态ht,可能会遗忘了最左边的输入,从右到左最后一个状态ht’’可能会遗忘到最右的输入,如果把ht和ht”结合起来,便能很好的记住所有输入,综上所述,把单向的LSTM换成双向的LSTM可以更好地记住输入的句子,不容易遗忘,但是decoder必须是单向的LSTM,decoder是一个文本生成器,必须按照顺序生成文本,所以decoder不能用双向LSTM。
二:char-level变成word-level,这节课我们使用char-level,这样比较方便,我们不需要使用embedding层,但最好还是用word-level,原因是英语平均每个单词有4.5个字母,如果用单词代替字符,那么输入的序列便会短4.5倍,序列更短,序列更短便更不容易被遗忘,但是想用word-level tokenization,我们需要足够大的数据集,用word -level tokenization,得到的vocabulary大约是一万,因此one-hot向量维度大约也是一万,因此我们需要用word -embedding得到低维词向量,embedding层参数数量太大,小数据集没有办法很好的训练embedding,会有过拟合的问题,所以我们需要足够大的数据集,我们才可以避免过拟合,或者我们对embedding层做预训练,才可以避免过拟合问题。
第三种改进方法是多任务信息,这次我们是把一句英语翻译成了一句德语,英语句子概括成了最终的状态向量h和c,decoder通过h和c获取英语句子的信息,然后生成一句德语,训练的时候,比较decoder的预测和真实值单词,从而获得损失函数和梯度,用来更新decoder和encoder,把英语翻译成德语是一个任务。我们还可以多加几个任务,比如我们可以把英语翻译成英语句子本身,添加一个decoder,然后根据h和c来生成英语句子,这样一来encoder只有一个,而训练数据多了1倍,所以encoder可以被训练的更好,我们还可以添加很多任务,英语翻译成其他语言等,可以更好地训练encoder。如果我们有10个decoder,将英语翻译成10种语言,那么我们对英语encoder的训练便多了10倍,encoder便会训练的更好。我们的目标是把英语翻译成德育,通过借助其他语言,我们可以把encoder变得更好,虽然德语decoder没有改进但是翻译的效果还是会变好。
三种改进办法:
一:双向LSTM代替单项LSTM
二:word-level tokenization代替char-level
三:多任务学习
还有一种方法是attention注意力机制,这个方法最强,对机器翻译提高非常大。下一节课我们就来学习它。
五、总结
Seq2Seq有一个encoder和一个decoder网络
在上文举的例子中,encoder的输入是一句英语,每输入一个词,RNN就会更新状态,把输入的信息积累在encoder的状态里,encoder最后一个状态就是从英文句子中提取的特征,encoder只输出最后一个状态,会扔掉之前所有的状态
把最后一个状态传递给decoder网络,encoder的最后一个状态作为decoder网络的初始状态,初始化之后,decoder就知道输入的英文句子,然后decoder便会作为一个文本生成器,生成一句德语
首先把起始符作为decoder RNN的输入,Decoder RNN会更新状态为s1,然后全连接层会输出预测的概率记作P1,根据概率分布P1作抽样,得到下一个字符记作Z1
decoder网络拿Z1作输入,更新状态为s2,并且输出预测概率P2,根据P2作抽样,得到新的字符Z2
同样的道理,现在decoder网络z2做输入,更新状态为s3,然后输出预测的概率分布得到P3,根据P3作抽样,得到新的字符z3,不停重复该过程,抽样得到的新的字符z,然后拿z作为下一轮输入,更新状态s,计算概率分布p,假如抽样得到了终止符,那么便终止文本生成,并返回这个文本生成的序列。