Commit 169496fe by xiaotong

updates of section 5

parent 00278199
......@@ -621,7 +621,7 @@ x_0\cdot w_0+x_1\cdot w_1+x_2\cdot w_2 & = & 0\cdot 1+0\cdot 1+1\cdot 1 \nonumbe
\item 从代数角度看,对于线性空间$ \rm V $,任意$ a,b\in {\rm V} $和数域中的任意$ \alpha $,线性变换$ T(\cdot) $需满足:$ T(a+b)=T(a)+T(b) $,且$ T(\alpha a)=\alpha T(a) $
\item 从几何角度上看,公式中的$ \mathbf x\cdot \mathbf w+\mathbf b $$ \mathbf x $右乘$ \mathbf w $相当于对$ \mathbf x $进行旋转变换,如图\ref{fig:rotation}所示,对三个点$ (0,0) $$ (0,1) $$ (1,0) $及其围成的矩形区域右乘如下矩阵:
\begin{equation}
\mathbf w=\begin{pmatrix} 1 & 0 & 0\\ 0 & -1 & 0\\ 0 & 0 & 1\end{pmatrix}
\mathbf w=\begin{pmatrix} 1 & 0 & 0\\ 0 & -1 & 0\\ 0 & 0 & 1\end{pmatrix}
\end{equation}
这样,矩形区域由第一象限旋转90度到了第四象限。
\end{itemize}
......@@ -1165,37 +1165,36 @@ y&=&{\rm{Sigmoid}}({\rm{Tanh}}(\mathbf x\cdot \mathbf w^1+\mathbf b^1)\cdot \mat
%--5.3.5 神经网络实例---------------------
\subsection{神经网络实例}\index{Chapter5.3.5}
\parinterval 在了解了神经网络前向计算过程的基础上,本小节进一步使用NiuTensor来演示搭建神经网络的过程。注意,搭建神经网络的过程本质上就是定义前向计算的过程。
\parinterval 首先构造一个单层神经网络。如图\ref{fig:code-niutensor-one}所示,简单的定义输入、权重和偏置后,定义激活函数为Sigmoid函数,输入$ \mathbf x $经过线性变换和激活函数,得到输出。
\parinterval 下面用几个实例来说明搭建神经网络的过程。注意,搭建神经网络的过程本质上就是定义前向计算的过程。首先构造一个单层神经网络。如图\ref{fig:code-niutensor-one}所示,简单的定义输入、权重和偏置后,定义激活函数为Sigmoid函数,输入$ \mathbf x $经过线性变换和激活函数,得到输出$ \mathbf y $
%----------------------------------------------
% 图
\begin{figure}[htp]
\centering
\input{./Chapter5/Figures/fig-code-niutensor-one}
\caption{使用NiuTensor搭建单层神经网络}
\caption{构建单层神经网络的示例}
\label{fig:code-niutensor-one}
\end{figure}
%-------------------------------------------
\parinterval 如图\ref{fig:code-niutensor-three}是使用NiuTensor构造三层神经网络的程序示例,首先定义输入和各层的权重、偏置,随后定义神经网络中各层的实现细节,在第一层中,$ \mathbf x $作为输入,$ \mathbf h1 $作为输出,其中$ \mathbf h1={\rm{Sigmoid}}(\mathbf x\cdot \mathbf w1+\mathbf b1) $。在第二层中,$ \mathbf h1 $作为输入,$ \mathbf h2 $作为输出,其中$ \mathbf h2={\rm{Tanh}}(\mathbf h1\cdot \mathbf w2) $。在第三层中,$ \mathbf h2 $作为输入,$ \mathbf y $作为输出,其中$ \mathbf y={\rm{Relu}}(\mathbf h2\cdot \mathbf w3) $
\parinterval 如图\ref{fig:code-niutensor-three}是一个构造三层神经网络的程序示例。在第一层中,$ \mathbf x $作为输入,$ \mathbf h1 $作为输出,其中$ \mathbf h1={\rm{Sigmoid}}(\mathbf x\cdot \mathbf w1+\mathbf b1) $。在第二层中,$ \mathbf h1 $作为输入,$ \mathbf h2 $作为输出,其中$ \mathbf h2={\rm{Tanh}}(\mathbf h1\cdot \mathbf w2) $。在第三层中,$ \mathbf h2 $作为输入,$ \mathbf y $作为输出,其中$ \mathbf y={\rm{Relu}}(\mathbf h2\cdot \mathbf w3) $$ \mathbf y $也会作为整个神经网络的输出。
%----------------------------------------------
% 图
\begin{figure}[htp]
\centering
\input{./Chapter5/Figures/fig-code-niutensor-three}
\caption{使用NiuTensor搭建三层神经网络}
\caption{构建三层神经网络的示例}
\label{fig:code-niutensor-three}
\end{figure}
%-------------------------------------------
\parinterval 除了比较简单的全连接神经网络,还可以使用NiuTensor搭建更复杂的循环神经网络和Transformer等结构。如图\ref{fig:code-niutensor-rnn},是使用NiuTensor搭建循环神经网络的程序示例。其中输入$ \mathbf x_1 $$ \mathbf x_2 $$ \mathbf x_3 $经过一个循环网络送给更上层的网络进行处理。NiuTensor也给出了丰富的实行实例,感兴趣的读者可以进一步参考
\parinterval 除了简单对若干全连接网络进行堆叠,也可以用张量定义更加复杂的神经网络结构,如循环神经网络和Transformer等结构(见\ref{sec5:nlm}节)。如图\ref{fig:code-niutensor-rnn}所示,输入$ \mathbf x_1 $$ \mathbf x_2 $$ \mathbf x_3 $经过一个循环神经网络送给更上层的网络进行处理。关于循环神经网络及其它神经网络结构在本章的后续内容还会有进一步介绍
%----------------------------------------------
% 图
\begin{figure}[htp]
\centering
\input{./Chapter5/Figures/fig-code-niutensor-rnn}
\caption{ NiuTensor搭建循环神经网络}
\caption{构建含有循环结构的神经网络的示例}
\label{fig:code-niutensor-rnn}
\end{figure}
%-------------------------------------------
......@@ -1203,7 +1202,7 @@ y&=&{\rm{Sigmoid}}({\rm{Tanh}}(\mathbf x\cdot \mathbf w^1+\mathbf b^1)\cdot \mat
%--5.4神经网络的参数训练-----------------------------------------
\section{神经网络的参数训练}\index{Chapter5.4}
\parinterval 简单来说,神经网络可以被看作是由变量和函数组成的表达式,例如:$ \mathbf y=\mathbf x+\mathbf b $$ \mathbf y={\rm{Relu}}(\mathbf x\cdot \mathbf w+\mathbf b) $$ \mathbf y={\rm{Sigmoid}}({\rm{Relu}}(\mathbf x\cdot \mathbf w^1+\mathbf b^1)\cdot \mathbf w^2+\mathbf b^2) $等等,其中的$ \mathbf x $$ \mathbf y $作为输入和输出变量, $ \mathbf w $$ \mathbf b $等其他变量作为{\small\sffamily\bfseries{模型参数}}(Model Parameters)。确定了函数表达式和模型参数,也就确定了神经网络模型。通常,表达式的形式需要系统开发者设计,而模型参数的数量非常巨大,因此需要自动学习,这个过程也称为模型学习或训练。为了实现这个目标,通常会准备一定量的带有标准答案的数据,称之为{\small\sffamily\bfseries{有标注数据}}(Annotated Data/Labeled Data)。这些数据会用于对模型参数的学习,这也对应了统计模型中的参数估计过程。在机器学习中,一般把这种使用有标注数据进行统计模型参数训练的过程称为{\small\sffamily\bfseries{有指导的训练}}{\small\sffamily\bfseries{有监督的训练}}(Supervised Training)。在本章中,如果没有特殊说明,模型训练都是指有监督的训练。那么神经网络内部是怎样利用有标注数据对参数进行训练的呢?
\parinterval 简单来说,神经网络可以被看作是由变量和函数组成的表达式,例如:$ \mathbf y=\mathbf x+\mathbf b $$ \mathbf y={\rm{Relu}}(\mathbf x\cdot \mathbf w+\mathbf b) $$ \mathbf y={\rm{Sigmoid}}({\rm{Relu}}(\mathbf x\cdot \mathbf w^1+\mathbf b^1)\cdot \mathbf w^2+\mathbf b^2) $等等,其中的$ \mathbf x $$ \mathbf y $作为输入和输出变量, $ \mathbf w $$ \mathbf b $等其他变量作为{\small\sffamily\bfseries{模型参数}}(Model Parameters)。确定了函数表达式和模型参数,也就确定了神经网络模型。通常,表达式的形式需要系统开发者设计,而模型参数的数量有时会非常巨大,因此需要自动学习,这个过程也被称为模型学习或{\small\bfnew{训练}}(Training)。为了实现这个目标,通常会准备一定量的带有标准答案的数据,称之为{\small\sffamily\bfseries{有标注数据}}(Annotated Data/Labeled Data)。这些数据会用于对模型参数的学习,这也对应了统计模型中的参数估计过程。在机器学习中,一般把这种使用有标注数据进行统计模型参数训练的过程称为{\small\sffamily\bfseries{有指导的训练}}{\small\sffamily\bfseries{有监督的训练}}(Supervised Training)。在本章中,如果没有特殊说明,模型训练都是指有监督的训练。那么神经网络内部是怎样利用有标注数据对参数进行训练的呢?
\parinterval 为了回答这个问题,可以把模型参数的学习过程看作是一个优化问题,即找到一组参数,使得模型达到某种最优的状态。这个问题又可以被转化为两个新的问题:
......@@ -1220,7 +1219,7 @@ y&=&{\rm{Sigmoid}}({\rm{Tanh}}(\mathbf x\cdot \mathbf w^1+\mathbf b^1)\cdot \mat
%--5.4.1 损失函数---------------------
\subsection{损失函数}\index{Chapter5.4.1}
\parinterval 在神经网络的有监督学习中,训练模型的数据是由输入和正确答案所组成的样本构成的。假设有多个输入样本$ \{\mathbf x_1,\mathbf x_2,\dots,\mathbf x_n\} $,每一个$ \mathbf x_i $都对应一个正确答案$ \mathbf {\widetilde y}_i $$ \{\mathbf x_i,\mathbf {\widetilde y}_i\} $就构成一个优化神经网络的{\small\sffamily\bfseries{训练数据集合}}training data set)。对于一个神经网络模型$ \mathbf y=f(\mathbf x) $,每个$ \mathbf x_i $也会有一个输出$ \mathbf y_i $如果可以度量正确答案$ \mathbf {\widetilde y}_i $和神经网络输出$ \mathbf y_i $之间的偏差,进而通过调整网络参数减小这种偏差,就可以得到更好的模型。
\parinterval 在神经网络的有监督学习中,训练模型的数据是由输入和正确答案所组成的样本构成的。假设有多个输入样本$ \{\mathbf x_1,\mathbf x_2,\dots,\mathbf x_n\} $,每一个$ \mathbf x_i $都对应一个正确答案$ \mathbf {\widetilde y}_i $$ \{\mathbf x_i,\mathbf {\widetilde y}_i\} $就构成一个优化神经网络的{\small\sffamily\bfseries{训练数据集合}}Training Data Set)。对于一个神经网络模型$ \mathbf y=f(\mathbf x) $,每个$ \mathbf x_i $也会有一个输出$ \mathbf y_i $如果可以度量正确答案$ \mathbf {\widetilde y}_i $和神经网络输出$ \mathbf y_i $之间的偏差,进而通过调整网络参数减小这种偏差,就可以得到更好的模型。
\parinterval 通常,可以通过设计{\small\sffamily\bfseries{损失函数}}(Loss Function)来度量正确答案$ \mathbf {\widetilde y}_i $和神经网络输出$ \mathbf y_i $之间的偏差。而这个损失函数往往充当训练的{\small\sffamily\bfseries{目标函数}}(Objective Function),神经网络训练就是通过不断调整神经网络内部的参数而使损失函数最小化。图\ref{fig:absolute-loss}展示了一个的绝对值损失函数的实例。
%----------------------------------------------
......@@ -1259,31 +1258,31 @@ y&=&{\rm{Sigmoid}}({\rm{Tanh}}(\mathbf x\cdot \mathbf w^1+\mathbf b^1)\cdot \mat
%--5.4.2 基于梯度的参数优化---------------------
\subsection{基于梯度的参数优化}\label{sec5:para-training}\index{Chapter5.4.2}
\parinterval 对于第$ i $个样本$ (\mathbf x_i,\mathbf {\widetilde y}_i) $如果把损失函数$ Loss(\mathbf {\widetilde y}_i,\mathbf y_i) $看作是参数$ \mathbf w $的函数\footnote{为了简化描述,可以用$ \mathbf w $表示神经网络中的所有参数}。因为输出$ \mathbf y_i $是由输入$ \mathbf x_i $和模型参数$ \mathbf w $决定,因此也把损失函数写为$ L(\mathbf x_i,\mathbf {\widetilde y}_i;\mathbf w) $。参数学习过程可以被描述为
\parinterval 对于第$ i $个样本$ (\mathbf x_i,\mathbf {\widetilde y}_i) $,把损失函数$ Loss(\mathbf {\widetilde y}_i,\mathbf y_i) $看作是参数$ \mathbf w $的函数\footnote{为了简化描述,可以用$ \mathbf w $表示神经网络中的所有参数}。因为输出$ \mathbf y_i $是由输入$ \mathbf x_i $和模型参数$ \mathbf w $决定,因此也把损失函数写为$ L(\mathbf x_i,\mathbf {\widetilde y}_i;\mathbf w) $。参数学习过程可以被描述为
%公式--------------------------------------------------------------------
\begin{eqnarray}
\hat{\mathbf w}&=&\mathop{\arg\min}_{\mathbf w}\frac{1}{n}\sum_{i=1}^{n}{L(\mathbf x_i,\mathbf {\widetilde y}_i;\mathbf w)}
\widehat{\mathbf w}&=&\mathop{\arg\min}_{\mathbf w}\frac{1}{n}\sum_{i=1}^{n}{L(\mathbf x_i,\mathbf {\widetilde y}_i;\mathbf w)}
\label{eqa1.28}
\end{eqnarray}
%公式--------------------------------------------------------------------
\parinterval 其中,$ \hat{\mathbf w} $表示在训练数据上使损失的平均值达到最小的参数,$ \frac{1}{n}\sum_{i=1}^{n}{L(\mathbf x_i,\mathbf {\widetilde y}_i;\mathbf w)} $\\被称作{\small\sffamily\bfseries{代价函数}}(Cost Function),它是损失函数均值期望的估计,记为$ J(\mathbf w) $
\noindent 其中,$ \widehat{\mathbf w} $表示在训练数据上使损失的平均值达到最小的参数。$ \frac{1}{n}\sum_{i=1}^{n}{L(\mathbf x_i,\mathbf {\widetilde y}_i;\mathbf w)} $被称作{\small\sffamily\bfseries{代价函数}}(Cost Function),它是损失函数均值期望的估计,记为$ J(\mathbf w) $
\parinterval 参数优化的核心问题是:找到使代价函数$ J(\mathbf w) $达到最小的$ \mathbf w $。然而$ J(\mathbf w) $可能会包含大量的参数,比如,基于神经网络的机器翻译模型的参数量可能会超过一亿个。这时不可能用手动方法进行调参。为了实现高效的参数优化,比较常用的手段是使用梯度下降方法(Gradient Descent Method)。
\parinterval 参数优化的核心问题是:找到使代价函数$ J(\mathbf w) $达到最小的$ \mathbf w $。然而$ J(\mathbf w) $可能会包含大量的参数,比如,基于神经网络的机器翻译模型的参数量可能会超过一亿个。这时不可能用手动方法进行调参。为了实现高效的参数优化,比较常用的手段是使用{\small\bfnew{梯度下降方法}}(Gradient Descent Method)。
%--5.4.2.1梯度下降---------------------
\subsubsection{(一)梯度下降}\index{Chapter5.4.2.1}
\subsubsection{梯度下降}\index{Chapter5.4.2.1}
\parinterval 梯度下降法是一种常用的优化方法,非常适用于目标函数可微分的问题。它基本思想是,给定函数上的第一个点,可以找到使函数值变化最大的方向,然后前进一``步'',这样模型就可以朝着更大(或更小)的函数值以最快的速度移动\footnote{梯度下降的一种实现是最速下降(Steepest Descent)。方法是每一步移动都选取合适的步长,进而使目标函数能得到最大程度的增长(或下降)。}。具体来说,梯度下降通过迭代更新参数$ \mathbf w $,不断沿着梯度的反方向让参数$ \mathbf w $朝着损失函数更小的方向移动:如果$ J(\mathbf w) $$ \mathbf w $可微分,则$ \frac{\partial J(\mathbf w)}{\partial \mathbf w} $将指向$ J(\mathbf w) $$ \mathbf w $处变化最大的方向,这里将其称之为梯度方向。$ \mathbf w $沿着梯度方向更新,新的$ \mathbf w $可以使函数更接近极值,其过程如图\ref{fig:gradient-descent}所示。
\parinterval 梯度下降法是一种常用的优化方法,非常适用于目标函数可微分的问题。它基本思想是,给定函数上的第一个点,可以找到使函数值变化最大的方向,然后前进一``步'',这样模型就可以朝着更大(或更小)的函数值以最快的速度移动\footnote{梯度下降的一种实现是{\scriptsize\bfnew{最速下降}}(Steepest Descent)。该方法的每一步移动都选取合适的步长,进而使目标函数能得到最大程度的增长(或下降)。}。具体来说,梯度下降通过迭代更新参数$ \mathbf w $,不断沿着梯度的反方向让参数$ \mathbf w $朝着损失函数更小的方向移动:如果$ J(\mathbf w) $$ \mathbf w $可微分,则$ \frac{\partial J(\mathbf w)}{\partial \mathbf w} $将指向$ J(\mathbf w) $$ \mathbf w $处变化最大的方向,这里将其称之为梯度方向。$ \mathbf w $沿着梯度方向更新,新的$ \mathbf w $可以使函数更接近极值,其过程如图\ref{fig:gradient-descent}所示。
%----------------------------------------------
% 图
\begin{figure}[htp]
\centering
\includegraphics{./Chapter5/Figures/fig-gradient-descent.pdf}
\caption{梯度下降}
\caption{函数上一个点沿着不同方向移动的示例}
\label{fig:gradient-descent}
\end{figure}
%-------------------------------------------
\parinterval 应用梯度下降算法,首先需要初始化参数w,一般情况下深度学习中的参数应该初始化为一个不太大的随机数。一旦初始化w后,就开始对模型进行不断的更新,{\small\sffamily\bfseries{参数更新的规则}}(Update Rule)为:
\parinterval 应用梯度下降算法时,首先需要初始化参数w。一般情况下深度学习中的参数应该初始化为一个不太大的随机数。一旦初始化$\mathbf{w}$后,就开始对模型进行不断的更新,{\small\sffamily\bfseries{参数更新的规则}}(Update Rule)为:
%公式--------------------------------------------------------------------
\begin{eqnarray}
\mathbf w_{t+1}&=&\mathbf w_{t}-\alpha \cdot \frac{\partial J(\mathbf w)}{\partial \mathbf w}
......@@ -1292,9 +1291,7 @@ y&=&{\rm{Sigmoid}}({\rm{Tanh}}(\mathbf x\cdot \mathbf w^1+\mathbf b^1)\cdot \mat
%公式--------------------------------------------------------------------
\noindent 其中$t $表示更新的步数,$ \alpha $是一个参数,被称作{\small\sffamily\bfseries{学习率}}(Learning Rate),表示更新步幅的大小。$ \alpha $的设置需要根据任务进行调整。
\parinterval 从优化的角度看,梯度下降是一种典型的 {\small\bfnew{基于梯度的方法}}(Gradient-based Method),属于基于一阶导数的方法。其他类似的方法还有牛顿法、共轭方向法、拟牛顿法等。\ref{sec:5.4.2.3}节会进一步介绍梯度下降的几种变形。
\parinterval 在具体实现时,公式\ref{eqa1.29}可以有以下不同的形式。
\parinterval 从优化的角度看,梯度下降是一种典型的 {\small\bfnew{基于梯度的方法}}(Gradient-based Method),属于基于一阶导数的方法。其他类似的方法还有牛顿法、共轭方向法、拟牛顿法等。在具体实现时,公式\ref{eqa1.29}可以有以下不同的形式。
%--批量梯度下降---------------------
\vspace{0.5em}
......@@ -1310,7 +1307,7 @@ J(\mathbf w)&=&\frac{1}{n}\sum_{i=1}^{n}{L(\mathbf x_i,\mathbf {\widetilde y}_i;
%公式--------------------------------------------------------------------
\parinterval\ref{eqa1.30}是式\ref{eqa1.29}的严格实现,也就是将全部训练样本的平均损失作为目标函数。由全数据集确定的方向能够更好地代表样本总体,从而朝着模型在数据上整体优化所在的方向更新参数。
\parinterval 不过,这种方法的缺点也十分明显,因为要在全部训练数据上最小化损失,每一次参数更新都需要计算在所有样本上的损失。在海量训练数据下,这种计算是非常消耗时间的。当训练数据规模很大时,很少使用这种方法。
\parinterval 不过,这种方法的缺点也十分明显,因为要在全部训练数据上最小化损失,每一次参数更新都需要计算在所有样本上的损失。在使用海量数据进行训练的情况下,这种计算是非常消耗时间的。当训练数据规模很大时,很少使用这种方法。
%--随机梯度下降---------------------
\vspace{0.5em}
......@@ -1324,9 +1321,9 @@ J(\mathbf w)&=&L(\mathbf x_i,\mathbf {\widetilde y}_i;\mathbf w)
\label{eqa1.31}
\end{eqnarray}
%公式--------------------------------------------------------------------
\noindent 由于每次只随机选取一个样本进行优化,这样更新的计算代价低,参数更新的速度大大加快,而且也适用于利用少量样本进行在线学习的情况\footnote{比如,训练数据不是一次给定的,而是随着模型的使用不断追加的。这时,需要不断地用新的训练样本更新模型,这种模式也被称作在线学习(Online Learning)}
\noindent 由于每次只随机选取一个样本$(\mathbf x_i,\mathbf {\widetilde y}_i)$进行优化,这样更新的计算代价低,参数更新的速度大大加快,而且也适用于利用少量样本进行在线学习的情况\footnote{比如,训练数据不是一次给定的,而是随着模型的使用不断追加的。这时,需要不断地用新的训练样本更新模型,这种模式也被称作在{\scriptsize\bfnew{线学习}}(Online Learning)}
\parinterval 因为随机梯度下降算法每次优化的只是某一个样本的损失函数,所以它的问题也非常明显:单个样本上的损失无法代表在全部样本上的损失,因此参数更新的效率低,方法收敛速度极慢。即使在目标函数为强凸函数的情况下,SGD仍旧无法做到线性收敛。
\parinterval 因为随机梯度下降算法每次优化的只是某一个样本上的损失,所以它的问题也非常明显:单个样本上的损失无法代表在全部样本上的损失,因此参数更新的效率低,方法收敛速度极慢。即使在目标函数为强凸函数的情况下,SGD仍旧无法做到线性收敛。
%--小批量梯度下降---------------------
\vspace{0.5em}
......@@ -1336,19 +1333,20 @@ J(\mathbf w)&=&L(\mathbf x_i,\mathbf {\widetilde y}_i;\mathbf w)
\parinterval 为了综合批量梯度下降和随机梯度下降的优缺点,在实际应用中一般采用这两个算法的折中\ \dash \ 小批量梯度下降。其思想是:每次迭代计算一小部分训练数据的损失函数,并对参数进行更新。这一小部分数据被称为一个批次(mini-batch或者batch)。小批量梯度下降的参数优化的目标函数如下:
%公式--------------------------------------------------------------------
\begin{eqnarray}
J(\mathbf w)&=&\frac{1}{m}\sum_{j=i}^{j+m-1}{L(\mathbf x_i,\mathbf {\widetilde y}_i;\mathbf w)}
J(\mathbf w)&=&\frac{1}{m}\sum_{i=j}^{j+m-1}{L(\mathbf x_i,\mathbf {\widetilde y}_i;\mathbf w)}
\label{eqa1.32}
\end{eqnarray}
%公式--------------------------------------------------------------------
\noindent 其中$ m $表示一个batch中的样本的数量,$ j $表示这个batch在全体训练数据的起始位置。这种梯度下降方法中,通过GPU等设备上的矩阵运算,每次在一个batch上优化神经网络参数并不会比单个样本上慢太多。而且使用batch可以大大减小收敛所需要的参数更新次数,同时可以使收敛到的结果更加接近梯度下降的效果。但是需要注意的是batch大小的选择对模型的最终性能是存在一定影响的。
\noindent 其中$ m $表示一个批次中的样本的数量,$ j $表示这个批次在全体训练数据的起始位置。这种方法可以更充分的利用GPU设备,因为批次中的样本可以一起计算。而且每次使用多个样本可以大大减小使模型收敛所需要的参数更新次数。但是需要注意的是批次大小的选择对模型的最终性能是存在一定影响的。
%--5.4.2.2梯度获取---------------------
\subsubsection{(二)梯度获取}\index{Chapter5.4.2.2}
\subsubsection{梯度获取}\index{Chapter5.4.2.2}
\parinterval 梯度下降算法中的核心是要得到目标函数相对于参数的梯度。下面将介绍三种常见的求梯度方法:数值微分、符号微分和自动微分,深度学习实现过程中多是采用自动微分方法计算梯度\cite{baydin2017automatic}
\parinterval 梯度下降算法的一个核心是要得到目标函数相对于参数的梯度。下面将介绍三种常见的求梯度方法:数值微分、符号微分和自动微分,深度学习实现过程中多是采用自动微分方法计算梯度\cite{baydin2017automatic}
%--数值微分---------------------
\vspace{0.5em}
\noindent {\small\sffamily\bfseries{数值微分(Numerical Differentiation)}}
\noindent {\small\sffamily\bfseries{a) 数值微分(Numerical Differentiation)}}
\vspace{0.5em}
\parinterval 数学上,梯度的求解其实就是求函数偏导的问题。导数是用极限来定义的,如下:
......@@ -1362,20 +1360,20 @@ J(\mathbf w)&=&\frac{1}{m}\sum_{j=i}^{j+m-1}{L(\mathbf x_i,\mathbf {\widetilde y
\parinterval 数值微分根据导数的原始定义完成,根据公式可知,要得到损失函数在某个参数状态$ \mathbf w $下的梯度,可以将$ \mathbf w $增大或减小一点($ \Delta \mathbf w $),例如,取$ \Delta \mathbf w=0.0001 $,之后观测损失函数的变化与$ \Delta \mathbf w $的比值。$ \Delta \mathbf w $的取值越小计算的结果越接近导数的真实值,但是对计算的精度要求越高。
\parinterval 这种求梯度的方法很简单,但是计算量太大,求解速度非常慢,而且这种方法造成的{\small\sffamily\bfseries{截断误差}}(Truncation Error)和{\small\sffamily\bfseries{舍入误差}}(Round-off Error)使这种方法也会在实际系统中造成困扰。在网络比较复杂、参数量稍微有点大的模型上一般不会使用这种方法。
\parinterval 这种求梯度的方法很简单,但是计算量很大,求解速度非常慢,而且这种方法造成的{\small\sffamily\bfseries{截断误差}}(Truncation Error)和{\small\sffamily\bfseries{舍入误差}}(Round-off Error)。在网络比较复杂、参数量稍微有点大的模型上一般不会使用这种方法。
\parinterval 数值微分中的截断误差和舍入误差是如何造成的呢?数值微分方法求梯度时,需用极限或无穷过程来求得。然而计算机需要将求解过程化为一系列有限的算术运算和逻辑运算。这样就要对某种无穷过程进行``截断'',即仅保留无穷过程的前段有限序列而舍弃它的后段。这就带来截断误差;舍入误差,是指运算得到的近似值和精确值之间的差异。由于数值微分方法计算复杂函数的梯度问题时,经过无数次的近似,每一次近似都产生了舍入误差,在这样的情况下,误差会随着运算次数增加而积累得很大,最终得出没有意义的运算结果。实际上,截断误差和舍入误差在训练复杂神经网络中也会出现,因此是实际系统研发中需要注意的问题。
\parinterval 截断误差和舍入误差是如何造成的呢?数值微分方法求梯度时,需用极限或无穷过程来求得。然而计算机需要将求解过程化为一系列有限的算术运算和逻辑运算。这样就要对某种无穷过程进行``截断'',即仅保留无穷过程的前段有限序列而舍弃它的后段。这就带来截断误差;舍入误差,是指运算得到的近似值和精确值之间的差异。由于数值微分方法计算复杂函数的梯度问题时,经过无数次的近似,每一次近似都产生了舍入误差,在这样的情况下,误差会随着运算次数增加而积累得很大,最终得出没有意义的运算结果。实际上,截断误差和舍入误差在训练复杂神经网络中,特别是使用低精度计算时,也会出现,因此是实际系统研发中需要注意的问题。
\parinterval 尽管数值微分不适用于大模型中的梯度求解,但是由于数值微分方法非常简单,因此在很多时候,可以利用这种方法来检验其他梯度计算方法的正确性。比如在实现反向传播的时候(详见\ref{sec:5.4.6}节),可以检验求导是否正确(Gradient Check),这个过程就是利用数值微分法实现的。
\parinterval 尽管数值微分不适用于大模型中的梯度求解,但是由于其非常简单,因此经常被用于检验其他梯度计算方法的正确性。比如在实现反向传播的时候(详见\ref{sec:5.4.6}节),可以检验求导是否正确(Gradient Check),这个过程就是利用数值微分实现的。
%--符号微分---------------------
\vspace{0.5em}
\noindent {\small\sffamily\bfseries{符号微分(Symbolic Differentiation)}}
\noindent {\small\sffamily\bfseries{b) 符号微分(Symbolic Differentiation)}}
\vspace{0.5em}
\parinterval 顾名思义,符号微分就是通过建立符号表达式求解微分的方法:借助符号表达式和求导公式,推导出目标函数关于自变量的微分表达式,最后再带入具体数值得到微分结果。例如,对于表达式$ L(\mathbf w)=\mathbf x\cdot \mathbf w+2\mathbf w^2 $,可以手动推导出微分表达式$ \frac{\partial L(\mathbf w)}{\partial \mathbf w}=\mathbf x+4\mathbf w $,最后将具体数值$ \mathbf x = {(\begin{array}{cc} 2 & -3\end{array})}^{\rm T} $$ \mathbf w = {(\begin{array}{cc} -1 & 1\end{array})}^{\rm T} $带入后,得到微分结果$\frac{\partial L(\mathbf w)}{\partial \mathbf w}= {(\begin{array}{cc} 2 & -3\end{array})}^{\rm T}+4{(\begin{array}{cc} -1 & 1\end{array})}^{\rm T}= {(\begin{array}{cc} -2 & 1\end{array})}^{\rm T}$
\parinterval 顾名思义,符号微分就是通过建立符号表达式求解微分的方法:借助符号表达式和求导公式,推导出目标函数关于自变量的微分表达式,最后再带入具体数值得到微分结果。例如,对于表达式$ L(\mathbf w)=\mathbf x\cdot \mathbf w+2\mathbf w^2 $,可以手动推导出微分表达式$ \frac{\partial L(\mathbf w)}{\partial \mathbf w}=\mathbf x+4\mathbf w $,最后将具体数值$ \mathbf x = {(\begin{array}{cc} 2 & -3\end{array})} $$ \mathbf w = {(\begin{array}{cc} -1 & 1\end{array})} $带入后,得到微分结果$\frac{\partial L(\mathbf w)}{\partial \mathbf w}= {(\begin{array}{cc} 2 & -3\end{array})}+4{(\begin{array}{cc} -1 & 1\end{array})}= {(\begin{array}{cc} -2 & 1\end{array})}$
\parinterval 使用这种求梯度的方法,要求必须将目标函数转化成一种完整的数学表达式,这个过程中存在表达式膨胀(Expression Swell)的问题,很容易导致符号微分求解的表达式急速``膨胀'',大大增加系统存储和处理表达式的负担。关于这个问题的一个实例请看表\ref{tab4}。在深层的神经网络中,神经元数量和参数量极大,损失函数的表达式会非常冗长,不易存储和管理,而且,仅仅写出损失函数的微分表达式就是一个很庞大的工作量。从另一方面来说,我们真正需要的是微分的结果值,而不是微分表达式,推导微分表达式仅仅作为中间产物。
\parinterval 使用这种求梯度的方法,要求必须将目标函数转化成一种完整的数学表达式,这个过程中存在{\small\bfnew{表达式膨胀}}(Expression Swell)的问题,很容易导致符号微分求解的表达式急速``膨胀'',大大增加系统存储和处理表达式的负担。关于这个问题的一个实例请看表\ref{tab4}。在深层的神经网络中,神经元数量和参数量极大,损失函数的表达式会非常冗长,不易存储和管理,而且,仅仅写出损失函数的微分表达式就是一个很庞大的工作量。从另一方面来说,这里真正需要的是微分的结果值,而不是微分表达式,推导微分表达式仅仅是求解过程中的中间产物。
%表4--------------------------------------------------------------------
\begin{table}[htp]
......@@ -1401,12 +1399,12 @@ $+2x^2+x+1)$ & \ \ $(x^4+2x^3+2x^2+x+1)$ & $+6x+1$ \\
\end{table}
%表4--------------------------------------------------------------------
%--自动微分---------------------
\noindent {\small\sffamily\bfseries{自动微分(Automatic Differentiation)}}
\noindent {\small\sffamily\bfseries{c) 自动微分(Automatic Differentiation)}}
\vspace{0.5em}
\parinterval 自动微分是一种介于数值微分和符号微分的方法:将符号微分应用于最基本的算子,如常数、幂函数、指数函数、对数函数、三角函数等,然后代入数值,保留中间结果,最后再应用于整个函数。通过这种方式,将复杂的微分变成了简单的步骤,这些步骤完全自动化,而且容易进行存储和计算。
\parinterval 由于它只对基本函数或常数运用符号微分法则,所以它非常适合嵌入编程语言的循环条件等结构中,形成一种程序化的微分过程。在具体实现,自动微分往往被当做是一种基于图的计算,相关的理论和技术方法相对成熟,因此是深度学习中使用最广泛的一种方法。不同于一般的编程模式,图计算先生成计算图,然后按照计算图执行计算过程。
\parinterval 由于它只对基本函数或常数运用符号微分法则,所以它非常适合嵌入编程语言的循环条件等结构中,形成一种程序化的微分过程。在具体实现,自动微分往往被当做是一种基于图的计算,相关的理论和技术方法相对成熟,因此是深度学习中使用最广泛的一种方法。不同于一般的编程模式,图计算先生成计算图,然后按照计算图执行计算过程。
\parinterval 自动微分可以用一种{\small\sffamily\bfseries{反向模式}}(Backward Mode)即反向传播思想进行描述。首先,从神经网络的输入,逐层计算每层网络的输出值。如图\ref{fig:forward-propagationt},第$ i $层的输出$ \mathbf h_i $作为第$ i+1 $层的输入,数据流在神经网络内部逐层传递。
%----------------------------------------------
......@@ -1429,7 +1427,7 @@ $+2x^2+x+1)$ & \ \ $(x^4+2x^3+2x^2+x+1)$ & $+6x+1$ \\
\end{itemize}
\vspace{0.5em}
\parinterval 对于反向计算的实现,一般从神经网络的输出开始,逆向逐层计算每层网络输入所对应的微分。如图\ref{fig:back-propagationt},在第$ i $层计算此处的梯度$ \frac{\partial L}{\partial \mathbf h_i} $,并将微分值向前一层传递,根据链式法则继续计算梯度。
\parinterval 对于反向计算的实现,一般从神经网络的输出开始,逆向逐层计算每层网络输入所对应的微分结果。如图\ref{fig:back-propagationt},在第$ i $层计算此处的梯度$ \frac{\partial L}{\partial \mathbf h_i} $,并将微分值向前一层传递,根据链式法则继续计算梯度。
%----------------------------------------------
% 图
\begin{figure}[htp]
......@@ -1442,20 +1440,20 @@ $+2x^2+x+1)$ & \ \ $(x^4+2x^3+2x^2+x+1)$ & $+6x+1$ \\
\parinterval 反向计算也是深度学习中反向传播方法的基础。其实现的内部细节将在\ref{sec:5.4.6}节详细阐述,所以在这里不再赘述。
%--5.4.2.3梯度下降---------------------
\subsubsection{(三)基于梯度的方法的变种和改进}\index{Chapter5.4.2.3}\label{sec:5.4.2.3}
\subsubsection{基于梯度的方法的变种和改进}\index{Chapter5.4.2.3}\label{sec:5.4.2.3}
\parinterval 参数优化通常基于梯度下降算法,即在每个更新步骤$ t $,沿梯度方向更新参数:
\begin{eqnarray}
\mathbf w_{t+1}&=&\mathbf w_{t}-\alpha \cdot \frac{\partial J(\mathbf w_t)}{\partial \mathbf w_t}
\label{}
\end{eqnarray}
\noindent 其中$ \alpha $是一个参数,表示更新步幅的大小,称作学习率(learning rate)。当然,这是一种最基本的梯度下降方法。如果函数的形状非均向,比如呈延伸状,搜索最优点的路径就会非常低效,因为这时梯度的方向并没有指向最小值的方向,并且随着参数的更新,梯度方向往往呈锯齿状,这将是一条相当低效的路径;此外这种梯度下降算法并不是总能到达最优点,而是在其附近徘徊;还有一个最令人苦恼的问题\ \dash \ 设置学习率,如果学习率设置的比较小,会导致训练收敛速度慢,如果学习率设置的比较大,会导致训练过程中因为优化幅度过大而频频跳过最优点。我们希望网络在优化的时候损失函数有一个很好的收敛速度同时又不至于摆动幅度太大。
\noindent 其中$ \alpha $是一个参数,表示更新步幅的大小,称作{\small\bfnew{学习率}}(Learning Rate)。当然,这是一种最基本的梯度下降方法。如果函数的形状非均向,比如呈延伸状,搜索最优点的路径就会非常低效,因为这时梯度的方向并没有指向最小值的方向,并且随着参数的更新,梯度方向往往呈锯齿状,这将是一条相当低效的路径;此外这种梯度下降算法并不是总能到达最优点,而是在其附近徘徊;还有一个最令人苦恼的问题\ \dash \ 设置学习率,如果学习率设置的比较小,会导致训练收敛速度慢,如果学习率设置的比较大,会导致训练过程中因为优化幅度过大而频频跳过最优点。我们希望网络在优化的时候损失函数有一个很好的收敛速度同时又不至于摆动幅度太大。
\parinterval 针对以上问题,很多学者尝试对梯度下降方法做出改进,如Momentum, Adagrad, Adadelta, RMSprop, Adam, AdaMax, Nadam, AMSGrad等等,在这里将介绍Momentum、AdaGrad、RMSprop、Adam这4 种方法。
%--Momentum---------------------
\vspace{0.5em}
\noindent {\small\sffamily\bfseries{Momentum }}
\noindent {\small\sffamily\bfseries{a) Momentum }}
\vspace{0.5em}
\parinterval Momentum梯度下降算法的参数更新公式如下\footnote{在梯度下降算法的几种改进方法的公式中,其更新对象是某个具体参数而非参数矩阵,因此不再使用加粗样式}
......@@ -1468,7 +1466,7 @@ w_{t+1}&=&w_t-\alpha v_t
%公式--------------------------------------------------------------------
\parinterval 该算法引入了一个``动量''的理念\cite{qian1999momentum},它是基于梯度的移动指数加权平均。公式中的$ v_t $是损失函数在前$ t-1 $次更新中累积的梯度动量,$ \beta $是梯度累积的一个指数,这里一般设置值为0.9。所以Momentum梯度下降算法的主要思想就是对网络的参数进行平滑处理,让梯度的摆动幅度变得更小。
\parinterval 这里的``梯度''不再只是现在的损失函数的梯度,而是之前的梯度的加权和。在基本的梯度下降算法中,如果在某个参数状态下,梯度方向变化特别大,甚至与上一次参数更新中梯度方向成90度夹角,下一次参数更新中梯度方向可能又是一次90度的改变,这时参数优化路径将会成``锯齿''状(如图\ref{fig:sawtooth }所示),优化效率极慢。而Momentum梯度下降算法不会让梯度发生90度的变化,而是让梯度慢慢发生改变:如果当前的梯度方向与之前的梯度方向相同,在原梯度方向上加速更新参数;如果当前的梯度方向与之前的梯度方向相反,并不会产生一个急转弯,而是尽量把优化路径平滑地进行改变。这样做的优点也非常明显,一方面杜绝了``锯齿''状优化路径的出现,另一方面将优化幅度变得更加平滑,不会导致频频跳过最优点。
\parinterval 这里的``梯度''不再只是现在的损失函数的梯度,而是之前的梯度的加权和。在原始的梯度下降算法中,如果在某个参数状态下,梯度方向变化特别大,甚至与上一次参数更新中梯度方向成90度夹角,下一次参数更新中梯度方向可能又是一次90度的改变,这时参数优化路径将会成``锯齿''状(如图\ref{fig:sawtooth }所示),优化效率极慢。而Momentum梯度下降算法不会让梯度发生90度的变化,而是让梯度慢慢发生改变:如果当前的梯度方向与之前的梯度方向相同,在原梯度方向上加速更新参数;如果当前的梯度方向与之前的梯度方向相反,并不会产生一个急转弯,而是尽量把优化路径平滑地进行改变。这样做的优点也非常明显,一方面杜绝了``锯齿''状优化路径的出现,另一方面将优化幅度变得更加平滑,不会导致频频跳过最优点。
%----------------------------------------------
% 图
\begin{figure}[htp]
......@@ -1480,10 +1478,10 @@ w_{t+1}&=&w_t-\alpha v_t
%-------------------------------------------
%--AdaGrad---------------------
\noindent {\small\sffamily\bfseries{AdaGrad }}
\noindent {\small\sffamily\bfseries{b) AdaGrad }}
\vspace{0.5em}
\parinterval 在神经网络的学习中,学习率的设置很重要。学习率过小, 会导致学习花费过多时间;反过来,学习率过大,则会导致学习发散,甚至造成模型的``跑偏''。在深度学习实现过程中,有一种被称为学习率衰减(learning rate decay)的方法,即最初设置较大的学习率,随着学习的进行,使学习率逐渐减小,这种方法相当于将``全体''参数的学习率值一起降低。AdaGrad梯度下降算法进一步发展了这个思想\cite{duchi2011adaptive}
\parinterval 在神经网络的学习中,学习率的设置很重要。学习率过小, 会导致学习花费过多时间;反过来,学习率过大,则会导致学习发散,甚至造成模型的``跑偏''。在深度学习实现过程中,有一种被称为学习率{\small\bfnew{衰减}}(Decay)的方法,即最初设置较大的学习率,随着学习的进行,使学习率逐渐减小,这种方法相当于将``全体''参数的学习率值一起降低。AdaGrad梯度下降算法进一步发展了这个思想\cite{duchi2011adaptive}
\parinterval AdaGrad会为参数的每个元素适当地调整学习率,与此同时进行学习。其参数更新公式为:
%公式--------------------------------------------------------------------
......@@ -1497,7 +1495,7 @@ w_{t+1}&=&w_t-\eta \frac{1}{\sqrt{z_t}}\cdot \frac{\partial L}{\partial w_t}
%--RMSprop ---------------------
\vspace{0.5em}
\noindent {\small\sffamily\bfseries{RMSprop }}
\noindent {\small\sffamily\bfseries{c) RMSprop }}
\vspace{0.5em}
\parinterval RMSprop算法是一种自适应学习率的方法\cite{tieleman2012rmsprop},它是对AdaGrad算法的一种改进,可以避免AdaGrad算法中学习率不断单调下降以至于过早衰减的缺点。
......@@ -1516,10 +1514,11 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}}\cdot \frac{\partial L}{\partial w
%--Adam ---------------------
\vspace{0.5em}
\noindent {\small\sffamily\bfseries{Adam }}
\noindent {\small\sffamily\bfseries{d) Adam }}
\vspace{0.5em}
\parinterval Adam梯度下降算法是在RMSProp算法的基础上进行改进的,可以将其看成是带有动量项的RMSProp算法\cite{kingma2014adam},该算法是目前最流行的优化算法。Adam 算法的参数更新公式如下,可以看到Adam算法相当于在RMSProp算法中引入了Momentum算法中的动量项,这样做使得Adam算法兼具了Momentum算法和\\RMSProp算法的优点:既能使梯度更为``平滑''地更新,同时可以为神经网络中的每个参数设置不同的学习率。
\parinterval Adam梯度下降算法是在RMSProp算法的基础上进行改进的,可以将其看成是带有动量项的RMSProp算法\cite{kingma2014adam}。该算法在自然语言处理领域非常流行。Adam 算法的参数更新公式如下,
%公式--------------------------------------------------------------------
\begin{eqnarray}
v_t&=&\beta v_{t-1}+(1-\beta)\frac{\partial L}{\partial w_t}\label{eqa1.40}\\
......@@ -1528,15 +1527,24 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\label{eqa1.42}
\end{eqnarray}
%公式--------------------------------------------------------------------
\parinterval 需要注意的是包括Adam在内的很多参数更新算法中的学习率都需要人为设置。而且模型学习的效果与学习率的设置关系极大,甚至在研发实际系统时工程师需要进行大量设置不同学习率的实验,以得到最佳的模型。第六章还会具体介绍在机器翻译中参数更新学习率设置的策略。
\noindent 可以看到Adam 算法相当于在RMSProp算法中引入了Momentum算法中的动量项,这样做使得Adam算法兼具了Momentum算法和RMSProp算法的优点:既能使梯度更为``平滑''地更新,同时可以为神经网络中的每个参数设置不同的学习率。
\parinterval 需要注意的是包括Adam在内的很多参数更新算法中的学习率都需要人为设置。而且模型学习的效果与学习率的设置关系极大,甚至在研发实际系统时工程师需要进行的实验,以得到最佳的模型。第六章还会具体介绍在机器翻译中参数更新学习率设置的策略。
%--5.4.3 参数更新的并行化策略---------------------
\subsection{参数更新的并行化策略}\index{Chapter5.4.3}
\parinterval 虽然通过GPU可以加速神经网络的运算,但当网络较深时,模型训练还是需要几天到几周的时间。如果希望尽可能缩短一次学习所需的时间,最直接的想法就是把不同的训练样本分配给多个GPU或CPU,然后在这些设备上同时进行训练,即实现并行化训练。这种方法也被称作{\small\sffamily\bfseries{数据并行}}。具体实现时,有两种常用的并行化策略:(参数)同步更新和(参数)异步更新。
\parinterval 虽然通过GPU可以加速神经网络的运算,但当神经网络较为复杂时,模型训练还是需要几天甚至几周的时间。如果希望尽可能缩短一次学习所需的时间,最直接的想法就是把不同的训练样本分配给多个GPU 或CPU,然后在这些设备上同时进行训练,即实现并行化训练。这种方法也被称作{\small\sffamily\bfseries{数据并行}}。具体实现时,有两种常用的并行化策略:(参数)同步更新和(参数)异步更新。
\begin{itemize}
\item {\small\sffamily\bfseries{同步更新}}(Synchronous Update)是指所有计算设备完成计算后,统一汇总并更新参数。当所有设备的反向传播算法完成之后同步更新参数,不会出现单个设备单独对参数进行更新的情况。这种方法效果稳定,但是效率比较低,在同步更新时,每一次参数更新都需要所有设备统一开始、统一结束,如果设备的运行速度不一致,那么每一次参数更新都需要等待最慢的设备结束才能开始。
\item {\small\sffamily\bfseries{异步更新}}(Asynchronous Update)是指每个计算设备可以随时更新参数。不同设备可以随时读取参数的最新值,然后根据当前参数值和分配的训练样本,各自执行反向传播过程并独立更新参数。由于设备间不需要相互等待,这种方法并行度高。但是不同设备读取参数的时间可能不同,会造成不同设备上的参数不同步,导致这种方法不十分稳定,有可能无法达到较好的训练结果。
\parinterval {\small\sffamily\bfseries{同步更新}}是指所有计算设备完成计算后,统一汇总并更新参数。当所有设备的反向传播算法完成之后同步更新参数,不会出现单个设备单独对参数进行更新的情况。这种方法效果稳定,但是效率比较低,在同步更新时,每一次参数更新都需要所有设备统一开始、统一结束,如果设备的运行速度不一致,那么每一次参数更新都需要等待最慢的设备结束才能开始。
\end{itemize}
\parinterval {\small\sffamily\bfseries{异步更新}}是指每个计算设备可以随时更新参数。不同设备可以随时读取参数的最新值,然后根据当前参数值和分配的训练样本,各自执行反向传播过程并独立更新参数。由于设备间不需要相互等待,这种方法并行度高。但是不同设备读取参数的时间可能不同,会造成不同设备上的参数不同步,导致这种方法不十分稳定,有可能无法达到较好的训练结果。
%----------------------------------------------
% 图
\begin{figure}[htp]
......@@ -1547,7 +1555,7 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\end {figure}
%-------------------------------------------
\parinterval\ref{fig:parallel}对比了同步更新和异步更新的区别,在这个例子中,使用4台设备对一个两层神经网络中的参数进行更新,其中使用了一个参数服务器(Parameter Server,图中的G4)来保存最新的参数,不同设备(Worker,图中的G1、G2、G3)可以通过同步或者异步的方式访问参数服务器。图中的$ \mathbf w_o $$ \mathbf w_h $分别代表输出层和隐藏层的全部参数,操作push(P)设备向参数服务器传送梯度,操作fetch(F)表示参数服务器向设备传送更新后的参数。
\parinterval\ref{fig:parallel}对比了同步更新和异步更新的区别,在这个例子中,使用4台设备对一个两层神经网络中的参数进行更新,其中使用了一个{\small\bfnew{参数服务器}}(Parameter Server,图中的G4)来保存最新的参数,不同设备(Worker,图中的G1、G2、G3)可以通过同步或者异步的方式访问参数服务器。图中的$ \mathbf w_o $$ \mathbf w_h $分别代表输出层和隐藏层的全部参数,操作表示push(P) 设备向参数服务器传送梯度,操作fetch(F)表示参数服务器向设备传送更新后的参数。
\parinterval 此外,在使用多个设备进行并行训练的时候,由于设备间带宽的限制,大量的数据传输会有较高的延时。对于复杂神经网络来说,设备间参数和梯度传递的时间消耗也会成为一个不得不考虑的因素。有时候,设备间数据传输的时间甚至比模型计算的时间都长,大大降低了并行度\cite{xiao2017fast}。对于这种问题,可以考虑对数据进行压缩或者减少传输的次数缓解问题。
%--5.4.4 梯度消失、梯度爆炸和稳定性训练---------------------
......@@ -1555,9 +1563,9 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\parinterval 深度学习中随着神经网络层数的增加,导数可能会出现指数级的下降或者指数级的增加,这种现象分别称为{\small\sffamily\bfseries{梯度消失}}(Gradient Vanishing)和{\small\sffamily\bfseries{梯度爆炸}}(Gradient Explosion)。出现这两种现象的本质原因是反向传播过程中链式法则导致梯度矩阵的多次相乘。这类问题很容易导致训练的不稳定。
%--5.4.4.1梯度消失现象及解决方法---------------------
\subsubsection{(一)梯度消失现象及解决方法}\index{Chapter5.4.4.1}
\subsubsection{易于优化的激活函数}\index{Chapter5.4.4.1}
\parinterval 网络训练过程中,如果每层网络的梯度都小于1,反向传播过程中,各层梯度的偏导数会与后面层传递而来的梯度相乘得到本层的梯度,并向前一层传递。该过程循环进行,最后导致梯度指数级地减小,这就产生了梯度消失现象。这种情况会导致神经网络层数较浅的部分梯度为0,无法更新参数。一般来说,产生很小梯度的原因是使用了类似于Sigmoid这样的激活函数,当输入的值过大或者过小的时候这类函数曲线会趋于直线,梯度近似为零。针对这个问题,主要的解决办法是使用更加易于优化的激活函数,比如,使用Relu代替Sigmoid和Tanh作为激活函数。
\parinterval 网络训练过程中,如果每层网络的梯度都小于1,各层梯度的偏导数会与后面层传递而来的梯度相乘得到本层的梯度,并向前一层传递。该过程循环进行,最后导致梯度指数级地减小,这就产生了梯度消失现象。这种情况会导致神经网络层数较浅的部分梯度接近0。一般来说,产生很小梯度的原因是使用了类似于Sigmoid这样的激活函数,当输入的值过大或者过小的时候这类函数曲线会趋于直线,梯度近似为零。针对这个问题,主要的解决办法是使用更加易于优化的激活函数,比如,使用Relu代替Sigmoid和Tanh作为激活函数。
\parinterval 缓解梯度消失问题最直接的想法就是希望各层的偏导数大于或等于1。图\ref{fig:derivative1}展示了Sigmoid激活函数$ y=\frac{1}{1+e^{-x}}$的函数曲线和导函数曲线,如果使用Sigmoid作为损失函数,其梯度不可能超过0.25,这样经过链式求导之后,很容易发生梯度消失。
%----------------------------------------------
......@@ -1594,7 +1602,7 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\parinterval 当然,梯度消失并不是仅仅可以通过改变激活函数就可以完全消除掉。随着网络层数的增加,很多因素都可能会造成梯度消失。后面也会进一步介绍其他手段,可以综合运用这些方法达到很好的缓解梯度消失问题的目的。
%--5.4.4.2梯度消失现象及解决方法---------------------
\subsubsection{(二)梯度爆炸现象及解决方法}\index{Chapter5.4.4.2}
\subsubsection{梯度裁剪}\index{Chapter5.4.4.2}
\parinterval 网络训练过程中,如果参数的初始值过大,而且每层网络的梯度都大于1,反向传播过程中,各层梯度的偏导数都会比较大,会导致梯度指数级地增长直至超出浮点数表示的范围,这就产生了梯度爆炸现象。如果发生这种情况,模型中离输入近的部分比离输入远的部分参数更新得更快,使网络变得非常不稳定。在极端情况下,模型的参数值变得非常大,甚至于溢出。针对梯度爆炸的问题,常用的解决办法为{\small\sffamily\bfseries{梯度裁剪}}(Gradient Clipping)。
......@@ -1607,19 +1615,19 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
%公式--------------------------------------------------------------------
\noindent 其中,$ \Vert \cdot \Vert $表示$ l_2 $范数。梯度裁剪经常被使用在层数较多的模型中,如循环神经网络。
%--5.4.4.3稳定性训练---------------------
\subsubsection{(三)稳定性训练}\index{Chapter5.4.4.3}
\subsubsection{稳定性训练}\index{Chapter5.4.4.3}
\parinterval 为了使神经网络模型训练更加稳定,通常还会考虑其他策略。
\parinterval (1)批量归一化(Batch Normalization)
\parinterval (1){\small\bfnew{批量归一化}}(Batch Normalization)
\parinterval 批量归一化,顾名思义,是以进行学习时的小批量样本为单位进行归一化\cite{ioffe2015batch}。具体而言,就是对神经网络隐层输出的每一个维度,沿着batch的方向进行均值为0、方差为1的归一化。在深层神经网络中,每一层网络都可以使用批量归一化操作。这样使神经网络任意一层的输入不至于过大或过小,从而防止隐层中异常值导致模型状态的巨大改变。
\parinterval 批量归一化,顾名思义,是以进行学习时的小批量样本为单位进行归一化\cite{ioffe2015batch}。具体而言,就是对神经网络隐层输出的每一个维度,沿着批次的方向进行均值为0、方差为1的归一化。在深层神经网络中,每一层网络都可以使用批量归一化操作。这样使神经网络任意一层的输入不至于过大或过小,从而防止隐层中异常值导致模型状态的巨大改变。
\parinterval (2)层归一化(Layer Normalization)
\parinterval (2){\small\bfnew{层归一化}}(Layer Normalization)
\parinterval 类似的,层归一化更多是针对自然语言这种序列处理任务\cite{ba2016layer},它和批量归一化的原理是一样的,只是归一化操作是在序列上同一层网络的输出结果上进行的,也就是归一化操作沿着序列方向进行。这种方法可以很好的避免序列上不同位置神经网络输出结果的不可比性。同时由于归一化后所有的结果都转化到一个可比的范围,使得隐层状态可以在不同层之间进行自由组合。
\parinterval (3)残差网络(Residual Networks)
\parinterval (3){\small\bfnew{残差网络}}(Residual Networks)
\parinterval 最初,残差网络是为了解决神经网络持续加深时的模型退化问题\cite{he2016deep},但是残差结构对解决梯度消失和梯度爆炸问题也有所帮助。有了残差结构,可以很轻松的构建几十甚至上百层的神经网络,而不用担心层数过深而造成的梯度消失问题。残差网络的结构如图\ref{fig:residual-structure}所示:
%----------------------------------------------
......@@ -1632,14 +1640,14 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\end{figure}
%-------------------------------------------
\parinterval\ref{fig:residual-structure}中右侧的曲线叫做跳接(shortcut connection),通过跳接在激活函数前,将上一层(或几层)之前的输出与本层计算的输出相加,将求和的结果输入到激活函数中作为本层的输出。假设残差结构的输入为$ \mathbf x_l $,输出为$ \mathbf x_{l+1} $,则有
\parinterval\ref{fig:residual-structure}中右侧的曲线叫做{\small\bfnew{跳接}}(Shortcut Connection),通过跳接在激活函数前,将上一层(或几层)之前的输出与本层计算的输出相加,将求和的结果输入到激活函数中作为本层的输出。假设残差结构的输入为$ \mathbf x_l $,输出为$ \mathbf x_{l+1} $,则有
%公式--------------------------------------------------------------------
\begin{eqnarray}
\mathbf x_{l+1}&=&F(\mathbf x_l)+\mathbf x_l
\label{eqa1.44}
\end{eqnarray}
%公式--------------------------------------------------------------------
\parinterval 相比较于标准网络的层与层之间循序连接的结构,残差网络提供了这样的跨层连接结构。这种结构在反向传播中有很大的好处,比如,对于$ \mathbf x_l $处的梯度可以如下进行计算:
\parinterval 相比较于简单的多层堆叠的结构,残差网络提供了跨层连接结构。这种结构在反向传播中有很大的好处,比如,对于$ \mathbf x_l $处的梯度可以如下进行计算:
%公式--------------------------------------------------------------------
\begin{eqnarray}
\frac{\partial L}{\partial \mathbf x_l}&=&\frac{\partial L}{\partial \mathbf x_{l+1}} \cdot \frac{\partial \mathbf x_{l+1}}{\partial \mathbf x_l}\nonumber\\
......@@ -1891,7 +1899,7 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
%-------------------------------------------
%--5.5神经语言模型-----------------------------------------
\section{神经语言模型}\index{Chapter5.5}
\section{神经语言模型}\label{sec5:nlm}\index{Chapter5.5}
\parinterval 神经网络给我们提供了一种工具,只要将问题的输入和输出定义好,就可以学习输入和输出之间的对应关系。显然,很多自然语言处理任务都可以用神经网络进行实现。比如,在机器翻译中,可以把输入的源语言句子和输出的目标语句子用神经网络建模;在文本分类中,可以把输入的文本内容和输出的类别标签进行神经网络建模,等等。
......
\boolfalse {citerequest}\boolfalse {citetracker}\boolfalse {pagetracker}\boolfalse {backtracker}\relax
\babel@toc {english}{}
\defcounter {refsection}{0}\relax
\select@language {english}
\defcounter {refsection}{0}\relax
\contentsline {part}{\@mypartnumtocformat {I}{神经机器翻译}}{7}{part.1}
\contentsline {part}{\@mypartnumtocformat {I}{神经机器翻译}}{7}{part.1}%
\ttl@starttoc {default@1}
\defcounter {refsection}{0}\relax
\contentsline {chapter}{\numberline {1}人工神经网络和神经语言建模}{9}{chapter.1}
\contentsline {chapter}{\numberline {1}人工神经网络和神经语言建模}{9}{chapter.1}%
\defcounter {refsection}{0}\relax
\contentsline {section}{\numberline {1.1}深度学习与人工神经网络}{10}{section.1.1}
\contentsline {section}{\numberline {1.1}深度学习与人工神经网络}{10}{section.1.1}%
\defcounter {refsection}{0}\relax
\contentsline {subsection}{\numberline {1.1.1}发展简史}{10}{subsection.1.1.1}
\contentsline {subsection}{\numberline {1.1.1}发展简史}{10}{subsection.1.1.1}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{早期的人工神经网络和第一次寒冬}{10}{section*.2}
\contentsline {subsubsection}{早期的人工神经网络和第一次寒冬}{10}{section*.2}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{神经网络的第二次高潮和第二次寒冬}{11}{section*.3}
\contentsline {subsubsection}{神经网络的第二次高潮和第二次寒冬}{11}{section*.3}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{深度学习和神经网络方法的崛起}{12}{section*.4}
\contentsline {subsubsection}{深度学习和神经网络方法的崛起}{12}{section*.4}%
\defcounter {refsection}{0}\relax
\contentsline {subsection}{\numberline {1.1.2}为什么需要深度学习}{13}{subsection.1.1.2}
\contentsline {subsection}{\numberline {1.1.2}为什么需要深度学习}{13}{subsection.1.1.2}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{端到端学习和表示学习}{13}{section*.6}
\contentsline {subsubsection}{端到端学习和表示学习}{13}{section*.6}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{深度学习的效果}{14}{section*.8}
\contentsline {subsubsection}{深度学习的效果}{14}{section*.8}%
\defcounter {refsection}{0}\relax
\contentsline {section}{\numberline {1.2}神经网络基础}{14}{section.1.2}
\contentsline {section}{\numberline {1.2}神经网络基础}{14}{section.1.2}%
\defcounter {refsection}{0}\relax
\contentsline {subsection}{\numberline {1.2.1}线性代数基础}{14}{subsection.1.2.1}
\contentsline {subsection}{\numberline {1.2.1}线性代数基础}{14}{subsection.1.2.1}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{标量、向量和矩阵}{15}{section*.10}
\contentsline {subsubsection}{标量、向量和矩阵}{15}{section*.10}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{矩阵的转置}{16}{section*.11}
\contentsline {subsubsection}{矩阵的转置}{16}{section*.11}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{矩阵加法和数乘}{16}{section*.12}
\contentsline {subsubsection}{矩阵加法和数乘}{16}{section*.12}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{矩阵乘法和矩阵点乘}{17}{section*.13}
\contentsline {subsubsection}{矩阵乘法和矩阵点乘}{17}{section*.13}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{线性映射}{18}{section*.14}
\contentsline {subsubsection}{线性映射}{18}{section*.14}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{范数}{19}{section*.15}
\contentsline {subsubsection}{范数}{19}{section*.15}%
\defcounter {refsection}{0}\relax
\contentsline {subsection}{\numberline {1.2.2}人工神经元和感知机}{20}{subsection.1.2.2}
\contentsline {subsection}{\numberline {1.2.2}人工神经元和感知机}{20}{subsection.1.2.2}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{感知机\ \raisebox {0.5mm}{------}\ 最简单的人工神经元模型}{21}{section*.18}
\contentsline {subsubsection}{感知机\ \raisebox {0.5mm}{------}\ 最简单的人工神经元模型}{21}{section*.18}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{神经元内部权重}{22}{section*.21}
\contentsline {subsubsection}{神经元内部权重}{22}{section*.21}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{神经元的输入\ \raisebox {0.5mm}{------}\ 离散 vs 连续}{23}{section*.23}
\contentsline {subsubsection}{神经元的输入\ \raisebox {0.5mm}{------}\ 离散 vs 连续}{23}{section*.23}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{神经元内部的参数学习}{23}{section*.25}
\contentsline {subsubsection}{神经元内部的参数学习}{23}{section*.25}%
\defcounter {refsection}{0}\relax
\contentsline {subsection}{\numberline {1.2.3}多层神经网络}{24}{subsection.1.2.3}
\contentsline {subsection}{\numberline {1.2.3}多层神经网络}{24}{subsection.1.2.3}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{线性变换和激活函数}{24}{section*.27}
\contentsline {subsubsection}{线性变换和激活函数}{24}{section*.27}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{单层神经网络$\rightarrow $多层神经网络}{26}{section*.34}
\contentsline {subsubsection}{单层神经网络$\rightarrow $多层神经网络}{26}{section*.34}%
\defcounter {refsection}{0}\relax
\contentsline {subsection}{\numberline {1.2.4}函数拟合能力}{27}{subsection.1.2.4}
\contentsline {subsection}{\numberline {1.2.4}函数拟合能力}{27}{subsection.1.2.4}%
\defcounter {refsection}{0}\relax
\contentsline {section}{\numberline {1.3}神经网络的张量实现}{31}{section.1.3}
\contentsline {section}{\numberline {1.3}神经网络的张量实现}{31}{section.1.3}%
\defcounter {refsection}{0}\relax
\contentsline {subsection}{\numberline {1.3.1} 张量及其计算}{32}{subsection.1.3.1}
\contentsline {subsection}{\numberline {1.3.1} 张量及其计算}{32}{subsection.1.3.1}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{张量}{32}{section*.44}
\contentsline {subsubsection}{张量}{32}{section*.44}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{张量的矩阵乘法}{34}{section*.47}
\contentsline {subsubsection}{张量的矩阵乘法}{34}{section*.47}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{张量的单元操作}{35}{section*.49}
\contentsline {subsubsection}{张量的单元操作}{35}{section*.49}%
\defcounter {refsection}{0}\relax
\contentsline {subsection}{\numberline {1.3.2}张量的物理存储形式}{36}{subsection.1.3.2}
\contentsline {subsection}{\numberline {1.3.2}张量的物理存储形式}{36}{subsection.1.3.2}%
\defcounter {refsection}{0}\relax
\contentsline {subsection}{\numberline {1.3.3}使用开源框架实现张量计算}{36}{subsection.1.3.3}
\contentsline {subsection}{\numberline {1.3.3}使用开源框架实现张量计算}{36}{subsection.1.3.3}%
\defcounter {refsection}{0}\relax
\contentsline {subsection}{\numberline {1.3.4}前向传播与计算图}{38}{subsection.1.3.4}
\contentsline {subsection}{\numberline {1.3.4}前向传播与计算图}{38}{subsection.1.3.4}%
\defcounter {refsection}{0}\relax
\contentsline {subsection}{\numberline {1.3.5}神经网络实例}{41}{subsection.1.3.5}
\contentsline {subsection}{\numberline {1.3.5}神经网络实例}{41}{subsection.1.3.5}%
\defcounter {refsection}{0}\relax
\contentsline {section}{\numberline {1.4}神经网络的参数训练}{42}{section.1.4}
\contentsline {section}{\numberline {1.4}神经网络的参数训练}{42}{section.1.4}%
\defcounter {refsection}{0}\relax
\contentsline {subsection}{\numberline {1.4.1}损失函数}{43}{subsection.1.4.1}
\contentsline {subsection}{\numberline {1.4.1}损失函数}{43}{subsection.1.4.1}%
\defcounter {refsection}{0}\relax
\contentsline {subsection}{\numberline {1.4.2}基于梯度的参数优化}{44}{subsection.1.4.2}
\contentsline {subsection}{\numberline {1.4.2}基于梯度的参数优化}{44}{subsection.1.4.2}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{(一)梯度下降}{44}{section*.67}
\contentsline {subsubsection}{梯度下降}{44}{section*.67}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{(二)梯度获取}{46}{section*.69}
\contentsline {subsubsection}{梯度获取}{46}{section*.69}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{(三)基于梯度的方法的变种和改进}{49}{section*.73}
\contentsline {subsubsection}{基于梯度的方法的变种和改进}{49}{section*.73}%
\defcounter {refsection}{0}\relax
\contentsline {subsection}{\numberline {1.4.3}参数更新的并行化策略}{52}{subsection.1.4.3}
\contentsline {subsection}{\numberline {1.4.3}参数更新的并行化策略}{52}{subsection.1.4.3}%
\defcounter {refsection}{0}\relax
\contentsline {subsection}{\numberline {1.4.4}梯度消失、梯度爆炸和稳定性训练}{54}{subsection.1.4.4}
\contentsline {subsection}{\numberline {1.4.4}梯度消失、梯度爆炸和稳定性训练}{54}{subsection.1.4.4}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{(一)梯度消失现象及解决方法}{54}{section*.76}
\contentsline {subsubsection}{易于优化的激活函数}{54}{section*.76}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{(二)梯度爆炸现象及解决方法}{55}{section*.80}
\contentsline {subsubsection}{梯度裁剪}{55}{section*.80}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{(三)稳定性训练}{56}{section*.81}
\contentsline {subsubsection}{稳定性训练}{56}{section*.81}%
\defcounter {refsection}{0}\relax
\contentsline {subsection}{\numberline {1.4.5}过拟合}{57}{subsection.1.4.5}
\contentsline {subsection}{\numberline {1.4.5}过拟合}{57}{subsection.1.4.5}%
\defcounter {refsection}{0}\relax
\contentsline {subsection}{\numberline {1.4.6}反向传播}{58}{subsection.1.4.6}
\contentsline {subsection}{\numberline {1.4.6}反向传播}{58}{subsection.1.4.6}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{(一)输出层的反向传播}{59}{section*.84}
\contentsline {subsubsection}{(一)输出层的反向传播}{59}{section*.84}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{(二)隐藏层的反向传播}{61}{section*.88}
\contentsline {subsubsection}{(二)隐藏层的反向传播}{61}{section*.88}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{(三)程序实现}{62}{section*.91}
\contentsline {subsubsection}{(三)程序实现}{62}{section*.91}%
\defcounter {refsection}{0}\relax
\contentsline {section}{\numberline {1.5}神经语言模型}{64}{section.1.5}
\contentsline {section}{\numberline {1.5}神经语言模型}{64}{section.1.5}%
\defcounter {refsection}{0}\relax
\contentsline {subsection}{\numberline {1.5.1}基于神经网络的语言建模}{64}{subsection.1.5.1}
\contentsline {subsection}{\numberline {1.5.1}基于神经网络的语言建模}{64}{subsection.1.5.1}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{(一)基于前馈神经网络的语言模型}{65}{section*.94}
\contentsline {subsubsection}{(一)基于前馈神经网络的语言模型}{65}{section*.94}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{(二)基于循环神经网络的语言模型}{67}{section*.97}
\contentsline {subsubsection}{(二)基于循环神经网络的语言模型}{67}{section*.97}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{(三)基于自注意力机制的语言模型}{68}{section*.99}
\contentsline {subsubsection}{(三)基于自注意力机制的语言模型}{68}{section*.99}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{(四)语言模型的评价}{69}{section*.101}
\contentsline {subsubsection}{(四)语言模型的评价}{69}{section*.101}%
\defcounter {refsection}{0}\relax
\contentsline {subsection}{\numberline {1.5.2}单词表示模型}{70}{subsection.1.5.2}
\contentsline {subsection}{\numberline {1.5.2}单词表示模型}{70}{subsection.1.5.2}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{(一)One-hot编码}{70}{section*.102}
\contentsline {subsubsection}{(一)One-hot编码}{70}{section*.102}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{(二)分布式表示}{70}{section*.104}
\contentsline {subsubsection}{(二)分布式表示}{70}{section*.104}%
\defcounter {refsection}{0}\relax
\contentsline {subsection}{\numberline {1.5.3}句子表示模型及预训练}{72}{subsection.1.5.3}
\contentsline {subsection}{\numberline {1.5.3}句子表示模型及预训练}{72}{subsection.1.5.3}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{(一)简单的上下文表示模型}{72}{section*.108}
\contentsline {subsubsection}{(一)简单的上下文表示模型}{72}{section*.108}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{(二)ELMO模型}{74}{section*.111}
\contentsline {subsubsection}{(二)ELMO模型}{74}{section*.111}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{(三)GPT模型}{74}{section*.113}
\contentsline {subsubsection}{(三)GPT模型}{74}{section*.113}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{(四)BERT模型}{75}{section*.115}
\contentsline {subsubsection}{(四)BERT模型}{75}{section*.115}%
\defcounter {refsection}{0}\relax
\contentsline {subsubsection}{(五)为什么要预训练?}{76}{section*.117}
\contentsline {subsubsection}{(五)为什么要预训练?}{76}{section*.117}%
\defcounter {refsection}{0}\relax
\contentsline {section}{\numberline {1.6}小结及深入阅读}{76}{section.1.6}
\contentsline {section}{\numberline {1.6}小结及深入阅读}{76}{section.1.6}%
\contentsfinish
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论