Commit 20c67c10 by xiaotong

wording (sec 9, back-propagation)

parent af60cafc
......@@ -10,11 +10,11 @@
\node [anchor=south west,inner sep=2pt] (step100) at ([xshift=0.5em,yshift=-0.8em]h.north east) {\scriptsize{$\textbf{s}^K = \textbf{h}^{K-1} \textbf{w}^K$}};
\node [anchor=south west] (slabel) at ([yshift=1em,xshift=0.3em]s.north) {\scriptsize{\red{\textbf{{已经得到:$\pi^K = \frac{\partial L}{\partial \textbf{s}^K}$}}}}};
\draw [->,red] ([yshift=0.3em]slabel.south) .. controls +(south:0.5) and +(north:0.5) .. ([xshift=0.5em]s.north);
\node [anchor=south west] (slabel) at ([yshift=1em,xshift=0.3em]s.north) {\scriptsize{\textbf{{已经得到:$\pi^K = \frac{\partial L}{\partial \textbf{s}^K}$}}}};
\draw [->] ([yshift=0.3em]slabel.south) .. controls +(south:0.5) and +(north:0.5) .. ([xshift=0.5em,yshift=0.1em]s.north);
{
\draw [->,very thick,red] ([yshift=1em,xshift=-0.1em]s.north) -- ([yshift=1em,xshift=0.1em]h.north) node [pos=0.5,above] {\scriptsize{{$\frac{\partial L}{\partial \textbf{w}^K} = ?$, $\frac{\partial L}{\partial \textbf{h}^{K-1}} = ?$}}};
\draw [->,very thick,red] ([yshift=1em,xshift=-0.1em]s.north) -- ([yshift=1.0em,xshift=0.1em]h.north) node [pos=0.5,above] {\scriptsize{{$\frac{\partial L}{\partial \textbf{w}^K} = ?$, $\frac{\partial L}{\partial \textbf{h}^{K-1}} = ?$}}};
\draw [-,very thick,red] ([yshift=0.5em]h.north) -- ([yshift=1.5em]h.north);
\draw [-,very thick,red] ([yshift=0.5em]s.north) -- ([yshift=1.5em]s.north);
}
......
......@@ -51,15 +51,15 @@
\node [anchor=south,draw,rounded corners,inner sep=2pt,minimum width=8em,minimum height=1.2em,fill=green!30!white,blur shadow={shadow xshift=1pt,shadow yshift=-1pt}] (h3) at ([yshift=1.5em]h2.north) {\scriptsize{h2 = Relu(h1 * w2)}};
\node [anchor=south,draw,rounded corners,inner sep=2pt,minimum width=8em,minimum height=1.2em,fill=green!30!white,blur shadow={shadow xshift=1pt,shadow yshift=-1pt}] (h4) at ([yshift=1.5em]h3.north) {\scriptsize{h3 = h2 + h1}};
{\draw [<-,very thick,red] (h1.north) -- (h2.south);}
{\draw [<-,very thick,red] (h2.north) -- (h3.south);}
{\draw [<-,very thick,red] (h3.north) -- (h4.south);}
{\draw [<-,very thick,red,rounded corners] (h2.east) -- ([xshift=0.5em]h2.east) -- ([xshift=0.5em,yshift=0.5em]h3.north east) -- ([xshift=-2em,yshift=0.5em]h3.north east) -- ([xshift=-2em,yshift=1.5em]h3.north east);}
{\draw [->,very thick] (h1.north) -- (h2.south);}
{\draw [->,very thick] (h2.north) -- (h3.south);}
{\draw [->,very thick] (h3.north) -- (h4.south);}
{\draw [->,very thick,rounded corners] (h2.east) -- ([xshift=0.5em]h2.east) -- ([xshift=0.5em,yshift=0.5em]h3.north east) -- ([xshift=-2em,yshift=0.5em]h3.north east) -- ([xshift=-2em,yshift=1.5em]h3.north east);}
\node [anchor=south,draw,rounded corners,inner sep=2pt,minimum width=8.0em,minimum height=1.2em,fill=red!30!white,blur shadow={shadow xshift=1pt,shadow yshift=-1pt}] (slayer) at ([yshift=1.5em]h4.north) {\tiny{h4 = Softmax(h3 * w4) (output)}};
\node [anchor=south] (losslabel) at (slayer.north) {\scriptsize{\textbf{Cross Entropy Loss}}};
{\draw [<-,very thick,red] (h4.north) -- (slayer.south);}
{\draw [->,very thick] (h4.north) -- (slayer.south);}
\end{tikzpicture}
\end{center}
......
......@@ -13,7 +13,7 @@
\node[parametershard,anchor=west,fill=yellow!10] (param1) at (0,0) {$W_o$};
\node (param2) at ([xshift=1em]param1.east) {};
\node[parametershard,anchor=west,fill=red!10] (param3) at ([xshift=1em]param2.east) {$W_h$};
\node[anchor=south,inner sep=1pt] (serverlabel) at ([yshift=0.2em]param2.north) {\footnotesize{\textbf{parameter server}: $\mathbf w_{new} = \mathbf w - \alpha\cdot \frac{\partial L}{\partial \mathbf w}$}};
\node[anchor=south,inner sep=1pt] (serverlabel) at ([yshift=0.2em]param2.north) {\footnotesize{\textbf{parameter server}: $\mathbf w_{\textrm{new}} = \mathbf w - \alpha\cdot \frac{\partial L}{\partial \mathbf w}$}};
}
\begin{pgfonlayer}{background}
......@@ -33,7 +33,7 @@
{
\draw[->,very thick,red] ([xshift=-0.5em,yshift=2pt]processor2.north) -- ([xshift=-0.5em,yshift=-2pt]serverbox.south) node [pos=0.5,align=right,xshift=-2em] (pushlabel) {\scriptsize{$\frac{\partial L}{\partial \mathbf w}$}};;
\draw[<-,very thick,blue] ([xshift=0.5em,yshift=2pt]processor2.north) -- ([xshift=0.5em,yshift=-2pt]serverbox.south) node [pos=0.5,align=left,xshift=2.2em] (fetchlabel) {\scriptsize{$\mathbf w_{new}$}};;;
\draw[<-,very thick,blue] ([xshift=0.5em,yshift=2pt]processor2.north) -- ([xshift=0.5em,yshift=-2pt]serverbox.south) node [pos=0.5,align=left,xshift=2.2em] (fetchlabel) {\scriptsize{$\mathbf w_{\textrm{new}}$}};;;
\draw[->,very thick,red] ([xshift=-0.5em,yshift=2pt]processor3.north) --
([xshift=3em,yshift=-2pt]serverbox.south);
\draw[<-,very thick,blue] ([xshift=0.5em,yshift=2pt]processor3.north) -- ([xshift=4em,yshift=-2pt]serverbox.south) node [pos=0.5,align=left,xshift=2.2em] (fetchlabel) {\scriptsize{fetch (F)}};
......
......@@ -1511,15 +1511,6 @@ $+2x^2+x+1)$ & \ \ $(x^4+2x^3+2x^2+x+1)$ & $+6x+1$ \\
\parinterval 针对以上问题,很多学者尝试对梯度下降方法做出改进,如Momentum, Adagrad, Adadelta, RMSprop, Adam, AdaMax, Nadam, AMSGrad等等,在这里将介绍Momentum、AdaGrad、RMSprop、Adam这4 种方法。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter9/Figures/fig-sawtooth}
\caption{Momentum梯度下降 vs 普通梯度下降}
\label{fig:5-46}
\end{figure}
%-------------------------------------------
%----------------------------------------------------------------------------------------
%
%----------------------------------------------------------------------------------------
......@@ -1539,6 +1530,16 @@ w_{t+1}&=&w_t-\alpha v_t
\parinterval 这里的``梯度''不再只是现在的损失函数的梯度,而是之前的梯度的加权和。在原始的梯度下降算法中,如果在某个参数状态下,梯度方向变化特别大,甚至与上一次参数更新中梯度方向成90度夹角,下一次参数更新中梯度方向可能又是一次90度的改变,这时参数优化路径将会成``锯齿''状(如图\ref{fig:5-46}所示),优化效率极慢。而Momentum梯度下降算法不会让梯度发生90度的变化,而是让梯度慢慢发生改变:如果当前的梯度方向与之前的梯度方向相同,在原梯度方向上加速更新参数;如果当前的梯度方向与之前的梯度方向相反,并不会产生一个急转弯,而是尽量把优化路径平滑地进行改变。这样做的优点也非常明显,一方面杜绝了``锯齿''状优化路径的出现,另一方面将优化幅度变得更加平滑,不会导致频频跳过最优点。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter9/Figures/fig-sawtooth}
\caption{Momentum梯度下降 vs 普通梯度下降}
\label{fig:5-46}
\end{figure}
%-------------------------------------------
%----------------------------------------------------------------------------------------
%
%----------------------------------------------------------------------------------------
......@@ -1597,7 +1598,7 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\noindent 可以看到Adam 算法相当于在RMSProp算法中引入了Momentum算法中的动量项,这样做使得Adam算法兼具了Momentum算法和RMSProp算法的优点:既能使梯度更为``平滑''地更新,同时可以为神经网络中的每个参数设置不同的学习率。
\parinterval 需要注意的是包括Adam在内的很多参数更新算法中的学习率都需要人为设置。而且模型学习的效果与学习率的设置关系极大,甚至在研发实际系统时工程师需要进行大量的实验,才能得到最佳的模型。第六章还会具体介绍在机器翻译中参数更新学习率设置的策略。
\parinterval 需要注意的是包括Adam在内的很多参数更新算法中的学习率都需要人为设置。而且模型学习的效果与学习率的设置关系极大,甚至在研发实际系统时工程师需要进行大量的实验,才能得到最佳的模型。
%----------------------------------------------------------------------------------------
% NEW SUB-SECTION
......@@ -1605,7 +1606,7 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\subsection{参数更新的并行化策略}
\parinterval 虽然通过GPU可以加速神经网络的运算,但当神经网络较为复杂时,模型训练还是需要几天甚至几周的时间。如果希望尽可能缩短一次学习所需的时间,最直接的想法就是把不同的训练样本分配给多个GPU 或CPU,然后在这些设备上同时进行训练,即实现并行化训练。这种方法也被称作{\small\sffamily\bfseries{数据并行}}\index{数据并行}。具体实现时,有两种常用的并行化策略:(参数)同步更新和(参数)异步更新。
\parinterval 当神经网络较为复杂时,模型训练还是需要几天甚至几周的时间。如果希望尽可能缩短一次学习所需的时间,最直接的想法就是把不同的训练样本分配给多个GPU 或CPU,然后在这些设备上同时进行训练,即实现并行化训练。这种方法也被称作{\small\sffamily\bfseries{数据并行}}\index{数据并行}。具体实现时,有两种常用的并行化策略:(参数)同步更新和(参数)异步更新。
\begin{itemize}
\vspace{0.5em}
......@@ -1615,6 +1616,10 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\vspace{0.5em}
\end{itemize}
\parinterval\ref{fig:5-47}对比了同步更新和异步更新的区别,在这个例子中,使用4台设备对一个两层神经网络中的参数进行更新,其中使用了一个{\small\bfnew{参数服务器}}\index{参数服务器}(Parameter Server\index{Parameter Server})来保存最新的参数,不同设备(Worker,图中的G1、G2、G3)可以通过同步或者异步的方式访问参数服务器。图中的$ \mathbf w_o $$ \mathbf w_h $分别代表输出层和隐藏层的全部参数,操作push(P) 表示设备向参数服务器传送梯度,操作fetch(F)表示参数服务器向设备传送更新后的参数。
\parinterval 此外,在使用多个设备进行并行训练的时候,由于设备间带宽的限制,大量的数据传输会有较高的延时。对于复杂神经网络来说,设备间参数和梯度传递的时间消耗也会成为一个不得不考虑的因素。有时候,设备间数据传输的时间甚至比模型计算的时间都长,大大降低了并行度\cite{xiao2017fast}。对于这种问题,可以考虑对数据进行压缩或者减少传输的次数来缓解问题。
%----------------------------------------------
\begin{figure}[htp]
\centering
......@@ -1624,9 +1629,6 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\end {figure}
%-------------------------------------------
\parinterval\ref{fig:5-47}对比了同步更新和异步更新的区别,在这个例子中,使用4台设备对一个两层神经网络中的参数进行更新,其中使用了一个{\small\bfnew{参数服务器}}\index{参数服务器}(Parameter Server\index{Parameter Server})来保存最新的参数,不同设备(Worker,图中的G1、G2、G3)可以通过同步或者异步的方式访问参数服务器。图中的$ \mathbf w_o $$ \mathbf w_h $分别代表输出层和隐藏层的全部参数,操作push(P) 表示设备向参数服务器传送梯度,操作fetch(F)表示参数服务器向设备传送更新后的参数。
\parinterval 此外,在使用多个设备进行并行训练的时候,由于设备间带宽的限制,大量的数据传输会有较高的延时。对于复杂神经网络来说,设备间参数和梯度传递的时间消耗也会成为一个不得不考虑的因素。有时候,设备间数据传输的时间甚至比模型计算的时间都长,大大降低了并行度\cite{xiao2017fast}。对于这种问题,可以考虑对数据进行压缩或者减少传输的次数来缓解问题。
%----------------------------------------------------------------------------------------
% NEW SUB-SECTION
......@@ -1644,41 +1646,6 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\parinterval 网络训练过程中,如果每层网络的梯度都小于1,各层梯度的偏导数会与后面层传递而来的梯度相乘得到本层的梯度,并向前一层传递。该过程循环进行,最后导致梯度指数级地减小,这就产生了梯度消失现象。这种情况会导致神经网络层数较浅的部分梯度接近0。一般来说,产生很小梯度的原因是使用了类似于Sigmoid这样的激活函数,当输入的值过大或者过小的时候这类函数曲线会趋于直线,梯度近似为零。针对这个问题,主要的解决办法是使用更加易于优化的激活函数,比如,使用ReLU代替Sigmoid和Tanh作为激活函数。
\parinterval 缓解梯度消失问题最直接的想法就是希望各层的偏导数大于或等于1。图\ref{fig:5-48}展示了Sigmoid激活函数$ y=\frac{1}{1+e^{-x}}$的函数曲线和导函数曲线,如果使用Sigmoid作为损失函数,其梯度不可能超过0.25,这样经过链式求导之后,很容易发生梯度消失。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter9/Figures/fig-derivative1}
\caption{Sigmoid激活函数的函数曲线和导函数曲线}
\label{fig:5-48}
\end {figure}
%-------------------------------------------
\parinterval 同理,Tanh作为激活函数也容易出现梯度消失现象,图\ref{fig:5-49}展示了Tanh激活函数$ y=\frac{e^x-e^{-x}}{e^x+e^{-x}}$的函数曲线和导函数曲线,可以看出,Tanh激活函数比Sigmoid激活函数要好一些,但是Tanh激活函数的导数也小于1,因此无法避免梯度消失现象。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter9/Figures/fig-derivative2}
\caption{Tanh激活函数的函数曲线和导函数曲线}
\label{fig:5-49}
\end {figure}
%-------------------------------------------
\parinterval ReLU激活函数的思想也很简单,如果激活函数的导数为1,那么就不存在梯度消失爆炸的问题了。图\ref{fig:5-50}展示了ReLU激活函数$ y={\rm{max}}(0,x)$的函数曲线和导函数曲线。可以很容易看出,ReLU函数的导数在正数部分是恒等于1的,因此在深层网络中使用ReLU激活函数就不会产生很小的梯度。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter9/Figures/fig-derivative3}
\caption{ReLU激活函数的函数曲线和导函数曲线}
\label{fig:5-50}
\end {figure}
%-------------------------------------------
\parinterval 当然,梯度消失并不是仅仅可以通过改变激活函数就可以完全消除掉。随着网络层数的增加,很多因素都可能会造成梯度消失。后面也会进一步介绍其他手段,可以综合运用这些方法达到很好的缓解梯度消失问题的目的。
%----------------------------------------------------------------------------------------
% NEW SUBSUB-SECTION
%----------------------------------------------------------------------------------------
......@@ -1703,19 +1670,18 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\parinterval 为了使神经网络模型训练更加稳定,通常还会考虑其他策略。
\parinterval {\red{考虑是否弄成小标题}}
\parinterval (1){\small\bfnew{批量归一化}}\index{批量归一化}(Batch Normalization)\index{Batch Normalization}
\parinterval 批量归一化,顾名思义,是以进行学习时的小批量样本为单位进行归一化\cite{ioffe2015batch}。具体而言,就是对神经网络隐层输出的每一个维度,沿着批次的方向进行均值为0、方差为1的归一化。在深层神经网络中,每一层网络都可以使用批量归一化操作。这样使神经网络任意一层的输入不至于过大或过小,从而防止隐层中异常值导致模型状态的巨大改变。
\begin{itemize}
\item {\small\bfnew{批量归一化}}\index{批量归一化}(Batch Normalization)\index{Batch Normalization}。批量归一化,顾名思义,是以进行学习时的小批量样本为单位进行归一化\cite{ioffe2015batch}。具体而言,就是对神经网络隐层输出的每一个维度,沿着批次的方向进行均值为0、方差为1的归一化。在深层神经网络中,每一层网络都可以使用批量归一化操作。这样使神经网络任意一层的输入不至于过大或过小,从而防止隐层中异常值导致模型状态的巨大改变。
\parinterval (2){\small\bfnew{层归一化}}\index{层归一化}(Layer Normalization)\index{Layer Normalization}
\item {\small\bfnew{层归一化}}\index{层归一化}(Layer Normalization)\index{Layer Normalization}。类似的,层归一化更多是针对自然语言处理这种序列处理任务\cite{Ba2016LayerN},它和批量归一化的原理是一样的,只是归一化操作是在序列上同一层网络的输出结果上进行的,也就是归一化操作沿着序列方向进行。这种方法可以很好的避免序列上不同位置神经网络输出结果的不可比性。同时由于归一化后所有的结果都转化到一个可比的范围,使得隐层状态可以在不同层之间进行自由组合。
\parinterval 类似的,层归一化更多是针对自然语言处理这种序列处理任务\cite{Ba2016LayerN},它和批量归一化的原理是一样的,只是归一化操作是在序列上同一层网络的输出结果上进行的,也就是归一化操作沿着序列方向进行。这种方法可以很好的避免序列上不同位置神经网络输出结果的不可比性。同时由于归一化后所有的结果都转化到一个可比的范围,使得隐层状态可以在不同层之间进行自由组合。
\item {\small\bfnew{残差网络}}\index{残差网络}(Residual Networks)\index{Residual Networks}。最初,残差网络是为了解决神经网络持续加深时的模型退化问题\cite{DBLP:journals/corr/HeZRS15},但是残差结构对解决梯度消失和梯度爆炸问题也有所帮助。有了残差结构,可以很轻松的构建几十甚至上百层的神经网络,而不用担心层数过深造成的梯度消失问题。残差网络的结构如图\ref{fig:5-51}所示。图\ref{fig:5-51}中右侧的曲线叫做{\small\bfnew{跳接}}\index{跳接}(Shortcut Connection)\index{Shortcut Connection},通过跳接在激活函数前,将上一层(或几层)之前的输出与本层计算的输出相加,将求和的结果输入到激活函数中作为本层的输出。假设残差结构的输入为$ \mathbf x_l $,输出为$ \mathbf x_{l+1} $,则有
\parinterval (3){\small\bfnew{残差网络}}\index{残差网络}(Residual Networks)\index{Residual Networks}
\begin{eqnarray}
\mathbf x_{l+1}&=&F(\mathbf x_l)+\mathbf x_l
\label{eq:5-44}
\end{eqnarray}
\parinterval 最初,残差网络是为了解决神经网络持续加深时的模型退化问题\cite{DBLP:journals/corr/HeZRS15},但是残差结构对解决梯度消失和梯度爆炸问题也有所帮助。有了残差结构,可以很轻松的构建几十甚至上百层的神经网络,而不用担心层数过深造成的梯度消失问题。残差网络的结构如图\ref{fig:5-51}所示:
%----------------------------------------------
\begin{figure}[htp]
......@@ -1726,13 +1692,7 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\end{figure}
%-------------------------------------------
\parinterval\ref{fig:5-51}中右侧的曲线叫做{\small\bfnew{跳接}}\index{跳接}(Shortcut Connection)\index{Shortcut Connection},通过跳接在激活函数前,将上一层(或几层)之前的输出与本层计算的输出相加,将求和的结果输入到激活函数中作为本层的输出。假设残差结构的输入为$ \mathbf x_l $,输出为$ \mathbf x_{l+1} $,则有
\begin{eqnarray}
\mathbf x_{l+1}&=&F(\mathbf x_l)+\mathbf x_l
\label{eq:5-44}
\end{eqnarray}
\parinterval 相比较于简单的多层堆叠的结构,残差网络提供了跨层连接结构。这种结构在反向传播中有很大的好处,比如,对于$ \mathbf x_l $处的梯度可以进行如下计算:
相比较于简单的多层堆叠的结构,残差网络提供了跨层连接结构。这种结构在反向传播中有很大的好处,比如,对于$ \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\\
&=&\frac{\partial L}{\partial \mathbf x_{l+1}} \cdot \left(1+\frac{\partial F(\mathbf x_l)}{\partial \mathbf x_l}\right)\nonumber\\
......@@ -1740,7 +1700,9 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\label{eq:5-45}
\end{eqnarray}
\parinterval 由上式可知,残差网络可以将后一层的梯度$ \frac{\partial L}{\partial \mathbf x_{l+1}} $不经过任何乘法项直接传递到$ \frac{\partial L}{\partial \mathbf x_l} $,从而缓解了梯度经过每一层后多次累乘造成的梯度消失问题。在第六章中还会看到,在机器翻译中残差结构可以和层归一化一起使用,而且这种组合可以取得很好的效果。
由上式可知,残差网络可以将后一层的梯度$ \frac{\partial L}{\partial \mathbf x_{l+1}} $不经过任何乘法项直接传递到$ \frac{\partial L}{\partial \mathbf x_l} $,从而缓解了梯度经过每一层后多次累乘造成的梯度消失问题。在{\chaptertwelve}中还会看到,在机器翻译中残差结构可以和层归一化一起使用,而且这种组合可以取得很好的效果。
\end{itemize}
%----------------------------------------------------------------------------------------
% NEW SUB-SECTION
......@@ -1754,7 +1716,7 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\parinterval 过拟合的模型通常会表现为部分非零参数过多或者参数的值过大。这种参数产生的原因在于模型需要复杂的参数才能匹配样本中的个别现象甚至噪声。基于此,常见的正则化方法有L1正则化和L2正则化,其命名方式是由$ R(\mathbf w) $的计算形式来决定的。在L1正则化中,$ R(\mathbf w) $即为参数$ w $$ l_1 $范数,即$ R(\mathbf w) ={\Vert \mathbf w\Vert}_1=\sum_{i=1}^{n}{\vert w_i\vert} $;在L2正则化中,$ R(\mathbf w) $即为参数$ w $$ l_2 $范数的平方,即$ R(\mathbf w) =({\Vert \mathbf w\Vert}_2)^2=\sum_{i=1}^{n}{w_i^2} $。L1正则化中的正则项衡量了模型权数中的绝对值大小,倾向于生成值为0的参数,从而让参数变得更加稀疏;而L2正则化由于平方的加入,当参数中的某一项小到一定程度,比如0.001的时候,参数的平方结果已经可以忽略不计了,因此L2正则化会倾向生成很小的参数,在这种情况下,即便训练数据中含有少量随机噪音,模型也不太容易通过增加个别参数的值来对噪声进行过度拟合,即提高了模型的抗扰动能力。
\parinterval 此外,在第六章即将介绍的Dropout和Label Smoothing方法也可以被看作是一种正则化操作。它们都可以提高模型在未见数据上的泛化能力。
\parinterval 此外,在{\chaptertwelve}即将介绍的Dropout和Label Smoothing方法也可以被看作是一种正则化操作。它们都可以提高模型在未见数据上的泛化能力。
%----------------------------------------------------------------------------------------
% NEW SUB-SECTION
......@@ -1762,7 +1724,7 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\subsection{反向传播}\label{sec:5.4.6}
\parinterval 为了获取梯度,最常用的做法是使用自动微分技术,通常通过{\small\sffamily\bfseries{反向传播}}\index{反向传播}back propagation)\index{back propagation}来实现。该方法分为两个计算过程:前向计算和反向计算。前向计算的目的是从输入开始,逐层计算,得到网络的输出,并记录计算图中每个节点的局部输出。反向计算过程从输出端反向计算梯度,这个过程可以被看作是一种梯度的``传播'',最终计算图中所有节点都会得到相应的梯度结果。
\parinterval 为了获取梯度,最常用的做法是使用自动微分技术,通常通过{\small\sffamily\bfseries{反向传播}}\index{反向传播}Back Propagation)\index{Back Propagation}来实现。该方法分为两个计算过程:前向计算和反向计算。前向计算的目的是从输入开始,逐层计算,得到网络的输出,并记录计算图中每个节点的局部输出。反向计算过程从输出端反向计算梯度,这个过程可以被看作是一种梯度的``传播'',最终计算图中所有节点都会得到相应的梯度结果。
\parinterval 这里,首先对反向传播算法中涉及到的符号进行统一说明。图\ref{fig:5-52}是一个多层神经网络,其中层$ k-1 $、层$ k $、层$ k+1 $均为神经网络中的隐藏层,层$ K $为神经网络中的输出层。为了化简问题,这里每层网络没有使用偏置项。
......@@ -1980,9 +1942,9 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
% NEW SUBSUB-SECTION
%----------------------------------------------------------------------------------------
\subsubsection{3. 程序实现}
\subsubsection{3. 实例}
\parinterval 在了解了反向传播的原理之后,实现反向传播就变得非常容易了。实际上,现在主流的深度学习框架都支持自动微分。为了进一步说明反向传播的过程,这里使用NiuTensor工具构建两个简单的实例,并分别尝试手动编写反向传播代码和使用NiuTensor自带的自动微分模块
\parinterval 为了进一步理解反向传播的过程,图\ref{fig:5-58}展示了一个简单的神经网络的反向传播程序示例
%----------------------------------------------
\begin{figure}[htp]
......@@ -1993,7 +1955,9 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\end{figure}
%-------------------------------------------
\parinterval\ref{fig:5-58}展示了一个简单的神经网络的反向传播程序示例。这种反向传播的实现方式正是上一节内容的代码实现:按层实现自动微分并将梯度向前一层传播。
\parinterval 此外,很多张量计算工具都提供了封装好的反向传播函数。如图\ref{fig:5-59}所示,在完成神经网络的搭建后,无论前向计算过程是怎样的,直接利用Backward 函数就可以实现整个神经网络的反向传播,系统开发人员可以完全不用关心其求解过程。
%----------------------------------------------
\begin{figure}[htp]
\centering
......@@ -2003,7 +1967,6 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\end{figure}
%-------------------------------------------
\parinterval 此外,NiuTensor还提供了一种更简单的一步完成神经网络反向传播的实现方式。如图\ref{fig:5-59}所示,在完成神经网络的搭建后,无论前向计算过程是怎样的,直接利用Backward 函数就可以实现整个神经网络的反向传播,系统开发人员可以完全不用关心其求解过程。
%----------------------------------------------------------------------------------------
% NEW SECTION
......@@ -2012,7 +1975,7 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\sectionnewpage
\section{神经语言模型}\label{sec5:nlm}
\parinterval 神经网络给我们提供了一种工具,只要将问题的输入和输出定义好,就可以学习输入和输出之间的对应关系。显然,很多自然语言处理任务都可以用神经网络进行实现。比如,在机器翻译中,可以把输入的源语言句子和输出的目标语言句子用神经网络建模;在文本分类中,可以把输入的文本内容和输出的类别标签进行神经网络建模,等等。
\parinterval 神经网络提供了一种工具,只要将问题的输入和输出定义好,就可以学习输入和输出之间的对应关系。显然,很多自然语言处理任务都可以用神经网络进行实现。比如,在机器翻译中,可以把输入的源语言句子和输出的目标语言句子用神经网络建模;在文本分类中,可以把输入的文本内容和输出的类别标签进行神经网络建模,等等。
\parinterval 为了更好地理解神经网络和深度学习在自然语言处理中的应用。这里介绍一种基于神经网络的语言建模方法\ \dash \ {\small\sffamily\bfseries{神经语言模型}}\index{神经语言模型}(Neural Language Model)\index{Neural Language Model}。可以说,神经语言模型是深度学习时代下自然语言处理的标志性成果,它所涉及的许多概念至今仍是研究的热点,比如:词嵌入、表示学习、预训练等。此外,神经语言模型也为机器翻译的建模提供了很好的思路。从某种意义上说,机器翻译的深度学习建模的很多灵感均来自神经语言模型,二者在一定程度上是统一的。
......@@ -2022,7 +1985,7 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\subsection{基于神经网络的语言建模}
\parinterval 回顾一下第二章的内容,语言建模的问题被定义为:对于一个词序列$ w_1w_2\dots w_m$,如何计算该词序列的可能性?词序列出现的概率可以通过链式法则得到:
\parinterval 回顾一下{\chaptertwo}的内容,语言建模的问题被定义为:对于一个词序列$ w_1w_2\dots w_m$,如何计算该词序列的可能性?词序列出现的概率可以通过链式法则得到:
\begin{eqnarray}
{\rm P}(w_1w_2\dots w_m)&=&{\rm P}(w_1){\rm P}(w_2|w_1){\rm P}(w_3|w_1w_2)\dots {\rm P}(w_m|w_1\dots w_{m-1})
\label{eq:5-57}
......@@ -2034,7 +1997,7 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\label{eq:5-58}
\end{eqnarray}
\noindent 其中,$ {\rm P}(w_m|w_{m-n+1}\dots w_{m-1}) $可以通过相对频次估计进行计算$ {\rm{count}}(\cdot) $表示在训练数据上的频次
\noindent 其中,$ {\rm P}(w_m|w_{m-n+1}\dots w_{m-1}) $可以通过相对频次估计进行计算。令$ {\rm{count}}(\cdot) $表示在训练数据上的频次,于是有
\begin{eqnarray}
{\rm P}(w_m|w_{m-n+1}\dots w_{m-1})&=&\frac{{\rm{count}}(w_{m-n+1}\dots w_m)}{{\rm{count}}(w_{m-n+1}\dots w_{m-1})}
\label{}
......
......@@ -136,10 +136,11 @@
%\include{Chapter3/chapter3}
%\include{Chapter4/chapter4}
%\include{Chapter5/chapter5}
\include{Chapter6/chapter6}
%\include{Chapter6/chapter6}
%\include{Chapter7/chapter7}
%\include{Chapter8/chapter8}
%\include{Chapter9/chapter9}
\include{Chapter9/chapter9}
%\include{Chapter10/chapter10}
%\include{Chapter11/chapter11}
%\include{Chapter12/chapter12}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论