Sequence to Sequence Learning with Nerual Networks 使用神经网络学习序列到序列的任务
例如机器翻译、摘要生成、机器对话、阅读理解等都是输入一个序列(句子、短语等),输出一个序列(句子、短语等)的任务,也可以说是序列生成任务或者Encoder——Decoder任务。本篇主要是在机器翻译上的工作。其实现在的谷歌翻译就是seq2seq加attention。
这篇文章是谷歌的ilya Sutskever在2014年发在NIPS上的,其实他并非第一个提出seq2seq的人。本文对英语到法语的WMT14数据集进行了翻译,得到34.81的BLEU分数;对统计机器翻译的用这个模型得到的重打分做平均,BLEU也提高3.2个点。本文的seq2seq集成了8个lstm(每个384M参数、8000维)和test时使用从左到右的Beam Search解码。
文章目录
背景知识
(一)BLEU评价指标
BLEU(bilingual evaluation understudy,双语互译质量评估辅助工具)是自动为机器翻译结果进行评价的指标。主要是通过算法翻译结果和人工的翻译语料去对比,从而判断模型好坏。论文可见《BLEU》。
那么我们就会有一到多个机器翻译结果(我们称为候选翻译集,candidate)和一到多个参考翻译集(reference),以及被翻译语言(源语言,source)和目标语言(target)。我们会统计n-gram的正确比例,然后加权平均(加权平均使用公式视情况)。下面是具体过程:
首先统计候选翻译和参考翻译n-gram下词的频次数,然后每个候选翻译和每个参考翻译分别比较出最小的词频,然后结果里再比出最大的。
C
o
u
n
t
w
i
,
j
c
l
i
p
=
m
i
n
(
C
o
u
n
t
w
i
,
R
e
f
j
_
C
o
u
n
t
w
i
)
Count_{w_i,j}^{clip} = min(Count_{w_i},Ref_{j}\_Count_{w_i})
Countwi,jclip=min(Countwi,Refj_Countwi)
C
o
u
n
t
c
l
i
p
=
m
a
x
(
C
o
u
n
t
w
i
,
j
c
l
i
p
)
,
i
=
1
,
2
,
3......
Count^{clip} = max(Count_{w_i,j}^{clip}), i=1,2,3......
Countclip=max(Countwi,jclip),i=1,2,3......
最后对n-gram结果加权平均,打出分值。
p
n
=
∑
c
∈
c
a
n
d
i
d
a
t
e
s
∑
n
−
g
r
a
m
∈
c
C
o
u
n
t
c
l
i
p
(
n
−
g
r
a
m
)
∑
c
∈
c
a
n
d
i
d
a
t
e
s
′
∑
n
−
g
r
a
m
∈
c
′
′
C
o
u
n
t
c
l
i
p
(
n
−
g
r
a
m
′
)
p_{n}=\frac{\sum_{c_{\in candidates}}\sum_{n-gram_{\in c}}Count_{clip}(n-gram)}{\sum_{c^{'}_{\in candidates}}\sum_{n-gram^{'}_{\in c^{'}}}Count_{clip}(n-gram^{'})}
pn=∑c∈candidates′∑n−gram∈c′′Countclip(n−gram′)∑c∈candidates∑n−gram∈cCountclip(n−gram)
p
a
v
e
=
e
x
p
(
1
4
∗
(
l
o
g
p
1
+
l
o
g
p
2
+
l
o
g
p
3
+
l
o
g
p
4
)
)
p_{ave}=exp(\frac{1}{4}*(log^{p_{1}}+log^{p_{2}}+log^{p_{3}}+log^{p_{4}}))
pave=exp(41∗(logp1+logp2+logp3+logp4))
当然,如果参考翻译是the boy is best,而候选翻译是the,那么这个时候打分也会是1,所以我们希望译句长度也是合适的,于是引入了惩罚项,让候选翻译和至少一个参考翻译长度相同,即达到最佳匹配长度。
对未达到的进行长度惩罚,达到的不惩罚,此时惩罚因子设为1。
B
P
=
{
e
1
−
r
c
…
…
i
f
…
c
≤
r
1
…
…
…
…
i
f
…
c
>
r
BP=\lbrace^{1…………if…c>r}_{e^{1-\frac{r}{c}}……if…c \leq r}
BP={e1−cr……if…c≤r1…………if…c>r
就有了最终打分函数,为了展示好看,一般乘100展示:
B
L
E
U
=
B
P
∗
e
x
p
(
∑
n
=
1
N
w
n
∗
l
o
g
p
n
)
BLEU=BP*exp(\sum_{n=1}^{N}w_{n}*log^{p_{n}})
BLEU=BP∗exp(n=1∑Nwn∗logpn)
下面放注释好的使用1-gram、2-gram混合的代码辅助理解,使用nltk封装好的函数:
# -*- coding: utf-8 -*-
# @Software: PyCharm
# @Author: Beilop Jiao
# @Contact: 781933206@qq.com
# @Time: 2021/1/9 19:09
from nltk.translate.bleu_score import sentence_bleu
#参考文献1和参考文献2
reference = [['the','boy','is','the', 'best'],
['he', 'is','the','only']]
#翻译结果1
candidate = ['the','the','the','the']
#使用uni-gram即1-gram和bi-gram即2-gram,然后加权平均。
#首先1-gram比较翻译结果the出现的频次,翻译结果是4次the,第一个参考出现2次,第二个1次;
#然后求翻译结果和参考翻译的最小频次,分别是2,1;
#然后比较上一步里最大的,除以翻译结果的频次,是2/4=0.5;
#使用2-gram,重复上面的过程;翻译结果是3次“the the”,显然参考集的2-gram没有重合,打分都为0。
#再加权平均一下,这里默认是对数加权平均,值很小:1.1125369292536926e-308。
#加上3-gram,4-gram后值会近乎0,这就是说翻译结果很差的意思。
#大家可以试试只用1-gram,计算出来是0.5。然后自己写个句子试试。weights改为(1,0,0,0)即可。
score = sentence_bleu(reference, candidate, weights=(1, 1, 0, 0))
print(score)
优点很明显:方便、快速、结果有参考价值
缺点也不少,主要有:
- 不考虑语言表达(语法)上的准确性。
- 测评精度会受常用词的干扰。
- 短译句的测评精度有时会较高。
- 没有考虑同义词或相似表达的情况,可能会导致合理翻译被否定。
(二)HMM隐马尔可夫模型
大家可以自己去看一下。
(三)Viterbi维特比算法
同上。
(四)BeamSearch
如果我们在一个10000维的序列里去找最优的路径,仍旧使用viterbi算法显然不大明智,每次搜索就是10000步的softmax,即使最好的TPU也撑不住。贪婪搜索每次只要一步softmax,但显然不一定搜索到的就是最好的。beam search相当于做了个折中,每次维持一个大小为 K 的翻译语句集合,每次只需要k步softmax即可。下面是保持每步大小为2的beam search。
论文使用的beam search大小是12,用大小为1和2的做了对比,其实大小为2的就已经提高很多。
实际上,viterbi就是保留大小为全部大小的beam search,而贪婪搜索就是保留大小为1的beam search。
Seq2Seq模型
即输入一个序列(句子/图像等),输出一个序列的模型。
基本思想就是使用一个Encoder把输入序列编码成定长向量C,Decoder解码这个向量产生输出。
下面是机器翻译常用的seq2seq模型,这里encoder和decoder一般都是RNN结构,可以是lstm或者gru等。C其实就是encoder模型最后一层的输出,而源语言和目标语言采用不同的embedding。
举个栗子说明和语言模型不同的地方:beijing的概率在这里是P(Beijing|欢迎,来,北京,welcome,to)。
通过把输入反序可以降低对近距离的词高度依赖的问题,让优化变得更容易。再举个栗子,把“欢迎,你,来,北京,玩”变为“玩,北京,来,你,欢迎”,降低了近距离的依赖,提高长期依赖。在这里就是降低了welcome对玩的依赖,to对北京的依赖,提高welcome对欢迎依赖等等。这样也可以学到更多关系。不需要担心倒置后“玩”和最后对应的“play”会因为依赖太长而翻译不好,因为对序列任务首要的是翻译好前面的词,前面的翻译好了,后面的词会自然而然地得到好的信息提高准确率。
可以看到对语义、语法、语态学习效果都相当不错,相近的句子在向量空间离得很近,即使还进行了调换语序等干扰:
以前的rnn模型有两个问题:
- 只能预测等长序列。
- 只能用前面信息预测后面信息的问题,翻译问题可能各语言语序不同,而需要用到全局信息。
seq2seq模型很好地解决了这两点。
deep nmt
在seq2seq模型基础上,本文集成了4个lstm作为encoder层,4个lstm作为decoder层,测试时使用beam search,我们称之为deep nmt模型,也就是深层神经机器翻译模型。这一点开头已经说过。
可以看到多层lstm里,每层lstm的每一时间步输出H不止作为本层下一时间步输入,还作为下一层lstm这个时间步的输入。
decoder也是一样。decoder在训练时依旧会输入参考翻译w,x,y,z去产生loss,训练参数,得到预测输出。而测试的时候decoder不再输入参考翻译,这也和beam search是有关的。
一般decoder层我们还会把最后一层lstm的当前时间步信息给到最前面一层的下一时间步。
当句子过长时我们为了避免遗忘c的信息,还会把c的信息输入到decoder的每一个时间步而不是第一个时间步,当然有attention后就不需要这个结构了,且效果更好。
Some Tricks
总结一下小技巧:
- Encoder 和Decoder使用不同LSTM,实际上每个LSTM都不共享参数。
- 深层LSTM效果更好,但如果太深会难以训练,也可能会梯度消失。
- 源语言倒序输入会大幅提高效果。
以下是本文的其他一部分炼丹术:
- 参数初始化为-0.8到0.8之间的均匀分布。
- 使用不带动量的SGD,学习率0.7,5个epoch后每0.5轮学习率减半,一共训练7.5轮。
- 批次128大小,梯度除以批次大小即可。
- 可能会有梯度爆炸问题,对梯度g求二范数s,如果s>5,令g=5g/s,这里的g是除以128之后的。
- 为了加快速度,对句子长度进行排序,让每个batch的句子长度都差不多,这样就不需要对句子补<PAD>让句子等长,大概会加速2倍。
应用
- GNMT谷歌翻译的基石。
- 多层LSTM配合attention是transformer出来之前的最强模型。