Commit 76491b8b by zengxin

合并分支 'zengxin' 到 'caorunzhe'

Zengxin

查看合并请求 !512
parents de7a01e6 752ce135
......@@ -417,25 +417,25 @@ NMT & 21.7 & 18.7 & -13.7 \\
\parinterval {\chapternine}已经对循环神经网络的基本知识进行过介绍,这里再回顾一下。简单来说,循环神经网络由循环单元组成。对于序列中的任意时刻,都有一个循环单元与之对应,它会融合当前时刻的输入和上一时刻循环单元的输出,生成当前时刻的输出。这样每个时刻的信息都会被传递到下一时刻,这也间接达到了记录历史信息的目的。比如,对于序列$\seq{x}=\{x_1, x_2,..., x_m\}$,循环神经网络会按顺序输出一个序列$\seq{h}=\{ \mathbi{h}_1, \mathbi{h}_2,..., \mathbi{h}_m \}$,其中$\mathbi{h}_i$表示$i$时刻循环神经网络的输出(通常为一个向量)。
\parinterval\ref{fig:10-9}展示了一个循环神经网络处理序列问题的实例。当前时刻循环单元的输入由上一个时刻的输出和当前时刻的输入组成,因此也可以理解为,网络当前时刻计算得到的输出是由之前的序列共同决定的,即网络在不断地传递信息的过程中记忆了历史信息。以最后一个时刻的循环单元为例,它在对“开始”这个单词的信息进行处理时,参考了之前所有词(“<sos>\ \ 我们”)的信息。
\parinterval\ref{fig:10-8}展示了一个循环神经网络处理序列问题的实例。当前时刻循环单元的输入由上一个时刻的输出和当前时刻的输入组成,因此也可以理解为,网络当前时刻计算得到的输出是由之前的序列共同决定的,即网络在不断地传递信息的过程中记忆了历史信息。以最后一个时刻的循环单元为例,它在对“开始”这个单词的信息进行处理时,参考了之前所有词(“<sos>\ \ 我们”)的信息。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter10/Figures/figure-structure-of-a-recurrent-network-model}
\caption{循环神经网络处理序列的实例}
\label{fig:10-9}
\label{fig:10-8}
\end{figure}
%----------------------------------------------
\parinterval 在神经机器翻译里使用循环神经网络也很简单。只需要把源语言句子和目标语言句子分别看作两个序列,之后使用两个循环神经网络分别对其进行建模。这个过程如图\ref{fig:10-10}所示。图中,下半部分是编码器,上半部分是解码器。编码器利用循环神经网络对源语言序列逐词进行编码处理,同时利用循环单元的记忆能力,不断累积序列信息,遇到终止符<eos>后便得到了包含源语言句子全部信息的表示结果。解码器利用编码器的输出和起始符<sos>开始逐词地进行解码,即逐词翻译,每得到一个译文单词,便将其作为当前时刻解码端循环单元的输入,这也是一个典型的神经语言模型的序列生成过程。解码器通过循环神经网络不断地累积已经得到的译文的信息,并继续生成下一个单词,直到遇到结束符<eos>,便得到了最终完整的译文。
\parinterval 在神经机器翻译里使用循环神经网络也很简单。只需要把源语言句子和目标语言句子分别看作两个序列,之后使用两个循环神经网络分别对其进行建模。这个过程如图\ref{fig:10-9}所示。图中,下半部分是编码器,上半部分是解码器。编码器利用循环神经网络对源语言序列逐词进行编码处理,同时利用循环单元的记忆能力,不断累积序列信息,遇到终止符<eos>后便得到了包含源语言句子全部信息的表示结果。解码器利用编码器的输出和起始符<sos>开始逐词地进行解码,即逐词翻译,每得到一个译文单词,便将其作为当前时刻解码端循环单元的输入,这也是一个典型的神经语言模型的序列生成过程。解码器通过循环神经网络不断地累积已经得到的译文的信息,并继续生成下一个单词,直到遇到结束符<eos>,便得到了最终完整的译文。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter10/Figures/figure-model-structure-based-on-recurrent-neural-network-translation}
\caption{基于循环神经网络翻译的模型结构}
\label{fig:10-10}
\label{fig:10-9}
\end{figure}
%----------------------------------------------
......@@ -454,19 +454,19 @@ NMT & 21.7 & 18.7 & -13.7 \\
\vspace{-0.5em}
\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 | \seq{{y}}_{<j},\seq{{x}})$有三个关键问题(图\ref{fig:10-11}):
\parinterval 求解$\funp{P}(y_j | \seq{{y}}_{<j},\seq{{x}})$有三个关键问题(图\ref{fig:10-10}):
\vspace{-0.5em}
\begin{itemize}
\vspace{0.5em}
\item 如何对$\seq{{x}}$$\seq{{y}}_{<j }$进行分布式表示,即词嵌入。首先,将由One-hot向量表示的源语言单词,即由0和1构成的离散化向量表示,转化为实数向量。可以把这个过程记为$\textrm{e}_x (\cdot)$。类似地,可以把目标语言序列$\seq{{y}}_{<j }$中的每个单词用同样的方式进行表示,记为$\textrm{e}_y (\cdot)$
\vspace{0.5em}
\item 如何在词嵌入的基础上获取整个序列的表示,即句子的表示学习。可以把词嵌入的序列作为循环神经网络的输入,循环神经网络最后一个时刻的输出向量便是整个句子的表示结果。如图\ref{fig:10-11}中,编码器最后一个循环单元的输出$\mathbi{h}_m$被看作是一种包含了源语言句子信息的表示结果,记为$\mathbi{C}$
\item 如何在词嵌入的基础上获取整个序列的表示,即句子的表示学习。可以把词嵌入的序列作为循环神经网络的输入,循环神经网络最后一个时刻的输出向量便是整个句子的表示结果。如图\ref{fig:10-10}中,编码器最后一个循环单元的输出$\mathbi{h}_m$被看作是一种包含了源语言句子信息的表示结果,记为$\mathbi{C}$
\vspace{0.5em}
\item 如何得到每个目标语言单词的概率,即译文单词的{\small\sffamily\bfseries{生成}}\index{生成}(Generation)\index{Generation}。与神经语言模型一样,可以用一个Softmax输出层来获取当前时刻所有单词的分布,即利用Softmax 函数计算目标语言词表中每个单词的概率。令目标语言序列$j$时刻的循环神经网络的输出向量(或状态)为$\mathbi{s}_j$。根据循环神经网络的性质,$ y_j$ 的生成只依赖前一个状态$\mathbi{s}_{j-1}$和当前时刻的输入(即词嵌入$\textrm{e}_y (y_{j-1})$)。同时考虑源语言信息$\mathbi{C}$$\funp{P}(y_j | \seq{{y}}_{<j},\seq{{x}})$可以被重新定义为:
\begin{eqnarray}
\funp{P} (y_j | \seq{{y}}_{<j},\seq{{x}}) = \funp{P} ( {y_j | \mathbi{s}_{j-1} ,y_{j-1},\mathbi{C}} )
\label{eq:10-4}
\label{eq:10-3}
\end{eqnarray}
$\funp{P}({y_j | \mathbi{s}_{j-1} ,y_{j-1},\mathbi{C}})$由Softmax实现,Softmax的输入是循环神经网络$j$时刻的输出。在具体实现时,$\mathbi{C}$可以被简单地作为第一个时刻循环单元的输入,即,当$j=1$ 时,解码器的循环神经网络会读入编码器最后一个隐层状态$ \mathbi{h}_m$(也就是$\mathbi{C}$),而其他时刻的隐层状态不直接与$\mathbi{C}$相关。最终,$\funp{P} (y_j | \seq{{y}}_{<j},\seq{{x}})$ 被表示为:
\begin{eqnarray}
......@@ -475,7 +475,7 @@ $\funp{P}({y_j | \mathbi{s}_{j-1} ,y_{j-1},\mathbi{C}})$由Softmax实现,Softm
\funp{P} (y_j |\mathbi{C} ,y_{j-1}) &j=1 \\
\funp{P} (y_j|\mathbi{s}_{j-1},y_{j-1}) \quad &j>1
\end{array} \right .
\label{eq:10-5}
\label{eq:10-4}
\end{eqnarray}
\vspace{0.5em}
\end{itemize}
......@@ -485,16 +485,15 @@ $\funp{P}({y_j | \mathbi{s}_{j-1} ,y_{j-1},\mathbi{C}})$由Softmax实现,Softm
\centering
\input{./Chapter10/Figures/figure-3-base-problom-of-p}
\caption{求解$\funp{P} (y_j | \seq{{y}}_{<j},\seq{{x}})$的三个基本问题}
\label{fig:10-11}
\label{fig:10-10}
\end{figure}
%----------------------------------------------
\parinterval 输入层(词嵌入)和输出层(Softmax)的内容已在{\chapternine}进行了介绍,因此这里的核心内容是设计循环神经网络结构,即设计循环单元的结构。至今,研究人员已经提出了很多优秀的循环单元结构。其中循环神经网络(RNN)
是最原始的循环单元结构。在RNN中,对于序列$\seq{{x}}=\{ \mathbi{x}_1, \mathbi{x}_2,...,\mathbi{x}_m \}$,每个时刻$t$都对应一个循环单元,它的输出是一个向量$\mathbi{h}_t$,可以被描述为:
\begin{eqnarray}
\mathbi{h}_t=f(\mathbi{x}_t \mathbi{U}+\mathbi{h}_{t-1} \mathbi{W}+\mathbi{b})
\label{eq:10-11}
\label{eq:10-5}
\end{eqnarray}
\noindent 其中$\mathbi{x}_t$是当前时刻的输入,$\mathbi{h}_{t-1}$是上一时刻循环单元的输出,$f(\cdot)$是激活函数,$\mathbi{U}$$\mathbi{W}$是参数矩阵,$\mathbi{b}$是偏置。
......@@ -510,7 +509,7 @@ $\funp{P}({y_j | \mathbi{s}_{j-1} ,y_{j-1},\mathbi{C}})$由Softmax实现,Softm
\parinterval RNN结构使得当前时刻循环单元的状态包含了之前时间步的状态信息。但是这种对历史信息的记忆并不是无损的,随着序列变长,RNN的记忆信息的损失越来越严重。在很多长序列处理任务中(如长文本生成)都观测到了类似现象。对于这个问题,研究者们提出了{\small\bfnew{长短时记忆}}\index{长短时记忆}(Long Short-term Memory)\index{Long Short-term Memory,LSTM}模型,也就是常说的LSTM模型\upcite{HochreiterLong}
\parinterval LSTM模型是RNN模型的一种改进。相比RNN仅传递前一时刻的状态$\mathbi{h}_{t-1}$,LSTM会同时传递两部分信息:状态信息$\mathbi{h}_{t-1}$和记忆信息$\mathbi{c}_{t-1}$。这里,$\mathbi{c}_{t-1}$是新引入的变量,它也是循环单元的一部分,用于显性地记录需要记录的历史内容,$\mathbi{h}_{t-1}$$\mathbi{c}_{t-1}$在循环单元中会相互作用。LSTM通过“门”单元来动态地选择遗忘多少以前的信息和记忆多少当前的信息。LSTM中所使用的门单元结构如图\ref{fig:10-15}所示,包括遗忘门,输入门和输出门。图中$\sigma$代表Sigmoid函数,它将函数输入映射为0-1范围内的实数,用来充当门控信号。
\parinterval LSTM模型是RNN模型的一种改进。相比RNN仅传递前一时刻的状态$\mathbi{h}_{t-1}$,LSTM会同时传递两部分信息:状态信息$\mathbi{h}_{t-1}$和记忆信息$\mathbi{c}_{t-1}$。这里,$\mathbi{c}_{t-1}$是新引入的变量,它也是循环单元的一部分,用于显性地记录需要记录的历史内容,$\mathbi{h}_{t-1}$$\mathbi{c}_{t-1}$在循环单元中会相互作用。LSTM通过“门”单元来动态地选择遗忘多少以前的信息和记忆多少当前的信息。LSTM中所使用的门单元结构如图\ref{fig:10-11}所示,包括遗忘门,输入门和输出门。图中$\sigma$代表Sigmoid函数,它将函数输入映射为0-1范围内的实数,用来充当门控信号。
%----------------------------------------------
\begin{figure}[htp]
......@@ -520,7 +519,7 @@ $\funp{P}({y_j | \mathbi{s}_{j-1} ,y_{j-1},\mathbi{C}})$由Softmax实现,Softm
\subfigure[记忆更新]{\input{./Chapter10/Figures/figure-lstm03}}
\subfigure[输出门]{\input{./Chapter10/Figures/figure-lstm04}}
\caption{LSTM中的门控结构}
\label{fig:10-15}
\label{fig:10-11}
\end{figure}
%----------------------------------------------
......@@ -528,43 +527,43 @@ $\funp{P}({y_j | \mathbi{s}_{j-1} ,y_{j-1},\mathbi{C}})$由Softmax实现,Softm
\begin{itemize}
\vspace{0.5em}
\item {\small\sffamily\bfseries{遗忘}}\index{遗忘}。顾名思义,遗忘的目的是忘记一些历史,在LSTM中通过遗忘门实现,其结构如图\ref{fig:10-15}(a)所示。$\mathbi{x}_{t}$表示时刻$t$的输入向量,$\mathbi{h}_{t-1}$是时刻$t-1$的循环单元的输出,$\mathbi{x}_{t}$$\mathbi{h}_{t-1}$都作为$t$时刻循环单元的输入。$\sigma$将对$\mathbi{x}_{t}$$\mathbi{h}_{t-1}$进行筛选,以决定遗忘的信息,其计算公式如下:
\item {\small\sffamily\bfseries{遗忘}}\index{遗忘}。顾名思义,遗忘的目的是忘记一些历史,在LSTM中通过遗忘门实现,其结构如图\ref{fig:10-11}(a)所示。$\mathbi{x}_{t}$表示时刻$t$的输入向量,$\mathbi{h}_{t-1}$是时刻$t-1$的循环单元的输出,$\mathbi{x}_{t}$$\mathbi{h}_{t-1}$都作为$t$时刻循环单元的输入。$\sigma$将对$\mathbi{x}_{t}$$\mathbi{h}_{t-1}$进行筛选,以决定遗忘的信息,其计算公式如下:
\begin{eqnarray}
\mathbi{f}_t=\sigma(\mathbi{W}_f [\mathbi{h}_{t-1},\mathbi{x}_{t}] + \mathbi{b}_f )
\label{eq:10-12}
\label{eq:10-6}
\end{eqnarray}
这里,$\mathbi{W}_f$是权值,$\mathbi{b}_f$是偏置,$[\mathbi{h}_{t-1},\mathbi{x}_{t}]$表示两个向量的拼接。该公式可以解释为,对$[\mathbi{h}_{t-1},\mathbi{x}_{t}]$进行变换,并得到一个实数向量$\mathbi{f}_t$$\mathbi{f}_t$的每一维都可以被理解为一个“门”,它决定可以有多少信息被留下(或遗忘)。
\vspace{0.5em}
\item {\small\sffamily\bfseries{记忆更新}}\index{记忆更新}。首先,要生成当前时刻需要新增加的信息,该部分由输入门完成,其结构如图\ref{fig:10-15}(b)红色线部分,图中“$\bigotimes$”表示进行点乘操作。输入门的计算分为两部分,首先利用$\sigma$决定门控参数$\mathbi{i}_t$,然后通过Tanh函数得到新的信息$\hat{\mathbi{c}}_t$,具体公式如下:
\item {\small\sffamily\bfseries{记忆更新}}\index{记忆更新}。首先,要生成当前时刻需要新增加的信息,该部分由输入门完成,其结构如图\ref{fig:10-11}(b)红色线部分,图中“$\bigotimes$”表示进行点乘操作。输入门的计算分为两部分,首先利用$\sigma$决定门控参数$\mathbi{i}_t$,然后通过Tanh函数得到新的信息$\hat{\mathbi{c}}_t$,具体公式如下:
\begin{eqnarray}
\mathbi{i}_t & = & \sigma (\mathbi{W}_i [\mathbi{h}_{t-1},\mathbi{x}_{t}] + \mathbi{b}_i ) \label{eq:10-13} \\
\hat{\mathbi{c}}_t & = & \textrm{Tanh} (\mathbi{W}_c [\mathbi{h}_{t-1},\mathbi{x}_{t}] + \mathbi{b}_c ) \label{eq:10-14}
\mathbi{i}_t & = & \sigma (\mathbi{W}_i [\mathbi{h}_{t-1},\mathbi{x}_{t}] + \mathbi{b}_i ) \label{eq:10-7} \\
\hat{\mathbi{c}}_t & = & \textrm{Tanh} (\mathbi{W}_c [\mathbi{h}_{t-1},\mathbi{x}_{t}] + \mathbi{b}_c ) \label{eq:10-8}
\end{eqnarray}
之后,用$\mathbi{i}_t$点乘$\hat{\mathbi{c}}_t$,得到当前需要记忆的信息,记为$\mathbi{i}_t \cdot \hat{\mathbi{c}}_t$。接下来需要更新旧的信息$\mathbi{c}_{t-1}$,得到新的记忆信息$\mathbi{c}_t$,更新的操作如图\ref{fig:10-15}(c)红色线部分所示,“$\bigoplus$”表示相加。具体规则是通过遗忘门选择忘记一部分上文信息$\mathbi{f}_t$,通过输入门计算新增的信息$\mathbi{i}_t \cdot \hat{\mathbi{c}}_t$,然后根据“$\bigotimes$”门与“$\bigoplus$”门进行相应的乘法和加法计算:
之后,用$\mathbi{i}_t$点乘$\hat{\mathbi{c}}_t$,得到当前需要记忆的信息,记为$\mathbi{i}_t \cdot \hat{\mathbi{c}}_t$。接下来需要更新旧的信息$\mathbi{c}_{t-1}$,得到新的记忆信息$\mathbi{c}_t$,更新的操作如图\ref{fig:10-11}(c)红色线部分所示,“$\bigoplus$”表示相加。具体规则是通过遗忘门选择忘记一部分上文信息$\mathbi{f}_t$,通过输入门计算新增的信息$\mathbi{i}_t \cdot \hat{\mathbi{c}}_t$,然后根据“$\bigotimes$”门与“$\bigoplus$”门进行相应的乘法和加法计算:
\begin{eqnarray}
\mathbi{c}_t = \mathbi{f}_t \cdot \mathbi{c}_{t-1} + \mathbi{i}_t \cdot \hat{\mathbi{c}_t}
\label{eq:10-15}
\label{eq:10-9}
\end{eqnarray}
\vspace{-1.0em}
\item {\small\sffamily\bfseries{输出}}\index{输出}。该部分使用输出门计算最终的输出信息$\mathbi{h}_t$,其结构如图\ref{fig:10-15}(d)红色线部分所示。在输出门中,首先将$\mathbi{x}_t$$\mathbi{h}_{t-1}$通过$\sigma$函数变换得到$\mathbi{o}_t$。其次,将上一步得到的新记忆信息$\mathbi{c}_t$通过Tanh函数进行变换,得到值在[-1,1]范围的向量。最后将这两部分进行点乘,具体公式如下:
\item {\small\sffamily\bfseries{输出}}\index{输出}。该部分使用输出门计算最终的输出信息$\mathbi{h}_t$,其结构如图\ref{fig:10-11}(d)红色线部分所示。在输出门中,首先将$\mathbi{x}_t$$\mathbi{h}_{t-1}$通过$\sigma$函数变换得到$\mathbi{o}_t$。其次,将上一步得到的新记忆信息$\mathbi{c}_t$通过Tanh函数进行变换,得到值在[-1,1]范围的向量。最后将这两部分进行点乘,具体公式如下:
\begin{eqnarray}
\mathbi{o}_t & = & \sigma (\mathbi{W}_o [\mathbi{h}_{t-1},\mathbi{x}_{t}] + \mathbi{b}_o ) \label{eq:10-16} \\
\mathbi{h}_t & = & \mathbi{o}_t \cdot \textrm{Tanh} (\mathbi{c}_t) \label{eq:6-17}
\mathbi{o}_t & = & \sigma (\mathbi{W}_o [\mathbi{h}_{t-1},\mathbi{x}_{t}] + \mathbi{b}_o ) \label{eq:10-10} \\
\mathbi{h}_t & = & \mathbi{o}_t \cdot \textrm{Tanh} (\mathbi{c}_t) \label{eq:10-11}
\end{eqnarray}
\vspace{0.5em}
\end{itemize}
\parinterval LSTM的完整结构如图\ref{fig:10-16}所示,模型的参数包括:参数矩阵$\mathbi{W}_f$$\mathbi{W}_i$$\mathbi{W}_c$\\$\mathbi{W}_o$和偏置$\mathbi{b}_f$$\mathbi{b}_i$$\mathbi{b}_c$$\mathbi{b}_o$。可以看出,$\mathbi{h}_t$是由$\mathbi{c}_{t-1}$$\mathbi{h}_{t-1}$$\mathbi{x}_t$共同决定的。此外,上述公式中激活函数的选择是根据函数各自的特点决定的。
\parinterval LSTM的完整结构如图\ref{fig:10-12}所示,模型的参数包括:参数矩阵$\mathbi{W}_f$$\mathbi{W}_i$$\mathbi{W}_c$\\$\mathbi{W}_o$和偏置$\mathbi{b}_f$$\mathbi{b}_i$$\mathbi{b}_c$$\mathbi{b}_o$。可以看出,$\mathbi{h}_t$是由$\mathbi{c}_{t-1}$$\mathbi{h}_{t-1}$$\mathbi{x}_t$共同决定的。此外,上述公式中激活函数的选择是根据函数各自的特点决定的。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter10/Figures/figure-the-whole-of-lstm}
\caption{LSTM的整体结构}
\label{fig:10-16}
\label{fig:10-12}
\end{figure}
%----------------------------------------------
......@@ -583,26 +582,26 @@ $\funp{P}({y_j | \mathbi{s}_{j-1} ,y_{j-1},\mathbi{C}})$由Softmax实现,Softm
\subfigure[更新门]{\input{./Chapter10/Figures/figure-gru02}}
\subfigure[隐藏状态更新]{\input{./Chapter10/Figures/figure-gru03}}
\caption{GRU中的门控结构}
\label{fig:10-17}
\label{fig:10-13}
\end{figure}
%----------------------------------------------
\parinterval GRU的输入和RNN是一样的,由输入$\mathbi{x}_t$$t-1$时刻的状态$\mathbi{h}_{t-1}$组成。GRU只有两个门信号,分别是重置门和更新门。重置门$\mathbi{r}_t$用来控制前一时刻隐藏状态的记忆程度,其结构如图\ref{fig:10-17}(a)。更新门用来更新记忆,使用一个门同时完成遗忘和记忆两种操作,其结构如图\ref{fig:10-17}(b)。重置门和更新门的计算公式如下:
\parinterval GRU的输入和RNN是一样的,由输入$\mathbi{x}_t$$t-1$时刻的状态$\mathbi{h}_{t-1}$组成。GRU只有两个门信号,分别是重置门和更新门。重置门$\mathbi{r}_t$用来控制前一时刻隐藏状态的记忆程度,其结构如图\ref{fig:10-13}(a)。更新门用来更新记忆,使用一个门同时完成遗忘和记忆两种操作,其结构如图\ref{fig:10-13}(b)。重置门和更新门的计算公式如下:
\begin{eqnarray}
\mathbi{r}_t & = &\sigma (\mathbi{W}_r [\mathbi{h}_{t-1},\mathbi{x}_{t}] ) \label{eq:10-18} \\
\mathbi{u}_t & = & \sigma (\mathbi{W}_u [\mathbi{h}_{t-1},\mathbi{x}_{t}]) \label{eq:10-19}
\mathbi{r}_t & = &\sigma (\mathbi{W}_r [\mathbi{h}_{t-1},\mathbi{x}_{t}] ) \label{eq:10-12} \\
\mathbi{u}_t & = & \sigma (\mathbi{W}_u [\mathbi{h}_{t-1},\mathbi{x}_{t}]) \label{eq:10-13}
\end{eqnarray}
\parinterval 当完成了重置门和更新门计算后,就需要更新当前隐藏状态,如图\ref{fig:10-17}(c)所示。在计算得到了重置门的权重$\mathbi{r}_t$后,使用其对前一时刻的状态$\mathbi{h}_{t-1}$进行重置($\mathbi{r}_t \cdot \mathbi{h}_{t-1}$),将重置后的结果与$\mathbi{x}_t$拼接,通过Tanh激活函数将数据变换到[-1,1]范围内:
\parinterval 当完成了重置门和更新门计算后,就需要更新当前隐藏状态,如图\ref{fig:10-13}(c)所示。在计算得到了重置门的权重$\mathbi{r}_t$后,使用其对前一时刻的状态$\mathbi{h}_{t-1}$进行重置($\mathbi{r}_t \cdot \mathbi{h}_{t-1}$),将重置后的结果与$\mathbi{x}_t$拼接,通过Tanh激活函数将数据变换到[-1,1]范围内:
\begin{eqnarray}
\hat{\mathbi{h}}_t = \textrm{Tanh} (\mathbi{W}_h [\mathbi{r}_t \cdot \mathbi{h}_{t-1},\mathbi{x}_{t}])
\label{eq:10-20}
\label{eq:10-14}
\end{eqnarray}
\parinterval $\hat{\mathbi{h}}_t$在包含了输入信息$\mathbi{x}_t$的同时,引入了$\mathbi{h}_{t-1}$的信息,可以理解为,记忆了当前时刻的状态。下一步是计算更新后的隐藏状态也就是更新记忆,如下所示:
\begin{eqnarray}
\mathbi{h}_t = (1-\mathbi{u}_t) \cdot \mathbi{h}_{t-1} +\mathbi{u}_t \cdot \hat{\mathbi{h}}_t
\label{eq:10-21}
\label{eq:10-15}
\end{eqnarray}
\noindent 这里,$\mathbi{u}_t$是更新门中得到的权重,将$\mathbi{u}_t$作用于$\hat{\mathbi{h}}_t$表示对当前时刻的状态进行“遗忘”,舍弃一些不重要的信息,将$(1-\mathbi{u}_t)$作用于$\mathbi{h}_{t-1}$,用于对上一时刻隐藏状态进行选择性记忆。
......@@ -615,14 +614,14 @@ $\funp{P}({y_j | \mathbi{s}_{j-1} ,y_{j-1},\mathbi{C}})$由Softmax实现,Softm
\subsection{双向模型}
\parinterval 前面提到的循环神经网络都是自左向右运行的,也就是说在处理一个单词的时候只能访问它前面的序列信息。但是,只根据句子的前文来生成一个序列的表示是不全面的,因为从最后一个词来看,第一个词的信息可能已经很微弱了。为了同时考虑前文和后文的信息,一种解决办法是使用双向循环网络,其结构如图\ref{fig:10-18}所示。这里,编码器可以看作有两个循环神经网络,第一个网络,即红色虚线框里的网络,从句子的右边进行处理,第二个网络从句子左边开始处理,最终将正向和反向得到的结果都融合后传递给解码器。
\parinterval 前面提到的循环神经网络都是自左向右运行的,也就是说在处理一个单词的时候只能访问它前面的序列信息。但是,只根据句子的前文来生成一个序列的表示是不全面的,因为从最后一个词来看,第一个词的信息可能已经很微弱了。为了同时考虑前文和后文的信息,一种解决办法是使用双向循环网络,其结构如图\ref{fig:10-14}所示。这里,编码器可以看作有两个循环神经网络,第一个网络,即红色虚线框里的网络,从句子的右边进行处理,第二个网络从句子左边开始处理,最终将正向和反向得到的结果都融合后传递给解码器。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter10/Figures/figure-bi-rnn}
\caption{基于双向循环神经网络的机器翻译模型结构}
\label{fig:10-18}
\label{fig:10-14}
\end{figure}
%----------------------------------------------
......@@ -636,14 +635,14 @@ $\funp{P}({y_j | \mathbi{s}_{j-1} ,y_{j-1},\mathbi{C}})$由Softmax实现,Softm
\parinterval 实际上,对于单词序列所使用的循环神经网络是一种很“深”的网络,因为从第一个单词到最后一个单词需要经过至少句子长度相当层数的神经元。比如,一个包含几十个词的句子也会对应几十个神经元层。但是,在很多深度学习应用中,更习惯把对输入序列的同一种处理作为“一层”。比如,对于输入序列,构建一个RNN,那么这些循环单元就构成了网络的“一层”。当然,这里并不是要混淆概念。只是要明确,在随后的讨论中,“层”并不是指一组神经元的全连接,它一般指的是网络结构中逻辑上的一层。
\parinterval 单层循环神经网络对输入序列进行了抽象,为了得到更深入的抽象能力,可以把多个循环神经网络叠在一起,构成多层循环神经网络。比如,图\ref{fig:10-19}就展示了基于两层循环神经网络的解码器和编码器结构。通常来说,层数越多模型的表示能力越强,因此在很多基于循环神经网络的机器翻译系统中一般会使用4$\sim$8层的网络。但是,过多的层也会增加模型训练的难度,甚至导致模型无法进行训练。{\chapterthirteen}还会对这个问题进行深入讨论。
\parinterval 单层循环神经网络对输入序列进行了抽象,为了得到更深入的抽象能力,可以把多个循环神经网络叠在一起,构成多层循环神经网络。比如,图\ref{fig:10-15}就展示了基于两层循环神经网络的解码器和编码器结构。通常来说,层数越多模型的表示能力越强,因此在很多基于循环神经网络的机器翻译系统中一般会使用4$\sim$8层的网络。但是,过多的层也会增加模型训练的难度,甚至导致模型无法进行训练。{\chapterthirteen}还会对这个问题进行深入讨论。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter10/Figures/figure-double-layer-rnn} \hspace{10em}
\caption{基于双层循环神经网络的机器翻译模型结构}
\label{fig:10-19}
\label{fig:10-15}
\end{figure}
%----------------------------------------------
......@@ -662,14 +661,14 @@ $\funp{P}({y_j | \mathbi{s}_{j-1} ,y_{j-1},\mathbi{C}})$由Softmax实现,Softm
\noindent 之所以能想到在横线处填“吃饭”、“吃东西”很有可能是因为看到了“没/吃饭”、 “很/饿”等关键信息。也就是这些关键的片段对预测缺失的单词起着关键性作用。而预测“吃饭”与前文中的“ 中午”、“又”之间的联系似乎不那么紧密。也就是说,在形成 “吃饭”的逻辑时,在潜意识里会更注意“没/吃饭”、“很/饿”等关键信息。也就是我们的关注度并不是均匀地分布在整个句子上的。
\parinterval 这个现象可以用注意力机制进行解释。注意力机制的概念来源于生物学的一些现象:当待接收的信息过多时,人类会选择性地关注部分信息而忽略其他信息。它在人类的视觉、听觉、嗅觉等方面均有体现,当我们在感受事物时,大脑会自动过滤或衰减部分信息,仅关注其中少数几个部分。例如,当看到图\ref{fig:10-20}时,往往不是“均匀地”看图像中的所有区域,可能最先注意到的是小狗的嘴,然后才会关注图片中其他的部分。那注意力机制是如何解决神经机器翻译的问题呢?下面就一起来看一看。
\parinterval 这个现象可以用注意力机制进行解释。注意力机制的概念来源于生物学的一些现象:当待接收的信息过多时,人类会选择性地关注部分信息而忽略其他信息。它在人类的视觉、听觉、嗅觉等方面均有体现,当我们在感受事物时,大脑会自动过滤或衰减部分信息,仅关注其中少数几个部分。例如,当看到图\ref{fig:10-16}时,往往不是“均匀地”看图像中的所有区域,可能最先注意到的是小狗的嘴,然后才会关注图片中其他的部分。那注意力机制是如何解决神经机器翻译的问题呢?下面就一起来看一看。
%----------------------------------------------
\begin{figure}[htp]
\centering
\includegraphics[scale=0.05]{./Chapter10/Figures/dog-hat-new.jpg}
\caption{戴帽子的狗}
\label{fig:10-20}
\label{fig:10-16}
\end{figure}
%----------------------------------------------
......@@ -688,27 +687,27 @@ $\funp{P}({y_j | \mathbi{s}_{j-1} ,y_{j-1},\mathbi{C}})$由Softmax实现,Softm
\vspace{0.5em}
\end{itemize}
\parinterval 更直观的,如图\ref{fig:10-21},目标语言中的“very long”仅依赖于源语言中的“很长”。这时如果将所有源语言编码成一个固定的实数向量,“很长”的信息就很可能被其他词的信息淹没掉。
\parinterval 更直观的,如图\ref{fig:10-17},目标语言中的“very long”仅依赖于源语言中的“很长”。这时如果将所有源语言编码成一个固定的实数向量,“很长”的信息就很可能被其他词的信息淹没掉。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter10/Figures/figure-attention-of-source-and-target-words}
\caption{源语言词和目标语言词的关注度}
\label{fig:10-21}
\label{fig:10-17}
\end{figure}
%----------------------------------------------
\parinterval 显然,以上问题的根本原因在于所使用的表示模型还比较“弱”。因此需要一个更强大的表示模型,在生成目标语言单词时能够有选择地获取源语言句子中更有用的部分。更准确的说,对于要生成的目标语单词,相关性更高的源语言片段应该在源语言句子的表示中体现出来,而不是将所有的源语言单词一视同仁。在神经机器翻译中引入注意力机制正是为了达到这个目的\upcite{bahdanau2014neural,DBLP:journals/corr/LuongPM15}。实际上,除了机器翻译,注意力机制也被成功地应用于图像处理、语音识别、自然语言处理等其他任务。也正是注意力机制的引入,使得包括机器翻译在内很多自然语言处理系统得到了飞跃发展。
\parinterval 神经机器翻译中的注意力机制并不复杂。对于每个目标语言单词$y_j$,系统生成一个源语言表示向量$\mathbi{C}_j$与之对应,$\mathbi{C}_j$会包含生成$y_j$所需的源语言的信息,或者说$\mathbi{C}_j$是一种包含目标语言单词与源语言单词对应关系的源语言表示。相比用一个静态的表示$\mathbi{C}$,注意机制使用的是动态的表示$\mathbi{C}_j$$\mathbi{C}_j$也被称作对于目标语言位置$j${\small\bfnew{上下文向量}}\index{上下文向量}(Context Vector\index{Context Vector})。图\ref{fig:10-22}对比了未引入注意力机制和引入了注意力机制的编码器- 解码器结构。可以看出,在注意力模型中,对于每一个目标单词的生成,都会额外引入一个单独的上下文向量参与运算。
\parinterval 神经机器翻译中的注意力机制并不复杂。对于每个目标语言单词$y_j$,系统生成一个源语言表示向量$\mathbi{C}_j$与之对应,$\mathbi{C}_j$会包含生成$y_j$所需的源语言的信息,或者说$\mathbi{C}_j$是一种包含目标语言单词与源语言单词对应关系的源语言表示。相比用一个静态的表示$\mathbi{C}$,注意机制使用的是动态的表示$\mathbi{C}_j$$\mathbi{C}_j$也被称作对于目标语言位置$j${\small\bfnew{上下文向量}}\index{上下文向量}(Context Vector\index{Context Vector})。图\ref{fig:10-18}对比了未引入注意力机制和引入了注意力机制的编码器- 解码器结构。可以看出,在注意力模型中,对于每一个目标单词的生成,都会额外引入一个单独的上下文向量参与运算。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter10/Figures/figure-encoder-decoder-with-attention}
\caption{不使用(a)和使用(b)注意力机制的翻译模型对比}
\label{fig:10-22}
\label{fig:10-18}
\end{figure}
%----------------------------------------------
......@@ -723,28 +722,28 @@ $\funp{P}({y_j | \mathbi{s}_{j-1} ,y_{j-1},\mathbi{C}})$由Softmax实现,Softm
\parinterval 根据这种思想,上下文向量$\mathbi{C}_j$被定义为对不同时间步编码器输出的状态序列$\{ \mathbi{h}_1, \mathbi{h}_2,...,\mathbi{h}_m \}$进行加权求和,如下:
\begin{eqnarray}
\mathbi{C}_j=\sum_{i} \alpha_{i,j} \mathbi{h}_i
\label{eq:10-22}
\label{eq:10-16}
\end{eqnarray}
\noindent 其中,$\alpha_{i,j}${\small\sffamily\bfseries{注意力权重}}\index{注意力权重}(Attention Weight)\index{Attention Weight},它表示目标语言第$j$个位置与源语言第$i$个位置之间的相关性大小。这里,将每个时间步编码器的输出$\mathbi{h}_i$ 看作源语言位置$i$的表示结果。进行翻译时,解码端可以根据当前的位置$j$,通过控制不同$\mathbi{h}_i$的权重得到$\mathbi{C}_j$,使得对目标语言位置$j$贡献大的$\mathbi{h}_i$$\mathbi{C}_j$的影响增大。也就是说,$\mathbi{C}_j$实际上就是\{${\mathbi{h}_1, \mathbi{h}_2,...,\mathbi{h}_m}$\}的一种组合,只不过不同的$\mathbi{h}_i$会根据对目标端的贡献给予不同的权重。图\ref{fig:10-23}展示了上下文向量$\mathbi{C}_j$的计算过程。
\noindent 其中,$\alpha_{i,j}${\small\sffamily\bfseries{注意力权重}}\index{注意力权重}(Attention Weight)\index{Attention Weight},它表示目标语言第$j$个位置与源语言第$i$个位置之间的相关性大小。这里,将每个时间步编码器的输出$\mathbi{h}_i$ 看作源语言位置$i$的表示结果。进行翻译时,解码端可以根据当前的位置$j$,通过控制不同$\mathbi{h}_i$的权重得到$\mathbi{C}_j$,使得对目标语言位置$j$贡献大的$\mathbi{h}_i$$\mathbi{C}_j$的影响增大。也就是说,$\mathbi{C}_j$实际上就是\{${\mathbi{h}_1, \mathbi{h}_2,...,\mathbi{h}_m}$\}的一种组合,只不过不同的$\mathbi{h}_i$会根据对目标端的贡献给予不同的权重。图\ref{fig:10-19}展示了上下文向量$\mathbi{C}_j$的计算过程。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter10/Figures/figure-calculation-process-of-context-vector-c}
\caption{上下文向量$\mathbi{C}_j$的计算过程}
\label{fig:10-23}
\label{fig:10-19}
\end{figure}
%----------------------------------------------
\parinterval 如图\ref{fig:10-23}所示,注意力权重$\alpha_{i,j}$的计算分为两步:
\parinterval 如图\ref{fig:10-19}所示,注意力权重$\alpha_{i,j}$的计算分为两步:
\begin{itemize}
\vspace{0.5em}
\item 使用目标语言上一时刻循环单元的输出$\mathbi{s}_{j-1}$与源语言第$i$个位置的表示$\mathbi{h}_i$之间的相关性,其用来表示目标语言位置$j$对源语言位置$i$的关注程度,记为$\beta_{i,j}$,由函数$a(\cdot)$实现:
\begin{eqnarray}
\beta_{i,j} = a(\mathbi{s}_{j-1},\mathbi{h}_i)
\label{eq:10-23}
\label{eq:10-17}
\end{eqnarray}
$a(\cdot)$可以被看作是目标语言表示和源语言表示的一种“统一化”,即把源语言和目标语言表示映射在同一个语义空间,进而语义相近的内容有更大的相似性。该函数有多种计算方式,比如,向量乘、向量夹角和单层神经网络等,数学表达如下:
......@@ -756,7 +755,7 @@ a (\mathbi{s},\mathbi{h}) = \left\{ \begin{array}{ll}
\textrm{Tanh}(\mathbi{W}[\mathbi{s},\mathbi{h}])\mathbi{v}^{\textrm{T}} & \textrm{拼接}[\mathbi{s},\mathbi{h}]+\textrm{单层网络}
\end{array}
\right.
\label{eq:10-24}
\label{eq:10-18}
\end{eqnarray}
其中$\mathbi{W}$$\mathbi{v}$是可学习的参数。
......@@ -765,39 +764,39 @@ a (\mathbi{s},\mathbi{h}) = \left\{ \begin{array}{ll}
\vspace{0.5em}
\begin{eqnarray}
\alpha_{i,j}=\frac{\textrm{exp}(\beta_{i,j})} {\sum_{i'} \textrm{exp}(\beta_{i',j})}
\label{eq:10-25}
\label{eq:10-19}
\end{eqnarray}
\vspace{0.5em}
最终,\{$\alpha_{i,j}$\}可以被看作是一个矩阵,它的长为目标语言句子长度,宽为源语言句子长度,矩阵中的每一项对应一个$\alpha_{i,j}$。图\ref{fig:10-24}给出了\{$\alpha_{i,j}$\}的一个矩阵表示。图中蓝色方框的大小表示不同的注意力权重$\alpha_{i,j}$的大小,方框越大,源语言位置$i$和目标语言位置$j$的相关性越高。能够看到,对于互译的中英文句子,\{$\alpha_{i,j}$\}可以较好的反应两种语言之间不同位置的对应关系。
最终,\{$\alpha_{i,j}$\}可以被看作是一个矩阵,它的长为目标语言句子长度,宽为源语言句子长度,矩阵中的每一项对应一个$\alpha_{i,j}$。图\ref{fig:10-20}给出了\{$\alpha_{i,j}$\}的一个矩阵表示。图中蓝色方框的大小表示不同的注意力权重$\alpha_{i,j}$的大小,方框越大,源语言位置$i$和目标语言位置$j$的相关性越高。能够看到,对于互译的中英文句子,\{$\alpha_{i,j}$\}可以较好的反应两种语言之间不同位置的对应关系。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter10/Figures/figure-matrix-representation-of-attention-weights-between-chinese-english-sentence-pairs}
\caption{一个汉英句对之间的注意力权重{$\alpha_{i,j}$}的矩阵表示}
\label{fig:10-24}
\label{fig:10-20}
\end{figure}
%----------------------------------------------
\vspace{0.5em}
\end{itemize}
\parinterval\ref{fig:10-25}展示了一个上下文向量的计算过程实例。首先,计算目标语言第一个单词“Have”与源语言中的所有单词的相关性,即注意力权重,对应图中第一列$\alpha_{i,1}$,则当前时刻所使用的上下文向量$\mathbi{C}_1 = \sum_{i=1}^8 \alpha_{i,1} \mathbi{h}_i$;然后,计算第二个单词“you”的注意力权重对应第二列$\alpha_{i,2}$,其上下文向量$\mathbi{C}_2 = \sum_{i=1}^8 \alpha_{i,2} \mathbi{h}_i$,以此类推,可以得到任意目标语言位置$j$的上下文向量$\mathbi{C}_j$。很容易看出,不同目标语言单词的上下文向量对应的源语言词的权重$\alpha_{i,j}$是不同的,不同的注意力权重为不同位置赋予了不同的重要性。
\parinterval\ref{fig:10-21}展示了一个上下文向量的计算过程实例。首先,计算目标语言第一个单词“Have”与源语言中的所有单词的相关性,即注意力权重,对应图中第一列$\alpha_{i,1}$,则当前时刻所使用的上下文向量$\mathbi{C}_1 = \sum_{i=1}^8 \alpha_{i,1} \mathbi{h}_i$;然后,计算第二个单词“you”的注意力权重对应第二列$\alpha_{i,2}$,其上下文向量$\mathbi{C}_2 = \sum_{i=1}^8 \alpha_{i,2} \mathbi{h}_i$,以此类推,可以得到任意目标语言位置$j$的上下文向量$\mathbi{C}_j$。很容易看出,不同目标语言单词的上下文向量对应的源语言词的权重$\alpha_{i,j}$是不同的,不同的注意力权重为不同位置赋予了不同的重要性。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter10/Figures/figure-example-of-context-vector-calculation-process}
\caption{上下文向量计算过程实例}
\label{fig:10-25}
\label{fig:10-21}
\end{figure}
%----------------------------------------------
\parinterval\ref{sec:10.3.1}节中,公式\eqref{eq:10-5}描述了目标语言单词生成概率$ \funp{P} (y_j | \mathbi{y}_{<j},\mathbi{x})$。在引入注意力机制后,不同时刻的上下文向量$\mathbi{C}_j$替换了传统模型中固定的句子表示$\mathbi{C}$。描述如下:
\parinterval\ref{sec:10.3.1}节中,公式\eqref{eq:10-4}描述了目标语言单词生成概率$ \funp{P} (y_j | \mathbi{y}_{<j},\mathbi{x})$。在引入注意力机制后,不同时刻的上下文向量$\mathbi{C}_j$替换了传统模型中固定的句子表示$\mathbi{C}$。描述如下:
\begin{eqnarray}
\funp{P} (y_j | \mathbi{y}_{<j},\mathbi{x}) \equiv \funp{P} (y_j | \mathbi{s}_{j-1},y_{j-1},\mathbi{C}_j )
\label{eq:10-26}
\label{eq:10-20}
\end{eqnarray}
\parinterval 这样,可以在生成每个$y_j$时动态的使用不同的源语言表示$\mathbi{C}_j$,并更准确地捕捉源语言和目标语言不同位置之间的相关性。表\ref{tab:10-7}展示了引入注意力机制前后译文单词生成公式的对比。
......@@ -827,47 +826,47 @@ a (\mathbi{s},\mathbi{h}) = \left\{ \begin{array}{ll}
\parinterval 那么,如何理解这个过程?注意力机制的本质又是什么呢?换一个角度来看,实际上,目标语言位置$j$可以被看作是一个查询,我们希望从源语言端找到与之最匹配的源语言位置,并返回相应的表示结果。为了描述这个问题,可以建立一个查询系统。假设有一个库,里面包含若干个$\mathrm{key}$-$\mathrm{value}$单元,其中$\mathrm{key}$代表这个单元的索引关键字,$\mathrm{value}$代表这个单元的值。比如,对于学生信息系统,$\mathrm{key}$可以是学号,$\mathrm{value}$可以是学生的身高。当输入一个查询$\mathrm{query}$,我们希望这个系统返回与之最匹配的结果。也就是,希望找到匹配的$\mathrm{key}$,并输出其对应的$\mathrm{value}$。比如,当查询某个学生的身高信息时,可以输入学生的学号,之后在库中查询与这个学号相匹配的记录,并把这个记录中的$\mathrm{value}$(即身高)作为结果返回。
\parinterval\ref{fig:10-26}展示了一个这样的查询系统。里面包含四个$\mathrm{key}$-$\mathrm{value}$单元,当输入查询$\mathrm{query}$,就把$\mathrm{query}$与这四个$\mathrm{key}$逐个进行匹配,如果完全匹配就返回相应的$\mathrm{value}$。在图中的例子中,$\mathrm{query}$$\mathrm{key}_3$是完全匹配的(因为都是横纹),因此系统返回第三个单元的值,即$\mathrm{value}_3$。当然,如果库中没有与$\mathrm{query}$匹配的$\mathrm{key}$,则返回一个空结果。
\parinterval\ref{fig:10-22}展示了一个这样的查询系统。里面包含四个$\mathrm{key}$-$\mathrm{value}$单元,当输入查询$\mathrm{query}$,就把$\mathrm{query}$与这四个$\mathrm{key}$逐个进行匹配,如果完全匹配就返回相应的$\mathrm{value}$。在图中的例子中,$\mathrm{query}$$\mathrm{key}_3$是完全匹配的(因为都是横纹),因此系统返回第三个单元的值,即$\mathrm{value}_3$。当然,如果库中没有与$\mathrm{query}$匹配的$\mathrm{key}$,则返回一个空结果。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter10/Figures/figure-query-model-corresponding-to-traditional-query-model-vs-attention-mechanism}
\caption{传统查询模型}
\label{fig:10-26}
\label{fig:10-22}
\end{figure}
%----------------------------------------------
\parinterval 也可以用这个系统描述翻译中的注意力问题,其中,$\mathrm{query}$即目标语言位置$j$的某种表示,$\mathrm{key}$$\mathrm{value}$即源语言每个位置$i$上的${\mathbi{h}_i}$(这里$\mathrm{key}$$\mathrm{value}$是相同的)。但是,这样的系统在机器翻译问题上并不好用,因为目标语言的表示和源语言的表示都在多维实数空间上,所以无法要求两个实数向量像字符串一样进行严格匹配,或者说这种严格匹配的模型可能会导致$\mathrm{query}$几乎不会命中任何的$\mathrm{key}$。既然无法严格精确匹配,注意力机制就采用了一个“模糊”匹配的方法。这里定义每个$\mathrm{key}_i$$\mathrm{query}$ 都有一个0~1之间的匹配度,这个匹配度描述了$\mathrm{key}_i$$\mathrm{query}$之间的相关程度,记为$\alpha_i$。而查询的结果(记为$\overline{\mathrm{value}}$)也不再是某一个单元的$\mathrm{value}$,而是所有单元$\mathrm{value}$$\alpha_i$的加权和:
\begin{eqnarray}
\overline{\mathrm{value}} = \sum_i \alpha_i \cdot {\mathrm{value}}_i
\label{eq:10-27}
\label{eq:10-21}
\end{eqnarray}
\noindent 也就是说所有的$\mathrm{value}_i$都会对查询结果有贡献,只是贡献度不同罢了。可以通过设计$\alpha_i$来捕捉$\mathrm{key}$$\mathrm{query}$之间的相关性,以达到相关度越大的$\mathrm{key}$所对应的$\mathrm{value}$对结果的贡献越大。
\parinterval 重新回到神经机器翻译问题上来。这种基于模糊匹配的查询模型可以很好的满足对注意力建模的要求。实际上,公式\eqref{eq:10-27}中的$\alpha_i$就是前面提到的注意力权重,它可以由注意力函数$a(\cdot)$计算得到。这样,$\overline{\mathrm{value}}$就是得到的上下文向量,它包含了所有\{$\mathbi{h}_i$\}的信息,只是不同$\mathbi{h}_i$的贡献度不同罢了。图\ref{fig:10-27}展示了将基于模糊匹配的查询模型应用于注意力机制的实例。
\parinterval 重新回到神经机器翻译问题上来。这种基于模糊匹配的查询模型可以很好的满足对注意力建模的要求。实际上,公式\eqref{eq:10-21}中的$\alpha_i$就是前面提到的注意力权重,它可以由注意力函数$a(\cdot)$计算得到。这样,$\overline{\mathrm{value}}$就是得到的上下文向量,它包含了所有\{$\mathbi{h}_i$\}的信息,只是不同$\mathbi{h}_i$的贡献度不同罢了。图\ref{fig:10-23}展示了将基于模糊匹配的查询模型应用于注意力机制的实例。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter10/Figures/figure-query-model-corresponding-to-attention-mechanism}
\caption{注意力机制所对应的查询模型}
\label{fig:10-27}
\label{fig:10-23}
\end{figure}
%----------------------------------------------
\parinterval 最后,从统计学的角度,如果把$\alpha_i$作为每个$\mathrm{value}_i$出现的概率的某种估计,即:$ \funp{P} (\mathrm{value}_i$) $= \alpha_i$,于是可以把公式\eqref{eq:10-27}重写为:
\parinterval 最后,从统计学的角度,如果把$\alpha_i$作为每个$\mathrm{value}_i$出现的概率的某种估计,即:$ \funp{P} (\mathrm{value}_i$) $= \alpha_i$,于是可以把公式\eqref{eq:10-21}重写为:
\begin{eqnarray}
\overline{\mathrm{value}} = \sum_i \funp{P} ( {\mathrm{value}}_i) \cdot {\mathrm{value}}_i
\label{eq:10-28}
\label{eq:10-22}
\end{eqnarray}
\noindent 显然, $\overline{\mathrm{value}}$就是$\mathrm{value}_i$在分布$ \funp{P}( \mathrm{value}_i$)下的期望,即
\begin{equation}
\mathbb{E}_{\sim \\ \funp{P} ( {\mathrm{\mathrm{value}}}_i )} ({\mathrm{value}}_i) = \sum_i \funp{P} ({\mathrm{value}}_i) \cdot {\mathrm{value}}_i
\label{eq:10-29}
\label{eq:10-23}
\end{equation}
从这个观点看,注意力机制实际上是得到了变量$\mathrm{value}$的期望。当然,严格意义上说,$\alpha_i$并不是从概率角度定义的,在实际应用中也并不必须追求严格的统计学意义。
......@@ -880,7 +879,7 @@ a (\mathbi{s},\mathbi{h}) = \left\{ \begin{array}{ll}
\parinterval 循环神经网络在机器翻译中有很多成功的应用,比如:RNNSearch\upcite{bahdanau2014neural}、Nematus\upcite{DBLP:journals/corr/SennrichFCBHHJL17}等系统就被很多研究者作为实验系统。在众多基于循环神经网络的系统中,GNMT系统是非常成功的一个\upcite{Wu2016GooglesNM}。GNMT是谷歌2016年发布的神经机器翻译系统。
\parinterval GNMT使用了编码器-解码器结构,构建了一个8层的深度网络,每层网络均由LSTM组成,且在编码器-解码器之间使用了多层注意力连接。其结构如图\ref{fig:10-35},编码器只有最下面2层为双向LSTM。GNMT在束搜索中也加入了长度惩罚和覆盖度因子来确保输出高质量的翻译结果。
\parinterval GNMT使用了编码器-解码器结构,构建了一个8层的深度网络,每层网络均由LSTM组成,且在编码器-解码器之间使用了多层注意力连接。其结构如图\ref{fig:10-24},编码器只有最下面2层为双向LSTM。GNMT在束搜索中也加入了长度惩罚和覆盖度因子来确保输出高质量的翻译结果。
\vspace{0.5em}
%----------------------------------------------
......@@ -888,17 +887,17 @@ a (\mathbi{s},\mathbi{h}) = \left\{ \begin{array}{ll}
\centering
\input{./Chapter10/Figures/figure-structure-of-gnmt}
\caption{GNMT结构}
\label{fig:10-35}
\label{fig:10-24}
\end{figure}
%----------------------------------------------
\parinterval 实际上,GNMT的主要贡献在于集成了多种优秀的技术,而且在大规模数据上证明了神经机器翻译的有效性。在引入注意力机制之前,神经机器翻译在较大规模的任务上的性能弱于统计机器翻译。加入注意力机制和深层网络后,神经机器翻译性能有了很大的提升。在英德和英法的任务中,GNMT的BLEU值不仅超过了当时优秀的神经机器翻译系统RNNSearch和LSTM(6层),还超过了当时处于领导地位的基于短语的统计机器翻译系统(PBMT)(表\ref{tab:10-10})。相比基于短语的统计机器翻译系统,在人工评价中,GNMT能将翻译错误平均减少60\%。这一结果也充分表明了神经机器翻译带来的巨大性能提升。
\parinterval 实际上,GNMT的主要贡献在于集成了多种优秀的技术,而且在大规模数据上证明了神经机器翻译的有效性。在引入注意力机制之前,神经机器翻译在较大规模的任务上的性能弱于统计机器翻译。加入注意力机制和深层网络后,神经机器翻译性能有了很大的提升。在英德和英法的任务中,GNMT的BLEU值不仅超过了当时优秀的神经机器翻译系统RNNSearch和LSTM(6层),还超过了当时处于领导地位的基于短语的统计机器翻译系统(PBMT)(表\ref{tab:10-8})。相比基于短语的统计机器翻译系统,在人工评价中,GNMT能将翻译错误平均减少60\%。这一结果也充分表明了神经机器翻译带来的巨大性能提升。
%----------------------------------------------
\begin{table}[htp]
\centering
\caption{GNMT与其他翻译模型对比\upcite{Wu2016GooglesNM}}
\label{tab:10-10}
\label{tab:10-8}
\begin{tabular}{l l l}
\multicolumn{1}{l|}{\multirow{3}{*}{\#}} & \multicolumn{2}{c}{BLEU[\%]} \\
\multicolumn{1}{l|}{} & 英德 & 英法 \\
......@@ -929,12 +928,12 @@ a (\mathbi{s},\mathbi{h}) = \left\{ \begin{array}{ll}
\begin{eqnarray}
\mathbi{w}_{\textrm{step}+1} = \mathbi{w}_{\textrm{step}} - \alpha \cdot \frac{ \partial L(\mathbi{w}_{\textrm{step}})} {\partial \mathbi{w}_{\textrm{step}} }
\label{eq:10-30}
\label{eq:10-24}
\end{eqnarray}
\noindent 其中,$\mathbi{w}_{\textrm{step}}$表示更新前的模型参数,$\mathbi{w}_{\textrm{step}+1}$表示更新后的模型参数,$L(\mathbi{w}_{\textrm{step}})$表示模型相对于$\mathbi{w}_{\textrm{step}}$ 的损失,$\frac{\partial L(\mathbi{w}_{\textrm{step}})} {\partial \mathbi{w}_{\textrm{step}} }$表示损失函数的梯度,$\alpha$是更新的步长。也就是说,给定一定量的训练数据,不断执行公式\eqref{eq:10-30}的过程。反复使用训练数据,直至模型参数达到收敛或者损失函数不再变化。通常,把公式的一次执行称为“一步”更新/训练,把访问完所有样本的训练称为“一轮”训练。
\noindent 其中,$\mathbi{w}_{\textrm{step}}$表示更新前的模型参数,$\mathbi{w}_{\textrm{step}+1}$表示更新后的模型参数,$L(\mathbi{w}_{\textrm{step}})$表示模型相对于$\mathbi{w}_{\textrm{step}}$ 的损失,$\frac{\partial L(\mathbi{w}_{\textrm{step}})} {\partial \mathbi{w}_{\textrm{step}} }$表示损失函数的梯度,$\alpha$是更新的步长。也就是说,给定一定量的训练数据,不断执行公式\eqref{eq:10-24}的过程。反复使用训练数据,直至模型参数达到收敛或者损失函数不再变化。通常,把公式的一次执行称为“一步”更新/训练,把访问完所有样本的训练称为“一轮”训练。
\parinterval 将公式\eqref{eq:10-30}应用于神经机器翻译有几个基本问题需要考虑:1)损失函数的选择;2)参数初始化的策略,也就是如何设置$\mathbi{w}_0$;3)优化策略和学习率调整策略;4)训练加速。下面对这些问题进行讨论。
\parinterval 将公式\eqref{eq:10-24}应用于神经机器翻译有几个基本问题需要考虑:1)损失函数的选择;2)参数初始化的策略,也就是如何设置$\mathbi{w}_0$;3)优化策略和学习率调整策略;4)训练加速。下面对这些问题进行讨论。
%----------------------------------------------------------------------------------------
% NEW SUBSUB-SECTION
......@@ -945,16 +944,16 @@ a (\mathbi{s},\mathbi{h}) = \left\{ \begin{array}{ll}
\parinterval 神经机器翻译在目标端的每个位置都会输出一个概率分布,表示这个位置上不同单词出现的可能性。设计损失函数时,需要知道当前位置输出的分布相比于标准答案的“差异”。对于这个问题,常用的是交叉熵损失函数。令$\mathbi{y}$表示机器翻译模型输出的分布,$\hat{\mathbi{y}}$ 表示标准答案,则交叉熵损失可以被定义为:
\begin{eqnarray}
L_{\textrm{ce}}(\mathbi{y},\hat{\mathbi{y}}) = - \sum_{k=1}^{|V|} \mathbi{y}[k] \textrm{log} (\hat{\mathbi{y}}[k])
\label{eq:10-3222}
\label{eq:10-25}
\end{eqnarray}
\noindent 其中$\mathbi{y}[k]$$\hat{\mathbi{y}}[k]$分别表示向量$\mathbi{y}$$\hat{\mathbi{y}}$的第$k$维,$|V|$表示输出向量的维度(等于词表大小)。假设有$n$个训练样本,模型输出的概率分布为$\mathbi{Y} = \{ \mathbi{y}_1,\mathbi{y}_2,..., \mathbi{y}_n \}$,标准答案的分布$\widehat{\mathbi{Y}}=\{ \hat{\mathbi{y}}_1, \hat{\mathbi{y}}_2,...,\hat{\mathbi{y}}_n \}$。这个训练样本集合上的损失函数可以被定义为:
\begin{eqnarray}
L(\mathbi{Y},\widehat{\mathbi{Y}}) = \sum_{j=1}^n L_{\textrm{ce}}(\mathbi{y}_j,\hat{\mathbi{y}}_j)
\label{eq:10-31}
\label{eq:10-26}
\end{eqnarray}
\parinterval 公式\eqref{eq:10-31}是一种非常通用的损失函数形式,除了交叉熵,也可以使用其他的损失函数,这时只需要替换$L_{\textrm{ce}} (\cdot)$即可。这里使用交叉熵损失函数的好处在于,它非常容易优化,特别是与Softmax组合,其反向传播的实现非常高效。此外,交叉熵损失(在一定条件下)也对应了极大似然的思想,这种方法在自然语言处理中已经被证明是非常有效的。
\parinterval 公式\eqref{eq:10-26}是一种非常通用的损失函数形式,除了交叉熵,也可以使用其他的损失函数,这时只需要替换$L_{\textrm{ce}} (\cdot)$即可。这里使用交叉熵损失函数的好处在于,它非常容易优化,特别是与Softmax组合,其反向传播的实现非常高效。此外,交叉熵损失(在一定条件下)也对应了极大似然的思想,这种方法在自然语言处理中已经被证明是非常有效的。
\parinterval 除了交叉熵,很多系统也使用了面向评价的损失函数,比如,直接利用评价指标BLEU定义损失函数\upcite{DBLP:conf/acl/ShenCHHWSL16}。不过这类损失函数往往不可微分,因此无法直接获取梯度。这时可以引入强化学习技术,通过策略梯度等方法进行优化。不过这类方法需要采样等手段,这里不做重点讨论,相关内容会在后面技术部分进行介绍。
......@@ -977,7 +976,7 @@ L(\mathbi{Y},\widehat{\mathbi{Y}}) = \sum_{j=1}^n L_{\textrm{ce}}(\mathbi{y}_j,\
\item 网络的权重矩阵$\mathbi{w}$一般使用Xavier参数初始化方法\upcite{pmlr-v9-glorot10a},可以有效稳定训练过程,特别是对于比较“深”的网络。令$d_{\textrm{in}}$$d_{\textrm{out}}$分别表示$\mathbi{w}$的输入和输出的维度大小\footnote{对于变换$\mathbi{y} = \mathbi{x} \mathbi{w}$$\mathbi{w}$的列数为$d_{\textrm{in}}$,行数为$d_{\textrm{out}}$},则该方法的具体实现如下:
\begin{eqnarray}
\mathbi{w} \sim U(-\sqrt{ \frac{6} { d_{\textrm{in}} + d_{\textrm{out}} } } , \sqrt{ \frac{6} { d_{\textrm{in}} + d_{\textrm{out}} } })
\label{eq:10-32}
\label{eq:10-27}
\vspace{0.5em}
\end{eqnarray}
......@@ -991,7 +990,7 @@ L(\mathbi{Y},\widehat{\mathbi{Y}}) = \sum_{j=1}^n L_{\textrm{ce}}(\mathbi{y}_j,\
\subsubsection{3. 优化策略}
%\vspace{0.5em}
\parinterval 公式\eqref{eq:10-30}展示了最基本的优化策略,也被称为标准的SGD优化器。实际上,训练神经机器翻译模型时,还有非常多的优化器可以选择,在{\chapternine}也有详细介绍,这里考虑Adam优化器\upcite{kingma2014adam}。 Adam 通过对梯度的{\small\bfnew{一阶矩估计}}\index{一阶矩估计}(First Moment Estimation)\index{First Moment Estimation}{\small\bfnew{二阶矩估计}}\index{二阶矩估计}(Second Moment Estimation)\index{Second Moment Estimation}进行综合考虑,计算出更新步长。
\parinterval 公式\eqref{eq:10-24}展示了最基本的优化策略,也被称为标准的SGD优化器。实际上,训练神经机器翻译模型时,还有非常多的优化器可以选择,在{\chapternine}也有详细介绍,这里考虑Adam优化器\upcite{kingma2014adam}。 Adam 通过对梯度的{\small\bfnew{一阶矩估计}}\index{一阶矩估计}(First Moment Estimation)\index{First Moment Estimation}{\small\bfnew{二阶矩估计}}\index{二阶矩估计}(Second Moment Estimation)\index{Second Moment Estimation}进行综合考虑,计算出更新步长。
\parinterval 通常,Adam收敛地比较快,不同任务基本上可以使用一套配置进行优化,虽性能不算差,但很难达到最优效果。相反,SGD虽能通过在不同的数据集上进行调整,来达到最优的结果,但是收敛速度慢。因此需要根据不同的需求来选择合适的优化器。若需要快得到模型的初步结果,选择Adam较为合适,若是需要在一个任务上得到最优的结果,选择SGD更为合适。
......@@ -1006,7 +1005,7 @@ L(\mathbi{Y},\widehat{\mathbi{Y}}) = \sum_{j=1}^n L_{\textrm{ce}}(\mathbi{y}_j,\
\vspace{-0.5em}
\begin{eqnarray}
\mathbi{w}' = \mathbi{w} \cdot \frac{\gamma} {\textrm{max}(\gamma,\| \mathbi{w} \|_2)}
\label{eq:10-33}
\label{eq:10-28}
\end{eqnarray}
%\vspace{0.5em}
......@@ -1019,14 +1018,14 @@ L(\mathbi{Y},\widehat{\mathbi{Y}}) = \sum_{j=1}^n L_{\textrm{ce}}(\mathbi{y}_j,\
\subsubsection{5. 学习率策略}
\vspace{0.5em}
\parinterval 在公式\eqref{eq:10-30}中, $\alpha$决定了每次参数更新时更新的步幅大小,称之为学习率。学习率作为基于梯度方法中的重要超参数,它决定目标函数能否收敛到较好的局部最优点以及收敛的速度。合理的学习率能够使模型快速、稳定地达到较好的状态。但是,如果学习率太小,收敛过程会很慢;而学习率太大,则模型的状态可能会出现震荡,很难达到稳定,甚至使模型无法收敛。图\ref{fig:10-28} 对比了不同学习率对优化过程的影响。
\parinterval 在公式\eqref{eq:10-24}中, $\alpha$决定了每次参数更新时更新的步幅大小,称之为学习率。学习率作为基于梯度方法中的重要超参数,它决定目标函数能否收敛到较好的局部最优点以及收敛的速度。合理的学习率能够使模型快速、稳定地达到较好的状态。但是,如果学习率太小,收敛过程会很慢;而学习率太大,则模型的状态可能会出现震荡,很难达到稳定,甚至使模型无法收敛。图\ref{fig:10-25} 对比了不同学习率对优化过程的影响。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter10/Figures/figure-convergence&lr}
\caption{学习率过小(左) vs 学习率过大(右) }
\label{fig:10-28}
\label{fig:10-25}
\end{figure}
%----------------------------------------------
......@@ -1034,22 +1033,22 @@ L(\mathbi{Y},\widehat{\mathbi{Y}}) = \sum_{j=1}^n L_{\textrm{ce}}(\mathbi{y}_j,\
\vspace{0.5em}
\parinterval\ref{fig:10-29}展示了一种常用的学习率调整策略。它分为两个阶段:预热阶段和衰减阶段。模型训练初期梯度通常很大,如果直接使用较大的学习率很容易让模型陷入局部最优。学习率的预热阶段便是通过在训练初期使学习率从小到大逐渐增加来减缓在初始阶段模型“跑偏”的现象。一般来说,初始学习率太高会使得模型进入一种损失函数曲面非常不平滑的区域,进而使得模型进入一种混乱状态,后续的优化过程很难取得很好的效果。一个常用的学习率预热方法是{\small\bfnew{逐渐预热}}\index{逐渐预热}(Gradual Warmup)\index{Gradual Warmup}。假设预热的更新次数为$N$,初始学习率为$\alpha_0$,则预热阶段第$\textrm{step}$次更新的学习率为:
\parinterval\ref{fig:10-26}展示了一种常用的学习率调整策略。它分为两个阶段:预热阶段和衰减阶段。模型训练初期梯度通常很大,如果直接使用较大的学习率很容易让模型陷入局部最优。学习率的预热阶段便是通过在训练初期使学习率从小到大逐渐增加来减缓在初始阶段模型“跑偏”的现象。一般来说,初始学习率太高会使得模型进入一种损失函数曲面非常不平滑的区域,进而使得模型进入一种混乱状态,后续的优化过程很难取得很好的效果。一个常用的学习率预热方法是{\small\bfnew{逐渐预热}}\index{逐渐预热}(Gradual Warmup)\index{Gradual Warmup}。假设预热的更新次数为$N$,初始学习率为$\alpha_0$,则预热阶段第$\textrm{step}$次更新的学习率为:
%\vspace{0.5em}
\begin{eqnarray}
\alpha_t = \frac{\textrm{step}}{N} \alpha_0 \quad,\quad 1 \leq t \leq T'
\label{eq:10-34}
\label{eq:10-29}
\end{eqnarray}
%-------
\noindent 另一方面,当模型训练逐渐接近收敛的时候,使用太大学习率会很容易让模型在局部最优解附近震荡,从而错过局部极小,因此需要通过减小学习率来调整更新的步长,以此来不断地逼近局部最优,这一阶段也称为学习率的衰减阶段。学习率衰减的方法有很多,比如指数衰减以及余弦衰减等,图\ref{fig:10-29}右侧展示的是{\small\bfnew{分段常数衰减}}\index{分段常数衰减}(Piecewise Constant Decay)\index{Piecewise Constant Decay},即每经过$m$次更新,学习率衰减为原来的$\beta_m$$\beta_m<1$)倍,其中$m$$\beta_m$为经验设置的超参。
\noindent 另一方面,当模型训练逐渐接近收敛的时候,使用太大学习率会很容易让模型在局部最优解附近震荡,从而错过局部极小,因此需要通过减小学习率来调整更新的步长,以此来不断地逼近局部最优,这一阶段也称为学习率的衰减阶段。学习率衰减的方法有很多,比如指数衰减以及余弦衰减等,图\ref{fig:10-26}右侧展示的是{\small\bfnew{分段常数衰减}}\index{分段常数衰减}(Piecewise Constant Decay)\index{Piecewise Constant Decay},即每经过$m$次更新,学习率衰减为原来的$\beta_m$$\beta_m<1$)倍,其中$m$$\beta_m$为经验设置的超参。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter10/Figures/figure-relationship-between-learning-rate-and-number-of-updates}
\caption{学习率与更新次数的变化关系}
\label{fig:10-29}
\label{fig:10-26}
\end{figure}
%----------------------------------------------
......@@ -1081,19 +1080,19 @@ L(\mathbi{Y},\widehat{\mathbi{Y}}) = \sum_{j=1}^n L_{\textrm{ce}}(\mathbi{y}_j,\
\begin{itemize}
\vspace{0.5em}
\item {\small\bfnew{数据并行}}。如果一台设备能完整放下一个神经机器翻译模型,那么数据并行可以把一个大批次均匀切分成$n$个小批次,然后分发到$n$个设备上并行计算,最后把结果汇总,相当于把运算时间变为原来的${1}/{n}$,数据并行的过程如图\ref{fig:10-30}所示。不过,需要注意的是,多设备并行需要对数据在不同设备间传输,特别是多个GPU的情况,设备间传输的带宽十分有限,设备间传输数据往往会造成额外的时间消耗\upcite{xiao2017fast}。通常,数据并行的训练速度无法随着设备数量增加呈线性增长。不过这个问题也有很多优秀的解决方案,比如采用多个设备的异步训练,但是这些内容已经超出本章的内容,因此这里不做过多讨论。
\item {\small\bfnew{数据并行}}。如果一台设备能完整放下一个神经机器翻译模型,那么数据并行可以把一个大批次均匀切分成$n$个小批次,然后分发到$n$个设备上并行计算,最后把结果汇总,相当于把运算时间变为原来的${1}/{n}$,数据并行的过程如图\ref{fig:10-27}所示。不过,需要注意的是,多设备并行需要对数据在不同设备间传输,特别是多个GPU的情况,设备间传输的带宽十分有限,设备间传输数据往往会造成额外的时间消耗\upcite{xiao2017fast}。通常,数据并行的训练速度无法随着设备数量增加呈线性增长。不过这个问题也有很多优秀的解决方案,比如采用多个设备的异步训练,但是这些内容已经超出本章的内容,因此这里不做过多讨论。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter10/Figures/figure-data-parallel-process}
\caption{数据并行过程}
\label{fig:10-30}
\label{fig:10-27}
\end{figure}
%----------------------------------------------
\vspace{0.5em}
\item {\small\bfnew{模型并行}}\index{模型并行}。另一种思路是,把较大的模型分成若干小模型,之后在不同设备上训练小模型。对于循环神经网络,不同层的网络天然就是一个相对独立的模型,因此非常适合使用这种方法。比如,对于$l$层的循环神经网络,把每层都看做一个小模型,然后分发到$l$个设备上并行计算。在序列较长的时候,该方法使其运算时间变为原来的${1}/{l}$。图\ref{fig:10-31}以三层循环网络为例展示了对句子“你\ \ 不错\ 。”进行模型并行的过程。其中,每一层网络都被放到了一个设备上。当模型根据已经生成的第一个词“你”,并预测下一个词时(图\ref{fig:10-31}(a)),同层的下一个时刻的计算和对“你”的第二层的计算就可以同时开展(图\ref{fig:10-31}(b))。以此类推,就完成了模型的并行计算。
\item {\small\bfnew{模型并行}}\index{模型并行}。另一种思路是,把较大的模型分成若干小模型,之后在不同设备上训练小模型。对于循环神经网络,不同层的网络天然就是一个相对独立的模型,因此非常适合使用这种方法。比如,对于$l$层的循环神经网络,把每层都看做一个小模型,然后分发到$l$个设备上并行计算。在序列较长的时候,该方法使其运算时间变为原来的${1}/{l}$。图\ref{fig:10-28}以三层循环网络为例展示了对句子“你\ \ 不错\ 。”进行模型并行的过程。其中,每一层网络都被放到了一个设备上。当模型根据已经生成的第一个词“你”,并预测下一个词时(图\ref{fig:10-28}(a)),同层的下一个时刻的计算和对“你”的第二层的计算就可以同时开展(图\ref{fig:10-28}(b))。以此类推,就完成了模型的并行计算。
\vspace{0.5em}
\end{itemize}
......@@ -1106,7 +1105,7 @@ L(\mathbi{Y},\widehat{\mathbi{Y}}) = \sum_{j=1}^n L_{\textrm{ce}}(\mathbi{y}_j,\
%\subfigure[]{\input{./Chapter10/Figures/figure-process05}} &\subfigure[]{\input{./Chapter10/Figures/figure-process06}}\\
\end{tabular}
%\caption{一个三层循环神经网络的模型并行过程}
%\label{fig:10-31}
%\label{fig:10-28}
\end{figure}
%----------------------------------------------
%-------------------------------------------
......@@ -1118,7 +1117,7 @@ L(\mathbi{Y},\widehat{\mathbi{Y}}) = \sum_{j=1}^n L_{\textrm{ce}}(\mathbi{y}_j,\
\subfigure[]{\input{./Chapter10/Figures/figure-process05}} &\subfigure[]{\input{./Chapter10/Figures/figure-process06}}
\end{tabular}
\caption{一个三层循环神经网络的模型并行过程}
\label{fig:10-31}
\label{fig:10-28}
\end{figure}
%----------------------------------------------
......@@ -1131,20 +1130,20 @@ L(\mathbi{Y},\widehat{\mathbi{Y}}) = \sum_{j=1}^n L_{\textrm{ce}}(\mathbi{y}_j,\
\begin{eqnarray}
\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}
\label{eq:10-30}
\end{eqnarray}
\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{\seq{{y}}}_{<j} , \seq{{x}})
\label{eq:10-36}
\label{eq:10-31}
\end{eqnarray}
\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{\seq{{y}}}_{<{j\ast}} \},\seq{{x}})
\label{eq:10-37}
\label{eq:10-32}
\end{eqnarray}
\noindent 这里,$\{ \hat{y}_{j1},...,\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\ast}} \},\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} $\}中的某个译文单词序列作为条件生成的。} 。这种方法也被称为束搜索,意思是搜索时始终考虑一个集束内的候选。
......@@ -1157,7 +1156,7 @@ L(\mathbi{Y},\widehat{\mathbi{Y}}) = \sum_{j=1}^n L_{\textrm{ce}}(\mathbi{y}_j,\
\vspace{1.0em}
\subsubsection{1. 贪婪搜索}
\vspace{0.6em}
\parinterval\ref{fig:10-32}展示了一个基于贪婪方法的神经机器翻译解码过程。每一个时间步的单词预测都依赖于其前一步单词的生成。在解码第一个单词时,由于没有之前的单词信息,会用<sos>进行填充作为起始的单词,且会用一个零向量(可以理解为没有之前时间步的信息)表示第0步的中间层状态。
\parinterval\ref{fig:10-29}展示了一个基于贪婪方法的神经机器翻译解码过程。每一个时间步的单词预测都依赖于其前一步单词的生成。在解码第一个单词时,由于没有之前的单词信息,会用<sos>进行填充作为起始的单词,且会用一个零向量(可以理解为没有之前时间步的信息)表示第0步的中间层状态。
\vspace{0.8em}
%----------------------------------------------
......@@ -1165,12 +1164,12 @@ L(\mathbi{Y},\widehat{\mathbi{Y}}) = \sum_{j=1}^n L_{\textrm{ce}}(\mathbi{y}_j,\
\centering
\input{./Chapter10/Figures/figure-decoding-process-based-on-greedy-method}
\caption{基于贪婪方法的解码过程}
\label{fig:10-32}
\label{fig:10-29}
\end{figure}
%----------------------------------------------
\vspace{0.2em}
\parinterval 解码端的每一步Softmax层会输出所有单词的概率,由于是基于贪心的方法,这里会选择概率最大(top-1)的单词作为输出。这个过程可以参考图\ref{fig:10-33}的内容。选择分布中概率最大的单词“Have”作为得到的第一个单词,并再次送入解码器,作为第二步的输入同时预测下一个单词。以此类推,直到生成句子的终止符为止,就得到了完整的译文。
\parinterval 解码端的每一步Softmax层会输出所有单词的概率,由于是基于贪心的方法,这里会选择概率最大(top-1)的单词作为输出。这个过程可以参考图\ref{fig:10-30}的内容。选择分布中概率最大的单词“Have”作为得到的第一个单词,并再次送入解码器,作为第二步的输入同时预测下一个单词。以此类推,直到生成句子的终止符为止,就得到了完整的译文。
\parinterval 贪婪搜索的优点在于速度快。在对翻译速度有较高要求的场景中,贪婪搜索是一种十分有效的系统加速方法。而且贪婪搜索的原理非常简单,易于快速实现。不过,由于每一步只保留一个最好的局部结果,贪婪搜索往往会带来翻译品质上的损失。
......@@ -1179,7 +1178,7 @@ L(\mathbi{Y},\widehat{\mathbi{Y}}) = \sum_{j=1}^n L_{\textrm{ce}}(\mathbi{y}_j,\
\centering
\input{./Chapter10/Figures/figure-decode-the-word-probability-distribution-at-the-first-position}
\caption{解码第一个位置输出的单词概率分布(“Have”的概率最高)}
\label{fig:10-33}
\label{fig:10-30}
\end{figure}
%----------------------------------------------
......@@ -1190,14 +1189,14 @@ L(\mathbi{Y},\widehat{\mathbi{Y}}) = \sum_{j=1}^n L_{\textrm{ce}}(\mathbi{y}_j,\
\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| \seq{{x}})$最大的前三个$y_2$作为新的输出结果,这样便得到了前两个位置的top-3译文。在预测其他位置时也是如此,不断重复此过程直到推断结束。可以看到,束搜索的搜索空间大小与束宽度有关,也就是:束宽度越大,搜索空间越大,更有可能搜索到质量更高的译文,但同时搜索会更慢。束宽度等于3,意味着每次只考虑三个最有可能的结果,贪婪搜索实际上便是束宽度为1的情况。在神经机器翻译系统实现中,一般束宽度设置在4~8之间。
\parinterval 束搜索是一种启发式图搜索算法。相比于全搜索,它可以减少搜索所占用的空间和时间,在每一步扩展的时候,剪掉一些质量比较差的结点,保留下一些质量较高的结点。具体到机器翻译任务,对于每一个目标语言位置,束搜索选择了概率最大的前$k$个单词进行扩展(其中$k$叫做束宽度,或简称为束宽)。如图\ref{fig:10-31}所示,假设\{$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]
\centering
\input{./Chapter10/Figures/figure-beam-search-process}
\caption{束搜索过程}
\label{fig:10-34}
\label{fig:10-31}
\end{figure}
%----------------------------------------------
......@@ -1222,13 +1221,13 @@ L(\mathbi{Y},\widehat{\mathbi{Y}}) = \sum_{j=1}^n L_{\textrm{ce}}(\mathbi{y}_j,\
\parinterval 为了解决上面提到的问题,可以使用其他特征与$\textrm{log } \funp{P} (\seq{{y}} | \seq{{x}})$一起组成新的模型得分$\textrm{score} ( \seq{{y}} , \seq{{x}})$。针对模型倾向于生成短句子的问题,常用的做法是引入惩罚机制。比如,可以定义一个惩罚因子,形式如下:
\begin{eqnarray}
\textrm{lp}(\seq{{y}}) = \frac {(5+ |\seq{{y}}|)^{\alpha}} {(5+1)^{\alpha}}
\label{eq:10-39}
\label{eq:10-33}
\end{eqnarray}
\noindent 其中,$|\seq{{y}}|$代表已经得到的译文长度,$\alpha$是一个固定的常数,用于控制惩罚的强度。同时在计算句子得分时,额外引入表示覆盖度的因子,如下:
\begin{eqnarray}
\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}
\label{eq:10-34}
\end{eqnarray}
\noindent $\textrm{cp}(\cdot)$会惩罚把某些源语言单词对应到很多目标语言单词的情况(覆盖度),被覆盖的程度用$\sum_j^{|\seq{{y}}|} \alpha_{ij}$度量。$\beta$也是需要经验性设置的超参数,用于对覆盖度惩罚的强度进行控制。
......@@ -1236,7 +1235,7 @@ L(\mathbi{Y},\widehat{\mathbi{Y}}) = \sum_{j=1}^n L_{\textrm{ce}}(\mathbi{y}_j,\
\parinterval 最终,模型得分定义如下:
\begin{eqnarray}
\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}
\label{eq:10-35}
\end{eqnarray}
\noindent 显然,当目标语言$\seq{{y}}$越短时,$\textrm{lp}(\seq{{y}})$的值越小,因为$\textrm{log } \funp{P}(\seq{{y}} | \seq{{x}})$是负数,所以句子得分$\textrm{score} ( \seq{{y}} , \seq{{x}})$越小。也就是说,模型会惩罚译文过短的结果。当覆盖度较高时,同样会使得分变低。通过这样的惩罚机制,使模型得分更为合理,从而帮助模型选择出质量更高的译文。
......
......@@ -88,10 +88,10 @@
\parinterval 在卷积计算中,不同深度下卷积核不同但是执行操作相同,这里以二维卷积核为例展示具体卷积计算。若设输入矩阵为$\mathbi{x}$,输出矩阵为$\mathbi{y}$,卷积滑动步幅为$\textrm{stride}$,卷积核为$\mathbi{w}$,且$\mathbi{w} \in \mathbb{R}^{Q \times U} $,那么卷积计算的公式为:
\begin{eqnarray}
\mathbi{y}_{i,j} = \sum \sum ( \mathbi{x}_{[j\times \textrm{stride}:j\times \textrm{stride}+U-1,i\times \textrm{stride}:i\times \textrm{stride}+Q-1]} \odot \mathbi{w} )
\label{eq:11-1-new}
\label{eq:11-1}
\end{eqnarray}
\noindent 其中$i$是输出矩阵的行下标,$j$是输出矩阵的列下标。图\ref{fig:11-4}展示了一个简单的卷积操作示例,其中$Q$为2,$U$为2,$\textrm{stride}$为1,根据公式\eqref{eq:11-1-new},图中蓝色位置$\mathbi{y}_{0,0}$的计算为:
\noindent 其中$i$是输出矩阵的行下标,$j$是输出矩阵的列下标$\odot$表示矩阵点乘,具体见{\chapternine}。图\ref{fig:11-4}展示了一个简单的卷积操作示例,其中$Q$为2,$U$为2,$\textrm{stride}$为1,根据公式\eqref{eq:11-1},图中蓝色位置$\mathbi{y}_{0,0}$的计算为:
\begin{eqnarray}
\mathbi{y}_{0,0} &=& \sum \sum ( \mathbi{x}_{[0\times 1:0\times 1+2-1,0\times 1:0\times 1+2-1]} \odot \mathbi{w}) \nonumber \\
&=& \sum \sum ( \mathbi{x}_{[0:1,0:1]} \odot \mathbi{w} ) \nonumber \\
......@@ -101,7 +101,7 @@
\end{pmatrix} \nonumber \\
&=& 0 \times 0 + 1 \times 1 + 3 \times 2 + 4 \times 3 \nonumber \\
&=& 19
\label{eq:11-2-new}
\label{eq:11-2}
\end{eqnarray}
\parinterval 卷积计算的作用是提取特征,用不同的卷积核计算可以获取不同的特征,比如图\ref{fig:11-5},通过设计的特定卷积核就可以获取图像边缘信息。在卷积神经网络中,不需要手动设计卷积核,只需要指定卷积层中卷积核的数量及大小,模型就可以自己学习卷积核具体的参数。
......@@ -307,20 +307,19 @@
\parinterval 如图所示,形式上,卷积操作可以分成两部分,分别使用两个卷积核来得到两个卷积结果:
\begin{eqnarray}
\mathbi{A} & = & \mathbi{x} * \mathbi{W} + \mathbi{b}_\mathbi{W} \\
\mathbi{B} & = & \mathbi{x} * \mathbi{V} + \mathbi{b}_\mathbi{V} \ \
\label{eq:11-1}
\mathbi{A} & = & \mathbi{x} * \mathbi{W} + \mathbi{b}_\mathbi{W} \label{eq:11-3} \\
\mathbi{B} & = & \mathbi{x} * \mathbi{V} + \mathbi{b}_\mathbi{V} \label{eq:11-4}
\end{eqnarray}
\noindent 其中,$\mathbi{A},\mathbi{B}\in \mathbb{R}^d$$\mathbi{W}\in \mathbb{R}^{K\times d \times d}$$\mathbi{V}\in \mathbb{R}^{K\times d \times d}$$\mathbi{b}_\mathbi{W}$$\mathbi{b}_\mathbi{V} \in \mathbb{R}^d $$\mathbi{W}$$\mathbi{V}$在此表示卷积核,$\mathbi{b}_\mathbi{W}$$\mathbi{b}_\mathbi{V}$为偏置矩阵。在卷积操作之后,引入非线性变换:
\begin{eqnarray}
\mathbi{y} & = & \mathbi{A} \otimes \sigma ( \mathbi{B} )
\label{eq:11-2}
\label{eq:11-5}
\end{eqnarray}
\noindent 其中,$\sigma$为Sigmoid函数,$\otimes$为按位乘运算。Sigmoid将$\mathbi{B}$映射为0-1范围内的实数,用来充当门控。可以看到,门控卷积神经网络中核心部分就是$\sigma ( \mathbi{B} )$,通过这个门控单元来对卷积输出进行控制,确定保留哪些信息。同时,在梯度反向传播的过程中,这种机制使得不同层之间存在线性的通道,梯度传导更加简单,利于深层网络的训练。这种思想和\ref{sec:11.2.3}节将要介绍的残差网络也很类似。
\parinterval 在ConvS2S模型中,为了保证卷积操作之后的序列长度不变,需要对输入进行填充,这一点已经在之前的章节中讨论过了。因此,在编码端每一次卷积操作前,需要对序列的头部和尾部分别做相应的填充(如图\ref{fig:11-14}左侧部分)。而在解码端中,由于需要训练和解码的一致性,模型在训练过程中不能使用未来的信息,需要对未来信息进行屏蔽,也就是屏蔽掉当前译文单词右侧的译文信息。从实践角度来看,只需要对解码端输入序列的头部填充$K-1$ 个空元素,其中$K$为卷积核的宽度(图\ref{fig:11-14-2}展示了卷积核宽度$K$=3时,解码端对输入序列的填充情况,图中三角形表示卷积操作)。
\parinterval 在ConvS2S模型中,为了保证卷积操作之后的序列长度不变,需要对输入进行填充,这一点已经在之前的章节中讨论过了。因此,在编码端每一次卷积操作前,需要对序列的头部和尾部分别做相应的填充(如图\ref{fig:11-14}左侧部分)。而在解码端中,由于需要训练和解码的一致性,模型在训练过程中不能使用未来的信息,需要对未来信息进行屏蔽,也就是屏蔽掉当前译文单词右侧的译文信息。从实践角度来看,只需要对解码端输入序列的头部填充$K-1$ 个空元素,其中$K$为卷积核的宽度(图\ref{fig:11-15}展示了卷积核宽度$K$=3时,解码端对输入序列的填充情况,图中三角形表示卷积操作)。
%----------------------------------------------
% 图14-2.
......@@ -328,7 +327,7 @@
\centering
\input{./Chapter11/Figures/figure-padding-method}
\caption{解码端的填充方法}
\label{fig:11-14-2}
\label{fig:11-15}
\end{figure}
%----------------------------------------------
......@@ -342,16 +341,16 @@
\parinterval 残差连接是一种训练深层网络的技术,其内容在{\chapternine}已经进行了介绍,即在多层神经网络之间通过增加直接连接的方式,从而将底层信息直接传递给上层。通过增加这样的直接连接,可以让不同层之间的信息传递更加高效,有利于深层神经网络的训练,其计算公式为:
\begin{eqnarray}
\mathbi{h}^{l+1} = F (\mathbi{h}^l) + \mathbi{h}^l
\label{eq:11-3}
\label{eq:11-6}
\end{eqnarray}
\noindent 其中,$\mathbi{h}^l$表示$l$层神经网络的输入向量,${F} (\mathbi{h}^l)$$l$层神经网络的运算。如果$l=2$,那么公式\eqref{eq:11-3}可以解释为:第3层的输入$\mathbi{h}^3$等于第2层的输出${F}(\mathbi{h}^2)$加上第2层的输入$\mathbi{h}^2$
\noindent 其中,$\mathbi{h}^l$表示$l$层神经网络的输入向量,${F} (\mathbi{h}^l)$$l$层神经网络的运算。如果$l=2$,那么公式\eqref{eq:11-6}可以解释为:第3层的输入$\mathbi{h}^3$等于第2层的输出${F}(\mathbi{h}^2)$加上第2层的输入$\mathbi{h}^2$
\parinterval 在ConvS2S中残差连接主要应用于门控卷积神经网络和多跳自注意力机制中,比如在编码器的多层门控卷积神经网络中,在每一层的输入和输出之间增加残差连接,具体的数学描述如下:
\begin{eqnarray}
%\mathbi{h}_i^l = \funp{v} (\mathbi{W}^l [\mathbi{h}_{i-\frac{k}{2}}^{l-1},...,\mathbi{h}_{i+\frac{k}{2}}^{l-1}] + b_{\mathbi{W}}^l ) + \mathbi{h}_i^{l-1}
\mathbi{h}^{l+1} = \mathbi{A}^{l} \otimes \sigma ( \mathbi{B}^{l} ) + \mathbi{h}^{l}
\label{eq:11-4}
\label{eq:11-7}
\end{eqnarray}
......@@ -376,37 +375,34 @@
\parinterval 在基于循环神经网络的翻译模型中,注意力机制已经被广泛使用\upcite{bahdanau2014neural},并用于避免循环神经网络将源语言序列压缩成一个固定维度的向量表示带来的信息损失。另一方面,注意力同样能够帮助解码端区分源语言中不同位置对当前目标语言位置的贡献度,其具体的计算过程如下:
\begin{eqnarray}
\mathbi{C}_j &=& \sum_i \alpha_{i,j} \mathbi{h}_i \\
\alpha_{i,j} &=& \frac{ \textrm{exp}(\funp{a} (\mathbi{s}_{j-1},\mathbi{h}_i)) }{\sum_{i'} \textrm{exp}( \funp{a} (\mathbi{s}_{j-1},\mathbi{h}_{i'}))}
\label{eq:11-5}
\mathbi{C}_j &=& \sum_i \alpha_{i,j} \mathbi{h}_i \label{eq:11-8} \\
\alpha_{i,j} &=& \frac{ \textrm{exp}(\funp{a} (\mathbi{s}_{j-1},\mathbi{h}_i)) }{\sum_{i'} \textrm{exp}( \funp{a} (\mathbi{s}_{j-1},\mathbi{h}_{i'}))} \label{eq:11-9}
\end{eqnarray}
\noindent 其中,$\mathbi{h}_i$表示源语端第$i$个位置的隐层状态,即编码器在第$i$个位置的输出。$\mathbi{s}_j$表示目标端第$j$个位置的隐层状态。给定$\mathbi{s}_j$$\mathbi{h}_i$,注意力机制通过函数$\funp{a}(\cdot)$计算目标语言表示$\mathbi{s}_j$与源语言表示$\mathbi{h}_i$之间的注意力权重$\alpha_{i,j}$,通过加权平均得到当前目标端位置所需的上下文表示$\mathbi{C}_j$。其中$\funp{a}(\cdot)$的具体计算方式在{\chapterten}已经详细讨论。
\parinterval 在ConvS2S模型中,解码器同样采用堆叠的多层门控卷积网络来对目标语言进行序列建模。区别于编码器,解码器在每一层卷积网络之后引入了注意力机制,用来参考源语言信息。ConvS2S选用了点乘注意力,并且通过类似残差连接的方式将注意力操作的输入与输出同时作用于下一层计算,称为多跳注意力。其具体计算方式如下:
\begin{eqnarray}
\alpha_{ij}^l = \frac{ \textrm{exp} (\mathbi{d}_{j}^l\mathbi{h}_i) }{\sum_{i^{'}=1}^m \textrm{exp} (\mathbi{d}_{j}^l\mathbi{h}_{i^{'}})}
\label{eq:11-6-1}
\label{eq:11-10}
\end{eqnarray}
\noindent 不同于公式\eqref{eq:11-5}中使用的目标语端隐层表示$\mathbi{s}_{j-1}$,公式\eqref{eq:11-6-1}中的$\mathbi{d}_{j}^l$同时结合了$\mathbi{s}_{j}$的卷积计算结果和目标语端的词嵌入$\mathbi{g}_j$,其具体计算公式如下:
\noindent 不同于公式\eqref{eq:11-9}中使用的目标语端隐层表示$\mathbi{s}_{j-1}$,公式\eqref{eq:11-10}中的$\mathbi{d}_{j}^l$同时结合了$\mathbi{s}_{j}$的卷积计算结果和目标语端的词嵌入$\mathbi{g}_j$,其具体计算公式如下:
\begin{eqnarray}
\mathbi{d}_{j}^l &=& \mathbi{W}_{d}^{l} \mathbi{z}_{j}^{l} + \mathbi{b}_{d}^{l} + \mathbi{g}_j \\
\mathbi{z}_j^l &=& \textrm{Conv}(\mathbi{s}_j^l)
\label{eq:11-6-2}
\mathbi{d}_{j}^l &=& \mathbi{W}_{d}^{l} \mathbi{z}_{j}^{l} + \mathbi{b}_{d}^{l} + \mathbi{g}_j \label{eq:11-11} \\
\mathbi{z}_j^l &=& \textrm{Conv}(\mathbi{s}_j^l) \label{eq:11-12}
\end{eqnarray}
\noindent 其中,$\mathbi{z}_j^l$表示第$l$层卷积网络输出中第$j$个位置的表示,$\mathbi{W}_{d}^{l}$$\mathbi{b}_{d}^{l}$是模型可学习的参数,$\textrm{Conv}(\cdot)$表示卷积操作。在获得第$l$层的注意力权重之后,就可以得到对应的一个上下文表示$\mathbi{C}_j^l$
\begin{eqnarray}
\mathbi{C}_j^l = \sum_i \alpha_{ij}^l (\mathbi{h}_i + \mathbi{e}_i)
\label{eq:11-7}
\label{eq:11-13}
\end{eqnarray}
\noindent 模型使用了更全面的源语言信息,同时考虑了源语言端编码表示$\mathbi{h}_i$以及词嵌入表示$\mathbi{e}_i$。在获得第$l$层的上下文向量$\mathbi{C}_j^l$后,模型将其与$\mathbi{z}_j^l$相加后送入下一层网络,这个过程可以被描述为:
\begin{eqnarray}
\mathbi{s}_j^{l+1} = \mathbi{C}_j^l + \mathbi{z}_j^l
\label{eq:11-8}
\label{eq:11-14}
\end{eqnarray}
\noindent 与循环网络中的注意力机制相比,该机制能够帮助模型甄别已经考虑了哪些先前的输入。也就是说,多跳的注意力机制会考虑模型之前更关注哪些单词,并且之后层中执行多次注意力的“跳跃”。
......@@ -431,10 +427,9 @@
\end{itemize}
\parinterval Nesterov加速梯度下降法和{\chapternine}介绍的Momentum梯度下降法类似,都使用了历史梯度信息,首先回忆一下Momentum梯度下降法,公式如下:
\begin{eqnarray}
\mathbi{w}_{t+1} & = & \mathbi{w}_t - \alpha \mathbi{v}_t \label{eq:11-9-update} \\
\mathbi{v}_t & = & \beta \mathbi{v}_{t-1} + (1-\beta)\frac{\partial J(\mathbi{w}_t)}{\partial \mathbi{w}_t} \label{eq:11-9-momentum}
\mathbi{w}_{t+1} & = & \mathbi{w}_t - \alpha \mathbi{v}_t \label{eq:11-15} \\
\mathbi{v}_t & = & \beta \mathbi{v}_{t-1} + (1-\beta)\frac{\partial J(\mathbi{w}_t)}{\partial \mathbi{w}_t} \label{eq:11-16}
\end{eqnarray}
\noindent 其中,$\mathbi{w}_t$表示第$t$步更新时的模型参数;$J(\mathbi{w}_t)$表示损失函数均值期望的估计;$\frac{\partial J(\mathbi{w}_t)}{\partial \mathbi{w}_t}$将指向$J(\mathbi{w}_t)$$\mathbi{w}_t$处变化最大的方向,即梯度方向;$\alpha$ 为学习率;$\mathbi{v}_t$为损失函数在前$t-1$步更新中累积的梯度动量,利用超参数$\beta$控制累积的范围。
......@@ -442,7 +437,7 @@
\parinterval 而在Nesterov加速梯度下降法中,使用的梯度不是来自于当前参数位置,而是按照之前梯度方向更新一小步的位置,以便于更好地“预测未来”,提前调整更新速率,因此,其动量的更新方式为:
\begin{eqnarray}
\mathbi{v}_t & = & \beta \mathbi{v}_{t-1} + (1-\beta)\frac{\partial J(\mathbi{w}_t)}{\partial (\mathbi{w}_{t} -\alpha \beta \mathbi{v}_{t-1} )}
\label{eq:11-10}
\label{eq:11-17}
\end{eqnarray}
\parinterval Nesterov加速梯度下降法其实是利用了二阶导数的信息,因此可以做到“向前看”,加速收敛过程\upcite{Bengio2013AdvancesIO}。为了模型的稳定训练。ConvS2S模型也采用了一些网络正则化和参数初始化的策略,使得模型在前向计算和反向计算过程中方差尽可能保持一致。
......@@ -484,7 +479,7 @@
在标准卷积中,若使用N表示卷积核的个数,也就是标准卷积输出序列的通道数,那么对于第$i$个位置的第$n$个通道$ \mathbi{z}_{i,n}^\textrm{\,std}$,其标准卷积具体计算方式如下:
\begin{eqnarray}
\mathbi{z}_{i,n}^\textrm{\,std} = \sum_{o=1}^{O} \sum_{k=0}^{K-1} \mathbi{W}_{k,o,n}^\textrm{\,std} \mathbi{x}_{i+k,o}
\label{eq:11-11}
\label{eq:11-18}
\end{eqnarray}
%在标准卷积中,$ \mathbi{z}^\textrm{\,std}$表示标准卷积的输出,$ \mathbi{z}_i^\textrm{\,std} \in \mathbb{R}^N$ ,N为卷积核的个数,也就是标准卷积输出序列的通道数。针对$ \mathbi{z}_i^\textrm{\,std} $ 中的第$n$个通道$ \mathbi{z}_{i,n}^\textrm{\,std}$,标准卷积具体计算方式如下:
......@@ -494,7 +489,7 @@
\parinterval 相应的,深度卷积只考虑不同词之间的依赖性,而不考虑不同通道之间的关系,相当于使用$O$个卷积核逐个通道对不同的词进行卷积操作。因此深度卷积不改变输出的表示维度,输出序列表示的通道数与输入序列一致,其计算方式如下:
\begin{eqnarray}
\mathbi{z}_{i,o}^\textrm{\,dw} = \sum_{k=0}^{K-1} \mathbi{W}_{k,o}^\textrm{\,dw} \mathbi{x}_{i+k,o}
\label{eq:11-12}
\label{eq:11-19}
\end{eqnarray}
\noindent 其中,$\mathbi{z}^\textrm{\,dw}$表示深度卷积的输出,$\mathbi{z}_i^\textrm{\,dw} \in \mathbb{R}^{O}$$\mathbi{W}^\textrm{\,dw} \in \mathbb{R}^{K \times O}$为深度卷积的参数,参数量只涉及卷积核大小及输入表示维度。
......@@ -503,7 +498,7 @@
\begin{eqnarray}
\mathbi{z}_{i,n}^\textrm{\,pw} &=& \sum\limits_{o=1}^{O} \mathbi{x}_{i,o} \mathbi{W}_{o,n}^\textrm{\,pw} \nonumber \\
&=& \mathbi{x}_i \mathbi{W}^\textrm{\,pw}
\label{eq:11-13}
\label{eq:11-20}
\end{eqnarray}
\noindent 其中$\mathbi{z}^\textrm{\,pw}$表示逐点卷积的输出,$\mathbi{z}_{i}^\textrm{\,pw} \in \mathbb{R}^{N}$$\mathbi{W}^\textrm{\,pw} \in \mathbb{R}^{O \times N}$为逐点卷积的参数。
......@@ -555,7 +550,7 @@
\parinterval 此外,和标准卷积不同的是,轻量卷积之前需要先对卷积参数进行归一化,具体计算过程如下:
\begin{eqnarray}
\mathbi{z}_{i,o}^\textrm{\,lw} &=& \sum_{k=0}^{K-1} \textrm{Softmax}(\mathbi{W}^\textrm{\,lw})_{k,[\frac{oa}{d}]} \mathbi{x}_{i+k,o}
\label{eq:11-14}
\label{eq:11-21}
\end{eqnarray}
\noindent 其中,$\mathbi{z}^\textrm{\,lw}$表示轻量卷积的输出,$\mathbi{z}_i^\textrm{\,lw} \in \mathbb{R}^d $$\mathbi{W}^\textrm{\,lw} \in \mathbb{R}^{K\times a}$为轻量卷积的参数。在这里,轻量卷积用来捕捉相邻词的特征,通过Softmax可以在保证关注到不同词的同时,对输出大小进行限制。
......@@ -570,7 +565,7 @@
\parinterval 在轻量卷积中,模型使用的卷积参数是静态的,与序列位置无关, 维度大小为$K\times a$;而在动态卷积中,为了增强模型的表示能力,卷积参数来自于当前位置输入的变换,具体如下:
\begin{eqnarray}
\funp{f} (\mathbi{x}_{i}) = \sum_{c=1}^d \mathbi{W}_{:,:,c} \odot \mathbi{x}_{i,c}
\label{eq:11-15}
\label{eq:11-22}
\end{eqnarray}
\parinterval 这里采用了最简单的线性变换,其中$\odot$表示矩阵的点乘(详见第九章介绍),$d$为通道数,$\mathbi{x}_i$是序列第$i$个位置的表示,$c$表示某个通道,$\mathbi{W} \in \mathbb{R}^{K \times a \times d}$为变换矩阵,$\mathbi{W}_{:,:,c}$表示其只在$d$这一维进行计算,最后生成的$\funp{f} (\mathbi{x}_i)\in \mathbb{R}^{K \times a}$就是与输入相关的卷积核参数。通过这种方式,模型可以根据不同位置的表示来确定如何关注其他位置信息的“权重”,更好地提取序列信息。同时,相比于注意力机制中两两位置确定出来的注意力权重,动态卷积线性复杂度的做法具有更高的计算效率。
......
......@@ -34,32 +34,32 @@
\vspace{0.5em}
\label{sec:12.1}
\parinterval 首先回顾一下循环神经网络处理文字序列的过程。如图\ref{fig:12-36}所示,对于单词序列$\{ w_1,...,w_m \}$,处理第$m$个单词$w_m$时(绿色方框部分),需要输入前一时刻的信息(即处理单词$w_{m-1}$),而$w_{m-1}$又依赖于$w_{m-2}$,以此类推。也就是说,如果想建立$w_m$$w_1$之间的关系,需要$m-1$次信息传递。对于长序列来说,词汇之间信息传递距离过长会导致信息在传递过程中丢失,同时这种按顺序建模的方式也使得系统对序列的处理十分缓慢。
\parinterval 首先回顾一下循环神经网络处理文字序列的过程。如图\ref{fig:12-1}所示,对于单词序列$\{ w_1,...,w_m \}$,处理第$m$个单词$w_m$时(绿色方框部分),需要输入前一时刻的信息(即处理单词$w_{m-1}$),而$w_{m-1}$又依赖于$w_{m-2}$,以此类推。也就是说,如果想建立$w_m$$w_1$之间的关系,需要$m-1$次信息传递。对于长序列来说,词汇之间信息传递距离过长会导致信息在传递过程中丢失,同时这种按顺序建模的方式也使得系统对序列的处理十分缓慢。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter12/Figures/figure-dependencies-between-words-in-a-recurrent-neural-network}
\caption{循环神经网络中单词之间的依赖关系}
\label{fig:12-36}
\label{fig:12-1}
\end{figure}
%----------------------------------------------
\parinterval 那么能否摆脱这种顺序传递信息的方式,直接对不同位置单词之间的关系进行建模,即将信息传递的距离拉近为1?自注意力机制的提出便有效解决了这个问题\upcite{DBLP:journals/corr/LinFSYXZB17}。图\ref{fig:12-37}给出了自注意力机制对序列进行建模的示例。对于单词$w_m$,自注意力机制直接建立它与前$m-1$个单词之间的关系。也就是说,$w_m$与序列中所有其他单词的距离都是1。这种方式很好地解决了长距离依赖问题,同时由于单词之间的联系都是相互独立的,因此也大大提高了模型的并行度。
\parinterval 那么能否摆脱这种顺序传递信息的方式,直接对不同位置单词之间的关系进行建模,即将信息传递的距离拉近为1?自注意力机制的提出便有效解决了这个问题\upcite{DBLP:journals/corr/LinFSYXZB17}。图\ref{fig:12-2}给出了自注意力机制对序列进行建模的示例。对于单词$w_m$,自注意力机制直接建立它与前$m-1$个单词之间的关系。也就是说,$w_m$与序列中所有其他单词的距离都是1。这种方式很好地解决了长距离依赖问题,同时由于单词之间的联系都是相互独立的,因此也大大提高了模型的并行度。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter12/Figures/figure-dependencies-between-words-of-attention}
\caption{自注意力机制中单词之间的依赖关系}
\label{fig:12-37}
\label{fig:12-2}
\end{figure}
%----------------------------------------------
\parinterval 自注意力机制也可以被看作是一个序列表示模型。比如,对于每个目标位置$j$,都生成一个与之对应的源语句子表示,它的形式为:
\begin{eqnarray}
\mathbi{C}_j & = & \sum_i \alpha_{i,j}\mathbi{h}_i
\label{eq:12-4201}
\label{eq:12-1}
\end{eqnarray}
\noindent 其中,$\mathbi{h}_i$ 为源语句子每个位置的表示结果,$\alpha_{i,j}$是目标位置$j$$\mathbi{h}_i$的注意力权重。以源语句子为例,自注意力机制将序列中每个位置的表示$\mathbi{h}_i$看作$\mathrm{query}$(查询),并且将所有位置的表示看作$\mathrm{key}$(键)和$\mathrm{value}$ (值)。自注意力模型通过计算当前位置与所有位置的匹配程度,也就是在注意力机制中提到的注意力权重,来对各个位置的$\mathrm{value}$进行加权求和。得到的结果可以被看作是在这个句子中当前位置的抽象表示。这个过程,可以叠加多次,形成多层注意力模型,对输入序列中各个位置进行更深层的表示。
......@@ -69,16 +69,16 @@
\centering
\input{./Chapter12/Figures/figure-example-of-self-attention-mechanism-calculation}
\caption{自注意力计算实例}
\label{fig:12-38}
\label{fig:12-3}
\end{figure}
%----------------------------------------------
\parinterval 举个例子,如图\ref{fig:12-38}所示,一个汉语句子包含5个词。这里,用$h$(他)表示“他”当前的表示结果,其中$h(\cdot)$是一个函数,用于返回输入单词所在位置对应的表示结果(向量)。如果把“他”看作目标,这时$\mathrm{query}$ 就是$h$(他),$\mathrm{key}$$\mathrm{value}$是图中所有位置的表示,即:{$h$(他)、$h$(什么)、$h$(也)、$h$(没)、$h$(学)}。在自注意力模型中,首先计算$\mathrm{query}$$\mathrm{key}$的相关度,这里用$\alpha_i$表示$h$(他)和位置$i$的表示之间的相关性。然后,把$\alpha_i$作为权重,对不同位置上的$\mathrm{value}$进行加权求和。最终,得到新的表示结果$\tilde{h}$ (他):
\parinterval 举个例子,如图\ref{fig:12-3}所示,一个汉语句子包含5个词。这里,用$h$(他)表示“他”当前的表示结果,其中$h(\cdot)$是一个函数,用于返回输入单词所在位置对应的表示结果(向量)。如果把“他”看作目标,这时$\mathrm{query}$ 就是$h$(他),$\mathrm{key}$$\mathrm{value}$是图中所有位置的表示,即:{$h$(他)、$h$(什么)、$h$(也)、$h$(没)、$h$(学)}。在自注意力模型中,首先计算$\mathrm{query}$$\mathrm{key}$的相关度,这里用$\alpha_i$表示$h$(他)和位置$i$的表示之间的相关性。然后,把$\alpha_i$作为权重,对不同位置上的$\mathrm{value}$进行加权求和。最终,得到新的表示结果$\tilde{h}$ (他):
\begin{eqnarray}
\tilde{h} (\textrm{} ) & = & \alpha_1 {h} (\textrm{} ) + \alpha_2 {h} (\textrm{什么}) + \alpha_3 {h} (\textrm{} ) + \nonumber \\
& & \alpha_4 {h} (\textrm{} ) +\alpha_5 {h} (\textrm{} )
\label{eq:12-42}
\label{eq:12-2}
\end{eqnarray}
......@@ -102,13 +102,13 @@
\parinterval 首先再来回顾一下{\chapterten}介绍的循环神经网络,虽然它很强大,但是也存在一些弊端。其中比较突出的问题是,循环神经网络每个循环单元都有向前依赖性,也就是当前时间步的处理依赖前一时间步处理的结果。这个性质可以使序列的“历史”信息不断被传递,但是也造成模型运行效率的下降。特别是对于自然语言处理任务,序列往往较长,无论是传统的RNN结构,还是更为复杂的LSTM结构,都需要很多次循环单元的处理才能够捕捉到单词之间的长距离依赖。由于需要多个循环单元的处理,距离较远的两个单词之间的信息传递变得很复杂。
\parinterval 针对这些问题,研究人员提出了一种全新的模型$\ \dash\ $Transformer\index{Transformer}\upcite{vaswani2017attention}。与循环神经网络等传统模型不同,Transformer模型仅仅使用自注意力机制和标准的前馈神经网络,完全不依赖任何循环单元或者卷积操作。自注意力机制的优点在于可以直接对序列中任意两个单元之间的关系进行建模,这使得长距离依赖等问题可以更好地被求解。此外,自注意力机制非常适合在GPU 上进行并行化,因此模型训练的速度更快。表\ref{tab:12-11}对比了RNN、CNN和Transformer层类型的复杂度\footnote{顺序操作数指模型处理一个序列所需要的操作数,由于Transformer和CNN都可以并行计算,所以是1;路径长度指序列中任意两个单词在网络中的距离。}
\parinterval 针对这些问题,研究人员提出了一种全新的模型$\ \dash\ $Transformer\index{Transformer}\upcite{vaswani2017attention}。与循环神经网络等传统模型不同,Transformer模型仅仅使用自注意力机制和标准的前馈神经网络,完全不依赖任何循环单元或者卷积操作。自注意力机制的优点在于可以直接对序列中任意两个单元之间的关系进行建模,这使得长距离依赖等问题可以更好地被求解。此外,自注意力机制非常适合在GPU 上进行并行化,因此模型训练的速度更快。表\ref{tab:12-1}对比了RNN、CNN和Transformer层类型的复杂度\footnote{顺序操作数指模型处理一个序列所需要的操作数,由于Transformer和CNN都可以并行计算,所以是1;路径长度指序列中任意两个单词在网络中的距离。}
%----------------------------------------------
\begin{table}[htp]
\centering
\caption{ RNN、CNN、Transformer的层类型复杂度对比\upcite{vaswani2017attention}$n$表示序列长度,$d$表示隐层大小,$k$表示卷积核大小) }
\label{tab:12-11}
\label{tab:12-1}
\begin{tabular}{c | c c c c}
\rule{0pt}{20pt} 模型 & 层类型 & \begin{tabular}[l]{@{}l@{}}复杂度\end{tabular} & \begin{tabular}[l]{@{}l@{}}最小顺序 \\ 操作数\end{tabular} & \begin{tabular}[l]{@{}l@{}}最大路径\\ 长度\end{tabular} \\ \hline
\rule{0pt}{13pt} Transformer & 自注意力 &$O(n^2\cdot d)$ &$O(1)$ &$O(1)$ \\
......@@ -118,13 +118,13 @@
\end{table}
%----------------------------------------------
\parinterval Transformer在被提出之后,很快就席卷了整个自然语言处理领域。实际上,也可以把Transformer当作一种表示模型,因此也被大量地使用在自然语言处理的其他领域,甚至图像处理\upcite{DBLP:journals/corr/abs-1802-05751}和语音处理\upcite{DBLP:conf/icassp/DongXX18,DBLP:conf/interspeech/GulatiQCPZYHWZW20}中也能看到它的影子。比如,目前非常流行的BERT等预训练模型就是基于Transformer。表\ref{tab:12-12}展示了Transformer在WMT英德和英法机器翻译任务上的性能。它能用更少的计算量(FLOPS)达到比其他模型更好的翻译品质\footnote{FLOPS = floating-point operations per second,即每秒浮点运算次数。它是度量计算机运算规模的常用单位}
\parinterval Transformer在被提出之后,很快就席卷了整个自然语言处理领域。实际上,也可以把Transformer当作一种表示模型,因此也被大量地使用在自然语言处理的其他领域,甚至图像处理\upcite{DBLP:journals/corr/abs-1802-05751}和语音处理\upcite{DBLP:conf/icassp/DongXX18,DBLP:conf/interspeech/GulatiQCPZYHWZW20}中也能看到它的影子。比如,目前非常流行的BERT等预训练模型就是基于Transformer。表\ref{tab:12-2}展示了Transformer在WMT英德和英法机器翻译任务上的性能。它能用更少的计算量(FLOPS)达到比其他模型更好的翻译品质\footnote{FLOPS = floating-point operations per second,即每秒浮点运算次数。它是度量计算机运算规模的常用单位}
%----------------------------------------------
\begin{table}[htp]
\centering
\caption{ 不同翻译模型性能对比\upcite{vaswani2017attention}}
\label{tab:12-12}
\label{tab:12-2}
\begin{tabular}{l l l l}
\multicolumn{1}{l|}{\multirow{2}{*}{系统}} & \multicolumn{2}{c}{BLEU[\%]} & \multirow{2}{*}{\parbox{6em}{模型训练代价 (FLOPs)}} \\
\multicolumn{1}{l|}{} & EN-DE & EN-FR & \\ \hline
......@@ -149,11 +149,11 @@
\centering
\input{./Chapter12/Figures/figure-transformer}
\caption{ Transformer结构}
\label{fig:12-39}
\label{fig:12-4}
\end{figure}
%----------------------------------------------
\parinterval\ref{fig:12-39}展示了Transformer的结构。编码器由若干层组成(绿色虚线框就代表一层)。每一层(Layer)的输入都是一个向量序列,输出是同样大小的向量序列,而Transformer层的作用是对输入进行进一步的抽象,得到新的表示结果。不过这里的层并不是指单一的神经网络结构,它里面由若干不同的模块组成,包括:
\parinterval\ref{fig:12-4}展示了Transformer的结构。编码器由若干层组成(绿色虚线框就代表一层)。每一层(Layer)的输入都是一个向量序列,输出是同样大小的向量序列,而Transformer层的作用是对输入进行进一步的抽象,得到新的表示结果。不过这里的层并不是指单一的神经网络结构,它里面由若干不同的模块组成,包括:
\begin{itemize}
\vspace{0.5em}
......@@ -169,20 +169,20 @@
\parinterval 以上操作就构成了Transformer的一层,各个模块执行的顺序可以简单描述为:Self-Attention $\to$ Residual Connection $\to$ Layer Normalization $\to$ Feed Forward Network $\to$ Residual Connection $\to$ Layer Normalization。编码器可以包含多个这样的层,比如,可以构建一个六层编码器,每层都执行上面的操作。最上层的结果作为整个编码的结果,会被传入解码器。
\parinterval 解码器的结构与编码器十分类似。它也是由若干层组成,每一层包含编码器中的所有结构,即:自注意力子层、前馈神经网络子层、残差连接和层标准化模块。此外,为了捕捉源语言的信息,解码器又引入了一个额外的{\small\sffamily\bfseries{编码-解码注意力子层}}\index{编码-解码注意力子层}(Encoder-Decoder Attention Sub-layer)\index{Encoder-Decoder Attention Sub-layer}。这个新的子层,可以帮助模型使用源语言句子的表示信息生成目标语不同位置的表示。编码-解码注意力子层仍然基于自注意力机制,因此它和自注意力子层的结构是相同的,只是$\mathrm{query}$$\mathrm{key}$$\mathrm{value}$的定义不同。比如,在解码端,自注意力子层的$\mathrm{query}$$\mathrm{key}$$\mathrm{value}$是相同的,它们都等于解码端每个位置的表示。而在编码-解码注意力子层中,$\mathrm{query}$是解码端每个位置的表示,此时$\mathrm{key}$$\mathrm{value}$是相同的,等于编码端每个位置的表示。图\ref{fig:12-40}给出了这两种不同注意力子层输入的区别。
\parinterval 解码器的结构与编码器十分类似。它也是由若干层组成,每一层包含编码器中的所有结构,即:自注意力子层、前馈神经网络子层、残差连接和层标准化模块。此外,为了捕捉源语言的信息,解码器又引入了一个额外的{\small\sffamily\bfseries{编码-解码注意力子层}}\index{编码-解码注意力子层}(Encoder-Decoder Attention Sub-layer)\index{Encoder-Decoder Attention Sub-layer}。这个新的子层,可以帮助模型使用源语言句子的表示信息生成目标语不同位置的表示。编码-解码注意力子层仍然基于自注意力机制,因此它和自注意力子层的结构是相同的,只是$\mathrm{query}$$\mathrm{key}$$\mathrm{value}$的定义不同。比如,在解码端,自注意力子层的$\mathrm{query}$$\mathrm{key}$$\mathrm{value}$是相同的,它们都等于解码端每个位置的表示。而在编码-解码注意力子层中,$\mathrm{query}$是解码端每个位置的表示,此时$\mathrm{key}$$\mathrm{value}$是相同的,等于编码端每个位置的表示。图\ref{fig:12-5}给出了这两种不同注意力子层输入的区别。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter12/Figures/figure-self-att-vs-enco-deco-att}
\caption{ 注意力模型的输入(自注意力子层 vs 编码-解码注意力子层)}
\label{fig:12-40}
\label{fig:12-5}
\end{figure}
%----------------------------------------------
\parinterval 此外,编码端和解码端都有输入的词序列。编码端的词序列输入是为了对其进行表示,进而解码端能从编码端访问到源语言句子的全部信息。解码端的词序列输入是为了进行目标语的生成,本质上它和语言模型是一样的,在得到前$n-1$个单词的情况下输出第$n$个单词。除了输入词序列的词嵌入,Transformer中也引入了位置嵌入,以表示每个位置信息。原因是,自注意力机制没有显性地对位置进行表示,因此也无法考虑词序。在输入中引入位置信息可以让自注意力机制间接地感受到每个词的位置,进而保证对序列表示的合理性。最终,整个模型的输出由一个Softmax层完成,它和循环神经网络中的输出层是完全一样的。
\parinterval 在进行更详细的介绍前,先利用图\ref{fig:12-39}简单了解一下Transformer模型是如何进行翻译的。首先,Transformer将源语言句子“我/很/好”的词嵌入融合位置编码后作为输入。然后,编码器对输入的源语句子进行逐层抽象,得到包含丰富的上下文信息的源语表示并传递给解码器。解码器的每一层,使用自注意力子层对输入解码端的表示进行加工,之后再使用编码-解码注意力子层融合源语句子的表示信息。就这样逐词生成目标语译文单词序列。解码器每个位置的输入是当前单词(比如,“I”),而这个位置的输出是下一个单词(比如,“am”),这个设计和标准的神经语言模型是完全一样的。
\parinterval 在进行更详细的介绍前,先利用图\ref{fig:12-4}简单了解一下Transformer模型是如何进行翻译的。首先,Transformer将源语言句子“我/很/好”的词嵌入融合位置编码后作为输入。然后,编码器对输入的源语句子进行逐层抽象,得到包含丰富的上下文信息的源语表示并传递给解码器。解码器的每一层,使用自注意力子层对输入解码端的表示进行加工,之后再使用编码-解码注意力子层融合源语句子的表示信息。就这样逐词生成目标语译文单词序列。解码器每个位置的输入是当前单词(比如,“I”),而这个位置的输出是下一个单词(比如,“am”),这个设计和标准的神经语言模型是完全一样的。
\parinterval 当然,这里可能还有很多疑惑,比如,什么是位置编码?Transformer的自注意力机制具体是怎么进行计算的,其结构是怎样的?层标准化又是什么?等等。下面就一一展开介绍。
......@@ -192,59 +192,59 @@
\section{位置编码}
\parinterval 在使用循环神经网络进行序列的信息提取时,每个时刻的运算都要依赖前一个时刻的输出,具有一定的时序性,这也与语言具有顺序的特点相契合。而采用自注意力机制对源语言和目标语言序列进行处理时,直接对当前位置和序列中的任意位置进行建模,忽略了词之间的顺序关系,例如图\ref{fig:12-41}中两个语义不同的句子,通过自注意力得到的表示$\tilde{h}$(机票)却是相同的。
\parinterval 在使用循环神经网络进行序列的信息提取时,每个时刻的运算都要依赖前一个时刻的输出,具有一定的时序性,这也与语言具有顺序的特点相契合。而采用自注意力机制对源语言和目标语言序列进行处理时,直接对当前位置和序列中的任意位置进行建模,忽略了词之间的顺序关系,例如图\ref{fig:12-6}中两个语义不同的句子,通过自注意力得到的表示$\tilde{h}$(机票)却是相同的。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter12/Figures/figure-calculation-of-context-vector-c}
\caption{“机票”的更进一步抽象表示$\tilde{\mathbi{h}}$的计算}
\label{fig:12-41}
\label{fig:12-6}
\end{figure}
%----------------------------------------------
\parinterval 为了解决这个问题,Transformer在原有的词向量输入基础上引入了位置编码,来表示单词之间的顺序关系。位置编码在Transformer结构中的位置如图\ref{fig:12-42},它是Transformer成功的一个重要因素。
\parinterval 为了解决这个问题,Transformer在原有的词向量输入基础上引入了位置编码,来表示单词之间的顺序关系。位置编码在Transformer结构中的位置如图\ref{fig:12-7},它是Transformer成功的一个重要因素。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter12/Figures/figure-transformer-input-and-position-encoding}
\caption{Transformer输入与位置编码}
\label{fig:12-42}
\label{fig:12-7}
\end{figure}
%----------------------------------------------
\parinterval 位置编码的计算方式有很多种,Transformer使用不同频率的正余弦函数:
\begin{eqnarray}
\textrm{PE}(\textrm{pos},2i) & = & \textrm{sin} (\frac{\textrm{pos}}{10000^{2i/d_{\textrm{model}}}}) \label{eq:12-43} \\
\textrm{PE}(\textrm{pos},2i+1) & = & \textrm{cos} (\frac{\textrm{pos}}{10000^{2i/d_{\textrm{model}}}}) \label{eq:12-44}
\textrm{PE}(\textrm{pos},2i) & = & \textrm{sin} (\frac{\textrm{pos}}{10000^{2i/d_{\textrm{model}}}}) \label{eq:12-3} \\
\textrm{PE}(\textrm{pos},2i+1) & = & \textrm{cos} (\frac{\textrm{pos}}{10000^{2i/d_{\textrm{model}}}}) \label{eq:12-4}
\end{eqnarray}
\noindent 式中PE($\cdot$)表示位置编码的函数,$\textrm{pos}$表示单词的位置,$i$代表位置编码向量中的第几维,$d_{\textrm{model}}$是Transformer的一个基础参数,表示每个位置的隐层大小。因为,正余弦函数的编码各占一半,因此当位置编码的维度为512 时,$i$ 的范围是0-255。 在Transformer中,位置编码的维度和词嵌入向量的维度相同(均为$d_{\textrm{model}}$),模型通过将二者相加作为模型输入,如图\ref{fig:12-43}所示。
\noindent 式中PE($\cdot$)表示位置编码的函数,$\textrm{pos}$表示单词的位置,$i$代表位置编码向量中的第几维,$d_{\textrm{model}}$是Transformer的一个基础参数,表示每个位置的隐层大小。因为,正余弦函数的编码各占一半,因此当位置编码的维度为512 时,$i$ 的范围是0-255。 在Transformer中,位置编码的维度和词嵌入向量的维度相同(均为$d_{\textrm{model}}$),模型通过将二者相加作为模型输入,如图\ref{fig:12-8}所示。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter12/Figures/figure-a-combination-of-position-encoding-and-word-encoding}
\caption{位置编码与词编码的组合}
\label{fig:12-43}
\label{fig:12-8}
\end{figure}
%----------------------------------------------
\parinterval 那么为什么通过这种计算方式可以很好的表示位置信息?有几方面原因。首先,正余弦函数是具有上下界的周期函数,用正余弦函数可将长度不同的序列的位置编码的范围都固定到$[-1,1]$,这样在与词的编码进行相加时,不至于产生太大差距。另外位置编码的不同维度对应不同的正余弦曲线,这为多维的表示空间赋予一定意义。最后,根据三角函数的性质:
\begin{eqnarray}
\textrm{sin}(\alpha + \beta) &=& \textrm{sin}\alpha \cdot \textrm{cos} \beta + \textrm{cos} \alpha \cdot \textrm{sin} \beta \nonumber \\
\textrm{sin}(\alpha + \beta) &=& \textrm{sin}\alpha \cdot \textrm{cos} \beta + \textrm{cos} \alpha \cdot \textrm{sin} \beta \label{eq:12-5} \\
\textrm{cos}(\alpha + \beta) &=& \textrm{cos} \alpha \cdot \textrm{cos} \beta - \textrm{sin} \alpha \cdot \textrm{sin} \beta
\label{eq:12-45}
\label{eq:12-6}
\end{eqnarray}
\parinterval 可以得到第$pos+k$个位置的编码为:
\begin{eqnarray}
\textrm{PE}(\textrm{pos}+k,2i) &=& \textrm{PE}(\textrm{pos},2i) \cdot \textrm{PE}(k,2i+1) + \nonumber \\
& & \textrm{PE}(\textrm{pos},2i+1) \cdot \textrm{PE}(k,2i)\\
& & \textrm{PE}(\textrm{pos},2i+1) \cdot \textrm{PE}(k,2i) \label{eq:12-7} \\
\textrm{PE}(\textrm{pos}+k ,2i+1) &=& \textrm{PE}(\textrm{pos},2i+1) \cdot \textrm{PE}(k,2i+1) - \nonumber \\
& & \textrm{PE}(\textrm{pos},2i) \cdot \textrm{PE}(k,2i)
\label{eq:12-46}
\label{eq:12-8}
\end{eqnarray}
\noindent 即对于任意固定的偏移量$k$$\textrm{PE}(\textrm{pos}+k)$能被表示成$\textrm{PE}(\textrm{pos})$的线性函数,换句话说,位置编码可以表示词之间的距离。在实践中发现,位置编码对Transformer系统的性能有很大影响。对其进行改进也会带来进一步的性能提升\upcite{Shaw2018SelfAttentionWR}
......@@ -255,14 +255,14 @@
\section{基于点乘的多头注意力机制}
\parinterval Transformer模型摒弃了循环单元和卷积等结构,完全基于注意力机制来构造模型,其中包含着大量的注意力计算。比如,可以通过自注意力机制对源语言和目标语言序列进行信息提取,并通过编码-解码注意力对双语句对之间的关系进行建模。图\ref{fig:12-44}中红色方框部分是Transformer中使用注意力机制的模块。而这些模块都是由基于点乘的多头注意力机制实现的。
\parinterval Transformer模型摒弃了循环单元和卷积等结构,完全基于注意力机制来构造模型,其中包含着大量的注意力计算。比如,可以通过自注意力机制对源语言和目标语言序列进行信息提取,并通过编码-解码注意力对双语句对之间的关系进行建模。图\ref{fig:12-9}中红色方框部分是Transformer中使用注意力机制的模块。而这些模块都是由基于点乘的多头注意力机制实现的。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter12/Figures/figure-position-of-self-attention-mechanism-in-the-model}
\caption{自注意力机制在模型中的位置}
\label{fig:12-44}
\label{fig:12-9}
\end{figure}
%----------------------------------------------
......@@ -282,7 +282,7 @@
\begin{eqnarray}
\textrm{Attention}(\mathbi{Q},\mathbi{K},\mathbi{V}) = \textrm{Softmax}
( \frac{\mathbi{Q}\mathbi{K}^{\textrm{T}}} {\sqrt{d_k}} + \mathbi{Mask} ) \mathbi{V}
\label{eq:12-47}
\label{eq:12-9}
\end{eqnarray}
\noindent 首先,通过对$\mathbi{Q}$$\mathbi{K}$的转置进行矩阵乘法操作,计算得到一个维度大小为$L \times L$的相关性矩阵,即$\mathbi{Q}\mathbi{K}^{\textrm{T}}$,它表示一个序列上任意两个位置的相关性。再通过系数1/$\sqrt{d_k}$进行放缩操作,放缩可以减少相关性矩阵的方差,具体体现在运算过程中实数矩阵中的数值不会过大,有利于模型训练。
......@@ -290,25 +290,25 @@
\parinterval 在此基础上,通过对相关性矩阵累加一个掩码矩阵$\mathbi{Mask}$,来屏蔽掉矩阵中的无用信息。比如,在编码端,如果需要对多个句子同时处理,由于这些句子长度不统一,需要对句子补齐。再比如,在解码端,训练的时候需要屏蔽掉当前目标语位置右侧的单词,因此这些单词在推断的时候是看不到的。
\parinterval 随后,使用Softmax函数对相关性矩阵在行的维度上进行归一化操作,这可以理解为对第$i$ 行进行归一化,结果对应了$\mathbi{V}$ 中不同位置上向量的注意力权重。对于$\mathrm{value}$ 的加权求和,可以直接用相关性系数和$\mathbi{V}$ 进行矩阵乘法得到,即$\textrm{Softmax}
( \frac{\mathbi{Q}\mathbi{K}^{\textrm{T}}} {\sqrt{d_k}} + \mathbi{Mask} )$$\mathbi{V}$进行矩阵乘。最终得到自注意力的输出,它和输入的$\mathbi{V}$的大小是一模一样的。图\ref{fig:12-45}展示了点乘注意力计算的全过程。
( \frac{\mathbi{Q}\mathbi{K}^{\textrm{T}}} {\sqrt{d_k}} + \mathbi{Mask} )$$\mathbi{V}$进行矩阵乘。最终得到自注意力的输出,它和输入的$\mathbi{V}$的大小是一模一样的。图\ref{fig:12-10}展示了点乘注意力计算的全过程。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter12/Figures/figure-point-product-attention-model}
\caption{点乘注意力模型 }
\label{fig:12-45}
\label{fig:12-10}
\end{figure}
%----------------------------------------------
\parinterval 下面举个简单的例子介绍点乘注意力的具体计算过程。如图\ref{fig:12-46}所示,用黄色、蓝色和橙色的矩阵分别表示$\mathbi{Q}$$\mathbi{K}$$\mathbi{V}$$\mathbi{Q}$$\mathbi{K}$$\mathbi{V}$中的每一个小格都对应一个单词在模型中的表示(即一个向量)。首先,通过点乘、放缩、掩码等操作得到相关性矩阵,即粉色部分。其次,将得到的中间结果矩阵(粉色)的每一行使用Softmax激活函数进行归一化操作,得到最终的权重矩阵,也就是图中的红色矩阵。红色矩阵中的每一行都对应一个注意力分布。最后,按行对$\mathbi{V}$进行加权求和,便得到了每个单词通过点乘注意力机制计算得到的表示。这里面,主要的计算消耗是两次矩阵乘法,即$\mathbi{Q}$$\mathbi{K}^{\textrm{T}}$的乘法、相关性矩阵和$\mathbi{V}$的乘法。这两个操作都可以在GPU上高效地完成,因此可以一次性计算出序列中所有单词之间的注意力权重,并完成所有位置表示的加权求和过程,这样大大提高了模型计算的并行度。
\parinterval 下面举个简单的例子介绍点乘注意力的具体计算过程。如图\ref{fig:12-11}所示,用黄色、蓝色和橙色的矩阵分别表示$\mathbi{Q}$$\mathbi{K}$$\mathbi{V}$$\mathbi{Q}$$\mathbi{K}$$\mathbi{V}$中的每一个小格都对应一个单词在模型中的表示(即一个向量)。首先,通过点乘、放缩、掩码等操作得到相关性矩阵,即粉色部分。其次,将得到的中间结果矩阵(粉色)的每一行使用Softmax激活函数进行归一化操作,得到最终的权重矩阵,也就是图中的红色矩阵。红色矩阵中的每一行都对应一个注意力分布。最后,按行对$\mathbi{V}$进行加权求和,便得到了每个单词通过点乘注意力机制计算得到的表示。这里面,主要的计算消耗是两次矩阵乘法,即$\mathbi{Q}$$\mathbi{K}^{\textrm{T}}$的乘法、相关性矩阵和$\mathbi{V}$的乘法。这两个操作都可以在GPU上高效地完成,因此可以一次性计算出序列中所有单词之间的注意力权重,并完成所有位置表示的加权求和过程,这样大大提高了模型计算的并行度。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter12/Figures/figure-process-of-5}
\caption{\ref{eq:12-47}的执行过程示例}
\label{fig:12-46}
\caption{公式\eqref{eq:12-9}的执行过程示例}
\label{fig:12-11}
\end{figure}
%----------------------------------------------
......@@ -320,7 +320,7 @@
\parinterval Transformer中使用的另一项重要技术是{\small\sffamily\bfseries{多头注意力}}\index{多头注意力}(Multi-head Attention)\index{Multi-head Attention}。“多头”可以理解成将原来的$\mathbi{Q}$$\mathbi{K}$$\mathbi{V}$按照隐层维度平均切分成多份。假设切分$h$份,那么最终会得到$\mathbi{Q} = \{ \mathbi{Q}_1, \mathbi{Q}_2,...,\mathbi{Q}_h \}$$\mathbi{K}=\{ \mathbi{K}_1,\mathbi{K}_2,...,\mathbi{K}_h \}$$\mathbi{V}=\{ \mathbi{V}_1, \mathbi{V}_2,...,\mathbi{V}_h \}$。多头注意力机制就是用每一个切分得到的$\mathbi{Q}$$\mathbi{K}$$\mathbi{V}$独立的进行注意力计算,即第$i$个头的注意力计算结果$\mathbi{head}_i = \textrm{Attention}(\mathbi{Q}_i,\mathbi{K}_i, \mathbi{V}_i)$
\parinterval 下面根据图\ref{fig:12-48}详细介绍多头注意力的计算过程:
\parinterval 下面根据图\ref{fig:12-12}详细介绍多头注意力的计算过程:
\begin{itemize}
\vspace{0.5em}
......@@ -337,15 +337,15 @@
\centering
\input{./Chapter12/Figures/figure-multi-head-attention-model}
\caption{多头注意力模型}
\label{fig:12-48}
\label{fig:12-12}
\end{figure}
%----------------------------------------------
\parinterval 多头机制可以被形式化描述为如下公式:
\begin{eqnarray}
\textrm{MultiHead}(\mathbi{Q}, \mathbi{K} , \mathbi{V})& = & \textrm{Concat} (\mathbi{head}_1, ... , \mathbi{head}_h ) \mathbi{W}^{\,o} \label{eq:12-48} \\
\textrm{MultiHead}(\mathbi{Q}, \mathbi{K} , \mathbi{V})& = & \textrm{Concat} (\mathbi{head}_1, ... , \mathbi{head}_h ) \mathbi{W}^{\,o} \label{eq:12-10} \\
\mathbi{head}_i & = &\textrm{Attention} (\mathbi{Q}\mathbi{W}_i^{\,Q} , \mathbi{K}\mathbi{W}_i^{\,K} , \mathbi{V}\mathbi{W}_i^{\,V} )
\label{eq:12-49}
\label{eq:12-11}
\end{eqnarray}
\parinterval 多头机制的好处是允许模型在不同的表示子空间里学习。在很多实验中发现,不同表示空间的头捕获的信息是不同的,比如,在使用Transformer处理自然语言时,有的头可以捕捉句法信息,有头可以捕捉词法信息。
......@@ -356,13 +356,13 @@
\subsection{掩码操作}
\parinterval 在公式\eqref{eq:12-47}中提到了{\small\bfnew{掩码}}\index{掩码}(Mask\index{Mask}),它的目的是对向量中某些值进行掩盖,避免无关位置的数值对运算造成影响。Transformer中的掩码主要应用在注意力机制中的相关性系数计算,具体方式是在相关性系数矩阵上累加一个掩码矩阵。该矩阵在需要掩码的位置的值为负无穷$-$inf(具体实现时是一个非常小的数,比如$-$1e9),其余位置为0,这样在进行了Softmax 归一化操作之后,被掩码掉的位置计算得到的权重便近似为0,也就是说对无用信息分配的权重为0,从而避免了其对结果产生影响。Transformer包含两种掩码:
\parinterval 在公式\eqref{eq:12-9}中提到了{\small\bfnew{掩码}}\index{掩码}(Mask\index{Mask}),它的目的是对向量中某些值进行掩盖,避免无关位置的数值对运算造成影响。Transformer中的掩码主要应用在注意力机制中的相关性系数计算,具体方式是在相关性系数矩阵上累加一个掩码矩阵。该矩阵在需要掩码的位置的值为负无穷$-$inf(具体实现时是一个非常小的数,比如$-$1e9),其余位置为0,这样在进行了Softmax 归一化操作之后,被掩码掉的位置计算得到的权重便近似为0,也就是说对无用信息分配的权重为0,从而避免了其对结果产生影响。Transformer包含两种掩码:
\begin{itemize}
\vspace{0.5em}
\item {\small\bfnew{句长补全掩码}}\index{句长补全掩码}(Padding Mask\index{Padding Mask})。在批量处理多个样本时(训练或解码),由于要对源语言和目标语言的输入进行批次化处理,而每个批次内序列的长度不一样,为了方便对批次内序列进行矩阵表示,需要进行对齐操作,即在较短的序列后面填充0来占位(padding操作)。而这些填充的位置没有意义,不参与注意力机制的计算,因此,需要进行掩码 操作,屏蔽其影响。
\vspace{0.5em}
\item {\small\bfnew{未来信息掩码}}\index{未来信息掩码}(Future Mask\index{Future Mask})。对于解码器来说,由于在预测的时候是自左向右进行的,即第$t$时刻解码器的输出只能依赖于$t$时刻之前的输出。且为了保证训练解码一致,避免在训练过程中观测到目标语端每个位置未来的信息,因此需要对未来信息进行屏蔽。具体的做法是:构造一个上三角值全为-inf的Mask矩阵,也就是说,在解码端计算中,在当前位置,通过未来信息掩码把序列之后的信息屏蔽掉了,避免了$t$ 时刻之后的位置对当前的计算产生影响。图\ref{fig:12-47}给出了一个具体的实例。
\item {\small\bfnew{未来信息掩码}}\index{未来信息掩码}(Future Mask\index{Future Mask})。对于解码器来说,由于在预测的时候是自左向右进行的,即第$t$时刻解码器的输出只能依赖于$t$时刻之前的输出。且为了保证训练解码一致,避免在训练过程中观测到目标语端每个位置未来的信息,因此需要对未来信息进行屏蔽。具体的做法是:构造一个上三角值全为-inf的Mask矩阵,也就是说,在解码端计算中,在当前位置,通过未来信息掩码把序列之后的信息屏蔽掉了,避免了$t$ 时刻之后的位置对当前的计算产生影响。图\ref{fig:12-13}给出了一个具体的实例。
%----------------------------------------------
% 图3.10
......@@ -370,7 +370,7 @@
\centering
\input{./Chapter12/Figures/figure-mask-instance-for-future-positions-in-transformer}
\caption{Transformer中对于未来位置进行的屏蔽的掩码实例}
\label{fig:12-47}
\label{fig:12-13}
\end{figure}
%----------------------------------------------
......@@ -409,7 +409,7 @@
\centering
\input{./Chapter12/Figures/figure-position-of-difference-and-layer-regularization-in-the-model}
\caption{残差和层标准化在模型中的位置}
\label{fig:12-50}
\label{fig:12-14}
\end{figure}
%----------------------------------------------
......@@ -417,25 +417,25 @@
\begin{eqnarray}
%x_{l+1} = x_l + F (x_l)
\mathbi{x}^{l+1} = F (\mathbi{x}^l) + \mathbi{x}^l
\label{eq:12-50}
\label{eq:12-12}
\end{eqnarray}
\noindent 其中$\mathbi{x}^l$表示第$l$层网络的输入向量,$F (\mathbi{x}^l)$是子层运算,这样会导致不同层(或子层)的结果之间的差异性很大,造成训练过程不稳定、训练时间较长。为了避免这种情况,在每层中加入了层标准化操作\upcite{Ba2016LayerN}。图\ref{fig:12-50} 中的红色方框展示了Transformer中残差和层标准化的位置。层标准化的计算公式如下:
\noindent 其中$\mathbi{x}^l$表示第$l$层网络的输入向量,$F (\mathbi{x}^l)$是子层运算,这样会导致不同层(或子层)的结果之间的差异性很大,造成训练过程不稳定、训练时间较长。为了避免这种情况,在每层中加入了层标准化操作\upcite{Ba2016LayerN}。图\ref{fig:12-14} 中的红色方框展示了Transformer中残差和层标准化的位置。层标准化的计算公式如下:
\begin{eqnarray}
\textrm{LN}(\mathbi{x}) = g \cdot \frac{\mathbi{x}- \mu} {\sigma} + b
\label{eq:12-51}
\label{eq:12-13}
\end{eqnarray}
\noindent 该公式使用均值$\mu$和方差$\sigma$对样本进行平移缩放,将数据规范化为均值为0,方差为1的标准分布。$g$$b$是可学习的参数。
\parinterval 在Transformer中经常使用的层标准化操作有两种结构,分别是{\small\bfnew{后标准化}}\index{后标准化}(Post-norm)\index{Post-norm}{\small\bfnew{前标准化}}\index{前标准化}(Pre-norm)\index{Pre-norm},结构如图\ref{fig:12-51}所示。后标准化中先进行残差连接再进行层标准化,而前标准化则是在子层输入之前进行层标准化操作。在很多实践中已经发现,前标准化的方式更有利于信息传递,因此适合训练深层的Transformer模型\upcite{WangLearning}
\parinterval 在Transformer中经常使用的层标准化操作有两种结构,分别是{\small\bfnew{后标准化}}\index{后标准化}(Post-norm)\index{Post-norm}{\small\bfnew{前标准化}}\index{前标准化}(Pre-norm)\index{Pre-norm},结构如图\ref{fig:12-15}所示。后标准化中先进行残差连接再进行层标准化,而前标准化则是在子层输入之前进行层标准化操作。在很多实践中已经发现,前标准化的方式更有利于信息传递,因此适合训练深层的Transformer模型\upcite{WangLearning}
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter12/Figures/figure-different-regularization-methods}
\caption{不同标准化方式 }
\label{fig:12-51}
\label{fig:12-15}
\end{figure}
%----------------------------------------------
......@@ -445,21 +445,21 @@
\section{前馈全连接网络子层}
\parinterval 在Transformer的结构中,每一个编码层或者解码层中都包含一个前馈神经网络,它在模型中的位置如图\ref{fig:12-52}中红色方框所示。
\parinterval 在Transformer的结构中,每一个编码层或者解码层中都包含一个前馈神经网络,它在模型中的位置如图\ref{fig:12-16}中红色方框所示。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter12/Figures/figure-position-of-feedforward-neural-network-in-the-model}
\caption{前馈神经网络在模型中的位置}
\label{fig:12-52}
\label{fig:12-16}
\end{figure}
%----------------------------------------------
\parinterval Transformer使用了全连接网络。全连接网络的作用主要体现在将经过注意力操作之后的表示映射到新的空间中,新的空间会有利于接下来的非线性变换等操作。实验证明,去掉全连接网络会对模型的性能造成很大影响。Transformer的全连接前馈神经网络包含两次线性变换和一次非线性变换(ReLU激活函数:ReLU$(\mathbi{x})=\textrm{max}(0,\mathbi{x})$),每层的前馈神经网络参数不共享,计算公式如下:
\begin{eqnarray}
\textrm{FFN}(\mathbi{x}) = \textrm{max} (0,\mathbi{x}\mathbi{W}_1 + \mathbi{b}_1)\mathbi{W}_2 + \mathbi{b}_2
\label{eq:12-52}
\label{eq:12-14}
\end{eqnarray}
\noindent 其中,$\mathbi{W}_1$$\mathbi{W}_2$$\mathbi{b}_1$$\mathbi{b}_2$为模型的参数。通常情况下,前馈神经网络的隐层维度要比注意力部分的隐层维度大,而且研究人员发现这种设置对Transformer是至关重要的。 比如,注意力部分的隐层维度为512,前馈神经网络部分的隐层维度为2048。当然,继续增大前馈神经网络的隐层大小,比如设为4096,甚至8192,还可以带来性能的增益,但是前馈部分的存储消耗较大,需要更大规模GPU 设备的支持。因此在具体实现时,往往需要在翻译准确性和存储/速度之间找到一个平衡。
......@@ -489,11 +489,11 @@
\item Transformer在学习率中同样应用了学习率{\small\bfnew{预热}}\index{预热}(Warmup)\index{Warmup}策略,其计算公式如下:
\begin{eqnarray}
lrate = d_{\textrm{model}}^{-0.5} \cdot \textrm{min} (\textrm{step}^{-0.5} , \textrm{step} \cdot \textrm{warmup\_steps}^{-1.5})
\label{eq:12-53}
\label{eq:12-15}
\end{eqnarray}
\vspace{0.5em}
其中,$\textrm{step}$表示更新的次数(或步数)。通常设置网络更新的前4000步为预热阶段即$\textrm{warmup\_steps}=4000$。Transformer的学习率曲线如图\ref{fig:12-54}所示。在训练初期,学习率从一个较小的初始值逐渐增大(线性增长),当到达一定的步数,学习率再逐渐减小。这样做可以减缓在训练初期的不稳定现象,同时在模型达到相对稳定之后,通过逐渐减小的学习率让模型进行更细致的调整。这种学习率的调整方法是Transformer系统一个很大的工程贡献。
其中,$\textrm{step}$表示更新的次数(或步数)。通常设置网络更新的前4000步为预热阶段即$\textrm{warmup\_steps}=4000$。Transformer的学习率曲线如图\ref{fig:12-17}所示。在训练初期,学习率从一个较小的初始值逐渐增大(线性增长),当到达一定的步数,学习率再逐渐减小。这样做可以减缓在训练初期的不稳定现象,同时在模型达到相对稳定之后,通过逐渐减小的学习率让模型进行更细致的调整。这种学习率的调整方法是Transformer系统一个很大的工程贡献。
\vspace{0.5em}
\end{itemize}
......@@ -502,7 +502,7 @@ lrate = d_{\textrm{model}}^{-0.5} \cdot \textrm{min} (\textrm{step}^{-0.5} , \te
\centering
\input{./Chapter12/Figures/figure-lrate-of-transformer}
\caption{Transformer模型的学习率曲线}
\label{fig:12-54}
\label{fig:12-17}
\end{figure}
%----------------------------------------------
......@@ -510,14 +510,14 @@ lrate = d_{\textrm{model}}^{-0.5} \cdot \textrm{min} (\textrm{step}^{-0.5} , \te
\begin{itemize}
\vspace{0.5em}
\item {\small\bfnew{小批量训练}}\index{小批量训练}(Mini-batch Training)\index{Mini-batch Training}:每次使用一定数量的样本进行训练,即每次从样本中选择一小部分数据进行训练。这种方法的收敛较快,同时易于提高设备的利用率。批次大小通常设置为2048或4096(token数即每个批次中的单词个数)。每一个批次中的句子并不是随机选择的,模型通常会根据句子长度进行排序,选取长度相近的句子组成一个批次。这样做可以减少padding数量,提高训练效率,如图\ref{fig:12-55}
\item {\small\bfnew{小批量训练}}\index{小批量训练}(Mini-batch Training)\index{Mini-batch Training}:每次使用一定数量的样本进行训练,即每次从样本中选择一小部分数据进行训练。这种方法的收敛较快,同时易于提高设备的利用率。批次大小通常设置为2048或4096(token数即每个批次中的单词个数)。每一个批次中的句子并不是随机选择的,模型通常会根据句子长度进行排序,选取长度相近的句子组成一个批次。这样做可以减少padding数量,提高训练效率,如图\ref{fig:12-18}
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter12/Figures/figure-comparison-of-the-number-of-padding-in-batch}
\caption{不同批次生成方法对比(白色部分为padding)}
\label{fig:12-55}
\label{fig:12-18}
\end{figure}
%----------------------------------------------
\vspace{0.5em}
......@@ -539,13 +539,13 @@ lrate = d_{\textrm{model}}^{-0.5} \cdot \textrm{min} (\textrm{step}^{-0.5} , \te
\vspace{0.5em}
\end{itemize}
\parinterval 在WMT'16数据 上的实验对比如表\ref{tab:12-13}所示。可以看出,Transformer Base的BLE\\U得分虽不如另外两种模型,但其参数量是最少的。而Transformer Deep的性能整体好于Transformer Big。
\parinterval 在WMT'16数据 上的实验对比如表\ref{tab:12-3}所示。可以看出,Transformer Base的BLE\\U得分虽不如另外两种模型,但其参数量是最少的。而Transformer Deep的性能整体好于Transformer Big。
%----------------------------------------------
\begin{table}[htp]
\centering
\caption{三种Transformer模型的对比}
\label{tab:12-13}
\label{tab:12-3}
\begin{tabular}{l | l l l}
\multirow{2}{*}{系统} & \multicolumn{2}{c}{BLEU[\%]} & 模型参数量 \\
& EN-DE & EN-FR & \\ \hline
......@@ -562,7 +562,7 @@ Transformer Deep(48层) & 30.2 & 43.1 & 194$\times 10^
\section{推断}
\parinterval Transformer解码器生成译文词序列的过程和其它神经机器翻译系统类似,都是从左往右生成,且下一个单词的预测依赖已经生成的单词。其具体推断过程如图\ref{fig:12-56}所示,其中$\mathbi{C}_i$是编码-解码注意力的结果,解码器首先根据“<sos>”和$\mathbi{C}_1$生成第一个单词“how”,然后根据“how”和$\mathbi{C}_2$生成第二个单词“are”,以此类推,当解码器生成“<eos>”时结束推断。
\parinterval Transformer解码器生成译文词序列的过程和其它神经机器翻译系统类似,都是从左往右生成,且下一个单词的预测依赖已经生成的单词。其具体推断过程如图\ref{fig:12-19}所示,其中$\mathbi{C}_i$是编码-解码注意力的结果,解码器首先根据“<sos>”和$\mathbi{C}_1$生成第一个单词“how”,然后根据“how”和$\mathbi{C}_2$生成第二个单词“are”,以此类推,当解码器生成“<eos>”时结束推断。
\parinterval 但是,Transformer在推断阶段无法对所有位置进行并行化操作,因为对于每一个目标语单词都需要对前面所有单词进行注意力操作,因此它推断速度非常慢。可以采用的加速手段有:Cache(缓存需要重复计算的变量)\upcite{Vaswani2018Tensor2TensorFN}、低精度计算\upcite{DBLP:journals/corr/CourbariauxB16,Lin2020TowardsF8}、共享注意力网络等\upcite{Xiao2019SharingAW}。关于Transformer模型的推断加速方法将会在{\chapterfourteen}进一步深入讨论。
......@@ -571,7 +571,7 @@ Transformer Deep(48层) & 30.2 & 43.1 & 194$\times 10^
\centering
\input{./Chapter12/Figures/figure-decode-of-transformer}
\caption{Transformer模型的推断过程示例}
\label{fig:12-56}
\label{fig:12-19}
\end{figure}
%----------------------------------------------
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论