Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
M
mtbookv2
概览
Overview
Details
Activity
Cycle Analytics
版本库
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
问题
0
Issues
0
列表
Board
标记
里程碑
合并请求
0
Merge Requests
0
CI / CD
CI / CD
流水线
作业
日程表
图表
维基
Wiki
代码片段
Snippets
成员
Collapse sidebar
Close sidebar
活动
图像
聊天
创建新问题
作业
提交
Issue Boards
Open sidebar
NiuTrans
mtbookv2
Commits
ce3686f8
Commit
ce3686f8
authored
Oct 09, 2020
by
zengxin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
chapter10 序列符号纠正
parent
f28cd4b0
隐藏空白字符变更
内嵌
并排
正在显示
1 个修改的文件
包含
33 行增加
和
33 行删除
+33
-33
Chapter10/chapter10.tex
+33
-33
没有找到文件。
Chapter10/chapter10.tex
查看文件 @
ce3686f8
...
...
@@ -415,7 +415,7 @@ NMT & 21.7 & 18.7 & -13.7 \\
\parinterval
显然,根据上下文中提到的“没/吃饭”、“很/饿”,最佳的答案是“吃饭”或者“吃东西”。也就是,对序列中某个位置的答案进行预测时需要记忆当前时刻之前的序列信息,因此,循环神经网络应运而生。实际上循环神经网络有着极为广泛的应用,例如语音识别、语言建模以及即将要介绍的神经机器翻译。
\parinterval
{
\chapternine
}
已经对循环神经网络的基本知识进行过介绍。这里再回顾一下。简单来说,循环神经网络由循环单元组成。对于序列中的任意时刻,都有一个循环单元与之对应,它会融合当前时刻的输入和上一时刻循环单元的输出,生成当前时刻的输出。这样每个时刻的信息都会被传递到下一时刻,这也间接达到了记录历史信息的目的。比如,对于序列
$
\
vectorn
{
\emph
{
x
}}
=
\{
x
_
1
, x
_
2
,..., x
_
m
\}
$
,循环神经网络会按顺序输出一个序列
$
\vectorn
{
\emph
{
h
}
}
=
\{
\vectorn
{
\emph
{
h
}}_
1
,
\vectorn
{
\emph
{
h
}}_
2
,...,
\vectorn
{
\emph
{
h
}}_
m
\}
$
,其中
$
\vectorn
{
\emph
{
h
}}_
i
$
表示
$
i
$
时刻循环神经网络的输出(通常为一个向量)。
\parinterval
{
\chapternine
}
已经对循环神经网络的基本知识进行过介绍。这里再回顾一下。简单来说,循环神经网络由循环单元组成。对于序列中的任意时刻,都有一个循环单元与之对应,它会融合当前时刻的输入和上一时刻循环单元的输出,生成当前时刻的输出。这样每个时刻的信息都会被传递到下一时刻,这也间接达到了记录历史信息的目的。比如,对于序列
$
\
seq
{
x
}
=
\{
x
_
1
, x
_
2
,..., x
_
m
\}
$
,循环神经网络会按顺序输出一个序列
$
\seq
{
h
}
=
\{
\vectorn
{
\emph
{
h
}}_
1
,
\vectorn
{
\emph
{
h
}}_
2
,...,
\vectorn
{
\emph
{
h
}}_
m
\}
$
,其中
$
\vectorn
{
\emph
{
h
}}_
i
$
表示
$
i
$
时刻循环神经网络的输出(通常为一个向量)。
\parinterval
图
\ref
{
fig:10-9
}
展示了一个循环神经网络处理序列问题的实例。当前时刻循环单元的输入由上一个时刻的输出和当前时刻的输入组成,因此也可以理解为,网络当前时刻计算得到的输出是由之前的序列共同决定的,即网络在不断地传递信息的过程中记忆了历史信息。以最后一个时刻的循环单元为例,它在对“开始”这个单词的信息进行处理时,参考了之前所有词(“<sos>
\
让
\
我们”)的信息。
...
...
@@ -439,38 +439,38 @@ NMT & 21.7 & 18.7 & -13.7 \\
\end{figure}
%----------------------------------------------
\parinterval
从数学模型上看,神经机器翻译模型与统计机器翻译的目标是一样的:在给定源语言句子
$
\
vectorn
{
\emph
{
x
}}$
的情况下,找出翻译概率最大的目标语言译文
$
\hat
{
\vectorn
{
\emph
{
y
}
}}$
:
\parinterval
从数学模型上看,神经机器翻译模型与统计机器翻译的目标是一样的:在给定源语言句子
$
\
seq
{
x
}$
的情况下,找出翻译概率最大的目标语言译文
$
\hat
{
\seq
{
y
}}$
:
\begin{eqnarray}
\hat
{
\
vectorn
{
\emph
{
y
}}}
=
\argmax
_{
\vectorn
{
\emph
{
y
}}}
\funp
{
P
}
(
\vectorn
{
\emph
{
y
}}
|
\vectorn
{
\emph
{
x
}}
)
\hat
{
\
seq
{{
y
}}}
=
\argmax
_{
\seq
{{
y
}}}
\funp
{
P
}
(
\seq
{{
y
}}
|
\seq
{
{
x
}}
)
\label
{
eq:10-1
}
\end{eqnarray}
\noindent
这里,用
$
\
vectorn
{
\emph
{
x
}}
=
\{
x
_
1
,x
_
2
,..., x
_
m
\}
$
表示输入的源语言单词序列,
$
\vectorn
{
\emph
{
y
}}
=
\{
y
_
1
,y
_
2
,..., y
_
n
\}
$
表示生成的目标语言单词序列。由于神经机器翻译在生成译文时采用的是自左向右逐词生成的方式,并在翻译每个单词时考虑已经生成的翻译结果,因此对
$
\funp
{
P
}
(
\vectorn
{
\emph
{
y
}}
|
\vectorn
{
\emph
{
x
}}
)
$
的求解可以转换为:
\noindent
这里,用
$
\
seq
{{
x
}}
=
\{
x
_
1
,x
_
2
,..., x
_
m
\}
$
表示输入的源语言单词序列,
$
\seq
{{
y
}}
=
\{
y
_
1
,y
_
2
,..., y
_
n
\}
$
表示生成的目标语言单词序列。由于神经机器翻译在生成译文时采用的是自左向右逐词生成的方式,并在翻译每个单词时考虑已经生成的翻译结果,因此对
$
\funp
{
P
}
(
\seq
{{
y
}}
|
\seq
{
{
x
}}
)
$
的求解可以转换为:
\begin{eqnarray}
\funp
{
P
}
(
\
vectorn
{
\emph
{
y
}}
|
\vectorn
{
\emph
{
x
}}
) =
\prod
_{
j=1
}^{
n
}
\funp
{
P
}
( y
_
j |
\vectorn
{
\emph
{
y
}}_{
<j
}
,
\vectorn
{
\emph
{
x
}}
)
\funp
{
P
}
(
\
seq
{{
y
}}
|
\seq
{{
x
}}
) =
\prod
_{
j=1
}^{
n
}
\funp
{
P
}
( y
_
j |
\seq
{{
y
}}_{
<j
}
,
\seq
{
{
x
}}
)
\label
{
eq:10-2
}
\end{eqnarray}
\vspace
{
-0.5em
}
\noindent
其中,
$
\
vectorn
{
\emph
{
y
}}_{
<j
}$
表示目标语言第
$
j
$
个位置之前已经生成的译文单词序列。
$
\funp
{
P
}
(
y
_
j |
\vectorn
{
\emph
{
y
}}_{
<j
}
,
\vectorn
{
\emph
{
x
}}
)
$
可以被解释为:根据源语言句子
$
\vectorn
{
\emph
{
x
}}
$
和已生成的目标语言译文片段
$
\vectorn
{
\emph
{
y
}}_{
<j
}
=
\{
y
_
1
, y
_
2
,..., y
_{
j
-
1
}
\}
$
,生成第
$
j
$
个目标语言单词
$
y
_
j
$
的概率。
\noindent
其中,
$
\
seq
{{
y
}}_{
<j
}$
表示目标语言第
$
j
$
个位置之前已经生成的译文单词序列。
$
\funp
{
P
}
(
y
_
j |
\seq
{{
y
}}_{
<j
}
,
\seq
{{
x
}}
)
$
可以被解释为:根据源语言句子
$
\seq
{{
x
}}
$
和已生成的目标语言译文片段
$
\seq
{
{
y
}}_{
<j
}
=
\{
y
_
1
, y
_
2
,..., y
_{
j
-
1
}
\}
$
,生成第
$
j
$
个目标语言单词
$
y
_
j
$
的概率。
\parinterval
求解
$
\funp
{
P
}
(
y
_
j |
\
vectorn
{
\emph
{
y
}}_{
<j
}
,
\vectorn
{
\emph
{
x
}}
)
$
有三个关键问题(图
\ref
{
fig:10-11
}
):
\parinterval
求解
$
\funp
{
P
}
(
y
_
j |
\
seq
{{
y
}}_{
<j
}
,
\seq
{
{
x
}}
)
$
有三个关键问题(图
\ref
{
fig:10-11
}
):
\vspace
{
-0.5em
}
\begin{itemize}
\vspace
{
0.5em
}
\item
如何对
$
\
vectorn
{
\emph
{
x
}}$
和
$
\vectorn
{
\emph
{
y
}}_{
<j
}$
进行分布式表示,即
{
\small\sffamily\bfseries
{
词嵌入
}}
\index
{
词嵌入
}
(Word Embedding)
\index
{
Word Embedding
}
。首先,将由one-hot向量表示的源语言单词,即由0和1构成的离散化向量表示,转化为实数向量。可以把这个过程记为
$
\textrm
{
e
}_
x
(
\cdot
)
$
。类似的,可以把目标语言序列
$
\vectorn
{
\emph
{
y
}}_{
<j
}$
中的每个单词用同样的方式进行表示,记为
$
\textrm
{
e
}_
y
(
\cdot
)
$
。
\item
如何对
$
\
seq
{{
x
}}$
和
$
\seq
{{
y
}}_{
<j
}$
进行分布式表示,即
{
\small\sffamily\bfseries
{
词嵌入
}}
\index
{
词嵌入
}
(Word Embedding)
\index
{
Word Embedding
}
。首先,将由one-hot向量表示的源语言单词,即由0和1构成的离散化向量表示,转化为实数向量。可以把这个过程记为
$
\textrm
{
e
}_
x
(
\cdot
)
$
。类似的,可以把目标语言序列
$
\seq
{
{
y
}}_{
<j
}$
中的每个单词用同样的方式进行表示,记为
$
\textrm
{
e
}_
y
(
\cdot
)
$
。
\vspace
{
0.5em
}
\item
如何在词嵌入的基础上获取整个序列的表示,即句子的
{
\small\sffamily\bfseries
{
表示学习
}}
\index
{
表示学习
}
(Representation Learning)
\index
{
Representation Learning
}
。可以把词嵌入的序列作为循环神经网络的输入,循环神经网络最后一个时刻的输出向量便是整个句子的表示结果。如图
\ref
{
fig:10-11
}
中,编码器最后一个循环单元的输出
$
\vectorn
{
\emph
{
h
}}_
m
$
被看作是一种包含了源语言句子信息的表示结果,记为
$
\vectorn
{
\emph
{
C
}}$
。
\vspace
{
0.5em
}
\item
如何得到每个目标语言单词的概率,即译文单词的
{
\small\sffamily\bfseries
{
生成
}}
\index
{
生成
}
(Generation)
\index
{
Generation
}
。与神经语言模型一样,可以用一个Softmax输出层来获取当前时刻所有单词的分布,即利用Softmax 函数计算目标语言词表中每个单词的概率。令目标语言序列
$
j
$
时刻的循环神经网络的输出向量(或状态)为
$
\vectorn
{
\emph
{
s
}}_
j
$
。根据循环神经网络的性质,
$
y
_
j
$
的生成只依赖前一个状态
$
\vectorn
{
\emph
{
s
}}_{
j
-
1
}$
和当前时刻的输入(即词嵌入
$
\textrm
{
e
}_
y
(
y
_{
j
-
1
}
)
$
)。同时考虑源语言信息
$
\vectorn
{
\emph
{
C
}}$
,
$
\funp
{
P
}
(
y
_
j |
\
vectorn
{
\emph
{
y
}}_{
<j
}
,
\vectorn
{
\emph
{
x
}}
)
$
可以被重新定义为:
\item
如何得到每个目标语言单词的概率,即译文单词的
{
\small\sffamily\bfseries
{
生成
}}
\index
{
生成
}
(Generation)
\index
{
Generation
}
。与神经语言模型一样,可以用一个Softmax输出层来获取当前时刻所有单词的分布,即利用Softmax 函数计算目标语言词表中每个单词的概率。令目标语言序列
$
j
$
时刻的循环神经网络的输出向量(或状态)为
$
\vectorn
{
\emph
{
s
}}_
j
$
。根据循环神经网络的性质,
$
y
_
j
$
的生成只依赖前一个状态
$
\vectorn
{
\emph
{
s
}}_{
j
-
1
}$
和当前时刻的输入(即词嵌入
$
\textrm
{
e
}_
y
(
y
_{
j
-
1
}
)
$
)。同时考虑源语言信息
$
\vectorn
{
\emph
{
C
}}$
,
$
\funp
{
P
}
(
y
_
j |
\
seq
{{
y
}}_{
<j
}
,
\seq
{
{
x
}}
)
$
可以被重新定义为:
\begin{eqnarray}
\funp
{
P
}
(y
_
j |
\
vectorn
{
\emph
{
y
}}_{
<j
}
,
\vectorn
{
\emph
{
x
}}
)
\equiv
\funp
{
P
}
(
{
y
_
j |
\vectorn
{
\emph
{
s
}}_{
j-1
}
,y
_{
j-1
}
,
\vectorn
{
\emph
{
C
}}}
)
\funp
{
P
}
(y
_
j |
\
seq
{{
y
}}_{
<j
}
,
\seq
{
{
x
}}
)
\equiv
\funp
{
P
}
(
{
y
_
j |
\vectorn
{
\emph
{
s
}}_{
j-1
}
,y
_{
j-1
}
,
\vectorn
{
\emph
{
C
}}}
)
\label
{
eq:10-4
}
\end{eqnarray}
$
\funp
{
P
}
(
{
y
_
j |
\vectorn
{
\emph
{
s
}}_{
j
-
1
}
,y
_{
j
-
1
}
,
\vectorn
{
\emph
{
C
}}}
)
$
由Softmax实现,Softmax的输入是循环神经网络
$
j
$
时刻的输出。在具体实现时,
$
\vectorn
{
\emph
{
C
}}$
可以被简单的作为第一个时刻循环单元的输入,即,当
$
j
=
1
$
时,解码器的循环神经网络会读入编码器最后一个隐层状态
$
\vectorn
{
\emph
{
h
}}_
m
$
(也就是
$
\vectorn
{
\emph
{
C
}}$
),而其他时刻的隐层状态不直接与
$
\vectorn
{
\emph
{
C
}}$
相关。最终,
$
\funp
{
P
}
(
y
_
j |
\
vectorn
{
\emph
{
y
}}_{
<j
}
,
\vectorn
{
\emph
{
x
}}
)
$
被表示为:
$
\funp
{
P
}
(
{
y
_
j |
\vectorn
{
\emph
{
s
}}_{
j
-
1
}
,y
_{
j
-
1
}
,
\vectorn
{
\emph
{
C
}}}
)
$
由Softmax实现,Softmax的输入是循环神经网络
$
j
$
时刻的输出。在具体实现时,
$
\vectorn
{
\emph
{
C
}}$
可以被简单的作为第一个时刻循环单元的输入,即,当
$
j
=
1
$
时,解码器的循环神经网络会读入编码器最后一个隐层状态
$
\vectorn
{
\emph
{
h
}}_
m
$
(也就是
$
\vectorn
{
\emph
{
C
}}$
),而其他时刻的隐层状态不直接与
$
\vectorn
{
\emph
{
C
}}$
相关。最终,
$
\funp
{
P
}
(
y
_
j |
\
seq
{{
y
}}_{
<j
}
,
\seq
{
{
x
}}
)
$
被表示为:
\begin{eqnarray}
\funp
{
P
}
(y
_
j |
\
vectorn
{
\emph
{
y
}}_{
<j
}
,
\vectorn
{
\emph
{
x
}}
)
\equiv
\funp
{
P
}
(y
_
j |
\
seq
{{
y
}}_{
<j
}
,
\seq
{
{
x
}}
)
\equiv
\left
\{
\begin{array}
{
ll
}
\funp
{
P
}
(y
_
j |
\vectorn
{
\emph
{
C
}}
,y
_{
j-1
}
)
&
j=1
\\
\funp
{
P
}
(y
_
j|
\vectorn
{
\emph
{
s
}}_{
j-1
}
,y
_{
j-1
}
)
\quad
&
j>1
...
...
@@ -484,13 +484,13 @@ $\funp{P}({y_j | \vectorn{\emph{s}}_{j-1} ,y_{j-1},\vectorn{\emph{C}}})$由Softm
\begin{figure}
[htp]
\centering
\input
{
./Chapter10/Figures/figure-3-base-problom-of-p
}
\caption
{
求解
$
\funp
{
P
}
(
y
_
j |
\
vectorn
{
\emph
{
y
}}_{
<j
}
,
\vectorn
{
\emph
{
x
}}
)
$
的三个基本问题
}
\caption
{
求解
$
\funp
{
P
}
(
y
_
j |
\
seq
{{
y
}}_{
<j
}
,
\seq
{
{
x
}}
)
$
的三个基本问题
}
\label
{
fig:10-11
}
\end{figure}
%----------------------------------------------
\parinterval
输入层(词嵌入)和输出层(Softmax)的内容已在
{
\chapternine
}
进行了介绍,因此这里的核心内容是设计循环神经网络结构,即设计循环单元的结构。至今,研究人员已经提出了很多优秀的循环单元结构。其中循环神经网络(RNN)
是最原始的循环单元结构。在RNN中,对于序列
$
\
vectorn
{
\emph
{
x
}}
=
\{
\vectorn
{
\emph
{
x
}}_
1
,
\vectorn
{
\emph
{
x
}}_
2
,...,
\vectorn
{
\emph
{
x
}}_
m
\}
$
,每个时刻
$
t
$
都对应一个循环单元,它的输出是一个向量
$
\vectorn
{
\emph
{
h
}}_
t
$
,可以被描述为:
是最原始的循环单元结构。在RNN中,对于序列
$
\
seq
{
{
x
}}
=
\{
\vectorn
{
\emph
{
x
}}_
1
,
\vectorn
{
\emph
{
x
}}_
2
,...,
\vectorn
{
\emph
{
x
}}_
m
\}
$
,每个时刻
$
t
$
都对应一个循环单元,它的输出是一个向量
$
\vectorn
{
\emph
{
h
}}_
t
$
,可以被描述为:
\begin{eqnarray}
\vectorn
{
\emph
{
h
}}_
t=f(
\vectorn
{
\emph
{
x
}}_
t
\vectorn
{
\emph
{
U
}}
+
\vectorn
{
\emph
{
h
}}_{
t-1
}
\vectorn
{
\emph
{
W
}}
+
\vectorn
{
\emph
{
b
}}
)
...
...
@@ -1127,27 +1127,27 @@ L(\vectorn{\emph{Y}},\widehat{\vectorn{\emph{Y}}}) = \sum_{j=1}^n L_{\textrm{ce}
%----------------------------------------------------------------------------------------
\subsection
{
推断
}
\parinterval
神经机器翻译的推断是一个典型的搜索问题(见
{
\chaptertwo
}
)。这个过程是指:利用已经训练好的模型对新的源语言句子进行翻译的过程。具体来说,首先利用编码器生成源语言句子的表示,之后利用解码器预测目标语言译文。也就是,对于源语言句子
$
\
vectorn
{
\emph
{
x
}}$
,生成一个使翻译概率
$
\funp
{
P
}
(
\vectorn
{
\emph
{
y
}}
|
\vectorn
{
\emph
{
x
}}
)
$
最大的目标语言译文
$
\hat
{
\vectorn
{
\emph
{
y
}}}$
,如下(详细过程见
\ref
{
sec:10.3.1
}
节):
\parinterval
神经机器翻译的推断是一个典型的搜索问题(见
{
\chaptertwo
}
)。这个过程是指:利用已经训练好的模型对新的源语言句子进行翻译的过程。具体来说,首先利用编码器生成源语言句子的表示,之后利用解码器预测目标语言译文。也就是,对于源语言句子
$
\
seq
{{
x
}}$
,生成一个使翻译概率
$
\funp
{
P
}
(
\seq
{{
y
}}
|
\seq
{{
x
}}
)
$
最大的目标语言译文
$
\hat
{
\seq
{
{
y
}}}$
,如下(详细过程见
\ref
{
sec:10.3.1
}
节):
\begin{eqnarray}
\hat
{
\
vectorn
{
\emph
{
y
}}}
&
=
&
\argmax
_{
\vectorn
{
\emph
{
y
}}}
\funp
{
P
}
(
\vectorn
{
\emph
{
y
}}
|
\vectorn
{
\emph
{
x
}}
)
\nonumber
\\
&
=
&
\argmax
_{
\
vectorn
{
\emph
{
y
}}}
\prod
_{
j=1
}^
n
\funp
{
P
}
(y
_
j |
\vectorn
{
\emph
{
y
}}_{
<j
}
,
\vectorn
{
\emph
{
x
}}
)
\hat
{
\
seq
{{
y
}}}
&
=
&
\argmax
_{
\seq
{{
y
}}}
\funp
{
P
}
(
\seq
{{
y
}}
|
\seq
{
{
x
}}
)
\nonumber
\\
&
=
&
\argmax
_{
\
seq
{{
y
}}}
\prod
_{
j=1
}^
n
\funp
{
P
}
(y
_
j |
\seq
{{
y
}}_{
<j
}
,
\seq
{
{
x
}}
)
\label
{
eq:10-35
}
\end{eqnarray}
\parinterval
在具体实现时,由于当前目标语言单词的生成需要依赖前面单词的生成,因此无法同时生成所有的目标语言单词。理论上,可以枚举所有的
$
\
vectorn
{
\emph
{
y
}}$
,之后利用
$
\funp
{
P
}
(
\vectorn
{
\emph
{
y
}}
|
\vectorn
{
\emph
{
x
}}
)
$
的定义对每个
$
\vectorn
{
\emph
{
y
}}$
进行评价,然后找出最好的
$
\vectorn
{
\emph
{
y
}}$
。这也被称作
{
\small\bfnew
{
全搜索
}}
\index
{
全搜索
}
(Full Search)
\index
{
Full Search
}
。但是,枚举所有的译文单词序列显然是不现实的。因此,在具体实现时,并不会访问所有可能的译文单词序列,而是用某种策略进行有效的搜索。常用的做法是自左向右逐词生成。比如,对于每一个目标语言位置
$
j
$
,可以执行
\parinterval
在具体实现时,由于当前目标语言单词的生成需要依赖前面单词的生成,因此无法同时生成所有的目标语言单词。理论上,可以枚举所有的
$
\
seq
{{
y
}}$
,之后利用
$
\funp
{
P
}
(
\seq
{{
y
}}
|
\seq
{{
x
}}
)
$
的定义对每个
$
\seq
{{
y
}}$
进行评价,然后找出最好的
$
\seq
{
{
y
}}$
。这也被称作
{
\small\bfnew
{
全搜索
}}
\index
{
全搜索
}
(Full Search)
\index
{
Full Search
}
。但是,枚举所有的译文单词序列显然是不现实的。因此,在具体实现时,并不会访问所有可能的译文单词序列,而是用某种策略进行有效的搜索。常用的做法是自左向右逐词生成。比如,对于每一个目标语言位置
$
j
$
,可以执行
\begin{eqnarray}
\hat
{
y
}_
j =
\argmax
_{
y
_
j
}
\funp
{
P
}
(y
_
j |
\hat
{
\
vectorn
{
\emph
{
y
}}}_{
<j
}
,
\vectorn
{
\emph
{
x
}}
)
\hat
{
y
}_
j =
\argmax
_{
y
_
j
}
\funp
{
P
}
(y
_
j |
\hat
{
\
seq
{{
y
}}}_{
<j
}
,
\seq
{
{
x
}}
)
\label
{
eq:10-36
}
\end{eqnarray}
\noindent
其中,
$
\hat
{
y
}_
j
$
表示位置
$
j
$
概率最高的单词,
$
\hat
{
\
vectorn
{
\emph
{
y
}}}_{
<j
}
=
\{
\hat
{
y
}_
1
,...,
\hat
{
y
}_{
j
-
1
}
\}
$
表示已经生成的最优译文单词序列。也就是,把最优的译文看作是所有位置上最优单词的组合。显然,这是一种贪婪搜索,因为无法保证
$
\{
\hat
{
y
}_
1
,...,
\hat
{
y
}_{
n
}
\}
$
是全局最优解。一种缓解这个问题的方法是,在每步中引入更多的候选。这里定义
$
\hat
{
y
}_{
jk
}
$
表示在目标语言第
$
j
$
个位置排名在第
$
k
$
位的单词。在每一个位置
$
j
$
,可以生成
$
k
$
个最可能的单词,而不是1个,这个过程可以被描述为
\noindent
其中,
$
\hat
{
y
}_
j
$
表示位置
$
j
$
概率最高的单词,
$
\hat
{
\
seq
{
{
y
}}}_{
<j
}
=
\{
\hat
{
y
}_
1
,...,
\hat
{
y
}_{
j
-
1
}
\}
$
表示已经生成的最优译文单词序列。也就是,把最优的译文看作是所有位置上最优单词的组合。显然,这是一种贪婪搜索,因为无法保证
$
\{
\hat
{
y
}_
1
,...,
\hat
{
y
}_{
n
}
\}
$
是全局最优解。一种缓解这个问题的方法是,在每步中引入更多的候选。这里定义
$
\hat
{
y
}_{
jk
}
$
表示在目标语言第
$
j
$
个位置排名在第
$
k
$
位的单词。在每一个位置
$
j
$
,可以生成
$
k
$
个最可能的单词,而不是1个,这个过程可以被描述为
\begin{eqnarray}
\{
\hat
{
y
}_{
j1
}
,...,
\hat
{
y
}_{
jk
}
\}
=
\argmax
_{
\{
\hat
{
y
}_{
j1
}
,...,
\hat
{
y
}_{
jk
}
\}
}
\funp
{
P
}
(y
_
j |
\{
\hat
{
\
vectorn
{
\emph
{
y
}}}_{
<
{
j
\ast
}}
\}
,
\vectorn
{
\emph
{
x
}}
)
\funp
{
P
}
(y
_
j |
\{
\hat
{
\
seq
{{
y
}}}_{
<
{
j
\ast
}}
\}
,
\seq
{
{
x
}}
)
\label
{
eq:10-37
}
\end{eqnarray}
\noindent
这里,
$
\{
\hat
{
y
}_{
j
1
}
,...,
\hat
{
y
}_{
jk
}
\}
$
表示对于位置
$
j
$
翻译概率最大的前
$
k
$
个单词,
$
\{
\hat
{
\
vectorn
{
\emph
{
y
}}}_{
<j
\ast
}
\}
$
表示前
$
j
-
1
$
步top-k单词组成的所有历史。
${
\hat
{
\vectorn
{
\emph
{
y
}}}_{
<j
\ast
}}$
可以被看作是一个集合,里面每一个元素都是一个目标语言单词序列,这个序列是前面生成的一系列top-k单词的某种组成。
$
\funp
{
P
}
(
y
_
j |
\{
\hat
{
\vectorn
{
\emph
{
y
}}}_{
<
{
j
^{
\textrm
{
*
}}}}
\}
,
\vectorn
{
\emph
{
x
}}
)
$
表示基于
\{
$
\hat
{
\vectorn
{
\emph
{
y
}}}_{
<j
\ast
}
$
\}
的某一条路径生成
$
y
_
j
$
的概率
\footnote
{
严格来说,
$
\funp
{
P
}
(
y
_
j |
{
\hat
{
\vectorn
{
\emph
{
y
}}}_{
<j
\ast
}
}
)
$
不是一个准确的数学表达,这里通过这种写法强调
$
y
_
j
$
是由
\{
$
\hat
{
\vectorn
{
\emph
{
y
}}}_{
<j
\ast
}
$
\}
中的某个译文单词序列作为条件生成的。
}
。这种方法也被称为束搜索,意思是搜索时始终考虑一个集束内的候选。
\noindent
这里,
$
\{
\hat
{
y
}_{
j
1
}
,...,
\hat
{
y
}_{
jk
}
\}
$
表示对于位置
$
j
$
翻译概率最大的前
$
k
$
个单词,
$
\{
\hat
{
\
seq
{{
y
}}}_{
<j
\ast
}
\}
$
表示前
$
j
-
1
$
步top-k单词组成的所有历史。
${
\hat
{
\seq
{{
y
}}}_{
<j
\ast
}}$
可以被看作是一个集合,里面每一个元素都是一个目标语言单词序列,这个序列是前面生成的一系列top-k单词的某种组成。
$
\funp
{
P
}
(
y
_
j |
\{
\hat
{
\seq
{{
y
}}}_{
<
{
j
^{
\textrm
{
*
}}}}
\}
,
\seq
{{
x
}}
)
$
表示基于
\{
$
\hat
{
\seq
{{
y
}}}_{
<j
\ast
}
$
\}
的某一条路径生成
$
y
_
j
$
的概率
\footnote
{
严格来说,
$
\funp
{
P
}
(
y
_
j |
{
\hat
{
\seq
{{
y
}}}_{
<j
\ast
}
}
)
$
不是一个准确的数学表达,这里通过这种写法强调
$
y
_
j
$
是由
\{
$
\hat
{
\seq
{
{
y
}}}_{
<j
\ast
}
$
\}
中的某个译文单词序列作为条件生成的。
}
。这种方法也被称为束搜索,意思是搜索时始终考虑一个集束内的候选。
\parinterval
不论是贪婪搜索还是束搜索都是一个自左向右的过程,也就是每个位置的处理需要等前面位置处理完才能执行。这是一种典型的
{
\small\bfnew
{
自回归模型
}}
\index
{
自回归模型
}
(Autoregressive Model)
\index
{
Autoregressive Model
}
,它通常用来描述时序上的随机过程,其中每一个时刻的结果对时序上其他部分的结果有依赖
\upcite
{
Akaike1969autoregressive
}
。相对应的,也有
{
\small\bfnew
{
非自回归模型
}}
\index
{
非自回归模型
}
(Non-autoregressive Model)
\index
{
Non-autoregressive Model
}
,它消除了不同时刻结果之间的直接依赖
\upcite
{
Gu2017NonAutoregressiveNM
}
。由于自回归模型是当今神经机器翻译主流的推断方法,这里仍以自回归的贪婪搜索和束搜索为基础进行讨论。
...
...
@@ -1190,7 +1190,7 @@ L(\vectorn{\emph{Y}},\widehat{\vectorn{\emph{Y}}}) = \sum_{j=1}^n L_{\textrm{ce}
\subsubsection
{
2. 束搜索
}
\vspace
{
0.5em
}
\parinterval
束搜索是一种启发式图搜索算法。相比于全搜索,它可以减少搜索所占用的空间和时间,在每一步扩展的时候,剪掉一些质量比较差的结点,保留下一些质量较高的结点。具体到机器翻译任务,对于每一个目标语言位置,束搜索选择了概率最大的前
$
K
$
个单词进行扩展(其中
$
k
$
叫做束宽度,或简称为束宽)。如图
\ref
{
fig:10-34
}
所示,假设
\{
$
y
_
1
, y
_
2
,..., y
_
n
$
\}
表示生成的目标语言序列,且
$
k
=
3
$
,则束搜索的具体过程为:在预测第一个位置时,可以通过模型得到
$
y
_
1
$
的概率分布,选取概率最大的前3个单词作为候选结果(假设分别为“have”, “has”, “it”)。在预测第二个位置的单词时,模型针对已经得到的三个候选结果(“have”, “has”, “it”)计算第二个单词的概率分布。因为
$
y
_
2
$
对应
$
|V|
$
种可能,总共可以得到
$
3
\times
|V|
$
种结果。然后从中选取使序列概率
$
\funp
{
P
}
(
y
_
2
,y
_
1
|
\
vectorn
{
\emph
{
x
}}
)
$
最大的前三个
$
y
_
2
$
作为新的输出结果,这样便得到了前两个位置的top-3译文。在预测其他位置时也是如此,不断重复此过程直到推断结束。可以看到,束搜索的搜索空间大小与束宽度有关,也就是:束宽度越大,搜索空间越大,更有可能搜索到质量更高的译文,但同时搜索会更慢。束宽度等于3,意味着每次只考虑三个最有可能的结果,贪婪搜索实际上便是集束宽度为1的情况。在神经机器翻译系统实现中,一般束宽度设置在4~8之间。
\parinterval
束搜索是一种启发式图搜索算法。相比于全搜索,它可以减少搜索所占用的空间和时间,在每一步扩展的时候,剪掉一些质量比较差的结点,保留下一些质量较高的结点。具体到机器翻译任务,对于每一个目标语言位置,束搜索选择了概率最大的前
$
K
$
个单词进行扩展(其中
$
k
$
叫做束宽度,或简称为束宽)。如图
\ref
{
fig:10-34
}
所示,假设
\{
$
y
_
1
, y
_
2
,..., y
_
n
$
\}
表示生成的目标语言序列,且
$
k
=
3
$
,则束搜索的具体过程为:在预测第一个位置时,可以通过模型得到
$
y
_
1
$
的概率分布,选取概率最大的前3个单词作为候选结果(假设分别为“have”, “has”, “it”)。在预测第二个位置的单词时,模型针对已经得到的三个候选结果(“have”, “has”, “it”)计算第二个单词的概率分布。因为
$
y
_
2
$
对应
$
|V|
$
种可能,总共可以得到
$
3
\times
|V|
$
种结果。然后从中选取使序列概率
$
\funp
{
P
}
(
y
_
2
,y
_
1
|
\
seq
{
{
x
}}
)
$
最大的前三个
$
y
_
2
$
作为新的输出结果,这样便得到了前两个位置的top-3译文。在预测其他位置时也是如此,不断重复此过程直到推断结束。可以看到,束搜索的搜索空间大小与束宽度有关,也就是:束宽度越大,搜索空间越大,更有可能搜索到质量更高的译文,但同时搜索会更慢。束宽度等于3,意味着每次只考虑三个最有可能的结果,贪婪搜索实际上便是集束宽度为1的情况。在神经机器翻译系统实现中,一般束宽度设置在4~8之间。
%----------------------------------------------
\begin{figure}
[htp]
...
...
@@ -1207,39 +1207,39 @@ L(\vectorn{\emph{Y}},\widehat{\vectorn{\emph{Y}}}) = \sum_{j=1}^n L_{\textrm{ce}
\subsubsection
{
3. 长度惩罚
}
\parinterval
这里用
$
\funp
{
P
}
(
\
vectorn
{
\emph
{
y
}}
|
\vectorn
{
\emph
{
x
}}
)
=
\prod
_{
j
=
1
}^
n
\funp
{
P
}
(
y
_
j |
\vectorn
{
\emph
{
y
}}_{
<j
}
,
\vectorn
{
\emph
{
x
}}
)
$
作为翻译模型。直接实现这个公式有一个明显的缺点:当句子过长时乘法运算容易产生溢出,也就是多个数相乘可能会产生浮点数无法表示的运算结果。为了解决这个问题,可以利用对数操作将乘法转换为加法,得到新的计算方式:
$
\textrm
{
log
}
\funp
{
P
}
(
\vectorn
{
\emph
{
y
}}
|
\vectorn
{
\emph
{
x
}}
)
=
\sum
_{
j
=
1
}^
n
\textrm
{
log
}
\funp
{
P
}
(
y
_
j |
\vectorn
{
\emph
{
y
}}_{
<j
}
,
\vectorn
{
\emph
{
x
}}
)
$
,对数函数不会改变函数的单调性,因此在具体实现时,通常用
$
\textrm
{
log
}
\funp
{
P
}
(
\vectorn
{
\emph
{
y
}}
|
\vectorn
{
\emph
{
x
}}
)
$
表示句子的得分,而不用
$
\funp
{
P
}
(
\vectorn
{
\emph
{
y
}}
|
\vectorn
{
\emph
{
x
}}
)
$
。
\parinterval
这里用
$
\funp
{
P
}
(
\
seq
{{
y
}}
|
\seq
{{
x
}}
)
=
\prod
_{
j
=
1
}^
n
\funp
{
P
}
(
y
_
j |
\seq
{{
y
}}_{
<j
}
,
\seq
{{
x
}}
)
$
作为翻译模型。直接实现这个公式有一个明显的缺点:当句子过长时乘法运算容易产生溢出,也就是多个数相乘可能会产生浮点数无法表示的运算结果。为了解决这个问题,可以利用对数操作将乘法转换为加法,得到新的计算方式:
$
\textrm
{
log
}
\funp
{
P
}
(
\seq
{{
y
}}
|
\seq
{{
x
}}
)
=
\sum
_{
j
=
1
}^
n
\textrm
{
log
}
\funp
{
P
}
(
y
_
j |
\seq
{{
y
}}_{
<j
}
,
\seq
{{
x
}}
)
$
,对数函数不会改变函数的单调性,因此在具体实现时,通常用
$
\textrm
{
log
}
\funp
{
P
}
(
\seq
{{
y
}}
|
\seq
{{
x
}}
)
$
表示句子的得分,而不用
$
\funp
{
P
}
(
\seq
{{
y
}}
|
\seq
{
{
x
}}
)
$
。
\parinterval
不管是使用
$
\funp
{
P
}
(
\
vectorn
{
\emph
{
y
}}
|
\vectorn
{
\emph
{
x
}}
)
$
还是
$
\textrm
{
log
}
\funp
{
P
}
(
\vectorn
{
\emph
{
y
}}
|
\vectorn
{
\emph
{
x
}}
)
$
计算句子得分,还面临两个问题:
\parinterval
不管是使用
$
\funp
{
P
}
(
\
seq
{{
y
}}
|
\seq
{{
x
}}
)
$
还是
$
\textrm
{
log
}
\funp
{
P
}
(
\seq
{{
y
}}
|
\seq
{
{
x
}}
)
$
计算句子得分,还面临两个问题:
\begin{itemize}
\vspace
{
0.5em
}
\item
$
\funp
{
P
}
(
\
vectorn
{
\emph
{
y
}}
|
\vectorn
{
\emph
{
x
}}
)
$
的范围是[0,1],如果句子过长,那么句子的得分就是很多个小于1的数相乘,或者说取log之后很多个小于0的数相加。这也就是说,句子的得分会随着长度的增加而变小,即模型倾向于生成短句子。
\item
$
\funp
{
P
}
(
\
seq
{{
y
}}
|
\seq
{
{
x
}}
)
$
的范围是[0,1],如果句子过长,那么句子的得分就是很多个小于1的数相乘,或者说取log之后很多个小于0的数相加。这也就是说,句子的得分会随着长度的增加而变小,即模型倾向于生成短句子。
\vspace
{
0.5em
}
\item
模型本身并没有考虑每个源语言单词被使用的程度,比如一个单词可能会被翻译很多“次”。这个问题在统计机器翻译中并不存在,因为所有词在翻译中必须被“覆盖”到。但是早期的神经机器翻译模型没有所谓覆盖度的概念,因此也无法保证每个单词被翻译的“程度”是合理的
\upcite
{
li-etal-2018-simple,TuModeling
}
。
\vspace
{
0.5em
}
\end{itemize}
\parinterval
为了解决上面提到的问题,可以使用其他特征与
$
\textrm
{
log
}
\funp
{
P
}
(
\
vectorn
{
\emph
{
y
}}
|
\vectorn
{
\emph
{
x
}}
)
$
一起组成新的模型得分
$
\textrm
{
score
}
(
\vectorn
{
\emph
{
y
}}
,
\vectorn
{
\emph
{
x
}}
)
$
。针对模型倾向于生成短句子的问题,常用的做法是引入惩罚机制。比如,可以定义一个惩罚因子,形式如下:
\parinterval
为了解决上面提到的问题,可以使用其他特征与
$
\textrm
{
log
}
\funp
{
P
}
(
\
seq
{{
y
}}
|
\seq
{{
x
}}
)
$
一起组成新的模型得分
$
\textrm
{
score
}
(
\seq
{{
y
}}
,
\seq
{
{
x
}}
)
$
。针对模型倾向于生成短句子的问题,常用的做法是引入惩罚机制。比如,可以定义一个惩罚因子,形式如下:
\begin{eqnarray}
\textrm
{
lp
}
(
\
vectorn
{
\emph
{
y
}}
) =
\frac
{
(5+ |
\vectorn
{
\emph
{
y
}}
|)
^{
\alpha
}}
{
(5+1)
^{
\alpha
}}
\textrm
{
lp
}
(
\
seq
{{
y
}}
) =
\frac
{
(5+ |
\seq
{
{
y
}}
|)
^{
\alpha
}}
{
(5+1)
^{
\alpha
}}
\label
{
eq:10-39
}
\end{eqnarray}
\noindent
其中,
$
|
\
vectorn
{
\emph
{
y
}}
|
$
代表已经得到的译文长度,
$
\alpha
$
是一个固定的常数,用于控制惩罚的强度。同时在计算句子得分时,额外引入表示覆盖度的因子,如下:
\noindent
其中,
$
|
\
seq
{
{
y
}}
|
$
代表已经得到的译文长度,
$
\alpha
$
是一个固定的常数,用于控制惩罚的强度。同时在计算句子得分时,额外引入表示覆盖度的因子,如下:
\begin{eqnarray}
\textrm
{
cp
}
(
\
vectorn
{
\emph
{
y
}}
,
\vectorn
{
\emph
{
x
}}
) =
\beta
\cdot
\sum
_{
i=1
}^{
|
\vectorn
{
\emph
{
x
}}
|
}
\textrm
{
log
}
\big
(
\textrm
{
min
}
(
\sum
_
j
^{
|
\vectorn
{
\emph
{
y
}}
|
}
\alpha
_{
ij
}
,1 )
\big
)
\textrm
{
cp
}
(
\
seq
{{
y
}}
,
\seq
{{
x
}}
) =
\beta
\cdot
\sum
_{
i=1
}^{
|
\seq
{{
x
}}
|
}
\textrm
{
log
}
\big
(
\textrm
{
min
}
(
\sum
_
j
^{
|
\seq
{
{
y
}}
|
}
\alpha
_{
ij
}
,1 )
\big
)
\label
{
eq:10-40
}
\end{eqnarray}
\noindent
$
\textrm
{
cp
}
(
\cdot
)
$
会惩罚把某些源语言单词对应到很多目标语言单词的情况(覆盖度),被覆盖的程度用
$
\sum
_
j
^{
|
\
vectorn
{
\emph
{
y
}}
|
}
\alpha
_{
ij
}$
度量。
$
\beta
$
也是需要经验性设置的超参数,用于对覆盖度惩罚的强度进行控制。
\noindent
$
\textrm
{
cp
}
(
\cdot
)
$
会惩罚把某些源语言单词对应到很多目标语言单词的情况(覆盖度),被覆盖的程度用
$
\sum
_
j
^{
|
\
seq
{
{
y
}}
|
}
\alpha
_{
ij
}$
度量。
$
\beta
$
也是需要经验性设置的超参数,用于对覆盖度惩罚的强度进行控制。
\parinterval
最终,模型得分定义如下:
\begin{eqnarray}
\textrm
{
score
}
(
\
vectorn
{
\emph
{
y
}}
,
\vectorn
{
\emph
{
x
}}
) =
\frac
{
\textrm
{
log
}
\funp
{
P
}
(
\vectorn
{
\emph
{
y
}}
|
\vectorn
{
\emph
{
x
}}
)
}
{
\textrm
{
lp
}
(
\vectorn
{
\emph
{
y
}}
)
}
+
\textrm
{
cp
}
(
\vectorn
{
\emph
{
y
}}
,
\vectorn
{
\emph
{
x
}}
)
\textrm
{
score
}
(
\
seq
{{
y
}}
,
\seq
{{
x
}}
) =
\frac
{
\textrm
{
log
}
\funp
{
P
}
(
\seq
{{
y
}}
|
\seq
{{
x
}}
)
}
{
\textrm
{
lp
}
(
\seq
{{
y
}}
)
}
+
\textrm
{
cp
}
(
\seq
{{
y
}}
,
\seq
{
{
x
}}
)
\label
{
eq:10-41
}
\end{eqnarray}
\noindent
显然,当目标语言
$
\
vectorn
{
\emph
{
y
}}$
越短时,
$
\textrm
{
lp
}
(
\vectorn
{
\emph
{
y
}}
)
$
的值越小,因为
$
\textrm
{
log
}
\funp
{
P
}
(
\vectorn
{
\emph
{
y
}}
|
\vectorn
{
\emph
{
x
}}
)
$
是负数,所以句子得分
$
\textrm
{
score
}
(
\vectorn
{
\emph
{
y
}}
,
\vectorn
{
\emph
{
x
}}
)
$
越小。也就是说,模型会惩罚译文过短的结果。当覆盖度较高时,同样会使得分变低。通过这样的惩罚机制,使模型得分更为合理,从而帮助模型选择出质量更高的译文。
\noindent
显然,当目标语言
$
\
seq
{{
y
}}$
越短时,
$
\textrm
{
lp
}
(
\seq
{{
y
}}
)
$
的值越小,因为
$
\textrm
{
log
}
\funp
{
P
}
(
\seq
{{
y
}}
|
\seq
{{
x
}}
)
$
是负数,所以句子得分
$
\textrm
{
score
}
(
\seq
{{
y
}}
,
\seq
{
{
x
}}
)
$
越小。也就是说,模型会惩罚译文过短的结果。当覆盖度较高时,同样会使得分变低。通过这样的惩罚机制,使模型得分更为合理,从而帮助模型选择出质量更高的译文。
%----------------------------------------------------------------------------------------
% NEW SECTION
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论