Commit af60cafc by xiaotong

wording (sec 9, gradient)

parent 3ff7eff5
......@@ -42,10 +42,10 @@
%% sigmoid box
\begin{scope}
{
\node [anchor=west] (flabel) at ([xshift=0.5in]y.east) {\scriptsize{sigmoid:}};
\node [anchor=north east] (slabel) at ([xshift=0]flabel.south east) {\scriptsize{sum:}};
\node [anchor=west,inner sep=2pt] (flabel2) at (flabel.east) {\scriptsize{$f(s)=1/(1+e^{-s})$}};
\node [anchor=west,inner sep=2pt] (flabel3) at (slabel.east) {\scriptsize{$s=x_1 \cdot w + b$}};
\node [anchor=west] (flabel) at ([xshift=0.5in]y.east) {\scriptsize{Sigmoid:}};
\node [anchor=north east] (slabel) at ([xshift=0]flabel.south east) {\scriptsize{Sum:}};
\node [anchor=west,inner sep=2pt] (flabel2) at (flabel.east) {\scriptsize{$f(s_2)=1/(1+e^{-s_2})$}};
\node [anchor=west,inner sep=2pt] (flabel3) at (slabel.east) {\scriptsize{$s_2=x_1 \cdot w_2 + b$}};
\draw [->,thick,dotted] ([yshift=-0.3em,xshift=-0.1em]n11.60) .. controls +(east:1) and +(west:2) .. ([xshift=-0.2em]flabel.west) ;
\begin{pgfonlayer}{background}
......@@ -136,10 +136,10 @@
%% sigmoid box
\begin{scope}
{
\node [anchor=west] (flabel) at ([xshift=0.8in]y.east) {\scriptsize{sigmoid:}};
\node [anchor=north east] (slabel) at ([xshift=0]flabel.south east) {\scriptsize{sum:}};
\node [anchor=west,inner sep=2pt] (flabel2) at (flabel.east) {\scriptsize{$f(s)=1/(1+e^{-s})$}};
\node [anchor=west,inner sep=2pt] (flabel3) at (slabel.east) {\scriptsize{$s=x_1 \cdot w + b$}};
\node [anchor=west] (flabel) at ([xshift=0.8in]y.east) {\scriptsize{Sigmoid:}};
\node [anchor=north east] (slabel) at ([xshift=0]flabel.south east) {\scriptsize{Sum:}};
\node [anchor=west,inner sep=2pt] (flabel2) at (flabel.east) {\scriptsize{$f(s_2)=1/(1+e^{-s_2})$}};
\node [anchor=west,inner sep=2pt] (flabel3) at (slabel.east) {\scriptsize{$s_2=x_1 \cdot w_2 + b$}};
\draw [->,thick,dotted] ([yshift=-0.3em,xshift=-0.1em]n11.60) .. controls +(east:1) and +(west:2) .. ([xshift=-0.2em]flabel.west) ;
\begin{pgfonlayer}{background}
{
......
......@@ -26,12 +26,12 @@
\end{pgfonlayer}
\node [anchor=west] (layer00label) at ([xshift=1.3em]x5.east) {\footnotesize{第0层}};
\node [anchor=west] (layer00label2) at (layer00label.east) {\footnotesize{\red{(输入层)}}};
\node [anchor=west] (layer00label2) at (layer00label.east) {\footnotesize{(输入层)}};
{
\node [anchor=west] (layer01label) at ([xshift=1em]layer01.east) {\footnotesize{第1层}};
}
{
\node [anchor=west] (layer01label2) at (layer01label.east) {\footnotesize{\red{({隐层})}}};
\node [anchor=west] (layer01label2) at (layer01label.east) {\footnotesize{({隐层})}};
}
%%% layer 2
......@@ -57,7 +57,7 @@
\node [anchor=west] (layer02label) at ([xshift=4.4em]layer02.east) {\footnotesize{第2层}};
{
\node [anchor=west] (layer02label2) at (layer02label.east) {\footnotesize{\red{({隐层})}}};
\node [anchor=west] (layer02label2) at (layer02label.east) {\footnotesize{({隐层})}};
}
}
......@@ -87,7 +87,7 @@
\node [anchor=west] (layer03label) at ([xshift=1em]layer03.east) {\footnotesize{第3层}};
{
\node [anchor=west] (layer03label2) at (layer03label.east) {\footnotesize{\red{({输出层})}}};
\node [anchor=west] (layer03label2) at (layer03label.east) {\footnotesize{({输出层})}};
}
}
......
......@@ -4,21 +4,21 @@ $$
\begin{smallmatrix} \underbrace{
\left\{
\begin{smallmatrix}
\left[
\left(
\begin{array}{cccc}
1& 0 &0 \\
0& 1 &0 \\
0& 0 &1
\end{array}
\right ]
\right )
\cdots
\left[
\left(
\begin{array}{cccc}
1& 0 &0 \\
0& 1 &0 \\
0& 0 &1
\end{array}
\right]
\right)
\end{smallmatrix}
\right\}
}\\5
......@@ -37,21 +37,21 @@ $$
\begin{smallmatrix} \underbrace{
\left\{
\begin{smallmatrix}
\left[
\left(
\begin{array}{cccc}
1 \\
1 \\
1
\end{array}
\right ]
\right)
\cdots
\left[
\left(
\begin{array}{cccc}
1 \\
1 \\
1
\end{array}
\right]
\right)
\end{smallmatrix}
\right\}
}\\5
......
......@@ -7,7 +7,7 @@
\begin{tikzpicture}
\begin{scope}[yshift=6.5em,xshift=1em]
\setcounter{mycount1}{1}
\draw[step=0.5cm,color=orange,line width=0.2mm] (-2,-2) grid (1,1);
\draw[step=0.5cm,color=orange,line width=0.4mm] (-2,-2) grid (1,1);
\foreach \y in {+0.5,-0.5,-1.5}
\foreach \x in {-1.5,-0.5,0.5}{
\node [fill=orange!20,inner sep=0pt,minimum height=0.98cm,minimum width=0.98cm] at (\x,\y) {\number\value{mycount1}};
......@@ -17,7 +17,7 @@
\begin{scope}[yshift=5.5em,xshift=0em]
\setcounter{mycount2}{2}
\draw[step=0.5cm,color=blue,line width=0.2mm] (-2,-2) grid (1,1);
\draw[step=0.5cm,color=blue,line width=0.4mm] (-2,-2) grid (1,1);
\foreach \y in {+0.5,-0.5,-1.5}
\foreach \x in {-1.5,-0.5,0.5}{
\node [fill=blue!20,inner sep=0pt,minimum height=0.98cm,minimum width=0.98cm] at (\x,\y) {\number\value{mycount2}};
......@@ -27,7 +27,7 @@
\begin{scope}[yshift=4.5em,xshift=-1em]
\setcounter{mycount3}{3}
\draw[step=0.5cm,color=ugreen,line width=0.2mm] (-2,-2) grid (1,1);
\draw[step=0.5cm,color=ugreen,line width=0.4mm] (-2,-2) grid (1,1);
\foreach \y in {+0.5,-0.5,-1.5}
\foreach \x in {-1.5,-0.5,0.5}{
\node [fill=green!20,inner sep=0pt,minimum height=0.98cm,minimum width=0.98cm] at (\x,\y) {\number\value{mycount3}};
......@@ -37,7 +37,7 @@
\begin{scope}[yshift=3.5em,xshift=-2em]
\setcounter{mycount4}{4}
\draw[step=0.5cm,color=red,line width=0.2mm] (-2,-2) grid (1,1);
\draw[step=0.5cm,color=red,line width=0.4mm] (-2,-2) grid (1,1);
\foreach \y in {+0.5,-0.5,-1.5}
\foreach \x in {-1.5,-0.5,0.5}{
\node [fill=red!20,inner sep=0pt,minimum height=0.98cm,minimum width=0.98cm] at (\x,\y) {\number\value{mycount4}};
......@@ -45,4 +45,4 @@
}
\end{scope}
\end{tikzpicture}
%%%------------------------------------------------------------------------------------------------------------
\ No newline at end of file
%%%------------------------------------------------------------------------------------------------------------
......@@ -44,10 +44,10 @@
%% sigmoid box
\begin{scope}
{
\node [anchor=west] (flabel) at ([xshift=1in]y.east) {\footnotesize{sigmoid:}};
\node [anchor=north east] (slabel) at ([xshift=0]flabel.south east) {\footnotesize{sum:}};
\node [anchor=west,inner sep=2pt] (flabel2) at (flabel.east) {\footnotesize{$f(s)=1/(1+e^{-s})$}};
\node [anchor=west,inner sep=2pt] (flabel3) at (slabel.east) {\footnotesize{$s=x_1 \cdot w + b$}};
\node [anchor=west] (flabel) at ([xshift=1in]y.east) {\footnotesize{Sigmoid:}};
\node [anchor=north east] (slabel) at ([xshift=0]flabel.south east) {\footnotesize{Sum:}};
\node [anchor=west,inner sep=2pt] (flabel2) at (flabel.east) {\footnotesize{$f(s_2)=1/(1+e^{-s_2})$}};
\node [anchor=west,inner sep=2pt] (flabel3) at (slabel.east) {\footnotesize{$s_2=x_1 \cdot w_2 + b$}};
\draw [->,thick,dotted] ([yshift=-0.3em,xshift=-0.1em]n11.60) .. controls +(east:1) and +(west:2) .. ([xshift=-0.2em]flabel.west) ;
\begin{pgfonlayer}{background}
......
......@@ -757,7 +757,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
%-------------------------------------------
\vspace{-0.5em}
\parinterval 那激活函数又是什么?神经元在接收到经过线性变换的结果后,通过激活函数的处理,得到最终的输出$ \mathbf y $。激活函数的目的是解决实际问题中的非线性变换,线性变换只能拟合直线,而激活函数的加入,使神经网络具有了拟合曲线的能力。 特别是在实际问题中,很多现象都无法用简单的线性关系描述,这时激活函数的非线性就为描述更加复杂的问题提供了工具。常见的非线性函数有Sigmoid、ReLU、Tanh等。如图\ref{fig:5-15}列举了几种激活函数的形式。\\
\parinterval 那激活函数又是什么?神经元在接收到经过线性变换的结果后,通过激活函数的处理,得到最终的输出$ \mathbf y $。激活函数的目的是解决实际问题中的非线性变换,线性变换只能拟合直线,而激活函数的加入,使神经网络具有了拟合曲线的能力。 特别是在实际问题中,很多现象都无法用简单的线性关系描述,这时可以使用非线性激活函数来描述更加复杂的问题。常见的非线性函数有Sigmoid、ReLU、Tanh等。如图\ref{fig:5-15}列举了几种激活函数的形式。\\
%----------------------------------------------
\begin{figure}\centering
......@@ -819,7 +819,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
\begin{figure}[htp]
\centering
\input{./Chapter9/Figures/fig-four-layers-of-neural-network}
\caption{具有四层神经元的三层神经网络}
\caption{具有四层神经元的(三层)神经网络}
\label{fig:5-17}
\end{figure}
%-------------------------------------------
......@@ -834,7 +834,11 @@ 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
\parinterval 神经网络方法之所以受到青睐一方面是由于它提供了端到端学习的模式,另一方面是由于它强大的函数拟合能力。理论上说,神经网络可以拟合任何形状的函数。下面就来看一下为什么神经网络会有这样的能力。
\parinterval 众所周知,单层神经网络无法解决线性不可分问题,比如经典的异或问题。但是具有一个隐藏层的两层神经网络在理论上就可以拟合所有的函数了。接下来我们分析一下为什么仅仅是多了一层,神经网络就能变得如此强大。在此之前,需要明确的一点是,``拟合''是把平面上一系列的点,用一条光滑的曲线连接起来,并用函数来表示这条拟合的曲线。在用神经网络解决问题时,可以通过拟合训练数据中的``数据点''来获得输入与输出之间的函数关系,并利用其对未知数据做出判断。可以假设输入与输出之间存在一种函数关系,而神经网络的``拟合''能力是要尽可能地逼近原函数输出值,与原函数输出值越逼近,则意味着拟合得越优秀。
\parinterval 众所周知,单层神经网络无法解决线性不可分问题,比如经典的异或问题。但是具有一个隐藏层的两层神经网络在理论上就可以拟合所有的函数了。接下来我们分析一下为什么仅仅是多了一层,神经网络就能变得如此强大。对于二维空间(平面),``拟合''是指:把平面上一系列的点,用一条光滑的曲线连接起来,并用函数来表示这条拟合的曲线。这个概念可以推广到更高维空间上。在用神经网络解决问题时,可以通过拟合训练数据中的`` 数据点''来获得输入与输出之间的函数关系,并利用其对未知数据做出判断。可以假设输入与输出之间存在一种函数关系,而神经网络的``拟合''能力是要尽可能地逼近原函数输出值,与原函数输出值越逼近,则意味着拟合得越好。
\parinterval 如图\ref{fig:5-18}是一个以Sigmoid作为隐藏层激活函数的两层神经网络。通过调整参数$ \mathbf w=(w_1,w_2) $$ \mathbf b=(b_1,b_2) $$ \mathbf w^{'}=(w'_{1},w'_{2}) $ 的值,可以不断地改变目标函数的形状。
%----------------------------------------------
\begin{figure}[htp]
......@@ -845,18 +849,17 @@ 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
\end{figure}
%-------------------------------------------
\parinterval 如图\ref{fig:5-18}是一个以Sigmoid作为隐藏层激活函数的两层神经网络。通过调整参数$ \mathbf w=(w_1,w_2) $$ \mathbf b=(b_1,b_2) $$ \mathbf w^{'}=(w'_{1},w'_{2}) $ 的值,可以不断地改变目标函数的形状
\parinterval 设置$ w'_1=1 $$ w_1=1 $$ b_1=0 $,其他参数设置为0。可以得到如图\ref{fig:5-19}(a)所示的目标函数,此时目标函数还是比较平缓的。通过调大$ w_1 $,可以将图\ref{fig:5-19}(a) 中函数的坡度调得更陡:当$ w_1=10 $时,如图\ref{fig:5-19}(b)所示,目标函数的坡度与图\ref{fig:5-19}(a)相比变得更陡了;当$ w_1=100 $时,如图\ref{fig:5-19}(c)所示,目标函数的坡度变得更陡、更尖锐,已经逼近一个阶梯函数
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter9/Figures/fig-weight}
\caption{通过改变权重$ w_1 $改变目标函数平滑程度}
\caption{通过调整权重$ w_1 $改变目标函数平滑程度}
\label{fig:5-19}
\end {figure}
%-------------------------------------------
\parinterval 设置$ w'_1=1 $$ w_1=1 $$ b_1=0 $,其他参数设置为0。可以得到如图\ref{fig:5-19}(a)所示的目标函数,此时目标函数还是比较平缓的。通过调大$ w_1 $,可以将图\ref{fig:5-19}(a) 中函数的坡度调得更陡:当$ w_1=10 $时,如图\ref{fig:5-19}(b)所示,目标函数的坡度与图\ref{fig:5-19}(a)相比变得更陡了;当$ w_1=100 $时,如图\ref{fig:5-19}(c)所示,目标函数的坡度变得更陡、更尖锐,已经逼近一个阶梯函数。
\parinterval 设置$ w'_1=1 $$ w_1=100 $$ b_1=0 $,其他参数设置为0。可以得到如图\ref{fig:5-20}(a)所示的目标函数,此时目标函数是一个阶梯函数,其``阶梯''恰好与y轴重合。通过改变$ b_1 $,可以将整个函数沿x轴向左右平移:当$ b_1=-2 $时,如图\ref{fig:5-20}(b)所示,与图\ref{fig:5-20}(a)相比目标函数的形状没有发生改变,但其位置沿x轴向右平移;当$ b_1=-4 $时,如图\ref{fig:5-20}(c)所示,目标函数的位置继续沿x轴向右平移。
......@@ -864,7 +867,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
\begin{figure}[htp]
\centering
\input{./Chapter9/Figures/fig-bias}
\caption{通过改变偏置量$ b_1 $改变目标函数位置}
\caption{通过调整偏置量$ b_1 $改变目标函数位置}
\label{fig:5-20}
\end {figure}
%-------------------------------------------
......@@ -886,7 +889,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
\begin{figure}[htp]
\centering
\input{./Chapter9/Figures/fig-w2}
\caption{通过设置第二组参数将目标函数分段数增加}
\caption{通过设置第二组参数$b_2$$w'_2$将目标函数分段数增加}
\label{fig:5-22}
\end {figure}
%-------------------------------------------
......@@ -915,7 +918,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
\end {figure}
%-------------------------------------------
\parinterval 两层神经元的神经网络在理论上可以拟合所有函数了,但是在实际问题中所使用的神经网络都远远超过了两层,这也是对深度学习这个概念中``深度''的一种体现。主要是有以下几方面的原因:
\parinterval 两层神经元的神经网络在理论上可以拟合所有函数了,但是在实际问题中所使用的神经网络都远远超过了两层,这也是对深度学习这个概念中``深度''的一种体现。使用深层神经网络主要有以下几方面的原因:
\begin{itemize}
\vspace{0.5em}
......@@ -972,7 +975,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
\end{figure}
%-------------------------------------------
\parinterval 虽然这里所使用的张量是出于编程实现的视角,但是数学中张量有严格的定义。从数学上看``张量并不是向量和矩阵的简单扩展,多维数组也并不是张量所必须的表达形式''。从某种意义上说,矩阵才是张量的扩展。当然,这个逻辑可能和大家在深度学习中的认知是不一致的。但是,本书仍然遵循深度学习中常用的概念,把张量理解为多维数组。在保证数学表达的简洁性的同时,使程序实现接口更加统一。
\parinterval 虽然这里所使用的张量是出于编程实现的视角,但是数学中张量有严格的定义。从数学上看``张量并不是向量和矩阵的简单扩展,多维数组也并不是张量所必须的表达形式''。从某种意义上说,矩阵才是张量的扩展。当然,这个逻辑可能和人们在深度学习中的认知是不一致的。但是,本书仍然遵循深度学习中常用的概念,把张量理解为多维数组。在保证数学表达的简洁性的同时,使程序实现接口更加统一。
%----------------------------------------------------------------------------------------
% NEW SUBSUB-SECTION
......@@ -1050,7 +1053,7 @@ f(x)=\begin{cases} 0 & x\le 0 \\x & x>0\end{cases}
\subsection{张量的物理存储形式}
\parinterval 在深度学习世界中,张量就是多维数组,无论是向量还是矩阵都可以看作是数学上``张量''的扩展。因此,张量的物理存储方式也与多维数组相同
\parinterval 在深度学习世界中,张量就是多维数组。因此,张量的物理存储方式也与多维数组相同。如下就是一些实例
\begin{itemize}
\vspace{0.5em}
......@@ -1077,17 +1080,11 @@ f(x)=\begin{cases} 0 & x\le 0 \\x & x>0\end{cases}
% NEW SUB-SECTION
%----------------------------------------------------------------------------------------
\subsection{实现张量计算的实现示例}
\parinterval {\red{前两段可能需要删掉}}
\parinterval 实现神经网络的开源系统有很多,比如,一个简单好用的Python工具包\ \dash \ Numpy(\url{https://numpy.org/})。Numpy提供了张量表示和使用的范式,可以很方便地定义、使用多维数组。
\subsection{张量的实现手段}
\parinterval 此外,如今深度学习框架已经非常成熟。比如, Tensorflow和Pytorch就是非常受欢迎的深度学习工具包,除此之外还有很多其他优秀的框架:CNTK、MXNet、\\PaddlePaddle、Keras、Chainer、dl4j、NiuTensor等。开发者可以根据自身的喜好和开发项目的要求选择所采用的框架。
\parinterval 实现神经网络的开源系统有很多,比如,使用经典的Python工具包Numpy。也可以使用成熟的深度学习框架,比如,Tensorflow和Pytorch就是非常受欢迎的深度学习工具包,除此之外还有很多其他优秀的框架:CNTK、MXNet、PaddlePaddle、\\Keras、Chainer、dl4j、NiuTensor等。开发者可以根据自身的喜好和开发项目的要求选择所采用的框架。
\parinterval 本节将使用NiuTensor来描述张量计算。NiuTensor是一个面向自然语言处理任务的张量库,它支持丰富的张量计算接口。此外,该NiuTensor内核基于C++语言编写,代码高度优化。该工具包获取网址为\url{https://developer.niutrans.com/ArticleContent/technicaldoc/doc1/1.NiuTensor}
\parinterval NiuTensor的使用非常简单,如图\ref{fig:5-30}是一个使用NiuTensor声明、定义张量的C++代码:
\parinterval 本节将使用NiuTensor来描述张量计算。NiuTensor是一个面向自然语言处理任务的张量库,它支持丰富的张量计算接口。如图\ref{fig:5-30}是一个使用NiuTensor声明、定义张量的C++代码:
%----------------------------------------------
\begin{figure}[htp]
......@@ -1145,7 +1142,8 @@ f(x)=\begin{cases} 0 & x\le 0 \\x & x>0\end{cases}
\end{figure}
%-------------------------------------------
\parinterval 除了上述单元算子外,NiuTensor还支持张量之间的高阶运算,其中最常用的是矩阵乘法,图\ref{fig:5-35}(b)是张量之间进行矩阵乘法的程序示例。表\ref{tab:5-2}展示了一些NiuTensor支持的其他函数操作,除此还有很多其他操作无法在此一一列举,有兴趣可以参考网站上的详细说明。
\parinterval 除了上述单元算子外,NiuTensor还支持张量之间的高阶运算,其中最常用的是矩阵乘法,图\ref{fig:5-35}(b)是张量之间进行矩阵乘法的程序示例。表\ref{tab:5-2}展示了一些NiuTensor支持的其他函数操作。
%--------------------------------------------------------------------
\begin{table}[htp]
\centering
......@@ -1182,7 +1180,7 @@ f(x)=\begin{cases} 0 & x\le 0 \\x & x>0\end{cases}
\subsection{前向传播与计算图}
\parinterval 有了张量这个工具,可以很容易地实现任意的神经网络。反过来,神经网络都可以被看作是张量的函数。一种经典的神经网络计算模型是:给定输入张量,通过各个神经网络层所对应的张量计算之后,最后得到输出张量。这个过程也被称作{\small\sffamily\bfseries{前向传播}}\index{前向传播},它常常被应用在使用神经网络对新的样本进行推断中。
\parinterval 有了张量这个工具,可以很容易地实现任意的神经网络。反过来,神经网络都可以被看作是张量的函数。一种经典的神经网络计算模型是:给定输入张量,通过各个神经网络层所对应的张量计算之后,最后得到输出张量。这个过程也被称作{\small\sffamily\bfseries{前向传播}}\index{前向传播}(Forward Prorogation\index{Forward Prorogation},它常常被应用在使用神经网络对新的样本进行推断中。
\parinterval 来看一个具体的例子,如图\ref{fig:5-37}(a)是一个根据天气情况判断穿衣指数(穿衣指数是人们穿衣薄厚的依据)的过程,将当天的天空状况、低空气温、水平气压作为输入,通过一层神经元在输入数据中提取温度、风速两方面的特征,并根据这两方面的特征判断穿衣指数。需要注意的是,在实际的神经网络中,并不能准确地知道神经元究竟可以提取到哪方面的特征,以上表述是为了让读者更好地理解神经网络的建模过程和前向传播过程。这里将上述过程建模为如图\ref{fig:5-37}(b)所示的两层神经网络。
......@@ -1211,7 +1209,9 @@ y&=&{\rm{Sigmoid}}({\rm{Tanh}}(\mathbf x\cdot \mathbf w^{[1]}+\mathbf b^{[1]})\c
\end{figure}
%-------------------------------------------
\parinterval\ref{fig:5-38}实际上是神经网络的一种{\small\bfnew{计算图}}\index{计算图}(Computation Graph)\index{Computation Graph}表示。现在很多深度学习框架都是把神经网络转化为计算图,这样可以把复杂的运算分解为简单的运算。通过对计算图中节点的遍历,可以方便地完成神经网络的计算。比如,可以对图中节点进行拓扑排序(由输入到输出),之后依次访问每个节点,同时完成相应的计算,这也就实现了一个前向计算的过程。构建计算图的方式有很多,比如,动态图、静态图等。在\ref{sec5:para-training}节会进一步对计算图在模型参数训练中的应用进行介绍。
\parinterval\ref{fig:5-38}实际上是神经网络的一种{\small\bfnew{计算图}}\index{计算图}(Computation Graph)\index{Computation Graph}表示。现在很多深度学习框架都是把神经网络转化为计算图,这样可以把复杂的运算分解为简单的运算,称为{\small\bfnew{算子}}\index{算子}(Calculus\index{Calculus})。通过对计算图中节点的遍历,可以方便地完成神经网络的计算。比如,可以对图中节点进行拓扑排序(由输入到输出),之后依次访问每个节点,同时完成相应的计算,这也就实现了一个前向计算的过程。
\parinterval 使用计算图的另一个优点在于,这种方式易于参数梯度的计算。在后面的内容中会看到,计算神经网络中参数的梯度是模型训练的重要步骤。在计算图中,可以使用{\small\bfnew{反向传播}}\index{反向传播} (Backward Prorogation\index{Backward Prorogation})的方式逐层计算不同节点上的梯度信息。在\ref{sec5:para-training} 节会看到使用计算图这种结构可以非常方便、高效地计算反向传播中所需的梯度信息。
%----------------------------------------------------------------------------------------
% NEW SECTION
......@@ -1220,7 +1220,7 @@ y&=&{\rm{Sigmoid}}({\rm{Tanh}}(\mathbf x\cdot \mathbf w^{[1]}+\mathbf b^{[1]})\c
\sectionnewpage
\section{神经网络的参数训练}
\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{模型参数}}\index{模型参数}(Model Parameters)\index{Model Parameters}。确定了函数表达式和模型参数,也就确定了神经网络模型。通常,表达式的形式需要系统开发者设计,而模型参数的数量有时会非常巨大,因此需要自动学习,这个过程也被称为模型学习或{\small\bfnew{训练}}\index{训练}(Training)\index{Training}。为了实现这个目标,通常会准备一定量的带有标准答案的数据,称之为{\small\sffamily\bfseries{有标注数据}}\index{有标注数据}(Annotated Data/Labeled Data)\index{Annotated Data/Labeled Data}。这些数据会用于对模型参数的学习,这也对应了统计模型中的参数估计过程。在机器学习中,一般把这种使用有标注数据进行统计模型参数训练的过程称为{\small\sffamily\bfseries{有指导的训练}}\index{有指导的训练}{\small\sffamily\bfseries{有监督的训练}}\index{有监督的训练}(Supervised Training)\index{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{模型参数}}\index{模型参数}(Model Parameters)\index{Model Parameters}。确定了函数表达式和模型参数,也就确定了神经网络模型。通常,表达式的形式需要系统开发者设计,而模型参数的数量有时会非常巨大,因此需要自动学习,这个过程也被称为模型学习或{\small\bfnew{训练}}\index{训练}(Training)\index{Training}。为了实现这个目标,通常会准备一定量的带有标准答案的数据,称之为{\small\sffamily\bfseries{有标注数据}}\index{有标注数据}(Annotated Data\index{Annotated Data}/Labeled Data\index{Labeled Data}。这些数据会用于对模型参数的学习,这也对应了统计模型中的参数估计过程。在机器学习中,一般把这种使用有标注数据进行统计模型参数训练的过程称为{\small\sffamily\bfseries{有指导的训练}}\index{有指导的训练}{\small\sffamily\bfseries{有监督的训练}}\index{有监督的训练}(Supervised Training)\index{Supervised Training}。在本章中,如果没有特殊说明,模型训练都是指有监督的训练。那么神经网络内部是怎样利用有标注数据对参数进行训练的呢?\\ \\
\vspace{-2.5em}
\parinterval 为了回答这个问题,可以把模型参数的学习过程看作是一个优化问题,即找到一组参数,使得模型达到某种最优的状态。这个问题又可以被转化为两个新的问题:
......@@ -1271,12 +1271,13 @@ y&=&{\rm{Sigmoid}}({\rm{Tanh}}(\mathbf x\cdot \mathbf w^{[1]}+\mathbf b^{[1]})\c
\rule{0pt}{15pt} Logistic损失 & $ L={\rm{log}}(1+\mathbf {\widetilde y}_i\cdot \mathbf y_i) $ & 回归 \\
\rule{0pt}{15pt} 平方损失 & $ L={(\mathbf {\widetilde y}_i-\mathbf y_i)}^2 $ & 回归 \\
\rule{0pt}{15pt} 指数损失 & $ L={\rm{exp}}(-\mathbf {\widetilde y}_i\cdot \mathbf y_i) $ & AdaBoost \\
\rule{0pt}{15pt} 交叉熵损失 & $ L=-\sum_{k}{\mathbf y_i^{[k]}{\rm {log}}\mathbf {\widetilde y}_i^{[k]}} $ & 多分类 \\
\rule{0pt}{15pt} 交叉熵损失 & $ L=-\sum_{k}{\mathbf y_i(k)}{\rm {log}}\mathbf {\widetilde y}_i(k) $ & 多分类 \\
\rule{0pt}{15pt} & 其中,$\mathbf y_i(k)$ 表示 $\mathbf y_i$的第$k$
\end{tabular}
\end{table}
%--------------------------------------------------------------------
\parinterval 在实际系统开发中,损失函数中除了损失项(即用来度量正确答案$ \mathbf {\widetilde y}_i $和神经网络输出$ \mathbf y_i $之间的偏差的部分)之外,还可以包括正则项,比如L1正则和L2正则。设置正则项本质上是要加入一些偏置,使模型在优化的过程中偏向某个方向多一些。关于正则项的内容将在\ref{sec:5.4.5}详细阐述。此外,在第七章的内容中还会看到,使用恰当的正则项可以大大提升基于神经网络的机器翻译系统性能
\parinterval 在实际系统开发中,损失函数中除了损失项(即用来度量正确答案$ \mathbf {\widetilde y}_i $和神经网络输出$ \mathbf y_i $之间的偏差的部分)之外,还可以包括正则项,比如L1正则和L2正则。设置正则项本质上是要加入一些偏置,使模型在优化的过程中偏向某个方向多一些。关于正则项的内容将在\ref{sec:5.4.5}介绍
%----------------------------------------------------------------------------------------
% NEW SUB-SECTION
......@@ -1292,7 +1293,7 @@ y&=&{\rm{Sigmoid}}({\rm{Tanh}}(\mathbf x\cdot \mathbf w^{[1]}+\mathbf b^{[1]})\c
\noindent 其中,$ \widehat{\mathbf w} $表示在训练数据上使损失的平均值达到最小的参数。$ \frac{1}{n}\sum_{i=1}^{n}{L(\mathbf x_i,\mathbf {\widetilde y}_i;\mathbf w)} $也被称作{\small\sffamily\bfseries{代价函数}}\index{代价函数}(Cost Function)\index{Cost Function},它是损失函数均值期望的估计,记为$ J(\mathbf w) $
\parinterval 参数优化的核心问题是:找到使代价函数$ J(\mathbf w) $达到最小的$ \mathbf w $。然而$ J(\mathbf w) $可能会包含大量的参数,比如,基于神经网络的机器翻译模型的参数量可能会超过一亿个。这时不可能用手动方法进行调参。为了实现高效的参数优化,比较常用的手段是使用{\small\bfnew{梯度下降方法}}\index{梯度下降方法}Gradient Descent Method)\index{Gradient Descent Method}
\parinterval 参数优化的核心问题是:找到使代价函数$ J(\mathbf w) $达到最小的$ \mathbf w $。然而$ J(\mathbf w) $可能会包含大量的参数,比如,基于神经网络的机器翻译模型的参数量可能会超过一亿个。这时不可能用手动方法进行调参。为了实现高效的参数优化,比较常用的手段是使用{\small\bfnew{梯度下降方法}}\index{梯度下降方法}The Gradient Descent Method)\index{The Gradient Descent Method}
%----------------------------------------------------------------------------------------
% NEW SUBSUB-SECTION
......@@ -1319,7 +1320,7 @@ y&=&{\rm{Sigmoid}}({\rm{Tanh}}(\mathbf x\cdot \mathbf w^{[1]}+\mathbf b^{[1]})\c
\noindent 其中$t $表示更新的步数,$ \alpha $是一个超参数,被称作{\small\sffamily\bfseries{学习率}}\index{学习率}(Learning Rate)\index{Learning Rate},表示更新步幅的大小。$ \alpha $的设置需要根据任务进行调整。
\parinterval 从优化的角度看,梯度下降是一种典型的 {\small\bfnew{基于梯度的方法}}\index{基于梯度的方法}Gradient-based Method)\index{Gradient-based Method},属于基于一阶导数的方法。其他类似的方法还有牛顿法、共轭方向法、拟牛顿法等。在具体实现时,公式\ref{eq:5-29}可以有以下不同的形式。\\
\parinterval 从优化的角度看,梯度下降是一种典型的 {\small\bfnew{基于梯度的方法}}\index{基于梯度的方法}The Gradient-based Method)\index{The Gradient-based Method},属于基于一阶导数的方法。其他类似的方法还有牛顿法、共轭方向法、拟牛顿法等。在具体实现时,公式\ref{eq:5-29}可以有以下不同的形式。\\
%----------------------------------------------------------------------------------------
%
......@@ -1371,7 +1372,7 @@ J(\mathbf w)&=&\frac{1}{m}\sum_{i=j}^{j+m-1}{L(\mathbf x_i,\mathbf {\widetilde y
\label{eq:5-32}
\end{eqnarray}
\noindent 其中$ m $表示一个批次中的样本的数量,$ j $表示这个批次在全体训练数据的起始位置。这种方法可以更充分的利用GPU设备,因为批次中的样本可以一起计算。而且每次使用多个样本可以大大减小使模型收敛所需要的参数更新次数。但是需要注意的是批次大小的选择对模型的最终性能是存在一定影响的。
\noindent 其中$ m $表示一个批次中的样本的数量,$ j $表示这个批次在全体训练数据的起始位置。这种方法可以更充分的利用GPU设备,因为批次中的样本可以一起计算。而且每次使用多个样本可以大大减小使模型收敛所需要的参数更新次数。但是需要注意的是批次大小的选择对模型的最终性能是存在一定影响的。
%----------------------------------------------------------------------------------------
% NEW SUBSUB-SECTION
......@@ -1452,7 +1453,15 @@ $+2x^2+x+1)$ & \ \ $(x^4+2x^3+2x^2+x+1)$ & $+6x+1$ \\
\parinterval 由于它只对基本函数或常数运用符号微分法则,所以它非常适合嵌入编程语言的循环条件等结构中,形成一种程序化的微分过程。在具体实现时,自动微分往往被当做是一种基于图的计算,相关的理论和技术方法相对成熟,因此是深度学习中使用最广泛的一种方法。不同于一般的编程模式,图计算先生成计算图,然后按照计算图执行计算过程。
\parinterval 自动微分可以用一种{\small\sffamily\bfseries{反向模式}}\index{反向模式}(Backward Mode)\index{Backward Mode}即反向传播思想进行描述。首先,从神经网络的输入,逐层计算每层网络的输出值。如图\ref{fig:5-44},第$ i $层的输出$ \mathbf h_i $作为第$ i+1 $层的输入,数据流在神经网络内部逐层传递。
\parinterval 自动微分可以用一种{\small\sffamily\bfseries{反向模式}}\index{反向模式}(Reverse Mode\index{Reverse Mode}/Backward Mode\index{Backward Mode})即反向传播思想进行描述\cite{baydin2017automatic}。令$h_i$是神经网络的计算图中第$i$个节点的输出。反向模式的自动微分是要计算:
\begin{eqnarray}
\bar{h_i} = \frac{\partial L}{\partial h_i} \label{eq:reverse-mode-v}
\end{eqnarray}
\noindent 这里,$\bar{h_i}$表示损失函数$L$相对于$h_i$的梯度信息,它会被保存在节点$i$处。为了计算$\bar{h_i}$,需要从网络的输出反向计算每一个节点处的梯度。具体实现时,这个过程由一个包括前向计算和反向计算的两阶段方法实现。
\parinterval 首先,从神经网络的输入,逐层计算每层网络的输出值。如图\ref{fig:5-44},第$ i $ 层的输出$ \mathbf h_i $ 作为第$ i+1 $ 层的输入,数据流在神经网络内部逐层传递。
%----------------------------------------------
\begin{figure}[htp]
......@@ -1467,9 +1476,9 @@ $+2x^2+x+1)$ & \ \ $(x^4+2x^3+2x^2+x+1)$ & $+6x+1$ \\
\begin{itemize}
\vspace{0.5em}
\item 动态图(如Pytorch、NiuTensor等):前向计算与计算图的搭建同时进行,函数表达式写完即能得到前向计算的结果,有着灵活、易于调试的优点。
\item 动态图:前向计算与计算图的搭建同时进行,函数表达式写完即能得到前向计算的结果,有着灵活、易于调试的优点。
\vspace{0.5em}
\item 静态图(如Tensorflow):先搭建计算图,后执行运算,函数表达式完成后,并不能得到前向计算结果,需要显性调用一个Forward函数。但是计算图可以进行深度优化,执行效率较高。
\item 静态图:先搭建计算图,后执行运算,函数表达式完成后,并不能得到前向计算结果,需要显性调用一个Forward函数。但是计算图可以进行深度优化,执行效率较高。
\vspace{0.5em}
\end{itemize}
......@@ -1498,7 +1507,7 @@ $+2x^2+x+1)$ & \ \ $(x^4+2x^3+2x^2+x+1)$ & $+6x+1$ \\
\label{}
\end{eqnarray}
\noindent 其中$ \alpha $是一个超参数,表示更新步幅的大小,称作{\small\bfnew{学习率}}\index{学习率}(Learning Rate)\index{Learning Rate}。当然,这是一种最基本的梯度下降方法。如果函数的形状非均向,比如呈延伸状,搜索最优点的路径就会非常低效,因为这时梯度的方向并没有指向最小值的方向,并且随着参数的更新,梯度方向往往呈锯齿状,这将是一条相当低效的路径;此外这种梯度下降算法并不是总能到达最优点,而是在其附近徘徊;还有一个最令人苦恼的问题\ \dash \ 设置学习率,如果学习率设置的比较小,会导致训练收敛速度慢,如果学习率设置的比较大,会导致训练过程中因为优化幅度过大而频频跳过最优点。我们希望网络在优化的时候损失函数有一个很好的收敛速度同时又不至于摆动幅度太大。
\noindent 其中$ \alpha $是一个超参数,表示更新步幅的大小,称作{\small\bfnew{学习率}}\index{学习率}(Learning Rate)\index{Learning Rate}。当然,这是一种最基本的梯度下降方法。如果函数的形状非均向,比如呈延伸状,搜索最优点的路径就会非常低效,因为这时梯度的方向并没有指向最小值的方向,并且随着参数的更新,梯度方向往往呈锯齿状,这将是一条相当低效的路径;此外这种梯度下降算法并不是总能到达最优点,而是在其附近徘徊;还有一个最令人苦恼的问题\ \dash \ 设置学习率,如果学习率设置的比较小,会导致训练收敛速度慢,如果学习率设置的比较大,会导致训练过程中因为优化幅度过大而频频跳过最优点。我们希望网络在优化的时候损失函数有一个很好的收敛速度同时又不至于摆动幅度太大。
\parinterval 针对以上问题,很多学者尝试对梯度下降方法做出改进,如Momentum, Adagrad, Adadelta, RMSprop, Adam, AdaMax, Nadam, AMSGrad等等,在这里将介绍Momentum、AdaGrad、RMSprop、Adam这4 种方法。
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论