Commit 071ff6c5 by 孟霞

合并分支 'mengxia' 到 'caorunzhe'

Mengxia

查看合并请求 !186
parents 1b087c3f a859a175
...@@ -25,5 +25,45 @@ ...@@ -25,5 +25,45 @@
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
\end{tcolorbox} \end{tcolorbox}
\hspace{0.1in} \scriptsize{(a) 构建单层神经网络}
\\
\begin{tcolorbox}
[bicolor,sidebyside,width=11cm,righthand width=4cm,size=title,frame engine=empty,
colback=blue!10!white,colbacklower=black!5!white]
{\scriptsize
\begin{tabbing}
\texttt{XTensor x, y, h1, h2;} \\
\texttt{XTensor w1, b1, w2, w3;} \\
\texttt{InitTensor3D(\&x, 3, 4, 5);} \\
\texttt{InitTensor2D(\&w1, 5, 3);} \\
\texttt{InitTensor1D(\&b1, 3);} \\
\texttt{InitTensor2D(\&w2, 3, 6);} \\
\texttt{InitTensor2D(\&w3, 6, 4);} \\
\texttt{...} \\
\texttt{h1 = Sigmoid(MMul(x, w1) + b1);} \\
\texttt{h2 = HandTanH(MMul(h1, w2));} \\
\texttt{y = Relu(MMul(h2, w3));}
\end{tabbing}
}
\tcblower
\begin{center}
\begin{tikzpicture}
\node [draw,circle,inner sep=2pt,fill=red!30!white,blur shadow={shadow xshift=1pt,shadow yshift=-1pt}] (x) at (0,0) {\footnotesize{$\textrm{x}$}};
\node [anchor=south,draw,rounded corners,inner sep=2pt,minimum width=4em,fill=green!30!white,blur shadow={shadow xshift=1pt,shadow yshift=-1pt}] (layer1) at ([yshift=0.7em]x.north) {\scriptsize{layer1}};
\node [anchor=south,draw,rounded corners,inner sep=2pt,minimum width=4em,fill=green!30!white,blur shadow={shadow xshift=1pt,shadow yshift=-1pt}] (layer2) at ([yshift=1.0em]layer1.north) {\scriptsize{layer2}};
\node [anchor=south,draw,rounded corners,inner sep=2pt,minimum width=4em,fill=green!30!white,blur shadow={shadow xshift=1pt,shadow yshift=-1pt}] (layer3) at ([yshift=1.0em]layer2.north) {\scriptsize{layer3}};
\node [anchor=south,draw,circle,inner sep=2pt,fill=red!30!white,blur shadow={shadow xshift=1pt,shadow yshift=-1pt}] (y) at ([yshift=0.7em]layer3.north) {\scriptsize{$\textrm{y}$}};
\draw [thick,->] (x.north) -- (layer1.south);
\draw [thick,->] (layer1.north) -- (layer2.south);
\draw [thick,->] (layer2.north) -- (layer3.south);
\draw [thick,->] (layer3.north) -- (y.south);
\node [anchor=west,align=left] (xshape) at (x.east) {\tiny{shape: 3*4*5}};
\node [anchor=west,align=left] (yshape) at (y.east) {\tiny{shape: 3*4*4}};
\node [anchor=south west,align=left,inner sep=2pt] (l1shape) at (layer1.north) {\tiny{shape: 3*4*3}};
\node [anchor=south west,align=left,inner sep=2pt] (l2shape) at (layer2.north) {\tiny{shape: 3*4*6}};
\end{tikzpicture}
\end{center}
\end{tcolorbox}
\hspace{0.1in} \scriptsize{(b) 构建三层神经网络}
%%%------------------------------------------------------------------------------------------------------------ %%%------------------------------------------------------------------------------------------------------------
%%%------------------------------------------------------------------------------------------------------------
\begin{tcolorbox}
[bicolor,sidebyside,width=11cm,righthand width=4cm,size=title,frame engine=empty,
colback=blue!10!white,colbacklower=black!5!white]
{\scriptsize
\begin{tabbing}
\texttt{XTensor x, y, h1, h2;} \\
\texttt{XTensor w1, b1, w2, w3;} \\
\texttt{InitTensor3D(\&x, 3, 4, 5);} \\
\texttt{InitTensor2D(\&w1, 5, 3);} \\
\texttt{InitTensor1D(\&b1, 3);} \\
\texttt{InitTensor2D(\&w2, 3, 6);} \\
\texttt{InitTensor2D(\&w3, 6, 4);} \\
\texttt{...} \\
\texttt{h1 = Sigmoid(MMul(x, w1) + b1);} \\
\texttt{h2 = HandTanH(MMul(h1, w2));} \\
\texttt{y = Relu(MMul(h2, w3));}
\end{tabbing}
}
\tcblower
\begin{center}
\begin{tikzpicture}
\node [draw,circle,inner sep=2pt,fill=red!30!white,blur shadow={shadow xshift=1pt,shadow yshift=-1pt}] (x) at (0,0) {\footnotesize{$\textrm{x}$}};
\node [anchor=south,draw,rounded corners,inner sep=2pt,minimum width=4em,fill=green!30!white,blur shadow={shadow xshift=1pt,shadow yshift=-1pt}] (layer1) at ([yshift=0.7em]x.north) {\scriptsize{layer1}};
\node [anchor=south,draw,rounded corners,inner sep=2pt,minimum width=4em,fill=green!30!white,blur shadow={shadow xshift=1pt,shadow yshift=-1pt}] (layer2) at ([yshift=1.0em]layer1.north) {\scriptsize{layer2}};
\node [anchor=south,draw,rounded corners,inner sep=2pt,minimum width=4em,fill=green!30!white,blur shadow={shadow xshift=1pt,shadow yshift=-1pt}] (layer3) at ([yshift=1.0em]layer2.north) {\scriptsize{layer3}};
\node [anchor=south,draw,circle,inner sep=2pt,fill=red!30!white,blur shadow={shadow xshift=1pt,shadow yshift=-1pt}] (y) at ([yshift=0.7em]layer3.north) {\scriptsize{$\textrm{y}$}};
\draw [thick,->] (x.north) -- (layer1.south);
\draw [thick,->] (layer1.north) -- (layer2.south);
\draw [thick,->] (layer2.north) -- (layer3.south);
\draw [thick,->] (layer3.north) -- (y.south);
\node [anchor=west,align=left] (xshape) at (x.east) {\tiny{shape: 3*4*5}};
\node [anchor=west,align=left] (yshape) at (y.east) {\tiny{shape: 3*4*4}};
\node [anchor=south west,align=left,inner sep=2pt] (l1shape) at (layer1.north) {\tiny{shape: 3*4*3}};
\node [anchor=south west,align=left,inner sep=2pt] (l2shape) at (layer2.north) {\tiny{shape: 3*4*6}};
\end{tikzpicture}
\end{center}
\end{tcolorbox}
%%%------------------------------------------------------------------------------------------------------------
...@@ -10,5 +10,33 @@ ...@@ -10,5 +10,33 @@
} }
\end{flushleft} \end{flushleft}
\end{tcolorbox} \end{tcolorbox}
\hspace{0.1in} \scriptsize{(a) NiuTensor定义张量程序}
\\
\begin{tcolorbox}[enhanced,width=11cm,frame engine=empty,boxrule=0.1mm,size=title,colback=blue!10!white]
\begin{flushleft}
{\scriptsize
\begin{tabbing}
\texttt{XTensor a, b, c;} \hspace{11.5em} \= // 声明张量tensor \\
\texttt{InitTensor1D(\&a, 10, X\_INT);} \> // 10维的整数型向量\\
\texttt{InitTensor1D(\&b, 10);} \> // 10维的向量,缺省类型(浮点)\\
\texttt{InitTensor4D(\&c, 10, 20, 30, 40);} \> // 10*20*30*40的4阶张量(浮点)
\end{tabbing}
}
\end{flushleft}
\end{tcolorbox}
\hspace{0.1in} \scriptsize{(b) 定义张量的简便方式程序}
\\
\begin{tcolorbox}[enhanced,width=11cm,frame engine=empty,boxrule=0.1mm,size=title,colback=blue!10!white]
\begin{flushleft}
{\scriptsize
\begin{tabbing}
\texttt{XTensor tensorGPU;} \hspace{10.5em} \= // 声明张量tensor \\
\texttt{InitTensor2D(\&tensorGPU, 10, 20,} $\backslash$ \> // 在编号为0的GPU上定义张量 \\
\hspace{6.7em} \texttt{X\_FLOAT, 0);}
\end{tabbing}
}
\end{flushleft}
\end{tcolorbox}
\hspace{0.1in} \scriptsize{(c) 在GPU上定义张量程序}
%%%------------------------------------------------------------------------------------------------------------ %%%------------------------------------------------------------------------------------------------------------
%%%------------------------------------------------------------------------------------------------------------
\begin{tcolorbox}[enhanced,width=11cm,frame engine=empty,boxrule=0.1mm,size=title,colback=blue!10!white]
\begin{flushleft}
{\scriptsize
\begin{tabbing}
\texttt{XTensor a, b, c;} \hspace{11.5em} \= // 声明张量tensor \\
\texttt{InitTensor1D(\&a, 10, X\_INT);} \> // 10维的整数型向量\\
\texttt{InitTensor1D(\&b, 10);} \> // 10维的向量,缺省类型(浮点)\\
\texttt{InitTensor4D(\&c, 10, 20, 30, 40);} \> // 10*20*30*40的4阶张量(浮点)
\end{tabbing}
}
\end{flushleft}
\end{tcolorbox}
%%%------------------------------------------------------------------------------------------------------------
%%%------------------------------------------------------------------------------------------------------------
\begin{tcolorbox}[enhanced,width=11cm,frame engine=empty,boxrule=0.1mm,size=title,colback=blue!10!white]
\begin{flushleft}
{\scriptsize
\begin{tabbing}
\texttt{XTensor tensorGPU;} \hspace{10.5em} \= // 声明张量tensor \\
\texttt{InitTensor2D(\&tensorGPU, 10, 20,} $\backslash$ \> // 在编号为0的GPU上定义张量 \\
\hspace{6.7em} \texttt{X\_FLOAT, 0);}
\end{tabbing}
}
\end{flushleft}
\end{tcolorbox}
%%%------------------------------------------------------------------------------------------------------------
%%%------------------------------------------------------------------------------------------------------------
\begin{tcolorbox}[enhanced,width=11cm,frame engine=empty,boxrule=0.1mm,size=title,colback=blue!10!white]
\begin{flushleft}
{\scriptsize
\begin{tabbing}
\texttt{XTensor a, b, c;} \hspace{10.0em} \= // 声明张量tensor \\
\texttt{InitTensor4D(\&a, 2, 2, 3, 4);} \> // a为2*2*3*4的4阶张量 \\
\texttt{InitTensor2D(\&b, 4, 5);} \> // b为4*5的矩阵 \\
\texttt{a.SetDataRand();} \> // 随机初始化a \\
\texttt{b.SetDataRand();} \> // 随机初始化b \\
\texttt{c = MMul(a, b);} \> // 矩阵乘的结果为2*2*3*5的4阶张量
\end{tabbing}
}
\end{flushleft}
\end{tcolorbox}
%%%------------------------------------------------------------------------------------------------------------
...@@ -17,5 +17,22 @@ ...@@ -17,5 +17,22 @@
} }
\end{flushleft} \end{flushleft}
\end{tcolorbox} \end{tcolorbox}
\hspace{0.1in} \scriptsize{(a) 张量进行1阶运算}
\\
\begin{tcolorbox}[enhanced,width=11cm,frame engine=empty,boxrule=0.1mm,size=title,colback=blue!10!white]
\begin{flushleft}
{\scriptsize
\begin{tabbing}
\texttt{XTensor a, b, c;} \hspace{10.0em} \= // 声明张量tensor \\
\texttt{InitTensor4D(\&a, 2, 2, 3, 4);} \> // a为2*2*3*4的4阶张量 \\
\texttt{InitTensor2D(\&b, 4, 5);} \> // b为4*5的矩阵 \\
\texttt{a.SetDataRand();} \> // 随机初始化a \\
\texttt{b.SetDataRand();} \> // 随机初始化b \\
\texttt{c = MMul(a, b);} \> // 矩阵乘的结果为2*2*3*5的4阶张量
\end{tabbing}
}
\end{flushleft}
\end{tcolorbox}
\hspace{0.1in} \scriptsize{(b) 张量之间的矩阵乘法}
%%%------------------------------------------------------------------------------------------------------------ %%%------------------------------------------------------------------------------------------------------------
%%%------------------------------------------------------------------------------------------------------------
\begin{tikzpicture} \begin{tikzpicture}
\begin{scope} \begin{scope}
\tikzstyle{rnnnode} = [draw,inner sep=5pt,minimum width=4em,minimum height=1.5em,fill=green!30!white,blur shadow={shadow xshift=1pt,shadow yshift=-1pt}] \tikzstyle{rnnnode} = [draw,inner sep=5pt,minimum width=4em,minimum height=1.5em,fill=green!30!white,blur shadow={shadow xshift=1pt,shadow yshift=-1pt}]
...@@ -63,7 +62,7 @@ ...@@ -63,7 +62,7 @@
\draw [->,thick] ([xshift=0.1em]node14.east)--([xshift=1em]node14.east); \draw [->,thick] ([xshift=0.1em]node14.east)--([xshift=1em]node14.east);
{ {
\node [anchor=south] (toplabel1) at ([yshift=2em,xshift=-2em]node32new.north) {\footnotesize{``苹果''的表示:}}; \node [anchor=south] (toplabel1) at ([yshift=2em,xshift=-1.3em]node32new.north) {\footnotesize{``苹果''的表示:}};
\node [anchor=west,fill=blue!20!white,minimum width=3em] (toplabel2) at (toplabel1.east) {\footnotesize{上下文}}; \node [anchor=west,fill=blue!20!white,minimum width=3em] (toplabel2) at (toplabel1.east) {\footnotesize{上下文}};
} }
{ {
...@@ -83,5 +82,3 @@ ...@@ -83,5 +82,3 @@
\end{scope} \end{scope}
\end{tikzpicture} \end{tikzpicture}
\ No newline at end of file
%%%------------------------------------------------------------------------------------------------------------
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
\end{scope} \end{scope}
%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{scope}[yshift=-2in] \begin{scope}[yshift=-1.8in]
\node [anchor=center,color=red] (node1) at (0,0) {}; \node [anchor=center,color=red] (node1) at (0,0) {};
......
\begin{tikzpicture} \begin{tikzpicture}
\begin{scope} \begin{scope}
\node [anchor=west] (w0) at (0,0) {\footnotesize{$w_{0}$}}; \node [anchor=west] (w0) at (0,0) {\footnotesize{$w_{0}$}};
\node [anchor=west] (w1) at ([xshift=6.5em]w0.east) {\footnotesize{$w_{1}$}}; \node [anchor=west] (w1) at ([xshift=7em]w0.east) {\footnotesize{$w_{1}$}};
\node [anchor=west] (w2) at ([xshift=6.5em]w1.east) {\footnotesize{$w_{2}$}}; \node [anchor=west] (w2) at ([xshift=7em]w1.east) {\footnotesize{$w_{2}$}};
\node [anchor=west] (w3) at ([xshift=6.5em]w2.east) {\footnotesize{$w_{3}$}}; \node [anchor=west] (w3) at ([xshift=7em]w2.east) {\footnotesize{$w_{3}$}};
\node [anchor=north] (index0) at ([yshift=0.5em]w0.south) {\footnotesize(index)}; \node [anchor=north] (index0) at ([yshift=0.5em]w0.south) {\footnotesize(index)};
\node [anchor=north] (index1) at ([yshift=0.5em]w1.south) {\footnotesize(index)}; \node [anchor=north] (index1) at ([yshift=0.5em]w1.south) {\footnotesize(index)};
\node [anchor=north] (index2) at ([yshift=0.5em]w2.south) {\footnotesize(index)}; \node [anchor=north] (index2) at ([yshift=0.5em]w2.south) {\footnotesize(index)};
\node [anchor=north] (index3) at ([yshift=0.5em]w3.south) {\footnotesize(index)}; \node [anchor=north] (index3) at ([yshift=0.5em]w3.south) {\footnotesize(index)};
{ {
\node [anchor=south,line width=0.8pt,draw,inner sep=3pt,fill=blue!20!white] (e0) at ([yshift=1em]w0.north) {\footnotesize{$\textbf{e}_0=w_{0} \textbf{C} + \textrm{PE}(0)$}}; \node [anchor=south,line width=0.8pt,draw,inner sep=3pt,fill=blue!30!white,minimum width=7.5em] (e0) at ([yshift=1em]w0.north) {\footnotesize{$\textbf{e}_0=w_{0} \textbf{C} + \textrm{PE}(0)$}};
\node [anchor=south,line width=0.8pt,draw,inner sep=3pt,fill=blue!20!white] (e1) at ([yshift=1em]w1.north) {\footnotesize{$\textbf{e}_1=w_{1} \textbf{C} + \textrm{PE}(1)$}}; \node [anchor=south,line width=0.8pt,draw,inner sep=3pt,fill=blue!30!white,minimum width=7.5em] (e1) at ([yshift=1em]w1.north) {\footnotesize{$\textbf{e}_1=w_{1} \textbf{C} + \textrm{PE}(1)$}};
\node [anchor=south,line width=0.8pt,draw,inner sep=3pt,fill=blue!20!white] (e2) at ([yshift=1em]w2.north) {\footnotesize{$\textbf{e}_2=w_{2} \textbf{C} + \textrm{PE}(2)$}}; \node [anchor=south,line width=0.8pt,draw,inner sep=3pt,fill=blue!30!white,minimum width=7.5em] (e2) at ([yshift=1em]w2.north) {\footnotesize{$\textbf{e}_2=w_{2} \textbf{C} + \textrm{PE}(2)$}};
\node [anchor=south,line width=0.8pt,draw,inner sep=3pt,fill=blue!20!white] (e3) at ([yshift=1em]w3.north) {\footnotesize{$\textbf{e}_3=w_{3} \textbf{C} + \textrm{PE}(3)$}}; \node [anchor=south,line width=0.8pt,draw,inner sep=3pt,fill=blue!30!white,minimum width=7.5em] (e3) at ([yshift=1em]w3.north) {\footnotesize{$\textbf{e}_3=w_{3} \textbf{C} + \textrm{PE}(3)$}};
} }
{ {
\node [anchor=south,line width=0.8pt,draw,inner sep=3pt,fill=ugreen!20!white] (h0) at ([xshift=-0.5em, yshift=2.0em]e0.north) {\footnotesize{$\textbf{h}_{0}=\textrm{SelfAtt}(\textbf{e}_0,\textbf{e}_3)$}}; \node [anchor=south,line width=0.8pt,draw,inner sep=3pt,fill=green!30!white] (h0) at ([ yshift=2.0em]e0.north) {\footnotesize{$\textbf{h}_{0}=\textrm{SelfAtt}(\textbf{e}_0,\textbf{e}_3)$}};
\node [anchor=south,line width=0.8pt,draw,inner sep=3pt,fill=ugreen!20!white] (h1) at ([xshift=0.5em, yshift=2.0em]e1.north) {\footnotesize{$\textbf{h}_{1}=\textrm{SelfAtt}(\textbf{e}_1,\textbf{e}_3)$}}; \node [anchor=south,line width=0.8pt,draw,inner sep=3pt,fill=green!30!white] (h1) at ([ yshift=2.0em]e1.north) {\footnotesize{$\textbf{h}_{1}=\textrm{SelfAtt}(\textbf{e}_1,\textbf{e}_3)$}};
\node [anchor=south,line width=0.8pt,draw,inner sep=3pt,fill=ugreen!20!white] (h2) at ([xshift=1.5em, yshift=2.0em]e2.north) {\footnotesize{$\textbf{h}_{2}=\textrm{SelfAtt}(\textbf{e}_2,\textbf{e}_3)$}}; \node [anchor=south,line width=0.8pt,draw,inner sep=3pt,fill=green!30!white] (h2) at ([ yshift=2.0em]e2.north) {\footnotesize{$\textbf{h}_{2}=\textrm{SelfAtt}(\textbf{e}_2,\textbf{e}_3)$}};
} }
{ {
\node [anchor=south,line width=0.8pt,draw,minimum width=9em,inner sep=3pt,fill=orange!20!white] (f1) at ([xshift=0.5em, yshift=2.0em]h2.north) {\footnotesize{$\textbf{f}_3=\textrm{FNN}([\textbf{h}_0,\textbf{h}_1,\textbf{h}_2,\textbf{e}_3])$}}; \node [anchor=south,line width=0.8pt,draw,minimum width=9em,inner sep=3pt,fill=orange!30!white] (f1) at ([yshift=2.0em]h2.north) {\footnotesize{$\textbf{f}_3=\textrm{FNN}([\textbf{h}_0,\textbf{h}_1,\textbf{h}_2,\textbf{e}_3])$}};
\node [anchor=south,line width=0.8pt,draw,minimum width=9em,inner sep=3pt,fill=orange!20!white] (o1) at ([yshift=1.2em]f1.north) {\footnotesize{$\textbf{y}=\textrm{Softmax}(f_3 \textbf{U})$}}; \node [anchor=south,line width=0.8pt,draw,minimum width=9.5em,inner sep=3pt,fill=orange!30!white] (o1) at ([yshift=1.2em]f1.north) {\footnotesize{$\textbf{y}=\textrm{Softmax}(f_3 \textbf{U})$}};
} }
\node [anchor=south] (ylabel) at ([yshift=1.2em]o1.north) {\footnotesize{$\textrm{P}(w_4|w_{0}w_{1}w_{2}w_{3})$}}; \node [anchor=south] (ylabel) at ([yshift=1.2em]o1.north) {\footnotesize{$\textrm{P}(w_4|w_{0}w_{1}w_{2}w_{3})$}};
...@@ -34,11 +34,11 @@ ...@@ -34,11 +34,11 @@
\draw [->,line width=0.6pt] ([yshift=0.1em]w2.north) -- ([yshift=-0.1em]e2.south); \draw [->,line width=0.6pt] ([yshift=0.1em]w2.north) -- ([yshift=-0.1em]e2.south);
\draw [->,line width=0.6pt] ([yshift=0.1em]w3.north) -- ([yshift=-0.1em]e3.south); \draw [->,line width=0.6pt] ([yshift=0.1em]w3.north) -- ([yshift=-0.1em]e3.south);
\draw [->,line width=0.6pt] ([yshift=0.1em]e0.north) -- ([xshift=0em,yshift=-0.1em]h0.south); \draw [->,line width=0.6pt] ([yshift=0.1em]e0.north) -- ([xshift=0em,yshift=-0.1em]h0.south);
\draw [->,line width=0.6pt] ([yshift=0.1em]e1.north) -- ([xshift=-0.5em,yshift=-0.1em]h1.south); \draw [->,line width=0.6pt] ([yshift=0.1em]e1.north) -- ([xshift=0em,yshift=-0.1em]h1.south);
\draw [->,line width=0.6pt] ([yshift=0.1em]e2.north) -- ([xshift=-1em,yshift=-0.1em]h2.south); \draw [->,line width=0.6pt] ([yshift=0.1em]e2.north) -- ([xshift=0em,yshift=-0.1em]h2.south);
\draw [->,line width=0.6pt] ([yshift=0.1em]e3.north) -- ([xshift=1em,yshift=-0.1em]h0.south); \draw [->,line width=0.6pt] ([yshift=0.1em]e3.north) -- ([xshift=0.4em,yshift=-0.1em]h0.south);
\draw [->,line width=0.6pt] ([yshift=0.1em]e3.north) -- ([xshift=1em,yshift=-0.1em]h1.south); \draw [->,line width=0.6pt] ([yshift=0.1em]e3.north) -- ([xshift=0.4em,yshift=-0.1em]h1.south);
\draw [->,line width=0.6pt] ([yshift=0.1em]e3.north) -- ([xshift=1em,yshift=-0.1em]h2.south); \draw [->,line width=0.6pt] ([yshift=0.1em]e3.north) -- ([xshift=0.4em,yshift=-0.1em]h2.south);
\draw [->,line width=0.6pt] ([yshift=0.1em]h0.north) -- ([xshift=-2em,yshift=-0.1em]f1.south); \draw [->,line width=0.6pt] ([yshift=0.1em]h0.north) -- ([xshift=-2em,yshift=-0.1em]f1.south);
\draw [->,line width=0.6pt] ([yshift=0.1em]e3.north) -- ([xshift=2em,yshift=-0.1em]f1.south); \draw [->,line width=0.6pt] ([yshift=0.1em]e3.north) -- ([xshift=2em,yshift=-0.1em]f1.south);
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
\node [rectangle,rounded corners,draw=black!50,densely dashed,inner sep=0.4em] [fit = (part2-1) (part2-2) (hidlabel) ] (inputshadow) {}; \node [rectangle,rounded corners,draw=black!50,densely dashed,inner sep=0.4em] [fit = (part2-1) (part2-2) (hidlabel) ] (inputshadow) {};
\node [circle,anchor=west,draw=ublue,minimum width=2.5em,fill=blue!20,inner sep=2pt] (part2-3) at ([xshift=2.0em,yshift=-1.7em]part1-3.east) {\scriptsize {偏置2}}; \node [circle,anchor=west,draw=ublue,minimum width=2.5em,fill=blue!20,inner sep=2pt] (part2-3) at ([xshift=2.0em,yshift=-1.7em]part1-3.east) {\scriptsize {偏置2}};
\node [anchor=north,minimum width=3.0em] (part2-4) at ([xshift=0.0em,yshift=-1.6em]part2-3.south) {\scriptsize{隐藏层}}; \node [anchor=north,minimum width=3.0em] (part2-4) at ([xshift=0.0em,yshift=-1.6em]part2-3.south) {\scriptsize{隐藏层}};
\node [anchor=north] (labela) at ([xshift=0.0em,yshift=-4em]part2-3.south) {\footnotesize {(a)}}; \node [anchor=north] (labela) at ([xshift=0.0em,yshift=-3em]part2-3.south) {\footnotesize {(a)}};
%右 %右
\node [anchor=west,draw=ublue,minimum width=3.0em,fill=purple!20] (part3-1) at ([xshift=2em,yshift=0.0em]part2-2.east) {\scriptsize {穿衣指数}}; \node [anchor=west,draw=ublue,minimum width=3.0em,fill=purple!20] (part3-1) at ([xshift=2em,yshift=0.0em]part2-2.east) {\scriptsize {穿衣指数}};
...@@ -67,7 +67,7 @@ y ...@@ -67,7 +67,7 @@ y
\node [rectangle,rounded corners,draw=black!50,densely dashed,inner sep=0.4em] [fit = (part2-1) (part2-2) (hidlabel) ] (inputshadow) {}; \node [rectangle,rounded corners,draw=black!50,densely dashed,inner sep=0.4em] [fit = (part2-1) (part2-2) (hidlabel) ] (inputshadow) {};
\node [circle,anchor=west,draw=ublue,minimum width=2.5em,fill=blue!20,inner sep=2pt] (part2-3) at ([xshift=2.0em,yshift=-1.7em]part1-3.east) {\large {$b^{[2]} $}}; \node [circle,anchor=west,draw=ublue,minimum width=2.5em,fill=blue!20,inner sep=2pt] (part2-3) at ([xshift=2.0em,yshift=-1.7em]part1-3.east) {\large {$b^{[2]} $}};
\node [anchor=north,minimum width=3.0em] (part2-4) at ([xshift=0.0em,yshift=-1.6em]part2-3.south) {\scriptsize{隐藏层}}; \node [anchor=north,minimum width=3.0em] (part2-4) at ([xshift=0.0em,yshift=-1.6em]part2-3.south) {\scriptsize{隐藏层}};
\node [anchor=north] (labelb) at ([xshift=0.0em,yshift=-4em]part2-3.south) {\footnotesize {(b)}}; \node [anchor=north] (labelb) at ([xshift=0.0em,yshift=-3em]part2-3.south) {\footnotesize {(b)}};
%右 %右
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
\parinterval {\small\sffamily\bfseries{人工神经网络}}\index{人工神经网络}(Artificial Neural Networks)\index{Artificial Neural Networks}{\small\sffamily\bfseries{神经网络}}\index{神经网络}(Neural Networks)\index{Neural Networks}是描述客观世界的一种数学模型。这种模型和生物学上的神经系统在行为上有一些相似之处,但是人们更多的是把它作为一种计算工具,而非一个生物学模型。近些年,随着机器学习领域的快速发展,人工神经网络被更多的使用在对图像和自然语言处理问题的建模上。特别是,研究人员发现深层神经网络可以被成功训练后,学术界也逐渐形成了一种新的机器学习范式\ \dash \ 深度学习。可以说,深度学习是近几年最受瞩目的研究领域之一,其应用也十分广泛。比如,图像识别的很多重要进展都来自深度学习模型的使用。包括机器翻译在内的很多自然语言处理任务中,深度学习也已经成为了一种标准模型。基于深度学习的表示学习方法也为自然语言处理开辟了新的思路。 \parinterval {\small\sffamily\bfseries{人工神经网络}}\index{人工神经网络}(Artificial Neural Networks)\index{Artificial Neural Networks}{\small\sffamily\bfseries{神经网络}}\index{神经网络}(Neural Networks)\index{Neural Networks}是描述客观世界的一种数学模型。这种模型和生物学上的神经系统在行为上有一些相似之处,但是人们更多的是把它作为一种计算工具,而非一个生物学模型。近些年,随着机器学习领域的快速发展,人工神经网络被更多的使用在对图像和自然语言处理问题的建模上。特别是,研究人员发现深层神经网络可以被成功训练后,学术界也逐渐形成了一种新的机器学习范式\ \dash \ 深度学习。可以说,深度学习是近几年最受瞩目的研究领域之一,其应用也十分广泛。比如,图像识别的很多重要进展都来自深度学习模型的使用。包括机器翻译在内的很多自然语言处理任务中,深度学习也已经成为了一种标准模型。基于深度学习的表示学习方法也为自然语言处理开辟了新的思路。
\parinterval 本章将对深度学习的概念和技术进行介绍,目的是为第六章和第七章神经机器翻译的内容进行铺垫。此外,本章也会对深度学习在语言建模方面的应用进行介绍。这样,读者可以更容易地理解如何使用深度学习方法描述自然语言处理问题。同时,进一步了解一些相关的学术前沿,如预训练模型。 \parinterval 本章将对深度学习的概念和技术进行介绍,目的是为第六章和第七章神经机器翻译的内容进行铺垫。此外,本章也会对深度学习在语言建模方面的应用进行介绍。这样,读者可以更容易地理解如何使用深度学习方法描述自然语言处理问题。同时,进一步了解一些相关的学术前沿,如预训练模型。\\ \\ \\
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
% NEW SECTION % NEW SECTION
...@@ -124,7 +124,7 @@ ...@@ -124,7 +124,7 @@
\parinterval 端到端学习使机器学习不再像以往传统的特征工程方法一样需要经过繁琐的数据预处理、特征选择、降维等过程,而是直接利用人工神经网络自动从简单特征中提取、组合更复杂的特征,大大提升了模型能力和工程效率。以图\ref{fig:5-2}中的图像分类为例,在传统方法中,图像分类需要很多阶段的处理。首先,需要提取一些手工设计的图像特征,在将其降维之后,需要利用SVM等分类算法对其进行分类。与这种多阶段的流水线似的处理流程相比,端到端深度学习只训练一个神经网络,输入就是图片的像素表示,输出直接是分类类别。 \parinterval 端到端学习使机器学习不再像以往传统的特征工程方法一样需要经过繁琐的数据预处理、特征选择、降维等过程,而是直接利用人工神经网络自动从简单特征中提取、组合更复杂的特征,大大提升了模型能力和工程效率。以图\ref{fig:5-2}中的图像分类为例,在传统方法中,图像分类需要很多阶段的处理。首先,需要提取一些手工设计的图像特征,在将其降维之后,需要利用SVM等分类算法对其进行分类。与这种多阶段的流水线似的处理流程相比,端到端深度学习只训练一个神经网络,输入就是图片的像素表示,输出直接是分类类别。
%------------------------------------------------------------------------------ %------------------------------------------------------------------------------
\begin{figure} \begin{figure}[htp]
\centering \centering
\subfigure[基于特征工程的机器学习方法做图像分类]{ \subfigure[基于特征工程的机器学习方法做图像分类]{
\begin{minipage}{.9\textwidth} \begin{minipage}{.9\textwidth}
...@@ -210,13 +210,14 @@ ...@@ -210,13 +210,14 @@
\subsubsection{标量、向量和矩阵} \subsubsection{标量、向量和矩阵}
\vspace{-0.5em}
\parinterval {\small\sffamily\bfseries{标量}}\index{标量}(Scalar)\index{Scalar}:标量亦称``无向量'',是一种只具有数值大小而没有方向的量,通俗地说,一个标量就是一个单独的数,这里特指实数\footnote{严格意义上,标量可以是复数等其他形式。这里为了方便讨论,仅以实数为对象。}。一般用小写斜体表示标量。比如,对于$ a=5 $$ a $就是一个标量。 \parinterval {\small\sffamily\bfseries{标量}}\index{标量}(Scalar)\index{Scalar}:标量亦称``无向量'',是一种只具有数值大小而没有方向的量,通俗地说,一个标量就是一个单独的数,这里特指实数\footnote{严格意义上,标量可以是复数等其他形式。这里为了方便讨论,仅以实数为对象。}。一般用小写斜体表示标量。比如,对于$ a=5 $$ a $就是一个标量。
\parinterval {\small\sffamily\bfseries{向量}}\index{向量}(Vector)\index{Vector}:向量是由一组实数组成的有序数组。与标量不同,向量既有大小也有方向。可以把向量看作空间中的点,每个元素是不同坐标轴上的坐标。公式\ref{eq:5-1}和公式\ref{eq:5-2}展示了一个行向量和一个列向量。本章默认使用行向量,如$ \mathbf a=(a_1, a_2, a_3) $$ \mathbf a $对应的列向量记为$ \mathbf a^{\rm T} $ \parinterval {\small\sffamily\bfseries{向量}}\index{向量}(Vector)\index{Vector}:向量是由一组实数组成的有序数组。与标量不同,向量既有大小也有方向。可以把向量看作空间中的点,每个元素是不同坐标轴上的坐标。公式\ref{eq:5-1}和公式\ref{eq:5-2}展示了一个行向量和一个列向量。本章默认使用行向量,如$ \mathbf a=(a_1, a_2, a_3) $$ \mathbf a $对应的列向量记为$ \mathbf a^{\rm T} $
\begin{eqnarray} \begin{eqnarray}
\mathbf a &=& \begin{pmatrix} \mathbf a &=& \begin{pmatrix}
1 & 2 & 5 & 7 1 & 2 & 5 & 7
\end{pmatrix}\label{eq:5-1}\\ \end{pmatrix}\label{eq:5-1}\\ \nonumber \\
\mathbf{a^{\textrm{T}}} &=& \begin{pmatrix} \mathbf{a^{\textrm{T}}} &=& \begin{pmatrix}
&1& \\ &1& \\
&2&\\ &2&\\
...@@ -225,7 +226,7 @@ ...@@ -225,7 +226,7 @@
\label{eq:5-2} \label{eq:5-2}
\end{eqnarray} \end{eqnarray}
\parinterval {\small\sffamily\bfseries{矩阵}}\index{矩阵}(Matrix)\index{Matrix}:矩阵是一个按照长方阵列排列的实数集合,最早来自于方程组的系数及常数所构成的方阵。在计算机领域,通常将矩阵看作二维数组。我们用粗体的符号$ \mathbf a $表示一个矩阵,如果该矩阵有$ m $$ n $列,那么有$ \mathbf a\in R^{m\times n} $。这里,用不加粗的符号来表示矩阵中的元素,其中每个元素都被一个行索引和一个列索引所确定。例如,$ a_{ij} $表示第$ i $行、第$ j $列的矩阵元素。如下,$ \mathbf a $定义了一个2行2列的矩阵。 \parinterval {\small\sffamily\bfseries{矩阵}}\index{矩阵}(Matrix)\index{Matrix}:矩阵是一个按照长方阵列排列的实数集合,最早来自于方程组的系数及常数所构成的方阵。在计算机领域,通常将矩阵看作二维数组。我们用粗体的符号$ \mathbf a $表示一个矩阵,如果该矩阵有$ m $$ n $列,那么有$ \mathbf a\in R^{m\times n} $。这里,用不加粗的符号来表示矩阵中的元素,其中每个元素都被一个行索引和一个列索引所确定。例如,$ a_{ij} $表示第$ i $行、第$ j $列的矩阵元素。如下,公式\ref{eq:5-3}$ \mathbf a $定义了一个2行2列的矩阵。
\begin{eqnarray} \begin{eqnarray}
\mathbf a & = & \begin{pmatrix} \mathbf a & = & \begin{pmatrix}
a_{11} & a_{12}\\ a_{11} & a_{12}\\
...@@ -246,7 +247,7 @@ ...@@ -246,7 +247,7 @@
\parinterval {\small\sffamily\bfseries{转置}}\index{转置}(Transpose)\index{Transpose}是矩阵的重要操作之一。矩阵的转置可以看作是将矩阵以对角线为镜像进行翻转:假设$ \mathbf a $$ m $$ n $列的矩阵,第$ i $行、第$ j $ 列的元素是$ a_{ij} $,即:$ \mathbf a={(a_{ij})}_{m\times n} $,把$ m\times n $矩阵$ \mathbf a $的行换成同序数的列得到一个$ n\times m $矩阵,则得到$ \mathbf a $的转置矩阵,记为$ \mathbf a^{\rm T} $,其中$ a_{ji}^{\rm T}=a_{ij} $。例如: \parinterval {\small\sffamily\bfseries{转置}}\index{转置}(Transpose)\index{Transpose}是矩阵的重要操作之一。矩阵的转置可以看作是将矩阵以对角线为镜像进行翻转:假设$ \mathbf a $$ m $$ n $列的矩阵,第$ i $行、第$ j $ 列的元素是$ a_{ij} $,即:$ \mathbf a={(a_{ij})}_{m\times n} $,把$ m\times n $矩阵$ \mathbf a $的行换成同序数的列得到一个$ n\times m $矩阵,则得到$ \mathbf a $的转置矩阵,记为$ \mathbf a^{\rm T} $,其中$ a_{ji}^{\rm T}=a_{ij} $。例如:
\begin{eqnarray} \begin{eqnarray}
\mathbf a & = & \begin{pmatrix} 1 & 3 & 2 & 6\\5 & 4 & 8 & 2\end{pmatrix} \\ \mathbf a & = & \begin{pmatrix} 1 & 3 & 2 & 6\\5 & 4 & 8 & 2\end{pmatrix} \\ \nonumber \\
{\mathbf a}^{\rm T} & = &\begin{pmatrix} 1 & 5\\3 & 4\\2 & 8\\6 & 2\end{pmatrix} {\mathbf a}^{\rm T} & = &\begin{pmatrix} 1 & 5\\3 & 4\\2 & 8\\6 & 2\end{pmatrix}
\end{eqnarray} \end{eqnarray}
...@@ -294,14 +295,14 @@ ...@@ -294,14 +295,14 @@
\vspace{0.5em} \vspace{0.5em}
\end{itemize} \end{itemize}
\parinterval 矩阵的{\small\bfnew{数乘}}\index{数乘}(Scalar Multiplication)\index{Scalar Multiplication}是指标量(实数)与矩阵的乘法运算,计算过程是将标量与矩阵的每个元素相乘,最终得到与原矩阵形状相同的矩阵。例如,矩阵$ \mathbf a={(a_{ij})}_{m\times n} $与标量$ k $进行数乘运算,其结果矩阵$ \mathbf b={(ka_{ij})}_{m\times n} $,即$ k{(a_{ij})}_{m\times n}={(ka_{ij})}_{m\times n} $。下面的式子展示了矩阵数乘的计算过程 \parinterval 矩阵的{\small\bfnew{数乘}}\index{数乘}(Scalar Multiplication)\index{Scalar Multiplication}是指标量(实数)与矩阵的乘法运算,计算过程是将标量与矩阵的每个元素相乘,最终得到与原矩阵形状相同的矩阵。例如,矩阵$ \mathbf a={(a_{ij})}_{m\times n} $与标量$ k $进行数乘运算,其结果矩阵$ \mathbf b={(ka_{ij})}_{m\times n} $,即$ k{(a_{ij})}_{m\times n}={(ka_{ij})}_{m\times n} $。下面的式子展示了矩阵数乘的计算过程
\begin{eqnarray} \begin{eqnarray}
\mathbf a & = & \mathbf a & = &
\begin{pmatrix} \begin{pmatrix}
3 & 2 & 7\\ 3 & 2 & 7\\
5 & 8 & 1 5 & 8 & 1
\end{pmatrix} \end{pmatrix}
\\ \\ \nonumber \\
2\mathbf a & = & 2\mathbf a & = &
\begin{pmatrix} \begin{pmatrix}
6 & 4 & 14\\ 6 & 4 & 14\\
...@@ -310,7 +311,7 @@ ...@@ -310,7 +311,7 @@
\label{eq:5-5} \label{eq:5-5}
\end{eqnarray} \end{eqnarray}
\parinterval 矩阵的数乘满足以下运算规律,其中$ k $$ l $是实数,$ \mathbf a $$ \mathbf b $是矩阵: \parinterval 矩阵的数乘满足以下运算规律,其中$ k $$ l $是实数,$ \mathbf a $$ \mathbf b $形状相同的矩阵:
\begin{itemize} \begin{itemize}
\vspace{0.5em} \vspace{0.5em}
...@@ -319,7 +320,7 @@ ...@@ -319,7 +320,7 @@
\item 左分配律:$ (k+l)\mathbf a=k\mathbf a+l\mathbf a $ \item 左分配律:$ (k+l)\mathbf a=k\mathbf a+l\mathbf a $
\vspace{0.5em} \vspace{0.5em}
\item 结合律:$ (kl)\mathbf a=k(l\mathbf a) $ \item 结合律:$ (kl)\mathbf a=k(l\mathbf a) $
\vspace{0.5em} %\vspace{0.5em}
\end{itemize} \end{itemize}
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
...@@ -328,13 +329,14 @@ ...@@ -328,13 +329,14 @@
\subsubsection{矩阵乘法和矩阵点乘} \subsubsection{矩阵乘法和矩阵点乘}
\parinterval 矩阵乘法是矩阵运算中最重要的操作之一,为了与矩阵点乘区分,通常也把矩阵乘法叫做矩阵叉乘。假设$ \mathbf a $$ m\times p $的矩阵,$ \mathbf b $$ p\times n $的矩阵,对$ \mathbf a $$ \mathbf b $作矩阵乘法的结果是一个$ m\times n $的矩阵$ \mathbf c $,其中矩阵$ \mathbf c $中第$ i $行、第$ j $列的元素可以表示为: \parinterval 矩阵乘法是矩阵运算中最重要的操作之一,为了与矩阵点乘区分,通常也把矩阵乘法叫做矩阵叉乘。假设$ \mathbf a $$ m\times p $的矩阵,$ \mathbf b $$ p\times n $的矩阵,对$ \mathbf a $$ \mathbf b $作矩阵乘法的结果是一个$ m\times n $的矩阵$ \mathbf c $,其中矩阵$ \mathbf c $中第$ i $行、第$ j $列的元素可以表示为:
\begin{eqnarray} \begin{eqnarray}
{(\mathbf a\mathbf b)}_{ij} &=& \sum_{k=1}^p a_{ik}b_{kj} {(\mathbf a\mathbf b)}_{ij} &=& \sum_{k=1}^p a_{ik}b_{kj}
\label{eq:5-6} \label{eq:5-6}
\end{eqnarray} \end{eqnarray}
\parinterval 注意只有当第一个矩阵的列数与第二个矩阵的行数相等时,两个矩阵才可以作矩阵乘法。公式\ref{eq:5-7}展示了矩阵乘法的运算过程,若$\mathbf a=\begin{pmatrix}a_{11} & a_{12}& a_{13}\\a_{21} & a_{22} & a_{23}\end{pmatrix}$\\$\mathbf b=\begin{pmatrix}b_{11} & b_{12}\\b_{21} & b_{22}\\b_{31} & b_{32}\end{pmatrix} $,则有: \parinterval 只有当第一个矩阵的列数与第二个矩阵的行数相等时,两个矩阵才可以作矩阵乘法。公式\ref{eq:5-7}展示了矩阵乘法的运算过程,若$\mathbf a=\begin{pmatrix}a_{11} & a_{12}& a_{13}\\a_{21} & a_{22} & a_{23}\end{pmatrix}$$\mathbf b=\begin{pmatrix}b_{11} & b_{12}\\b_{21} & b_{22}\\b_{31} & b_{32}\end{pmatrix} $,则有:
\vspace{-0.5em}
\begin{eqnarray} \begin{eqnarray}
\mathbf c & = & \mathbf a\mathbf b \nonumber \\ \mathbf c & = & \mathbf a\mathbf b \nonumber \\
& = & \begin{pmatrix} & = & \begin{pmatrix}
...@@ -367,7 +369,7 @@ ...@@ -367,7 +369,7 @@
1 & 0\\ 1 & 0\\
-1 & 3 -1 & 3
\end{pmatrix} \end{pmatrix}
\\ \\ \nonumber \\
\mathbf b &=& \mathbf b &=&
\begin{pmatrix} \begin{pmatrix}
3 & 1\\ 3 & 1\\
...@@ -398,7 +400,7 @@ f(c\mathbf v)&=&cf(\mathbf v) ...@@ -398,7 +400,7 @@ f(c\mathbf v)&=&cf(\mathbf v)
\label{eq:5-10} \label{eq:5-10}
\end{eqnarray} \end{eqnarray}
\parinterval 利用矩阵$ \mathbf a\in R^{m\times n} $,可以实现两个有限维欧氏空间的映射函数$f:R^n\rightarrow R^m$。例如$ n $维列向量$ \mathbf x ^{\rm T}$$ m\times n $的矩阵$ \mathbf a $,向量$ \mathbf x ^{\rm T}$左乘矩阵$ \mathbf a $,可将向量$ \mathbf x ^{\rm T}$映射为$ m $列向量,对于 \parinterval 利用矩阵$ \mathbf a\in R^{m\times n} $,可以实现两个有限维欧氏空间的映射函数$f:R^n\rightarrow R^m$。例如$ n $维列向量$ \mathbf x ^{\rm T}$$ m\times n $的矩阵$ \mathbf a $,向量$ \mathbf x ^{\rm T}$左乘矩阵$ \mathbf a $,可将向量$ \mathbf x ^{\rm T}$映射为$ m $列向量,对于
\begin{eqnarray} \begin{eqnarray}
\mathbf x^{\textrm{T}} & = & {\begin{pmatrix} x_1, & x_2, & \dots &, x_n \end{pmatrix}}^{\rm T} \mathbf x^{\textrm{T}} & = & {\begin{pmatrix} x_1, & x_2, & \dots &, x_n \end{pmatrix}}^{\rm T}
\label{eq:5-11} \label{eq:5-11}
...@@ -512,9 +514,11 @@ l_p(\mathbf x) & = & {\Vert{\mathbf x}\Vert}_p \nonumber \\ ...@@ -512,9 +514,11 @@ l_p(\mathbf x) & = & {\Vert{\mathbf x}\Vert}_p \nonumber \\
% NEW SUBSUB-SECTION % NEW SUBSUB-SECTION
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
\vspace{-0.5em}
\subsubsection{感知机\ \dash \ 最简单的人工神经元模型} \subsubsection{感知机\ \dash \ 最简单的人工神经元模型}
\parinterval 感知机是人工神经元的一种实例,在上世纪50-60年代被提出后,对神经网络研究产生了深远的影响。感知机模型如图\ref {fig:5-5}所示,其输入是一个$n$维二值向量$ \mathbf x=(x_0,x_1,\dots,x_n) $,其中$ x_i=0 $$ 1 $。权重$ \mathbf w=(w_0,w_1,\dots,w_n) $,每个输入变量对应一个权重$ w_i $(实数)。偏置$ b $是一个实数变量($ -\sigma $)。输出也是一个二值结果,即$ y=0 $$ 1 $$ y $值的判定由输入的加权和是否大于(或小于)一个阈值$ \sigma $决定(图\ref{fig:5-5}): \vspace{0.5em}
\parinterval 感知机是人工神经元的一种实例,在上世纪50-60年代被提出后,对神经网络研究产生了深远的影响。感知机模型如图\ref {fig:5-5}所示,其输入是一个$n$维二值向量$ \mathbf x=(x_0,x_1,\dots,x_n) $,其中$ x_i=0 $$ 1 $。权重$ \mathbf w=(w_0,w_1,\dots,w_n) $,每个输入变量对应一个权重$ w_i $(实数)。偏置$ b $是一个实数变量($ -\sigma $)。输出也是一个二值结果,即$ y=0 $$ 1 $$ y $值的判定由输入的加权和是否大于(或小于)一个阈值$ \sigma $决定(公式\ref{eq:5-19}):
\begin{eqnarray} \begin{eqnarray}
y=\begin{cases} 0 & \sum_{i}{x_i\cdot w_i}-\sigma <0\\1 & \sum_{i}{x_i\cdot w_i}-\sigma \geqslant 0\end{cases} y=\begin{cases} 0 & \sum_{i}{x_i\cdot w_i}-\sigma <0\\1 & \sum_{i}{x_i\cdot w_i}-\sigma \geqslant 0\end{cases}
\label{eq:5-19} \label{eq:5-19}
...@@ -563,6 +567,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 ...@@ -563,6 +567,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
% NEW SUBSUB-SECTION % NEW SUBSUB-SECTION
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
\vspace{-1em}
\subsubsection{神经元内部权重} \subsubsection{神经元内部权重}
\parinterval 在上面的例子中,连接权重代表着每个输入因素对最终输出结果的重要程度,为了得到令人满意的决策,需要不断调整权重。如果你是守财奴,则会对票价看得更重一些,这样你会用不均匀的权重计算每个因素的影响,比如:$ w_0=0.5 $$ w_1=2 $$ w_2=0.5 $,此时感知机模型如图\ref{fig:5-7}所示。在这种情况下,女友很希望和你一起去看音乐会,但是剧场很远而且票价500元,会导致你不去看音乐会,因为 \parinterval 在上面的例子中,连接权重代表着每个输入因素对最终输出结果的重要程度,为了得到令人满意的决策,需要不断调整权重。如果你是守财奴,则会对票价看得更重一些,这样你会用不均匀的权重计算每个因素的影响,比如:$ w_0=0.5 $$ w_1=2 $$ w_2=0.5 $,此时感知机模型如图\ref{fig:5-7}所示。在这种情况下,女友很希望和你一起去看音乐会,但是剧场很远而且票价500元,会导致你不去看音乐会,因为
...@@ -670,14 +675,6 @@ 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 ...@@ -670,14 +675,6 @@ 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 为了建立多层神经网络,首先需要把前面提到的简单的神经元进行扩展,把多个神经元组成一``层''神经元。比如,很多实际问题需要同时有多个输出,这时可以把多个相同的神经元并列起来,每个神经元都会有一个单独的输出,这就构成一``层'',形成了单层神经网络。单层神经网络中的每一个神经元都对应着一组权重和一个输出,可以把单层神经网络中的不同输出看作一个事物不同角度的描述。
举个简单的例子,预报天气时,往往需要预测温度、湿度和风力,这就意味着如果使用单层神经网络进行预测,需要设置3个神经元。如图\ref{fig:5-10},权重矩阵为:
\begin{eqnarray}
\mathbf w=\begin{pmatrix} w_{00} & w_{01} & w_{02}\\ w_{10} & w_{11} & w_{12}\end{pmatrix}
\end{eqnarray}
\noindent 它的第一列元素$ \begin{pmatrix} w_{00}\\ w_{10}\end{pmatrix} $是输入相对第一个输出$ y_0 $ 的权重,参数向量$ \mathbf b=(b_0,b_1,b_2) $的第一个元素$ b_0 $是对应于第一个输出$ y_0 $ 的偏置量;类似的,可以得到$ y_1 $$ y_2 $。预测天气的单层模型如图\ref{fig:5-11}所示(在本例中,假设输入$ \mathbf x=(x_0,x_1) $)。
%---------------------------------------------- %----------------------------------------------
\begin{figure}[htp] \begin{figure}[htp]
\centering \centering
...@@ -687,6 +684,13 @@ 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 ...@@ -687,6 +684,13 @@ 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} \end{figure}
%------------------------------------------- %-------------------------------------------
\parinterval 举个简单的例子,预报天气时,往往需要预测温度、湿度和风力,这就意味着如果使用单层神经网络进行预测,需要设置3个神经元。如图\ref{fig:5-10},权重矩阵为:
\begin{eqnarray}
\mathbf w=\begin{pmatrix} w_{00} & w_{01} & w_{02}\\ w_{10} & w_{11} & w_{12}\end{pmatrix}
\end{eqnarray}
\noindent 它的第一列元素$ \begin{pmatrix} w_{00}\\ w_{10}\end{pmatrix} $是输入相对第一个输出$ y_0 $ 的权重,参数向量$ \mathbf b=(b_0,b_1,b_2) $的第一个元素$ b_0 $是对应于第一个输出$ y_0 $ 的偏置量;类似的,可以得到$ y_1 $$ y_2 $。预测天气的单层模型如图\ref{fig:5-11}所示(在本例中,假设输入$ \mathbf x=(x_0,x_1) $)。
%---------------------------------------------- %----------------------------------------------
\begin{figure}[htp] \begin{figure}[htp]
\centering \centering
...@@ -698,6 +702,15 @@ 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 ...@@ -698,6 +702,15 @@ 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 在神经网络中,对于输入向量$ \mathbf x\in R^m $,一层神经网络首先将其经过线性变换映射到$ R^n $,再经过激活函数变成$ \mathbf y\in R^n $。还是上面天气预测的例子,每个神经元获得相同的输入,权重矩阵$ \mathbf w $是一个$ 2\times 3 $矩阵,矩阵中每个元素$ w_{ij} $代表第$ j $个神经元中$ x_{i} $对应的权重值,假设编号为0的神经元负责预测温度,则$ w_{i0} $含义为预测温度时,输入$ x_{i} $对其影响程度。此外所有神经元的偏置$ b_{0} $$ b_{1} $$ b_{2} $组成了最终的偏置向量$ \mathbf b $。在该例中则有,权重矩阵$ \mathbf w=\begin{pmatrix} w_{00} & w_{01} & w_{02}\\ w_{10} & w_{11} & w_{12}\end{pmatrix} $,偏置向量$ \mathbf b=(b_0,b_1,b_2) $ \parinterval 在神经网络中,对于输入向量$ \mathbf x\in R^m $,一层神经网络首先将其经过线性变换映射到$ R^n $,再经过激活函数变成$ \mathbf y\in R^n $。还是上面天气预测的例子,每个神经元获得相同的输入,权重矩阵$ \mathbf w $是一个$ 2\times 3 $矩阵,矩阵中每个元素$ w_{ij} $代表第$ j $个神经元中$ x_{i} $对应的权重值,假设编号为0的神经元负责预测温度,则$ w_{i0} $含义为预测温度时,输入$ x_{i} $对其影响程度。此外所有神经元的偏置$ b_{0} $$ b_{1} $$ b_{2} $组成了最终的偏置向量$ \mathbf b $。在该例中则有,权重矩阵$ \mathbf w=\begin{pmatrix} w_{00} & w_{01} & w_{02}\\ w_{10} & w_{11} & w_{12}\end{pmatrix} $,偏置向量$ \mathbf b=(b_0,b_1,b_2) $
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter5/Figures/fig-rotation}
\caption{ $ \mathbf w $$ \mathbf x $的旋转作用}
\label{fig:5-12}
\end{figure}
%-------------------------------------------
\parinterval 那么,线性变换的本质是什么? \parinterval 那么,线性变换的本质是什么?
\begin{itemize} \begin{itemize}
...@@ -709,18 +722,9 @@ 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 ...@@ -709,18 +722,9 @@ 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
\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{eqnarray} \end{eqnarray}
这样,矩形区域由第一象限旋转90度到了第四象限。 这样,矩形区域由第一象限旋转90度到了第四象限。
\vspace{0.3em} \vspace{0.5em}
\end{itemize} \end{itemize}
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter5/Figures/fig-rotation}
\caption{ $ \mathbf w $$ \mathbf x $的旋转作用}
\label{fig:5-12}
\end{figure}
%-------------------------------------------
\parinterval 公式$ \mathbf x\cdot \mathbf w+\mathbf b $中的公式中的$ \mathbf b $相当于对其进行平移变换。其过程如图\ref{fig:5-13}所示,偏置矩阵$ \mathbf b=\begin{pmatrix} 0.5 & 0 & 0\\ 0 & 0 & 0\\ 0 & 0 & 0\end{pmatrix} $将矩形区域沿x轴向右平移了一段距离。 \parinterval 公式$ \mathbf x\cdot \mathbf w+\mathbf b $中的公式中的$ \mathbf b $相当于对其进行平移变换。其过程如图\ref{fig:5-13}所示,偏置矩阵$ \mathbf b=\begin{pmatrix} 0.5 & 0 & 0\\ 0 & 0 & 0\\ 0 & 0 & 0\end{pmatrix} $将矩形区域沿x轴向右平移了一段距离。
%---------------------------------------------- %----------------------------------------------
...@@ -734,6 +738,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 ...@@ -734,6 +738,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
\parinterval 也就是说,线性变换提供了对输入数据进行空间中旋转、平移的能力。当然,线性变换也适用于更加复杂的情况,这也为神经网络提供了拟合不同函数的能力。比如,可以利用线性变换将三维图形投影到二维平面上,或者将二维平面上的图形映射到三维空间。如图\ref{fig:5-14},通过一个简单的线性变换,可以将三维图形投影到二维平面上。 \parinterval 也就是说,线性变换提供了对输入数据进行空间中旋转、平移的能力。当然,线性变换也适用于更加复杂的情况,这也为神经网络提供了拟合不同函数的能力。比如,可以利用线性变换将三维图形投影到二维平面上,或者将二维平面上的图形映射到三维空间。如图\ref{fig:5-14},通过一个简单的线性变换,可以将三维图形投影到二维平面上。
\vspace{-0.5em}
%---------------------------------------------- %----------------------------------------------
\begin{figure}[htp] \begin{figure}[htp]
\centering \centering
...@@ -743,7 +748,8 @@ 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 ...@@ -743,7 +748,8 @@ 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} \end{figure}
%------------------------------------------- %-------------------------------------------
\parinterval 那激活函数又是什么?神经元在接收到经过线性变换的结果后,通过激活函数的处理,得到最终的输出$ \mathbf y $。激活函数的目的是解决实际问题中的非线性变换,线性变换只能拟合直线,而激活函数的加入,使神经网络具有了拟合曲线的能力。 特别是在实际问题中,很多现象都无法用简单的线性关系描述,这时激活函数的非线性就为描述更加复杂的问题提供了工具。常见的非线性函数有Sigmoid、ReLU、Tanh等。如图\ref{fig:5-15}列举了几种激活函数的形式。 \vspace{-0.5em}
\parinterval 那激活函数又是什么?神经元在接收到经过线性变换的结果后,通过激活函数的处理,得到最终的输出$ \mathbf y $。激活函数的目的是解决实际问题中的非线性变换,线性变换只能拟合直线,而激活函数的加入,使神经网络具有了拟合曲线的能力。 特别是在实际问题中,很多现象都无法用简单的线性关系描述,这时激活函数的非线性就为描述更加复杂的问题提供了工具。常见的非线性函数有Sigmoid、ReLU、Tanh等。如图\ref{fig:5-15}列举了几种激活函数的形式。\\
%---------------------------------------------- %----------------------------------------------
\begin{figure}\centering \begin{figure}\centering
...@@ -796,20 +802,10 @@ 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 ...@@ -796,20 +802,10 @@ 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
% NEW SUBSUB-SECTION % NEW SUBSUB-SECTION
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
\vspace{-0.5em}
\subsubsection{单层神经网络$\rightarrow$多层神经网络} \subsubsection{单层神经网络$\rightarrow$多层神经网络}
\parinterval 单层神经网络由线性变换和激活函数两部分构成,但在实际问题中,单层网络并不能很好地拟合复杂函数。因此很自然地想到将单层网络扩展到多层神经网络,即深层神经网络。将一层神经网络的最终输出向量作为另一层神经网络的输入向量,通过这种方式可以将多层神经网络连接在一起,如图\ref{fig:5-16}所示。 \parinterval 单层神经网络由线性变换和激活函数两部分构成,但在实际问题中,单层网络并不能很好地拟合复杂函数。因此很自然地想到将单层网络扩展到多层神经网络,即深层神经网络。将一层神经网络的最终输出向量作为另一层神经网络的输入向量,通过这种方式可以将多个单层神经网络连接在一起。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter5/Figures/fig-more-layers}
\caption{单层神经网络$\to$多层神经网路}
\label{fig:5-16}
\end{figure}
%-------------------------------------------
\parinterval 在多层神经网络中,通常包括输入层、输出层和至少一个隐藏层。如图\ref{fig:5-17}是一个由四层神经网络构成的模型,包括输入层、输出层和两个隐藏层。
%---------------------------------------------- %----------------------------------------------
\begin{figure}[htp] \begin{figure}[htp]
...@@ -820,6 +816,8 @@ 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 ...@@ -820,6 +816,8 @@ 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} \end{figure}
%------------------------------------------- %-------------------------------------------
\parinterval 在多层神经网络中,通常包括输入层、输出层和至少一个隐藏层。如图\ref{fig:5-17}是一个由四层神经网络构成的模型,包括输入层、输出层和两个隐藏层。\\
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
% NEW SUB-SECTION % NEW SUB-SECTION
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
...@@ -830,8 +828,6 @@ 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 ...@@ -830,8 +828,6 @@ 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 如图\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] \begin{figure}[htp]
\centering \centering
...@@ -841,7 +837,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 ...@@ -841,7 +837,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} \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 如图\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] \begin{figure}[htp]
...@@ -852,6 +848,8 @@ 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 ...@@ -852,6 +848,8 @@ 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} \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轴向右平移。 \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轴向右平移。
%---------------------------------------------- %----------------------------------------------
...@@ -887,8 +885,6 @@ 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 ...@@ -887,8 +885,6 @@ 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 以如图\ref{fig:5-23}(a)所示的目标函数为例,为了拟合该函数,可以将其看成分成无数小段的分段函数,如图\ref{fig:5-23}(b)所示。
%---------------------------------------------- %----------------------------------------------
\begin{figure}[htp] \begin{figure}[htp]
\centering \centering
...@@ -898,6 +894,8 @@ 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 ...@@ -898,6 +894,8 @@ 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} \end {figure}
%------------------------------------------- %-------------------------------------------
\parinterval 以如图\ref{fig:5-23}(a)所示的目标函数为例,为了拟合该函数,可以将其看成分成无数小段的分段函数,如图\ref{fig:5-23}(b)所示。
\parinterval 如图\ref{fig:5-24}(a)所示,上例中两层神经网络的函数便可以拟合出目标函数的一小段。为了使两层神经网络可以拟合出目标函数更多的一小段,需要增加隐层神经元的个数。如图\ref{fig:5-24}(b),将原本的两层神经网络神经元个数增多一倍,由2个神经元扩展到4个神经元,其函数的分段数也增加一倍,而此时的函数恰好可以拟合目标函数中的两个小段。以此类推,理论上,该两层神经网络便可以通过不断地增加隐层神经元数量去拟合任意函数。 \parinterval 如图\ref{fig:5-24}(a)所示,上例中两层神经网络的函数便可以拟合出目标函数的一小段。为了使两层神经网络可以拟合出目标函数更多的一小段,需要增加隐层神经元的个数。如图\ref{fig:5-24}(b),将原本的两层神经网络神经元个数增多一倍,由2个神经元扩展到4个神经元,其函数的分段数也增加一倍,而此时的函数恰好可以拟合目标函数中的两个小段。以此类推,理论上,该两层神经网络便可以通过不断地增加隐层神经元数量去拟合任意函数。
%---------------------------------------------- %----------------------------------------------
...@@ -943,7 +941,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 ...@@ -943,7 +941,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
% NEW SUBSUB-SECTION % NEW SUBSUB-SECTION
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
\subsubsection{张量} \subsubsection{\ 张量}
\parinterval 对于神经网络中的某层神经元$ \mathbf y=f(\mathbf x\cdot \mathbf w+\mathbf b) $,其中$ \mathbf w $是权重矩阵,例如$ \begin{pmatrix} 1 & 2\\ 3 & 4\end{pmatrix} $$ \mathbf b $ 是偏置向量,例如$ (1,3) $。在这里,输入$ \mathbf x $和输出$ \mathbf y $,可以不是简单的向量或是矩阵形式,而是深度学习中更加通用的数学量\ \dash \ {\small\bfnew{张量}}\index{张量}(Tensor)\index{Tensor},比如下式中的几种情况都可以看作是深度学习中定义数据的张量: \parinterval 对于神经网络中的某层神经元$ \mathbf y=f(\mathbf x\cdot \mathbf w+\mathbf b) $,其中$ \mathbf w $是权重矩阵,例如$ \begin{pmatrix} 1 & 2\\ 3 & 4\end{pmatrix} $$ \mathbf b $ 是偏置向量,例如$ (1,3) $。在这里,输入$ \mathbf x $和输出$ \mathbf y $,可以不是简单的向量或是矩阵形式,而是深度学习中更加通用的数学量\ \dash \ {\small\bfnew{张量}}\index{张量}(Tensor)\index{Tensor},比如下式中的几种情况都可以看作是深度学习中定义数据的张量:
\begin{eqnarray} \begin{eqnarray}
...@@ -977,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 ...@@ -977,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
\label{eq:5-66} \label{eq:5-66}
\end{eqnarray} \end{eqnarray}
\noindent 其中,$ u $$ v_i $的同型向量,$ c $为任意实数。这个性质非常重要,根据这个性质可以推导出张量的其他定义。 \noindent 其中,$ u $$ v_i $形状相同的向量,$ c $为任意实数。这个性质非常重要,根据这个性质可以推导出张量的其他定义。
\parinterval 从我们的物理世界看,如果一个物理量在物体的某个位置上只是一个单值,那么它是一个标量,例如密度;如果一个物理量在同一个位置、从多个方向上看,有不同的值,那么这个物理量就是一个张量,比如物理学中常用的应力的描述就是一个典型的张量。举一个简单的例子:$ \mathbf T(\mathbf v,\mathbf u) $是一个三维空间$(\textrm{x},\textrm{y},\textrm{z})$上的2阶张量,其中$ \mathbf v $$ \mathbf u $ 是两个向量,如图\ref{fig:5-26}所示,向量$ \mathbf v $在某个两两垂直的三维坐标系中可以表示为$ {(\begin{array}{ccc} a & b & c\end{array})}^{\rm T} $,同理向量$ \mathbf u $在某个两两垂直的三维坐标系中可以表示为$ {(\begin{array}{ccc} a' & b' & c' \end{array})}^{\rm T} $。但在三维空间$(\textrm{x},\textrm{y},\textrm{z})$中,向量$ \mathbf v $和向量$ \mathbf u $分别被表示为$ {(\begin{array}{ccc} v_x & v_y & v_z\end{array})}^{\rm T} $$ {(\begin{array}{ccc} u_x & u_y & u_z\end{array})}^{\rm T} $ \parinterval 从我们的物理世界看,如果一个物理量在物体的某个位置上只是一个单值,那么它是一个标量,例如密度;如果一个物理量在同一个位置、从多个方向上看,有不同的值,那么这个物理量就是一个张量,比如物理学中常用的应力的描述就是一个典型的张量。举一个简单的例子:$ \mathbf T(\mathbf v,\mathbf u) $是一个三维空间$(\textrm{x},\textrm{y},\textrm{z})$上的2阶张量,其中$ \mathbf v $$ \mathbf u $ 是两个向量,如图\ref{fig:5-26}所示,向量$ \mathbf v $在某个两两垂直的三维坐标系中可以表示为$ {(\begin{array}{ccc} a & b & c\end{array})}^{\rm T} $,同理向量$ \mathbf u $在某个两两垂直的三维坐标系中可以表示为$ {(\begin{array}{ccc} a' & b' & c' \end{array})}^{\rm T} $。但在三维空间$(\textrm{x},\textrm{y},\textrm{z})$中,向量$ \mathbf v $和向量$ \mathbf u $分别被表示为$ {(\begin{array}{ccc} v_x & v_y & v_z\end{array})}^{\rm T} $$ {(\begin{array}{ccc} u_x & u_y & u_z\end{array})}^{\rm T} $
...@@ -1047,6 +1045,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 ...@@ -1047,6 +1045,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
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
\subsubsection{张量的单元操作} \subsubsection{张量的单元操作}
\vspace{0.5em}
\parinterval 对于神经网络中的某层神经元$ \mathbf y=f(\mathbf x\cdot \mathbf w+\mathbf b) $,也包含有其他张量单元操作:1)加法:$ \mathbf s+\mathbf b $,其中张量$ \mathbf s=\mathbf x\cdot \mathbf w $;2)激活函数:$ f(\cdot) $。具体来说: \parinterval 对于神经网络中的某层神经元$ \mathbf y=f(\mathbf x\cdot \mathbf w+\mathbf b) $,也包含有其他张量单元操作:1)加法:$ \mathbf s+\mathbf b $,其中张量$ \mathbf s=\mathbf x\cdot \mathbf w $;2)激活函数:$ f(\cdot) $。具体来说:
...@@ -1062,14 +1061,13 @@ 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 ...@@ -1062,14 +1061,13 @@ 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
\label{fig:5-28} \label{fig:5-28}
\end {figure} \end {figure}
%------------------------------------------- %-------------------------------------------
\vspace{0.5em} \vspace{0.5em}
\item 除了单位加之外,张量之间也可以使用减法操作、乘法操作。此外也可以对张量作激活操作,这里将其称作为函数的{\small\bfnew{向量化}}\index{向量化}(Vectorization)\index{Vectorization}。例如,对向量(1阶张量)作ReLU激活,ReLU激活函数的公式为: \item 除了单位加之外,张量之间也可以使用减法操作、乘法操作。此外也可以对张量作激活操作,这里将其称作为函数的{\small\bfnew{向量化}}\index{向量化}(Vectorization)\index{Vectorization}。例如,对向量(1阶张量)作ReLU激活,ReLU激活函数的公式为:
\begin{eqnarray} \begin{eqnarray}
f(x)=\begin{cases} 0 & x\le 0 \\x & x>0\end{cases} f(x)=\begin{cases} 0 & x\le 0 \\x & x>0\end{cases}
\label{eq:5-26} \label{eq:5-26}
\end{eqnarray} \end{eqnarray}
\vspace{-0.5em}
例如$ {\rm{ReLU}}\left( \begin{pmatrix} 2\\-.3\end{pmatrix}\right)=\begin{pmatrix} 2\\0\end{pmatrix} $ 例如$ {\rm{ReLU}}\left( \begin{pmatrix} 2\\-.3\end{pmatrix}\right)=\begin{pmatrix} 2\\0\end{pmatrix} $
\vspace{0.5em} \vspace{0.5em}
\end{itemize} \end{itemize}
...@@ -1151,7 +1149,7 @@ f(x)=\begin{cases} 0 & x\le 0 \\x & x>0\end{cases} ...@@ -1151,7 +1149,7 @@ f(x)=\begin{cases} 0 & x\le 0 \\x & x>0\end{cases}
\vspace{0.5em} \vspace{0.5em}
\end{itemize} \end{itemize}
\parinterval 定义XTensor的程序示例如图\ref{fig:5-32}所示: \parinterval 定义XTensor的程序示例如图\ref{fig:5-32}(a)所示:
%---------------------------------------------- %----------------------------------------------
\begin{figure}[htp] \begin{figure}[htp]
...@@ -1162,46 +1160,18 @@ f(x)=\begin{cases} 0 & x\le 0 \\x & x>0\end{cases} ...@@ -1162,46 +1160,18 @@ f(x)=\begin{cases} 0 & x\le 0 \\x & x>0\end{cases}
\end{figure} \end{figure}
%------------------------------------------- %-------------------------------------------
\parinterval 除此之外,NiuTensor还提供更简便的张量定义方式,如图\ref{fig:5-33}所示。也可以在GPU上定义张量,如图\ref{fig:5-34}所示。NiuTensor支持张量的各种代数运算,各种单元算子,如$ + $$ - $$ \ast $$ / $、Log(取对数)、Exp(指数运算)、Power(幂方运算)、Absolute(绝对值)等,还有Sigmoid、Softmax等激活函数。图\ref{fig:5-35}是一段对张量进行1阶运算的程序示例。 \parinterval 除此之外,NiuTensor还提供更简便的张量定义方式,如图\ref{fig:5-32}(b)所示。也可以在GPU上定义张量,如图\ref{fig:5-32}(c)所示。NiuTensor支持张量的各种代数运算,各种单元算子,如$ + $$ - $$ \ast $$ / $、Log(取对数)、Exp(指数运算)、Power(幂方运算)、Absolute(绝对值)等,还有Sigmoid、Softmax等激活函数。图\ref{fig:5-35}(a)是一段对张量进行1阶运算的程序示例。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter5/Figures/fig-code-tensor-define-3}
\caption{ 定义张量的简便方式程序示例}
\label{fig:5-33}
\end{figure}
%-------------------------------------------
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter5/Figures/fig-code-tensor-define-GPU}
\caption{ 在GPU上定义张量程序示例}
\label{fig:5-34}
\end{figure}
%-------------------------------------------
%---------------------------------------------- %----------------------------------------------
\begin{figure}[htp] \begin{figure}[htp]
\centering \centering
\input{./Chapter5/Figures/fig-code-tensor-operation} \input{./Chapter5/Figures/fig-code-tensor-operation}
\caption{ 张量进行1阶运算} \caption{ 张量代数运算程序示例}
\label{fig:5-35} \label{fig:5-35}
\end{figure} \end{figure}
%------------------------------------------- %-------------------------------------------
\parinterval 除了上述单元算子外,NiuTensor还支持张量之间的高阶运算,其中最常用的是矩阵乘法,图\ref{fig:5-36}是张量之间进行矩阵乘法的程序示例。表\ref{tab:5-2}展示了一些NiuTensor支持的其他函数操作,除此还有很多其他操作无法在此一一列举,有兴趣可以参考网站上的详细说明。 \parinterval 除了上述单元算子外,NiuTensor还支持张量之间的高阶运算,其中最常用的是矩阵乘法,图\ref{fig:5-35}(b)是张量之间进行矩阵乘法的程序示例。表\ref{tab:5-2}展示了一些NiuTensor支持的其他函数操作,除此还有很多其他操作无法在此一一列举,有兴趣可以参考网站上的详细说明。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter5/Figures/fig-code-tensor-mul}
\caption{张量之间的矩阵乘法}
\label{fig:5-36}
\end{figure}
%-------------------------------------------
%-------------------------------------------------------------------- %--------------------------------------------------------------------
\begin{table}[htp] \begin{table}[htp]
\centering \centering
...@@ -1259,6 +1229,7 @@ y&=&{\rm{Sigmoid}}({\rm{Tanh}}(\mathbf x\cdot \mathbf w^{[1]}+\mathbf b^{[1]})\c ...@@ -1259,6 +1229,7 @@ y&=&{\rm{Sigmoid}}({\rm{Tanh}}(\mathbf x\cdot \mathbf w^{[1]}+\mathbf b^{[1]})\c
\label{eq:5-27} \label{eq:5-27}
\end{eqnarray} \end{eqnarray}
\parinterval 前向计算实现如图\ref{fig:5-38}所示,图中对各张量和其他参数的形状做了详细说明。输入$ \mathbf x=(x_1,x_2,x_3) $是一个$1\times 3$的张量,其三个维度分别对应天空状况、低空气温、水平气压三个方面的数据。输入数据经过隐藏层的线性变换$ \mathbf x\cdot \mathbf w^{[1]}+\mathbf b^{[1]}$和Tanh函数的激活,得到新的张量$ \mathbf a=(a_1,a_2) $,其中$a_1$$a_2$分别对应着从输入数据中提取出的温度和风速两方面特征;神经网络在获取到天气情况的特征$ \mathbf a $后,继续对其进行线性变换$ \mathbf a\cdot \mathbf w^{[2]}+ b^{[2]} $和Sigmoid函数的激活操作,得到神经网络的最终输出$ y $,即神经网络此时预测的穿衣指数。
%---------------------------------------------- %----------------------------------------------
\begin{figure}[htp] \begin{figure}[htp]
\centering \centering
...@@ -1268,8 +1239,6 @@ y&=&{\rm{Sigmoid}}({\rm{Tanh}}(\mathbf x\cdot \mathbf w^{[1]}+\mathbf b^{[1]})\c ...@@ -1268,8 +1239,6 @@ y&=&{\rm{Sigmoid}}({\rm{Tanh}}(\mathbf x\cdot \mathbf w^{[1]}+\mathbf b^{[1]})\c
\end{figure} \end{figure}
%------------------------------------------- %-------------------------------------------
\parinterval 前向计算实现如图\ref{fig:5-38}所示,图中对各张量和其他参数的形状做了详细说明。输入$ \mathbf x=(x_1,x_2,x_3) $是一个$1\times 3$的张量,其三个维度分别对应天空状况、低空气温、水平气压三个方面的数据。输入数据经过隐藏层的线性变换$ \mathbf x\cdot \mathbf w^{[1]}+\mathbf b^{[1]}$和Tanh函数的激活,得到新的张量$ \mathbf a=(a_1,a_2) $,其中$a_1$$a_2$分别对应着从输入数据中提取出的温度和风速两方面特征;神经网络在获取到天气情况的特征$ \mathbf a $后,继续对其进行线性变换$ \mathbf a\cdot \mathbf w^{[2]}+ b^{[2]} $和Sigmoid函数的激活操作,得到神经网络的最终输出$ y $,即神经网络此时预测的穿衣指数。
\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}表示。现在很多深度学习框架都是把神经网络转化为计算图,这样可以把复杂的运算分解为简单的运算。通过对计算图中节点的遍历,可以方便地完成神经网络的计算。比如,可以对图中节点进行拓扑排序(由输入到输出),之后依次访问每个节点,同时完成相应的计算,这也就实现了一个前向计算的过程。构建计算图的方式有很多,比如,动态图、静态图等。在\ref{sec5:para-training}节会进一步对计算图在模型参数训练中的应用进行介绍。
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
...@@ -1278,28 +1247,19 @@ y&=&{\rm{Sigmoid}}({\rm{Tanh}}(\mathbf x\cdot \mathbf w^{[1]}+\mathbf b^{[1]})\c ...@@ -1278,28 +1247,19 @@ y&=&{\rm{Sigmoid}}({\rm{Tanh}}(\mathbf x\cdot \mathbf w^{[1]}+\mathbf b^{[1]})\c
\subsection{神经网络实例} \subsection{神经网络实例}
\parinterval 下面用几个实例来说明搭建神经网络的过程。注意,搭建神经网络的过程本质上就是定义前向计算的过程。首先构造一个单层神经网络。如图\ref{fig:5-39}所示,简单的定义输入、权重和偏置后,定义激活函数为Sigmoid函数,输入$ \mathbf x $经过线性变换和激活函数,得到输出$ \mathbf y $ \parinterval 下面用几个实例来说明搭建神经网络的过程。搭建神经网络的过程本质上就是定义前向计算的过程。首先构造一个单层神经网络。如图\ref{fig:5-39}(a)所示,简单的定义输入、权重和偏置后,定义激活函数为Sigmoid函数,输入$ \mathbf x $经过线性变换和激活函数,得到输出$ \mathbf y $
\parinterval\ref{fig:5-39}(b)是一个构造三层神经网络的程序示例。在第一层中,$ \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] \begin{figure}[htp]
\centering \centering
\input{./Chapter5/Figures/fig-code-niutensor-one} \input{./Chapter5/Figures/fig-code-niutensor-one}
\caption{构建单层神经网络的示例} \caption{构建神经网络的程序示例}
\label{fig:5-39} \label{fig:5-39}
\end{figure} \end{figure}
%------------------------------------------- %-------------------------------------------
\parinterval\ref{fig:5-40}是一个构造三层神经网络的程序示例。在第一层中,$ \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{构建三层神经网络的示例}
\label{fig:5-40}
\end{figure}
%-------------------------------------------
\parinterval 除了简单对若干全连接网络进行堆叠,也可以用张量定义更加复杂的神经网络结构,如循环神经网络和Transformer等结构(见\ref{sec5:nlm}节)。如图\ref{fig:5-41}所示,输入$ \mathbf x_1 $$ \mathbf x_2 $$ \mathbf x_3 $经过一个循环神经网络送给更上层的网络进行处理。关于循环神经网络及其它神经网络结构在本章的后续内容还会有进一步介绍。 \parinterval 除了简单对若干全连接网络进行堆叠,也可以用张量定义更加复杂的神经网络结构,如循环神经网络和Transformer等结构(见\ref{sec5:nlm}节)。如图\ref{fig:5-41}所示,输入$ \mathbf x_1 $$ \mathbf x_2 $$ \mathbf x_3 $经过一个循环神经网络送给更上层的网络进行处理。关于循环神经网络及其它神经网络结构在本章的后续内容还会有进一步介绍。
%---------------------------------------------- %----------------------------------------------
...@@ -1318,8 +1278,9 @@ y&=&{\rm{Sigmoid}}({\rm{Tanh}}(\mathbf x\cdot \mathbf w^{[1]}+\mathbf b^{[1]})\c ...@@ -1318,8 +1278,9 @@ y&=&{\rm{Sigmoid}}({\rm{Tanh}}(\mathbf x\cdot \mathbf w^{[1]}+\mathbf b^{[1]})\c
\sectionnewpage \sectionnewpage
\section{神经网络的参数训练} \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/Labeled Data)\index{Annotated Data/Labeled Data}。这些数据会用于对模型参数的学习,这也对应了统计模型中的参数估计过程。在机器学习中,一般把这种使用有标注数据进行统计模型参数训练的过程称为{\small\sffamily\bfseries{有指导的训练}}\index{有指导的训练}{\small\sffamily\bfseries{有监督的训练}}\index{有监督的训练}(Supervised Training)\index{Supervised Training}。在本章中,如果没有特殊说明,模型训练都是指有监督的训练。那么神经网络内部是怎样利用有标注数据对参数进行训练的呢?\\ \\
\vspace{-2.5em}
\parinterval 为了回答这个问题,可以把模型参数的学习过程看作是一个优化问题,即找到一组参数,使得模型达到某种最优的状态。这个问题又可以被转化为两个新的问题: \parinterval 为了回答这个问题,可以把模型参数的学习过程看作是一个优化问题,即找到一组参数,使得模型达到某种最优的状态。这个问题又可以被转化为两个新的问题:
\begin{itemize} \begin{itemize}
...@@ -1340,8 +1301,6 @@ y&=&{\rm{Sigmoid}}({\rm{Tanh}}(\mathbf x\cdot \mathbf w^{[1]}+\mathbf b^{[1]})\c ...@@ -1340,8 +1301,6 @@ y&=&{\rm{Sigmoid}}({\rm{Tanh}}(\mathbf x\cdot \mathbf w^{[1]}+\mathbf b^{[1]})\c
\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{训练数据集合}}\index{训练数据集合}(Training Data Set)\index{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{训练数据集合}}\index{训练数据集合}(Training Data Set)\index{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{损失函数}}\index{损失函数}(Loss Function)\index{Loss Function}来度量正确答案$ \mathbf {\widetilde y}_i $和神经网络输出$ \mathbf y_i $之间的偏差。而这个损失函数往往充当训练的{\small\sffamily\bfseries{目标函数}}\index{目标函数}(Objective Function)\index{Objective Function},神经网络训练就是通过不断调整神经网络内部的参数而使损失函数最小化。图\ref{fig:5-42}展示了一个绝对值损失函数的实例。
%---------------------------------------------- %----------------------------------------------
\begin{figure}[htp] \begin{figure}[htp]
\centering \centering
...@@ -1351,6 +1310,8 @@ y&=&{\rm{Sigmoid}}({\rm{Tanh}}(\mathbf x\cdot \mathbf w^{[1]}+\mathbf b^{[1]})\c ...@@ -1351,6 +1310,8 @@ y&=&{\rm{Sigmoid}}({\rm{Tanh}}(\mathbf x\cdot \mathbf w^{[1]}+\mathbf b^{[1]})\c
\end{figure} \end{figure}
%------------------------------------------- %-------------------------------------------
\parinterval 通常,可以通过设计{\small\sffamily\bfseries{损失函数}}\index{损失函数}(Loss Function)\index{Loss Function}来度量正确答案$ \mathbf {\widetilde y}_i $和神经网络输出$ \mathbf y_i $之间的偏差。而这个损失函数往往充当训练的{\small\sffamily\bfseries{目标函数}}\index{目标函数}(Objective Function)\index{Objective Function},神经网络训练就是通过不断调整神经网络内部的参数而使损失函数最小化。图\ref{fig:5-42}展示了一个绝对值损失函数的实例。
\parinterval 这里用$ Loss(\mathbf {\widetilde y}_i,\mathbf y_i) $表示网络输出$ \mathbf y_i $相对于答案$ \mathbf {\widetilde y}_i $的损失,简记为$ L $。表\ref{tab:5-3}是几种常见损失函数的定义。需要注意的是,没有一种损失函数可以适用于所有的问题。损失函数的选择取决于许多因素,包括:数据中是否有离群点、模型结构的选择、是否易于找到函数的导数以及预测结果的置信度等。对于相同的神经网络,不同的损失函数会对训练得到的模型产生不同的影响。对于新的问题,如果无法找到已有的、适合于该问题的损失函数,研究人员也可以自定义损失函数。因此设计新的损失函数也是神经网络中有趣的研究方向。 \parinterval 这里用$ Loss(\mathbf {\widetilde y}_i,\mathbf y_i) $表示网络输出$ \mathbf y_i $相对于答案$ \mathbf {\widetilde y}_i $的损失,简记为$ L $。表\ref{tab:5-3}是几种常见损失函数的定义。需要注意的是,没有一种损失函数可以适用于所有的问题。损失函数的选择取决于许多因素,包括:数据中是否有离群点、模型结构的选择、是否易于找到函数的导数以及预测结果的置信度等。对于相同的神经网络,不同的损失函数会对训练得到的模型产生不同的影响。对于新的问题,如果无法找到已有的、适合于该问题的损失函数,研究人员也可以自定义损失函数。因此设计新的损失函数也是神经网络中有趣的研究方向。
%-------------------------------------------------------------------- %--------------------------------------------------------------------
...@@ -1416,14 +1377,14 @@ y&=&{\rm{Sigmoid}}({\rm{Tanh}}(\mathbf x\cdot \mathbf w^{[1]}+\mathbf b^{[1]})\c ...@@ -1416,14 +1377,14 @@ 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 $的设置需要根据任务进行调整。 \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{基于梯度的方法}(Gradient-based Method)\index{Gradient-based Method},属于基于一阶导数的方法。其他类似的方法还有牛顿法、共轭方向法、拟牛顿法等。在具体实现时,公式\ref{eq:5-29}可以有以下不同的形式。\\
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
% %
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
\vspace{0.5em} \vspace{0.5em}
\noindent {\small\sffamily\bfseries{a)批量梯度下降\index{批量梯度下降}(Batch Gradient Descent)\index{Batch Gradient Descent}}} \noindent {\small\sffamily\bfseries{批量梯度下降\index{批量梯度下降}(Batch Gradient Descent)\index{Batch Gradient Descent}}}
\vspace{0.5em} \vspace{0.5em}
\parinterval 批量梯度下降是梯度下降方法中最原始的形式,这种梯度下降方法在每一次迭代时使用所有的样本进行参数更新。参数优化的目标函数是 \parinterval 批量梯度下降是梯度下降方法中最原始的形式,这种梯度下降方法在每一次迭代时使用所有的样本进行参数更新。参数优化的目标函数是
...@@ -1441,7 +1402,7 @@ J(\mathbf w)&=&\frac{1}{n}\sum_{i=1}^{n}{L(\mathbf x_i,\mathbf {\widetilde y}_i; ...@@ -1441,7 +1402,7 @@ J(\mathbf w)&=&\frac{1}{n}\sum_{i=1}^{n}{L(\mathbf x_i,\mathbf {\widetilde y}_i;
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
\vspace{0.5em} \vspace{0.5em}
\noindent {\small\sffamily\bfseries{b)随机梯度下降\index{随机梯度下降}(Stochastic Gradient Descent)\index{Stochastic Gradient Descent}}} \noindent {\small\sffamily\bfseries{随机梯度下降\index{随机梯度下降}(Stochastic Gradient Descent)\index{Stochastic Gradient Descent}}}
\vspace{0.5em} \vspace{0.5em}
\parinterval 随机梯度下降(简称SGD)不同于批量梯度下降,每次迭代只使用一个样本对参数进行更新。SGD的目标函数是 \parinterval 随机梯度下降(简称SGD)不同于批量梯度下降,每次迭代只使用一个样本对参数进行更新。SGD的目标函数是
...@@ -1459,7 +1420,7 @@ J(\mathbf w)&=&L(\mathbf x_i,\mathbf {\widetilde y}_i;\mathbf w) ...@@ -1459,7 +1420,7 @@ J(\mathbf w)&=&L(\mathbf x_i,\mathbf {\widetilde y}_i;\mathbf w)
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
\vspace{0.5em} \vspace{0.5em}
\noindent {\small\sffamily\bfseries{c)小批量梯度下降\index{小批量梯度下降}(Mini-Batch Gradient Descent)\index{Mini-Batch Gradient Descent}}} \noindent {\small\sffamily\bfseries{小批量梯度下降\index{小批量梯度下降}(Mini-Batch Gradient Descent)\index{Mini-Batch Gradient Descent}}}
\vspace{0.5em} \vspace{0.5em}
\parinterval 为了综合批量梯度下降和随机梯度下降的优缺点,在实际应用中一般采用这两个算法的折中\ \dash \ 小批量梯度下降。其思想是:每次迭代计算一小部分训练数据的损失函数,并对参数进行更新。这一小部分数据被称为一个批次(mini-batch或者batch)。小批量梯度下降的参数优化的目标函数如下: \parinterval 为了综合批量梯度下降和随机梯度下降的优缺点,在实际应用中一般采用这两个算法的折中\ \dash \ 小批量梯度下降。其思想是:每次迭代计算一小部分训练数据的损失函数,并对参数进行更新。这一小部分数据被称为一个批次(mini-batch或者batch)。小批量梯度下降的参数优化的目标函数如下:
...@@ -1483,7 +1444,7 @@ J(\mathbf w)&=&\frac{1}{m}\sum_{i=j}^{j+m-1}{L(\mathbf x_i,\mathbf {\widetilde y ...@@ -1483,7 +1444,7 @@ J(\mathbf w)&=&\frac{1}{m}\sum_{i=j}^{j+m-1}{L(\mathbf x_i,\mathbf {\widetilde y
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
\vspace{0.5em} \vspace{0.5em}
\noindent {\small\sffamily\bfseries{a) 数值微分\index{数值微分}(Numerical Differentiation)\index{Numerical Differentiation}}} \noindent {\small\sffamily\bfseries{数值微分\index{数值微分}(Numerical Differentiation)\index{Numerical Differentiation}}}
\vspace{0.5em} \vspace{0.5em}
\parinterval 数学上,梯度的求解其实就是求函数偏导的问题。导数是用极限来定义的,如下: \parinterval 数学上,梯度的求解其实就是求函数偏导的问题。导数是用极限来定义的,如下:
...@@ -1500,14 +1461,13 @@ J(\mathbf w)&=&\frac{1}{m}\sum_{i=j}^{j+m-1}{L(\mathbf x_i,\mathbf {\widetilde y ...@@ -1500,14 +1461,13 @@ J(\mathbf w)&=&\frac{1}{m}\sum_{i=j}^{j+m-1}{L(\mathbf x_i,\mathbf {\widetilde y
\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{ 符号微分\index{符号微分}(Symbolic Differentiation)\index{Symbolic Differentiation}}}
\noindent {\small\sffamily\bfseries{b) 符号微分\index{符号微分}(Symbolic Differentiation)\index{Symbolic Differentiation}}}
\vspace{0.5em} \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})} $$ \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 顾名思义,符号微分就是通过建立符号表达式求解微分的方法:借助符号表达式和求导公式,推导出目标函数关于自变量的微分表达式,最后再带入具体数值得到微分结果。例如,对于表达式$ 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})}$
...@@ -1543,7 +1503,7 @@ $+2x^2+x+1)$ & \ \ $(x^4+2x^3+2x^2+x+1)$ & $+6x+1$ \\ ...@@ -1543,7 +1503,7 @@ $+2x^2+x+1)$ & \ \ $(x^4+2x^3+2x^2+x+1)$ & $+6x+1$ \\
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
\vspace{0.5em} \vspace{0.5em}
\noindent {\small\sffamily\bfseries{c) 自动微分\index{自动微分}(Automatic Differentiation)\index{Automatic Differentiation}}} \noindent {\small\sffamily\bfseries{自动微分\index{自动微分}(Automatic Differentiation)\index{Automatic Differentiation}}}
\vspace{0.5em} \vspace{0.5em}
\parinterval 自动微分是一种介于数值微分和符号微分的方法:将符号微分应用于最基本的算子,如常数、幂函数、指数函数、对数函数、三角函数等,然后代入数值,保留中间结果,最后再应用于整个函数。通过这种方式,将复杂的微分变成了简单的步骤,这些步骤完全自动化,而且容易进行存储和计算。 \parinterval 自动微分是一种介于数值微分和符号微分的方法:将符号微分应用于最基本的算子,如常数、幂函数、指数函数、对数函数、三角函数等,然后代入数值,保留中间结果,最后再应用于整个函数。通过这种方式,将复杂的微分变成了简单的步骤,这些步骤完全自动化,而且容易进行存储和计算。
...@@ -1561,7 +1521,7 @@ $+2x^2+x+1)$ & \ \ $(x^4+2x^3+2x^2+x+1)$ & $+6x+1$ \\ ...@@ -1561,7 +1521,7 @@ $+2x^2+x+1)$ & \ \ $(x^4+2x^3+2x^2+x+1)$ & $+6x+1$ \\
\end{figure} \end{figure}
%------------------------------------------- %-------------------------------------------
\parinterval 前向计算实际上就是网络构建的过程,所有的计算都会被转化为计算图上的节点,前向计算和反向计算都依赖计算图来完成。通常,构建计算图有以下两种实现方式: \parinterval 前向计算实际上就是网络构建的过程,所有的计算都会被转化为计算图上的节点,前向计算和反向计算都依赖计算图来完成。构建计算图有以下两种实现方式:
\begin{itemize} \begin{itemize}
\vspace{0.5em} \vspace{0.5em}
...@@ -1600,12 +1560,21 @@ $+2x^2+x+1)$ & \ \ $(x^4+2x^3+2x^2+x+1)$ & $+6x+1$ \\ ...@@ -1600,12 +1560,21 @@ $+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 种方法。 \parinterval 针对以上问题,很多学者尝试对梯度下降方法做出改进,如Momentum, Adagrad, Adadelta, RMSprop, Adam, AdaMax, Nadam, AMSGrad等等,在这里将介绍Momentum、AdaGrad、RMSprop、Adam这4 种方法。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter5/Figures/fig-sawtooth}
\caption{Momentum梯度下降 vs 普通梯度下降}
\label{fig:5-46}
\end{figure}
%-------------------------------------------
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
% %
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
\vspace{0.5em} \vspace{0.5em}
\noindent {\small\sffamily\bfseries{a) Momentum \index{Momentum}}} \noindent {\small\sffamily\bfseries{Momentum \index{Momentum}}}
\vspace{0.5em} \vspace{0.5em}
\parinterval Momentum梯度下降算法的参数更新公式如下\footnote{在梯度下降算法的几种改进方法的公式中,其更新对象是某个具体参数而非参数矩阵,因此不再使用加粗样式} \parinterval Momentum梯度下降算法的参数更新公式如下\footnote{在梯度下降算法的几种改进方法的公式中,其更新对象是某个具体参数而非参数矩阵,因此不再使用加粗样式}
...@@ -1619,21 +1588,12 @@ w_{t+1}&=&w_t-\alpha v_t ...@@ -1619,21 +1588,12 @@ w_{t+1}&=&w_t-\alpha v_t
\parinterval 这里的``梯度''不再只是现在的损失函数的梯度,而是之前的梯度的加权和。在原始的梯度下降算法中,如果在某个参数状态下,梯度方向变化特别大,甚至与上一次参数更新中梯度方向成90度夹角,下一次参数更新中梯度方向可能又是一次90度的改变,这时参数优化路径将会成``锯齿''状(如图\ref{fig:5-46}所示),优化效率极慢。而Momentum梯度下降算法不会让梯度发生90度的变化,而是让梯度慢慢发生改变:如果当前的梯度方向与之前的梯度方向相同,在原梯度方向上加速更新参数;如果当前的梯度方向与之前的梯度方向相反,并不会产生一个急转弯,而是尽量把优化路径平滑地进行改变。这样做的优点也非常明显,一方面杜绝了``锯齿''状优化路径的出现,另一方面将优化幅度变得更加平滑,不会导致频频跳过最优点。 \parinterval 这里的``梯度''不再只是现在的损失函数的梯度,而是之前的梯度的加权和。在原始的梯度下降算法中,如果在某个参数状态下,梯度方向变化特别大,甚至与上一次参数更新中梯度方向成90度夹角,下一次参数更新中梯度方向可能又是一次90度的改变,这时参数优化路径将会成``锯齿''状(如图\ref{fig:5-46}所示),优化效率极慢。而Momentum梯度下降算法不会让梯度发生90度的变化,而是让梯度慢慢发生改变:如果当前的梯度方向与之前的梯度方向相同,在原梯度方向上加速更新参数;如果当前的梯度方向与之前的梯度方向相反,并不会产生一个急转弯,而是尽量把优化路径平滑地进行改变。这样做的优点也非常明显,一方面杜绝了``锯齿''状优化路径的出现,另一方面将优化幅度变得更加平滑,不会导致频频跳过最优点。
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter5/Figures/fig-sawtooth}
\caption{Momentum梯度下降 vs 普通梯度下降}
\label{fig:5-46}
\end{figure}
%-------------------------------------------
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
% %
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
\vspace{0.5em} \vspace{0.5em}
\noindent {\small\sffamily\bfseries{b) AdaGrad \index{AdaGrad}}} \noindent {\small\sffamily\bfseries{AdaGrad \index{AdaGrad}}}
\vspace{0.5em} \vspace{0.5em}
\parinterval 在神经网络的学习中,学习率的设置很重要。学习率过小, 会导致学习花费过多时间;反过来,学习率过大,则会导致学习发散,甚至造成模型的``跑偏''。在深度学习实现过程中,有一种被称为学习率{\small\bfnew{衰减}}\index{衰减}(Decay)\index{Decay}的方法,即最初设置较大的学习率,随着学习的进行,使学习率逐渐减小,这种方法相当于将``全体''参数的学习率值一起降低。AdaGrad梯度下降算法进一步发展了这个思想\cite{duchi2011adaptive} \parinterval 在神经网络的学习中,学习率的设置很重要。学习率过小, 会导致学习花费过多时间;反过来,学习率过大,则会导致学习发散,甚至造成模型的``跑偏''。在深度学习实现过程中,有一种被称为学习率{\small\bfnew{衰减}}\index{衰减}(Decay)\index{Decay}的方法,即最初设置较大的学习率,随着学习的进行,使学习率逐渐减小,这种方法相当于将``全体''参数的学习率值一起降低。AdaGrad梯度下降算法进一步发展了这个思想\cite{duchi2011adaptive}
...@@ -1652,7 +1612,7 @@ w_{t+1}&=&w_t-\eta \frac{1}{\sqrt{z_t}}\cdot \frac{\partial L}{\partial w_t} ...@@ -1652,7 +1612,7 @@ w_{t+1}&=&w_t-\eta \frac{1}{\sqrt{z_t}}\cdot \frac{\partial L}{\partial w_t}
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
\vspace{0.5em} \vspace{0.5em}
\noindent {\small\sffamily\bfseries{c) RMSprop \index{RMSprop}}} \noindent {\small\sffamily\bfseries{RMSprop \index{RMSprop}}}
\vspace{0.5em} \vspace{0.5em}
\parinterval RMSprop算法是一种自适应学习率的方法\cite{tieleman2012rmsprop},它是对AdaGrad算法的一种改进,可以避免AdaGrad算法中学习率不断单调下降以至于过早衰减的缺点。 \parinterval RMSprop算法是一种自适应学习率的方法\cite{tieleman2012rmsprop},它是对AdaGrad算法的一种改进,可以避免AdaGrad算法中学习率不断单调下降以至于过早衰减的缺点。
...@@ -1673,7 +1633,7 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}}\cdot \frac{\partial L}{\partial w ...@@ -1673,7 +1633,7 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}}\cdot \frac{\partial L}{\partial w
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
\vspace{0.5em} \vspace{0.5em}
\noindent {\small\sffamily\bfseries{d) Adam \index{Adam} }} \noindent {\small\sffamily\bfseries{Adam \index{Adam} }}
\vspace{0.5em} \vspace{0.5em}
\parinterval Adam梯度下降算法是在RMSProp算法的基础上进行改进的,可以将其看成是带有动量项的RMSProp算法\cite{kingma2014adam}。该算法在自然语言处理领域非常流行。Adam 算法的参数更新公式如下, \parinterval Adam梯度下降算法是在RMSProp算法的基础上进行改进的,可以将其看成是带有动量项的RMSProp算法\cite{kingma2014adam}。该算法在自然语言处理领域非常流行。Adam 算法的参数更新公式如下,
...@@ -1776,7 +1736,7 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t ...@@ -1776,7 +1736,7 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\parinterval 网络训练过程中,如果参数的初始值过大,而且每层网络的梯度都大于1,反向传播过程中,各层梯度的偏导数都会比较大,会导致梯度指数级地增长直至超出浮点数表示的范围,这就产生了梯度爆炸现象。如果发生这种情况,模型中离输入近的部分比离输入远的部分参数更新得更快,使网络变得非常不稳定。在极端情况下,模型的参数值变得非常大,甚至于溢出。针对梯度爆炸的问题,常用的解决办法为{\small\sffamily\bfseries{梯度裁剪}}\index{梯度裁剪}(Gradient Clipping)\index{Gradient Clipping} \parinterval 网络训练过程中,如果参数的初始值过大,而且每层网络的梯度都大于1,反向传播过程中,各层梯度的偏导数都会比较大,会导致梯度指数级地增长直至超出浮点数表示的范围,这就产生了梯度爆炸现象。如果发生这种情况,模型中离输入近的部分比离输入远的部分参数更新得更快,使网络变得非常不稳定。在极端情况下,模型的参数值变得非常大,甚至于溢出。针对梯度爆炸的问题,常用的解决办法为{\small\sffamily\bfseries{梯度裁剪}}\index{梯度裁剪}(Gradient Clipping)\index{Gradient Clipping}
\parinterval 梯度裁剪的思想是设置一个梯度剪切阈值。在更新梯度的时候,如果梯度超过这个阈值,就将其强制限制在这个范围之内。假设梯度为$ \mathbf g $,梯度剪切阈值为$ \theta $,梯度裁剪的公式为 \parinterval 梯度裁剪的思想是设置一个梯度剪切阈值。在更新梯度的时候,如果梯度超过这个阈值,就将其强制限制在这个范围之内。假设梯度为$ \mathbf g $,梯度剪切阈值为$ \theta $,梯\\ \\度裁剪的公式为
\begin{eqnarray} \begin{eqnarray}
\mathbf g&=&{\rm{min}}(\frac{\theta}{\Vert \mathbf g\Vert},1)\mathbf g \mathbf g&=&{\rm{min}}(\frac{\theta}{\Vert \mathbf g\Vert},1)\mathbf g
\label{eq:5-43} \label{eq:5-43}
...@@ -2045,13 +2005,9 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t ...@@ -2045,13 +2005,9 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\noindent 其中$ \frac{\partial L}{\partial \mathbf h^k} $表示损失函数$ L $相对该隐藏层输出$ \mathbf h^k $的梯度。进一步,由于$ \mathbf s^k =\mathbf h^{k-1}\mathbf w^k$,可以得到 \noindent 其中$ \frac{\partial L}{\partial \mathbf h^k} $表示损失函数$ L $相对该隐藏层输出$ \mathbf h^k $的梯度。进一步,由于$ \mathbf s^k =\mathbf h^{k-1}\mathbf w^k$,可以得到
\begin{eqnarray} \begin{eqnarray}
\frac{\partial L}{\partial \mathbf w^k}&=&{\left[\mathbf h^{k-1}\right]}^{\rm T}\cdot \frac{\partial L}{\partial \mathbf s^k}\label{eq:5-55} \frac{\partial L}{\partial \mathbf w^k}&=&{\left[\mathbf h^{k-1}\right]}^{\rm T}\cdot \frac{\partial L}{\partial \mathbf s^k}\label{eq:5-55}\\ \nonumber\\
\label{eq:5-56}
\end{eqnarray}
\begin{eqnarray}
\frac{\partial L}{\partial \mathbf h^{k-1}}&= &\frac{\partial L}{\partial \mathbf s^k} \cdot {\left[\mathbf w^k\right]}^{\rm T} \frac{\partial L}{\partial \mathbf h^{k-1}}&= &\frac{\partial L}{\partial \mathbf s^k} \cdot {\left[\mathbf w^k\right]}^{\rm T}
\label{eq:5-56-2} \label{eq:5-56}
\end{eqnarray} \end{eqnarray}
\parinterval $ \frac{\partial L}{\partial \mathbf h^{k-1}} $需要继续向第$ k-1 $隐藏层传递。$ \frac{\partial L}{\partial \mathbf w^k} $会作为参数的梯度用于参数更新。图\ref{fig:5-57}展示了隐藏层反向传播的计算过程。 \parinterval $ \frac{\partial L}{\partial \mathbf h^{k-1}} $需要继续向第$ k-1 $隐藏层传递。$ \frac{\partial L}{\partial \mathbf w^k} $会作为参数的梯度用于参数更新。图\ref{fig:5-57}展示了隐藏层反向传播的计算过程。
...@@ -2075,8 +2031,6 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t ...@@ -2075,8 +2031,6 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\parinterval 在了解了反向传播的原理之后,实现反向传播就变得非常容易了。实际上,现在主流的深度学习框架都支持自动微分。为了进一步说明反向传播的过程,这里使用NiuTensor工具构建两个简单的实例,并分别尝试手动编写反向传播代码和使用NiuTensor自带的自动微分模块。 \parinterval 在了解了反向传播的原理之后,实现反向传播就变得非常容易了。实际上,现在主流的深度学习框架都支持自动微分。为了进一步说明反向传播的过程,这里使用NiuTensor工具构建两个简单的实例,并分别尝试手动编写反向传播代码和使用NiuTensor自带的自动微分模块。
\parinterval\ref{fig:5-58}展示了一个简单的神经网络的反向传播程序示例。这种反向传播的实现方式正是上一节内容的代码实现:按层实现自动微分并将梯度向前一层传播。
%---------------------------------------------- %----------------------------------------------
\begin{figure}[htp] \begin{figure}[htp]
\centering \centering
...@@ -2086,8 +2040,7 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t ...@@ -2086,8 +2040,7 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\end{figure} \end{figure}
%------------------------------------------- %-------------------------------------------
\parinterval 此外,NiuTensor还提供了一种更简单的一步完成神经网络反向传播的实现方式。如图\ref{fig:5-59}所示,在完成神经网络的搭建后,无论前向计算过程是怎样的,直接利用Backward 函数就可以实现整个神经网络的反向传播,系统开发人员可以完全不用关心其求解过程。 \parinterval\ref{fig:5-58}展示了一个简单的神经网络的反向传播程序示例。这种反向传播的实现方式正是上一节内容的代码实现:按层实现自动微分并将梯度向前一层传播。
%---------------------------------------------- %----------------------------------------------
\begin{figure}[htp] \begin{figure}[htp]
\centering \centering
...@@ -2097,6 +2050,8 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t ...@@ -2097,6 +2050,8 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\end{figure} \end{figure}
%------------------------------------------- %-------------------------------------------
\parinterval 此外,NiuTensor还提供了一种更简单的一步完成神经网络反向传播的实现方式。如图\ref{fig:5-59}所示,在完成神经网络的搭建后,无论前向计算过程是怎样的,直接利用Backward 函数就可以实现整个神经网络的反向传播,系统开发人员可以完全不用关心其求解过程。
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
% NEW SECTION % NEW SECTION
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
...@@ -2140,6 +2095,7 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t ...@@ -2140,6 +2095,7 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\label{eq:5-59} \label{eq:5-59}
\end{eqnarray} \end{eqnarray}
\vspace{-0.4em}
\parinterval $ g(w_1\dots w_m) $实际上是一个多层神经网络。与$n$-gram语言模型不同的是$ g(w_1\dots \\ w_m) $并不包含对$ w_1\dots w_m $的任何假设,比如,在神经网络模型中,单词不再是离散的符号,而是连续空间上的点。这样两个单词之间也不再是简单的非0即1的关系,而是具有可计算的距离。此外,由于没有对$ w_1\dots w_m $进行任何结构性的假设,神经语言模型对问题进行端到端学习。通过设计不同的神经网络$ g(\cdot)$,可以从不同的角度``定义''序列的表示问题。当然,这么说可能还有一些抽象,下面就一起看看神经语言模型究竟是什么样子的。 \parinterval $ g(w_1\dots w_m) $实际上是一个多层神经网络。与$n$-gram语言模型不同的是$ g(w_1\dots \\ w_m) $并不包含对$ w_1\dots w_m $的任何假设,比如,在神经网络模型中,单词不再是离散的符号,而是连续空间上的点。这样两个单词之间也不再是简单的非0即1的关系,而是具有可计算的距离。此外,由于没有对$ w_1\dots w_m $进行任何结构性的假设,神经语言模型对问题进行端到端学习。通过设计不同的神经网络$ g(\cdot)$,可以从不同的角度``定义''序列的表示问题。当然,这么说可能还有一些抽象,下面就一起看看神经语言模型究竟是什么样子的。
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
...@@ -2150,6 +2106,15 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t ...@@ -2150,6 +2106,15 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\parinterval 最具代表性的神经语言模型是Bengio等人提出的{\small\sffamily\bfseries{前馈神经网络语言模型}}\index{前馈神经网络语言模型}(Feed-forward Neural Network Language Model\index{Feed-forward Neural Network Language Model},简称FNNLM)。这种语言模型的目标是用神经网络计算$ {\rm P}(w_m|w_{m-n+1}\dots w_{m-1}) $,之后将多个$n$-gram的概率相乘得到整个序列的概率\cite{bengio2003neural} \parinterval 最具代表性的神经语言模型是Bengio等人提出的{\small\sffamily\bfseries{前馈神经网络语言模型}}\index{前馈神经网络语言模型}(Feed-forward Neural Network Language Model\index{Feed-forward Neural Network Language Model},简称FNNLM)。这种语言模型的目标是用神经网络计算$ {\rm P}(w_m|w_{m-n+1}\dots w_{m-1}) $,之后将多个$n$-gram的概率相乘得到整个序列的概率\cite{bengio2003neural}
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter5/Figures/fig-4-gram}
\caption{4-gram前馈神经网络语言架构}
\label{fig:5-60}
\end{figure}
%-------------------------------------------
\parinterval 为了有一个直观的认识,这里以4-gram的FNNLM语言模型为例,即根据前三个单词$ w_{i-3} $$ w_{i-2} $$ w_{i-1} $预测当前单词$ w_i $的概率。如图\ref{fig:5-60}所示,$ w_{i-3} $$ w_{i-2} $$ w_{i-1} $为该语言模型的输入(绿色方框),输入为每个词的One-hot向量表示(维度大小与词表大小一致),每个One-hot向量仅一维为1,其余为0,比如:$ (0,0,1,\dots,0) $表示词表中第三个单词。之后把One-hot向量乘以一个矩阵$ \mathbf C $得到单词的分布式表示(紫色方框)。令$ w_i $为第$ i $个词的One-hot表示,$ \mathbf e_i $为第$ i $个词的分布式表示,有: \parinterval 为了有一个直观的认识,这里以4-gram的FNNLM语言模型为例,即根据前三个单词$ w_{i-3} $$ w_{i-2} $$ w_{i-1} $预测当前单词$ w_i $的概率。如图\ref{fig:5-60}所示,$ w_{i-3} $$ w_{i-2} $$ w_{i-1} $为该语言模型的输入(绿色方框),输入为每个词的One-hot向量表示(维度大小与词表大小一致),每个One-hot向量仅一维为1,其余为0,比如:$ (0,0,1,\dots,0) $表示词表中第三个单词。之后把One-hot向量乘以一个矩阵$ \mathbf C $得到单词的分布式表示(紫色方框)。令$ w_i $为第$ i $个词的One-hot表示,$ \mathbf e_i $为第$ i $个词的分布式表示,有:
\begin{eqnarray} \begin{eqnarray}
\mathbf e_i&=&w_i\mathbf C \mathbf e_i&=&w_i\mathbf C
...@@ -2170,9 +2135,9 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t ...@@ -2170,9 +2135,9 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
%---------------------------------------------- %----------------------------------------------
\begin{figure}[htp] \begin{figure}[htp]
\centering \centering
\input{./Chapter5/Figures/fig-4-gram} \input{./Chapter5/Figures/fig-code-FNNLM}
\caption{4-gram前馈神经网络语言架构} \caption{FNNLM模型代码示例(NiuTensor)}
\label{fig:5-60} \label{fig:5-61}
\end{figure} \end{figure}
%------------------------------------------- %-------------------------------------------
...@@ -2190,15 +2155,6 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t ...@@ -2190,15 +2155,6 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\label{} \label{}
\end{eqnarray} \end{eqnarray}
%----------------------------------------------
\begin{figure}[htp]
\centering
\input{./Chapter5/Figures/fig-code-FNNLM}
\caption{FNNLM模型代码示例(NiuTensor)}
\label{fig:5-61}
\end{figure}
%-------------------------------------------
\parinterval 虽然FNNLM模型形式简单,却为处理自然语言提供了一个全新的视角。首先,该模型重新定义了``词是什么''\ \dash \ 它并非词典的一项,而是可以用一个连续实数向量进行表示的可计算的``量''。此外,由于$n$-gram不再是离散的符号序列,模型不需要记录$n$-gram,所以很好的缓解了上面所提到的数据稀疏问题,模型体积也大大减小。 \parinterval 虽然FNNLM模型形式简单,却为处理自然语言提供了一个全新的视角。首先,该模型重新定义了``词是什么''\ \dash \ 它并非词典的一项,而是可以用一个连续实数向量进行表示的可计算的``量''。此外,由于$n$-gram不再是离散的符号序列,模型不需要记录$n$-gram,所以很好的缓解了上面所提到的数据稀疏问题,模型体积也大大减小。
\parinterval 当然,FNNLM模型也引发后人的许多思考,比如:神经网络每一层都学到了什么?是词法、句法,还是一些其他知识?如何理解词的分布式表示?等等。在随后的内容中也会看到,随着近几年深度学习和自然语言处理的发展,部分问题已经得到了很好的解答,但是仍有许多问题需要进一步探索。 \parinterval 当然,FNNLM模型也引发后人的许多思考,比如:神经网络每一层都学到了什么?是词法、句法,还是一些其他知识?如何理解词的分布式表示?等等。在随后的内容中也会看到,随着近几年深度学习和自然语言处理的发展,部分问题已经得到了很好的解答,但是仍有许多问题需要进一步探索。
...@@ -2261,7 +2217,7 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t ...@@ -2261,7 +2217,7 @@ w_{t+1}&=&w_t-\frac{\eta}{\sqrt{z_t+\epsilon}} v_t
\end{figure} \end{figure}
%------------------------------------------- %-------------------------------------------
\parinterval 在传统的语言模型中,给定一个单词$ w_i $,其他单词对它的影响并没有被显性地建模。而在基于注意力机制的语言模型中,当前需要预测的单词会更加关注与该位置联系较大的单词。具体来说,注意力机制会计算位置$ i $与其他任意位置之间的相关度,称为{\small\sffamily\bfseries{注意力权重}}\index{注意力权重}(Attention Weight)\index{Attention Weight},通过这个权重可以更多地使用与$ w_i $关联紧密的位置的信息。举个简单的例子,在``我\ 喜欢\ 学习\ 数学''这个句子中,需要预测``数学''这个词,通过注意力机制很可能知道``数学''与``学习''的联系更紧密,所以在预测过程中``学习''所占的权重会更大,预测结果会更加精确。 \parinterval 在传统的语言模型中,给定一个单词$ w_i $,其他单词对它的影响并没有被显性地建模。而在基于注意力机制的语言模型中,当前需要预测的单词会更加关注与该位置联系较大的单词。具体来说,注意力机制会计算位置$ i $与其他任意位置之间的相关度,称为{\small\sffamily\bfseries{注意力权重}}\index{注意力权重}(Attention Weight)\index{Attention Weight},通过这个权重可以更多地使用与$ w_i $关联紧密的位置的信息。举个简单的例子,在``我\ 喜欢\ 学习\ 数学''这个句子中,需要预测``数学''这个词,通过注意力机制很可能知道``数学''与``学习''的联系更紧密,所以在预测过程中``学习''所占的权重会更大,预测结果会更加精确。\\ \\ \\
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
% NEW SUBSUB-SECTION % NEW SUBSUB-SECTION
...@@ -2443,7 +2399,7 @@ Jobs was the CEO of {\red{\underline{apple}}}. ...@@ -2443,7 +2399,7 @@ Jobs was the CEO of {\red{\underline{apple}}}.
\label{fig:5-71} \label{fig:5-71}
\end{figure} \end{figure}
%------------------------------------------- %-------------------------------------------
\vspace{-0.5em}
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
% NEW SUBSUB-SECTION % NEW SUBSUB-SECTION
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
...@@ -2472,6 +2428,7 @@ Jobs was the CEO of {\red{\underline{apple}}}. ...@@ -2472,6 +2428,7 @@ Jobs was the CEO of {\red{\underline{apple}}}.
\label{fig:5-72} \label{fig:5-72}
\end{figure} \end{figure}
%------------------------------------------- %-------------------------------------------
\vspace{-0.5em}
%---------------------------------------------------------------------------------------- %----------------------------------------------------------------------------------------
% NEW SUUBSUB-SECTION % NEW SUUBSUB-SECTION
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论