diff --git a/Makefile b/Makefile index 3acb1e4..aacec0b 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ all: graphics $(TARGET) $(TARGET): $(SOURCES) $(IMAGEDEPS) $(TEX) $(basename $@) - $(MKIDX) $(basename $@) + # $(MKIDX) $(basename $@) $(MKGLS) $(basename $@) $(TEX) $(basename $@) $(TEX) $(basename $@) diff --git a/README.md b/README.md index b3f3cac..f851128 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ $ ./bootstrap.sh This is a simple shell script to download the following free fonts into `fonts/` folder: - [Google Noto Sans CJK SC](https://noto-website-2.storage.googleapis.com/pkgs/NotoSansCJKsc-hinted.zip) -- [Google Roboto](https://github.com/google/roboto/releases/download/v2.134/roboto-unhinted.zip) +- [Google Noto Serif CJK SC](https://noto-website.storage.googleapis.com/pkgs/NotoSerifCJKsc-hinted.zip) - [Adobe Source Serif Pro](https://github.com/adobe-fonts/source-serif-pro/archive/1.017R.zip) - [Adobe Source Code Pro](https://github.com/adobe-fonts/source-code-pro/archive/2.030R-ro/1.050R-it.zip) @@ -62,24 +62,13 @@ fonts/ ├── NotoSansCJKsc-Thin.otf ├── NotoSansMonoCJKsc-Bold.otf ├── NotoSansMonoCJKsc-Regular.otf -├── Roboto-Black.ttf -├── Roboto-BlackItalic.ttf -├── Roboto-Bold.ttf -├── Roboto-BoldItalic.ttf -├── Roboto-Italic.ttf -├── Roboto-Light.ttf -├── Roboto-LightItalic.ttf -├── Roboto-Medium.ttf -├── Roboto-MediumItalic.ttf -├── Roboto-Regular.ttf -├── Roboto-Thin.ttf -├── Roboto-ThinItalic.ttf -├── RobotoCondensed-Bold.ttf -├── RobotoCondensed-BoldItalic.ttf -├── RobotoCondensed-Italic.ttf -├── RobotoCondensed-Light.ttf -├── RobotoCondensed-LightItalic.ttf -├── RobotoCondensed-Regular.ttf +├── NotoSerifCJKsc-Black.otf +├── NotoSerifCJKsc-Bold.otf +├── NotoSerifCJKsc-DemiLight.otf +├── NotoSerifCJKsc-Light.otf +├── NotoSerifCJKsc-Medium.otf +├── NotoSerifCJKsc-Regular.otf +├── NotoSerifCJKsc-Thin.otf ├── SourceCodePro-Black.otf ├── SourceCodePro-BlackIt.otf ├── SourceCodePro-Bold.otf @@ -115,3 +104,4 @@ $ make to typeset and generate the PDF document. Or use your favorite GUI TeX application, but choose `XeLaTeX` as the typeset engine to support the chinese fonts. + diff --git a/bootstrap.sh b/bootstrap.sh index 09f9325..85ae112 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -1,8 +1,8 @@ #!/bin/sh NOTO_SANS_CJKSC_URL="https://noto-website-2.storage.googleapis.com/pkgs/NotoSansCJKsc-hinted.zip" -ROBOTO_URL="https://github.com/google/roboto/releases/download/v2.134/roboto-unhinted.zip" -SOURCE_SERIF_PRO_URL="https://github.com/adobe-fonts/source-serif-pro/archive/1.017R.zip" +NOTO_SERIF_CJKSC_URL="https://noto-website.storage.googleapis.com/pkgs/NotoSerifCJKsc-hinted.zip" +SOURCE_SERIF_PRO_URL="https://github.com/adobe-fonts/source-serif-pro/archive/2.000R.zip" SOURCE_CODE_PRO_URL="https://github.com/adobe-fonts/source-code-pro/archive/2.030R-ro/1.050R-it.zip" SYSTEM=`uname -s` @@ -23,18 +23,19 @@ trap "{ cd -; rm -rf $TMPDIR; exit 255; }" INT cd $TMPDIR # switch to temp dir echo "Change to ${TMPDIR}" -# Get NotoSans +# Get Noto Sans filename=$(basename ${NOTO_SANS_CJKSC_URL}) wget ${NOTO_SANS_CJKSC_URL} unzip $filename cp -f *.otf ${FONTDIR} +rm -rf $TMPDIR/* -# Get Roboto -filename=$(basename ${ROBOTO_URL}) -folder=${filename%.zip} -wget ${ROBOTO_URL} +# Get Noto Serif +filename=$(basename ${NOTO_SERIF_CJKSC_URL}) +wget ${NOTO_SERIF_CJKSC_URL} unzip $filename -cp -f ${folder}/*.ttf ${FONTDIR} +cp -f *.otf ${FONTDIR} +rm -rf $TMPDIR/* # Get Source Serif Pro filename=$(basename ${SOURCE_SERIF_PRO_URL}) @@ -42,6 +43,7 @@ folder="source-serif-pro-${filename%.zip}" wget ${SOURCE_SERIF_PRO_URL} unzip $filename cp -f ${folder}/OTF/*.otf ${FONTDIR} +rm -rf $TMPDIR/* # Get Source Code Pro filename=$(basename ${SOURCE_CODE_PRO_URL}) diff --git a/chap1.tex b/chap1.tex index decda82..21c092c 100644 --- a/chap1.tex +++ b/chap1.tex @@ -47,7 +47,7 @@ \chapter{使用神经网络识别手写数字} 当然,如果仅仅为了编写一个计算机程序来识别手写数字,本章的内容可以简短很多!但前 进的道路上,我们将扩展出很多关于神经网络的关键的思想,其中包括两个重要的人工神经 -元(\gls*{perceptron}和\gls*{sigmoid-neuron}),以及标准的神经网络学习算法,即% +元(\gls*{perceptron}和 \gls*{sigmoid-neuron}),以及标准的神经网络学习算法,即% \gls*{sgd}算法。自始至终,我专注于解释事情的原委,并构筑你对神经网络的直观感受。 这需要一个漫长的讨论,而不是仅仅介绍些基本的技巧,但这对于更深入的理解是值得的。 作为收益,在本章的最后,我们会准备好了解什么是深度学习,以及它为什么很重要。 @@ -63,8 +63,8 @@ \section{感知机} McCulloch} 和 \href{http://en.wikipedia.org/wiki/Walter_Pitts}{Walter Pitts} 早期\href{http://scholar.google.ca/scholar?cluster=4035975255085082870}{著作}的 影响。今天,使用其它人工神经元模型更为普遍~——~在这本书中,以及更多现代的神经网络 -著作中,主要使用的是一种叫做\textbf{\gls{sigmoid-neuron}}的神经元模型。我们很快 -会讲到\gls*{sigmoid-neuron}。但是要理解为什么\gls*{sigmoid-neuron}被定义为那样的 +著作中,主要使用的是一种叫做 \textbf{\gls{sigmoid-neuron}}的神经元模型。我们很快 +会讲到 \gls*{sigmoid-neuron}。但是要理解为什么 \gls*{sigmoid-neuron}被定义为那样的 方式,值得花点时间先来理解下\gls*{perceptron}。 \gls*{perceptron}是如何工作的呢?一个\gls*{perceptron}接受几个二进制输入, @@ -256,19 +256,19 @@ \section{S 型神经元} 决这个问题。但是目前为止,我们还没发现有什么办法能让\gls*{perceptron}网络进行学 习。 -我们可以引入一种称为\gls*{sigmoid-neuron}的新的人工神经元来克服这个问题。% +我们可以引入一种称为 \gls*{sigmoid-neuron}的新的人工神经元来克服这个问题。 \gls*{sigmoid-neuron}和\gls*{perceptron}类似,但是经过修改后,\gls*{weight}和% \gls*{bias}的微小改动只引起输出的微小变化。这对于让神经元网络学习起来是很关键的。 -好了, 让我来描述下\gls*{sigmoid-neuron}。我们用描绘\gls*{perceptron}的相同方式来 -描绘\gls*{sigmoid-neuron}\index{\gls*{sigmoid-neuron}}: +好了, 让我来描述下 \gls*{sigmoid-neuron}。我们用描绘\gls*{perceptron}的相同方式来 +描绘 \gls*{sigmoid-neuron}: \begin{center} \includegraphics{tikz9} \end{center} 正如一个\gls*{perceptron},\gls*{sigmoid-neuron}有多个输入,$x_1,x_2,\ldots$。但 是这些输入可以取 $0$ 和$1$ 中的任意值,而不仅仅是 $0$ 或 $1$。例如, -$0.638\ldots$ 是一个\gls*{sigmoid-neuron}的有效输入。同样,\gls*{sigmoid-neuron} +$0.638\ldots$ 是一个 \gls*{sigmoid-neuron}的有效输入。同样,\gls*{sigmoid-neuron} 对每个输入有\gls*{weight},$w_1,w_2,\ldots$,和一个总的\gls*{bias},$b$。但是输 出不是 $0$ 或 $1$。相反,它现在是 $\sigma(w \cdot x+b)$,这里 $\sigma$ 被称为 S 型函数\footnote{顺便提一下,$\sigma$ 有时被称为\textbf{逻辑函数},而这种新的神经 @@ -280,15 +280,15 @@ \section{S 型神经元} \end{equation} 把它们放在一起来更清楚地说明,一个具有输入 $x_1,x_2,\ldots$,\gls*{weight} -$w_1,w_2,\ldots$,和\gls*{bias} $b$ 的\gls*{sigmoid-neuron}的输出是: +$w_1,w_2,\ldots$,和\gls*{bias} $b$ 的 \gls*{sigmoid-neuron}的输出是: \begin{equation} \frac{1}{1+\exp(-\sum_j w_j x_j-b)} \label{eq:4}\tag{4} \end{equation} 初看上去,\gls*{sigmoid-neuron}和\gls*{perceptron}有很大的差别。如果你不熟悉 S -型函数的代数形式,它看上去晦涩难懂又令人生畏。实际上,\gls*{perceptron}和% -\gls*{sigmoid-neuron}之间有很多相似的地方,跨过理解上的障碍,S 型函数的代数形式 +型函数的代数形式,它看上去晦涩难懂又令人生畏。实际上,\gls*{perceptron}和 +\gls*{sigmoid-neuron}之间有很多相似的地方,跨过理解上的障碍,\gls*{sigmoid-func}的代数形式 具有很多技术细节。 为了理解和\gls*{perceptron}模型的相似性,假设 $z \equiv w \cdot x + b$ 是一个很 @@ -345,8 +345,8 @@ \section{S 型神经元} 无论如何,$\sigma$ 在神经网络的工作中被普遍使用,并且是这本书中我们最常使用的激 活函数。 -我们应该如何解释一个\gls*{sigmoid-neuron}的输出呢?很明显,\gls*{perceptron}和% -\gls*{sigmoid-neuron}之间一个很大的不同是\gls*{sigmoid-neuron}不仅仅输出 $0$ 或 +我们应该如何解释一个 \gls*{sigmoid-neuron}的输出呢?很明显,\gls*{perceptron}和 +\gls*{sigmoid-neuron}之间一个很大的不同是 \gls*{sigmoid-neuron}不仅仅输出 $0$ 或 $1$。它可以输出 $0$ 到 $1$ 之间的任何实数,所以诸如 $0.173\ldots$ 和 $0.689\ldots$ 的值是合理的输出。这是非常有用的,例如,当我们想要输出来表示一个神 经网络的图像像素输入的平均强度。但有时候这会是个麻烦。假设我们希望网络的输出表示 @@ -366,7 +366,7 @@ \subsection*{练习} 上题中相同的设置~——~一个\gls*{perceptron}网络。同样假设所有输入被选中。我们不 需要实际的输入值,仅仅需要固定这些输入。假设对于网络中任何特定% \gls*{perceptron}的输入 $x$, \gls*{weight}和\gls*{bias}遵循 $w \cdot x + b - \neq 0$。现在用\gls*{sigmoid-neuron}替换所有网络中的\gls*{perceptron},并且把% + \neq 0$。现在用 \gls*{sigmoid-neuron}替换所有网络中的\gls*{perceptron},并且把% \gls*{weight}和\gls*{bias}乘以一个正的常量$c>0$。证明在$c \rightarrow \infty$ 的极限情况下,\gls*{sigmoid-neuron}网络的行为和\gls*{perceptron}网络的完全一致。 当一个\gls*{perceptron}的 $w \cdot x + b = 0$ 时又为什么会不同? @@ -381,8 +381,8 @@ \section{神经网络的架构} \end{center} 前面提过,这个网络中最左边的称为输入层,其中的神经元称为\textbf{输入神经 - 元}\index{输入神经元}。最右边的,即\textbf{输出}层包含有\textbf{输出神经 - 元}\index{输出神经元},在本例中,输出层只有一个神经元。中间层,既然这层中的神 + 元}。最右边的,即\textbf{输出}层包含有\textbf{输出神经 + 元},在本例中,输出层只有一个神经元。中间层,既然这层中的神 经元既不是输入也不是输出,则被称为\textbf{\gls{hidden-layer}}。“隐藏”这一术语 也许听上去有些神秘~——~我第一次听到这个词,以为它必然有一些深层的哲学或数学涵 意~——~但它实际上仅仅意味着“既非输入也非输出”。上面的网络仅有一个隐藏层,但有些 @@ -391,7 +391,7 @@ \section{神经网络的架构} \includegraphics{tikz11} \end{center} -有些令人困惑的是,由于历史的原因,尽管是由\gls*{sigmoid-neuron}而不是% +有些令人困惑的是,由于历史的原因,尽管是由 \gls*{sigmoid-neuron}而不是% \gls*{perceptron}构成,这种多层网络有时被称为\textbf{\gls{mlp}}或者\textbf{MLP}。 在这本书中我不会使用 MLP 这个术语,因为我认为这会引起混淆,但这里想提醒你它的存 在。 @@ -415,13 +415,13 @@ \section{神经网络的架构} 将难于理解,所以我们不允许这样的环路。 然而,也有一些人工神经网络的模型,其中反馈环路是可行的。这些模型被称为% -\href{http://en.wikipedia.org/wiki/Recurrent_neural_network}{递归神经网络}\index{递归神经网络}。这种 +\href{http://en.wikipedia.org/wiki/Recurrent_neural_network}{\gls{rnn}}。这种 模型的设计思想,是具有休眠前会在一段有限的时间内保持激活状态的神经元。这种激活状 态可以刺激其它神经元,使其随后被激活并同样保持一段有限的时间。这样会导致更多的神 经元被激活,随着时间的推移,我们得到一个级联的神经元激活系统。因为一个神经元的输 出只在一段时间后而不是即刻影响它的输入,在这个模型中回路并不会引起问题。 -递归神经网络比前馈网络影响力小得多,部分原因是递归网络的学习算法(至少目前为止) +\gls*{rnn}比前馈网络影响力小得多,部分原因是递归网络的学习算法(至少目前为止) 不够强大。但是递归网络仍然很有吸引力。它们原理上比前馈网络更接近我们大脑的实际工 作。并且递归网络能解决一些重要的问题,这些问题如果仅仅用前馈网络来解决,则更加困 难。然而为了篇幅,本书将专注于使用更广泛的前馈网络。 @@ -567,7 +567,7 @@ \section{使用梯度下降算法进行学习} 网络的期望输出。注意这里 $T$ 是转置操作,把一个行向量转换成一个列向量。 我们希望有一个算法,能让我们找到\gls*{weight}和\gls*{bias},以至于网络的输出 -$y(x)$ 能够拟合所有的训练输入$x$。为了量化我们如何实现这个目标,我们定义一个 +$y(x)$ 能够拟合所有的训练输入$x$。为了量化我们如何实现这个目标,我们定义一个% \textbf{\gls{cost-func}}\footnote{有时被称为\textbf{损失}或\textbf{目标}函数。我 们在这本书中使用了代价函数这个术语,但是你应该注意其他的术语,因为它经常被用于 研究论文和其他神经网络的讨论中。}: @@ -580,8 +580,8 @@ \section{使用梯度下降算法进行学习} 练输入数据的个数,$a$ 是表示当输入为 $x$ 时输出的向量,求和则是在总的训练输入 $x$ 上进行的。当然,输出 $a$ 取决于 $x$, $w$ 和 $b$,但是为了保持符号的简洁性, 我没有明确地指出这种依赖关系。符号 $\|v\|$ 是指向量 $v$ 的模。我们把 $C$ 称为% -\textbf{二次}\gls*{cost-func}\index{二次代价函数};有时也被称为\textbf{均方误 - 差}\index{均方误差}或者\textbf{MSE}\index{MSE}。观察二次代价函数的形式我们可以 +\textbf{二次}\gls*{cost-func};有时也被称为\textbf{均方误 + 差}或者\textbf{MSE}。观察二次代价函数的形式我们可以 看到 $C(w,b)$ 是非负的,因为求和公式中的每一项都是非负的。此外,代价函数 $C(w,b)$ 的值相当小,即 $C(w,b) \approx 0$,精确地说,是当对于所有的训练输入 $x$, $y(x)$ 接近于输出 $a$ 时。因此如果我们的学习算法能找到合适的\gls*{weight}和% @@ -589,7 +589,7 @@ \section{使用梯度下降算法进行学习} 就不怎么好了,那意味着对于大量地输入, $y(x)$ 与输出 $a$ 相差很大。因此我们的训 练算法的目的,是最小化\gls*{weight}和\gls*{bias}的代价函数 $C(w,b)$。换句话说, 我们想要找到一系列能让代价尽可能小的\gls*{weight}和\gls*{bias}。我们将采用称为% -\textbf{梯度下降}\index{梯度下降}的算法来达到这个目的。 +\textbf{梯度下降}的算法来达到这个目的。 为什么要介绍二次代价呢?毕竟我们最初感兴趣的内容不是能正确分类的图像数量吗?为什 么不试着直接最大化这个数量,而是去最小化一个类似二次代价的间接评量呢?这么做是因 @@ -916,7 +916,7 @@ \section{实现我们的网络来分类数字} 分。我们将测试集保持原样,但是将 60,000 个图像的 MNIST 训练集分成两个部分:一部 分 50,000 个图像,我们将用来训练我们的神经网络,和一个单独的 10,000 个图像的% \textbf{验证集}。在本章中我们不使用验证数据,但是在本书的后面我们将会发现它对于 -解决如何去设置某些神经网络中的\textbf{超参数}\index{超参数}是很有用的~——~例如% +解决如何去设置某些神经网络中的\textbf{\gls{hyper-params}}是很有用的~——~例如% \gls*{learning-rate}等,这些参数不被我们的学习算法直接选择。尽管验证数据不是原始 MNIST 规范的一部分,然而许多人以这种方式使用 MNIST,并且在神经网络中使用验证数据 是很普遍的。从现在起当我提到“MNIST 训练数据”时,我指的是我们的 50,000 个图像数 @@ -1042,13 +1042,13 @@ \subsection*{练习} \lstinline!training_data! 是一个 \lstinline!(x, y)! 元组的列表,表示训练输入和其 对应的期望输出。变量 \lstinline!epochs! 和 \lstinline!mini_batch_size! 正如你预 -料的~——~\gls*{epoch}数量,和采样时的\minibatch{}的大小。\lstinline!eta! 是% +料的~——~\gls*{epoch}数量,和采样时的\gls*{mini-batch}{}的大小。\lstinline!eta! 是% \gls*{learning-rate},$\eta$。如果给出了可选参数 \lstinline!test_data!,那么程序 会在每个训练器后评估网络,并打印出部分进展。这对于追踪进度很有用,但相当拖慢执行 速度。 代码如下工作。在每个\gls*{epoch},它首先随机地将训练数据打乱,然后将它分成多个适 -当大小的\minibatch{}。这是一个简单的从训练数据的随机采样方法。然后对于每一个 +当大小的\gls*{mini-batch}{}。这是一个简单的从训练数据的随机采样方法。然后对于每一个 \lstinline!mini_batch! 我们应用一次梯度下降。这是通过代码 \lstinline!self.update_mini_batch(mini_batch, eta)! 完成的,它仅仅使用 \lstinline!mini_batch! 中的训练数据,根据单次梯度下降的迭代更新网络的% @@ -1171,8 +1171,8 @@ \subsection*{练习} 当然,为了获得这些准确性,我不得不对训练的\gls*{epoch}数量,\gls*{mini-batch}大 小和\gls*{learning-rate} $\eta$ 做特别的选择。正如我上面所提到的,这些在我们的神 -经网络中被称为超参数\index{超参数},以区别于通过我们的学习算法所学到的参数 -(\gls*{weight}和\gls*{bias})。如果我们选择了糟糕的超参数,我们会得到较差的结果。 +经网络中被称为\gls*{hyper-params},以区别于通过我们的学习算法所学到的参数 +(\gls*{weight}和\gls*{bias})。如果我们选择了糟糕的\gls*{hyper-params},我们会得到较差的结果。 假如我们选定\gls*{learning-rate}为 $\eta = 0.001$, \begin{lstlisting}[language=Python] @@ -1197,9 +1197,9 @@ \subsection*{练习} 这表明我们应该再次增加\gls*{learning-rate}。(如果改变能够改善一些事情,试着做更 多!)如果我们这样做几次,我们最终会得到一个像 $\eta = 1.0$ 的% \gls*{learning-rate}(或者调整到$3.0$),这跟我们之前的实验很接近。因此即使我们 -最初选择了糟糕的超参数,我们至少获得了足够的信息来帮助我们改善对于超参数的选择。 +最初选择了糟糕的\gls*{hyper-params},我们至少获得了足够的信息来帮助我们改善对于\gls*{hyper-params}的选择。 -通常,调试一个神经网络是具有挑战性的。尤其是当初始的超参数的选择产生的结果还不如 +通常,调试一个神经网络是具有挑战性的。尤其是当初始的\gls*{hyper-params}的选择产生的结果还不如 随机噪点的时候。假如我们使用之前成功的具有 30 个隐藏神经元的网络结构,但是学习速 率改为 $\eta = 100.0$: @@ -1232,7 +1232,7 @@ \subsection*{练习} 从这得到的教训是调试一个神经网络不是琐碎的,就像常规编程那样,它是一门艺术。你需 要学习调试的艺术来获得神经网络更好的结果。更普通的是,我们需要启发式方法来选择好 -的超参数和好的结构。我们将在整本书中讨论这些,包括上面我是怎么样选择超参数的。 +的\gls*{hyper-params}和好的结构。我们将在整本书中讨论这些,包括上面我是怎么样选择\gls*{hyper-params}的。 \subsection*{练习} diff --git a/chap2.tex b/chap2.tex index e6d4131..47c1f82 100644 --- a/chap2.tex +++ b/chap2.tex @@ -114,7 +114,7 @@ \section{热身:神经网络中使用矩阵快速计算输出的方法} 在使用方程~\eqref{eq:25} 计算 $a^l$ 的过程中,我们计算了中间量 $z^l \equiv w^l a^{l-1}+b^l$。这个量其实是非常有用的:我们称 $z^l$ 为 $l$ 层神经元的\textbf{带权 - 输入}\index{带权输入}。在本章后面,我们会充分利用带权输入 $z^l$。方 + 输入}。在本章后面,我们会充分利用带权输入 $z^l$。方 程~\eqref{eq:25} 有时候会以带权输入的形式写作 $a^l = \sigma(z^l)$。同样要指出的 是 $z^l$ 的每个元素是 $z^l_j = \sum_k w^l_{jk} a^{l-1}_k+b^l_j$,其实 $z^l_j$ 就 是第 $l$ 层第 $j$ 个神经元的激活函数的带权输入。 @@ -353,8 +353,7 @@ \section{反向传播的四个基本方程} \hyperref[fig:StepFunction]{上一章中S型函数的图形},当 $\sigma(z^L_j)$ 近似为$0$ 或者 $1$ 的时候 $\sigma$ 函数变得非常平。这时 $\sigma'(z^L_j) \approx 0$。所以如 果输出神经元处于或者低激活值($\approx 0$)或者高激活值($\approx 1$)时,最终层 -的\gls*{weight}学习缓慢。这样的情形,我们常常称输出神经元已经\textbf{饱和}\index{饱 - 和}了,并且,\gls*{weight}学习也会终止(或者学习非常缓慢)。类似的结果对于输出 +的\gls*{weight}学习缓慢。这样的情形,我们常常称输出神经元已经\textbf{饱和}了,并且,\gls*{weight}学习也会终止(或者学习非常缓慢)。类似的结果对于输出 神经元的\gls*{bias}也是成立的。 针对前面的层,我们也有类似的观点。特别地,注意在~\eqref{eq:bp2} 中的项 @@ -380,7 +379,7 @@ \section{反向传播的四个基本方程} \begin{minipage}{0.7\textwidth} \begin{framed} \centering - \textbf{总结:\gls*{bp}的四个方程式}\index{\gls*{bp}的四个方程式}\label{backpropsummary}\\ + \textbf{总结:\gls*{bp}的四个方程式}\label{backpropsummary}\\ \vspace{1.5ex} \begin{tabular}{ll} $\delta^L = \nabla_a C \odot \sigma'(z^L)$ & \hspace{2cm}\eqref{eq:bp1} \\[1.5ex] @@ -424,7 +423,7 @@ \section{四个基本方程的证明(可选)} \label{sec:proof_of_the_four_fundamental_equations} 我们现在证明这四个基本的方程 \eqref{eq:bp1}--\eqref{eq:bp4}。所有这些都是多元微 -积分的\href{https://en.wikipedia.org/wiki/Chain_rule}{链式法则}\index{链式法则}% +积分的\href{https://en.wikipedia.org/wiki/Chain_rule}{链式法则}% 的推论。如果你熟悉链式法则,那么我鼓励你在读之前尝试自己推导。 让我们从方程~\eqref{eq:bp1} 开始,它给出了输出误差 $\delta^L$ 的表达式。为了证明 diff --git a/chap3.tex b/chap3.tex index e3879d5..183de7f 100644 --- a/chap3.tex +++ b/chap3.tex @@ -9,10 +9,10 @@ \chapter{改进神经网络的学习方法} 中大部分工作学习和研究的基础。本章,我会解释若干技术能够用来提升我们关于% \gls*{bp}的初级的实现,最终改进网络学习的方式。 -本章涉及的技术包括:更好的代价函数的选择~——~% -\hyperref[sec:the_cross-entropy_cost_function]{交叉熵}代价函数;四种称为% -\hyperref[sec:overfitting_and_regularization]{“规范化”的方法}(L1 和 L2 规范化, - 弃权和训练数据的人为扩展),这会让我们的网络在训练集之外的数据上更好地泛化;% +本章涉及的技术包括:更好的\gls*{cost-func}的选择~——~% +\hyperref[sec:the_cross-entropy_cost_function]{交叉熵}\gls*{cost-func};四种称为% +\hyperref[sec:overfitting_and_regularization]{“\gls*{regularization}”的方法}(L1 和 L2 \gls*{regularization}, + \gls*{dropout}和训练数据的人为扩展),这会让我们的网络在训练集之外的数据上更好地泛化;% \hyperref[sec:weight_initialization]{更好的\gls*{weight}初始化方法};还有% \hyperref[sec:how_to_choose_a_neural_network's_hyper-parameters]{帮助选择好的超 参数的启发式想法}。同样我也会再给出一些简要的% @@ -54,7 +54,7 @@ \section{交叉熵代价函数} 输出接近 $0.0$ 的。注意这些图像实际上是正在进行梯度的计算,然后使用梯度更新来对% \gls*{weight}和\gls*{bias}进行更新,并且展示结果。设置\gls*{learning-rate} $\eta=0.15$ 进行学习一方面足够慢的让我们跟随学习的过程,另一方面也保证了学习的时 -间不会太久,几秒钟应该就足够了。代价函数是我们在第一章用到的二次函数,$C$。这里 +间不会太久,几秒钟应该就足够了。\gls*{cost-func}是我们在第一章用到的二次函数,$C$。这里 我也会给出准确的形式,所以不需要翻到前面查看定义了。 \begin{center} \includegraphics{saturation1-0} @@ -67,7 +67,7 @@ \section{交叉熵代价函数} \includegraphics{saturation1-250}\includegraphics{saturation1-300} \end{center} -正如你所见,神经元快速地学到了使得代价函数下降的\gls*{weight}和\gls*{bias},给出 +正如你所见,神经元快速地学到了使得\gls*{cost-func}下降的\gls*{weight}和\gls*{bias},给出 了最终的输出为 $0.09$。这虽然不是我们的目标输出 $0.0$,但是已经挺好了。假设我们 现在将初始\gls*{weight}和\gls*{bias}都设置为 $2.0$。此时初始输出为 $0.98$,这是 和目标值的差距相当大的。现在看看神经元学习的过程。 @@ -92,10 +92,10 @@ \section{交叉熵代价函数} 出现。为何学习如此缓慢?我们能够找到避免这种情况的方法吗? 为了理解这个问题的源头,想想我们的神经元是通过改变\gls*{weight}和\gls*{bias},并 -以一个代价函数的偏导数($\partial C/\partial w$ 和 $\partial C/\partial b$)决定 +以一个\gls*{cost-func}的偏导数($\partial C/\partial w$ 和 $\partial C/\partial b$)决定 的速度学习。所以,我们在说“学习缓慢”时,实际上就是说这些偏导数很小。理解他们为 何这么小就是我们面临的挑战。为了理解这些,让我们计算偏导数看看。我们一直在用的是 -方程~\eqref{eq:6}表示的二次代价函数,定义如下 +方程~\eqref{eq:6}表示的二次\gls*{cost-func},定义如下 \begin{equation} C = \frac{(y-a)^2}{2} \label{eq:54}\tag{54} @@ -124,7 +124,7 @@ \section{交叉熵代价函数} \subsection{引入交叉熵代价函数} \label{sec:introducing_the_cross-entropy_cost_function} -那么我们如何解决这个问题呢?研究表明,我们可以通过使用交叉熵代价函数来替换二次代 +那么我们如何解决这个问题呢?研究表明,我们可以通过使用交叉熵\gls*{cost-func}来替换二次代 价函数。为了理解什么是交叉熵,我们稍微改变一下之前的简单例子。假设,我们现在要训 练一个包含若干输入变量的的神经元,$x_1, x_2, \ldots$ 对应的\gls*{weight}为 $w_1, w_2, \ldots$ 和\gls*{bias} $b$: @@ -134,7 +134,7 @@ \subsection{引入交叉熵代价函数} \end{center} 神经元的输出就是 $a = \sigma(z)$,其中 $z = \sum_j w_j x_j+b$ 是输入的带权和。我 -们如下定义这个神经元的交叉熵代价函数: +们如下定义这个神经元的交叉熵\gls*{cost-func}: \begin{equation} C = -\frac{1}{n} \sum_x \left[y \ln a + (1-y ) \ln (1-a) \right] \label{eq:57}\tag{57} @@ -144,10 +144,10 @@ \subsection{引入交叉熵代价函数} 输出。 表达式~\eqref{eq:57} 是否解决学习缓慢的问题并不明显。实际上,甚至将这个定义看做 -是代价函数也不是显而易见的!在解决学习缓慢前,我们来看看交叉熵为何能够解释成一个 -代价函数。 +是\gls*{cost-func}也不是显而易见的!在解决学习缓慢前,我们来看看交叉熵为何能够解释成一个 +\gls*{cost-func}。 -将交叉熵看做是代价函数有两点原因。第一,它是非负的,$C > 0$。可以看出:(a) +将交叉熵看做是\gls*{cost-func}有两点原因。第一,它是非负的,$C > 0$。可以看出:(a) \eqref{eq:57} 中的求和中的所有独立的项都是负数的,因为对数函数的定义域是 $(0,1)$; (b)求和前面有一个负号。 @@ -160,8 +160,8 @@ \subsection{引入交叉熵代价函数} 输出之间的差距越小,最终的交叉熵的值就越低了。 综上所述,交叉熵是非负的,在神经元达到很好的正确率的时候会接近 $0$。这些其实就是 -我们想要的代价函数的特性。其实这些特性也是二次代价函数具备的。所以,交叉熵就是很 -好的选择了。但是交叉熵代价函数有一个比二次代价函数更好的特性就是它避免了学习速度 +我们想要的\gls*{cost-func}的特性。其实这些特性也是二次\gls*{cost-func}具备的。所以,交叉熵就是很 +好的选择了。但是交叉熵\gls*{cost-func}有一个比二次\gls*{cost-func}更好的特性就是它避免了学习速度 下降的问题。为了弄清楚这个情况,我们来算算交叉熵函数关于\gls*{weight}的偏导数。 我们将 $a=\sigma(z)$ 代入到 \eqref{eq:57} 中应用两次链式法则,得到: \begin{align} @@ -191,7 +191,7 @@ \subsection{引入交叉熵代价函数} 这是一个优美的公式。它告诉我们\gls*{weight}学习的速度受到 $\sigma(z)-y$,也就是 输出中的误差的控制。更大的误差,更快的学习速度。这是我们直觉上期待的结果。特别地, -这个代价函数还避免了像在二次代价函数中类似方程中 $\sigma'(z)$ 导致的学习缓慢,见 +这个\gls*{cost-func}还避免了像在二次\gls*{cost-func}中类似方程中 $\sigma'(z)$ 导致的学习缓慢,见 方程~\eqref{eq:55}。当我们使用交叉熵的时候,$\sigma'(z)$ 被约掉了,所以我们不再 需要关心它是不是变得很小。这种约除就是交叉熵带来的特效。实际上,这也并不是非常奇 迹的事情。我们在后面可以看到,交叉熵其实只是满足这种特性的一种选择罢了。 @@ -203,7 +203,7 @@ \subsection{引入交叉熵代价函数} \label{eq:62}\tag{62} \end{equation} -再一次,这避免了二次代价函数中类似方程~\eqref{eq:56} 中 $\sigma'(z)$ 项导致的学习 +再一次,这避免了二次\gls*{cost-func}中类似方程~\eqref{eq:56} 中 $\sigma'(z)$ 项导致的学习 缓慢。 \subsection*{练习} @@ -238,13 +238,13 @@ \subsection*{练习} \end{center} 成功了!这次神经元的学习速度相当快,跟我们预期的那样。如果你观测的足够仔细,你可 -以发现代价函数曲线要比二次代价函数训练前面部分要陡很多。那个交叉熵导致的陡度让我 +以发现\gls*{cost-func}曲线要比二次\gls*{cost-func}训练前面部分要陡很多。那个交叉熵导致的陡度让我 们高兴,这正是我们期待的当神经元开始出现严重错误时能以最快速度学习。 -我还没有提及刚才的示例用了什么\gls*{learning-rate}。刚开始使用二次代价函数的时候, +我还没有提及刚才的示例用了什么\gls*{learning-rate}。刚开始使用二次\gls*{cost-func}的时候, 我们使用了 $\eta = 0.15$。在新例子中,我们还应该使用同样的\gls*{learning-rate}吗? -实际上,根据不同的代价函数,我们不能够直接去套用同样的\gls*{learning-rate}。这好 -比苹果和橙子的比较。对于这两种代价函数,我只是通过简单的实验来找到一个能够让我们 +实际上,根据不同的\gls*{cost-func},我们不能够直接去套用同样的\gls*{learning-rate}。这好 +比苹果和橙子的比较。对于这两种\gls*{cost-func},我只是通过简单的实验来找到一个能够让我们 看清楚变化过程的\gls*{learning-rate}的值。尽管我不愿意提及,但如果你仍然好奇,这 个例子中我使用了 $\eta = 0.005$。 @@ -252,9 +252,9 @@ \subsection*{练习} \gls*{learning-rate}的选择是任意挑选的时候神经元学习的速度?!这样的反对其实没有 抓住重点。上面的图例不是想讨论学习的绝对速度。而是想研究学习速度的变化情况。 -特别地,当我们使用二次代价函数时,学习在神经元犯了明显的错误的时候却比学习快接近 +特别地,当我们使用二次\gls*{cost-func}时,学习在神经元犯了明显的错误的时候却比学习快接近 真实值的时候缓慢;而使用交叉熵学习正是在神经元犯了明显错误的时候速度更快。特别地, -当我们使用二次代价函数时,当神经元在接近正确的输出前犯了明显错误的时候,学习变得% +当我们使用二次\gls*{cost-func}时,当神经元在接近正确的输出前犯了明显错误的时候,学习变得% \textbf{更加缓慢};而使用交叉熵,在神经元犯明显错误时学习得更快。这些现象并不依 赖于如何设置\gls*{learning-rate}。 @@ -271,11 +271,11 @@ \subsection*{练习} 式~\eqref{eq:63} 确实会在很多的神经网络中避免学习的缓慢。如果你感兴趣,你可以尝 试一下下面问题的推导。 -那么我们应该在什么时候用交叉熵来替换二次代价函数?实际上,如果在输出神经元是% +那么我们应该在什么时候用交叉熵来替换二次\gls*{cost-func}?实际上,如果在输出神经元是% \gls*{sigmoid-neuron}时,交叉熵一般都是更好的选择。为什么?考虑一下我们初始化网 络的\gls*{weight}和\gls*{bias}的时候通常使用某种随机方法。可能会发生这样的情况, 这些初始选择会对某些训练输入误差相当明显~——~比如说,目标输出是 $1$,而实际值是 -$0$,或者完全反过来。如果我们使用二次代价函数,那么这就会导致学习速度的下降。它 +$0$,或者完全反过来。如果我们使用二次\gls*{cost-func},那么这就会导致学习速度的下降。它 并不会完全终止学习的过程,因为这些\gls*{weight}会持续从其他的样本中进行学习,但 是显然这不是我们想要的效果。 @@ -304,14 +304,14 @@ \subsection*{问题} \begin{itemize} \item \textbf{多层多神经元网络}\quad 用% \hyperref[ch:HowTheBackpropagationAlgorithmWorks]{上一章}的定义符号,证明对二 - 次代价函数,关于输出层的\gls*{weight}的偏导数为 + 次\gls*{cost-func},关于输出层的\gls*{weight}的偏导数为 \begin{equation} \frac{\partial C}{\partial w^L_{jk}} = \frac{1}{n} \sum_x a^{L-1}_k (a^L_j-y_j) \sigma'(z^L_j) \label{eq:65}\tag{65} \end{equation} 项 $\sigma'(z^L_j)$ 会在一个输出神经元困在错误值时导致学习速度的下降。证明对于 - 交叉熵代价函数,针对一个训练样本 $x$ 的输出误差 $\delta^L$为 + 交叉熵\gls*{cost-func},针对一个训练样本 $x$ 的输出误差 $\delta^L$为 \begin{equation} \delta^L = a^L-y \label{eq:66}\tag{66} @@ -325,9 +325,9 @@ \subsection*{问题} 这里 $\sigma'(z^L_j)$ 就消失了,所以交叉熵避免了学习缓慢的问题,不仅仅是在一个 神经元上,而且在多层多元网络上都起作用。这个分析过程稍作变化对\gls*{bias}也是一样的。 如果你对这个还不确定,那么请仔细研读一下前面的分析。 -\item \textbf{在输出层使用线性神经元时使用二次代价函数}\quad 假设我们有一个多层 - 多神经元网络,最终输出层的神经元都是\textbf{线性神经元},输出不再是 S 型函数作 - 用的结果,而是 $a^L_j = z^L_j$。证明如果我们使用二次代价函数,那么对单个训练样 +\item \textbf{在输出层使用线性神经元时使用二次\gls*{cost-func}}\quad 假设我们有一个多层 + 多神经元网络,最终输出层的神经元都是\textbf{线性神经元},输出不再是 \gls*{sigmoid-func}作 + 用的结果,而是 $a^L_j = z^L_j$。证明如果我们使用二次\gls*{cost-func},那么对单个训练样 本 $x$ 的输出误差就是 \begin{equation} \delta^L = a^L-y @@ -341,8 +341,8 @@ \subsection*{问题} \frac{\partial C}{\partial b^L_{j}} &= \frac{1}{n} \sum_x (a^L_j-y_j) \label{eq:70}\tag{70} \end{align} - 这表明如果输出神经元是线性的那么二次代价函数不再会导致学习速度下降的问题。在此 - 情形下,二次代价函数就是一种合适的选择。 + 这表明如果输出神经元是线性的那么二次\gls*{cost-func}不再会导致学习速度下降的问题。在此 + 情形下,二次\gls*{cost-func}就是一种合适的选择。 \end{itemize} \subsection{使用交叉熵来对 MNIST 数字进行分类} @@ -357,7 +357,7 @@ \subsection{使用交叉熵来对 MNIST 数字进行分类} 样,我们会使用一个包含 $30$ 个隐藏元的网络,而\gls*{mini-batch}的大小也设置为 $10$。我们将\gls*{learning-rate}设置为 $\eta=0.5$ \footnote{在第一章里我们用了二 次代价和 $\eta = 3.0$ 的\gls*{learning-rate}。上面已经讨论过,准确地说出当代价 - 函数改变时用“相同的”学习速率意味什么,这是不可能的。对于两种代价函数,在给出其 + 函数改变时用“相同的”学习速率意味什么,这是不可能的。对于两种\gls*{cost-func},在给出其 它超参数的选择后,我都做了实验来找出一个接近优化性能的\gls*{learning-rate},顺 便提一下,有一个非常粗略的将交叉熵和二次代价的\gls*{learning-rate}联系起来的推 导。正如我们之前看到的,二次代价的梯度项中有一个额外的 $\sigma' = @@ -384,15 +384,15 @@ \subsection{使用交叉熵来对 MNIST 数字进行分类} 注意看下,\lstinline!net.large_weight_initializer()! 命令使用和第一章介绍的同样 的方式来进行\gls*{weight}和\gls*{bias}的初始化。我们需要运行这个命令,是因为我们 要在这一章的后面才改变默认的\gls*{weight}初始化命令。运行上面的代码我们得到了一 -个 95.49\% 准确率的网络。这跟我们在第一章中使用二次代价函数得到的结果相当接近了, +个 95.49\% 准确率的网络。这跟我们在第一章中使用二次\gls*{cost-func}得到的结果相当接近了, 95.42\%。 同样也来试试使用 $100$ 个隐藏神经元,交叉熵及其他参数保持不变的情况。在这个情形 -下,我们获得了 96.82\% 的准确率。相比第一章使用二次代价函数的结果 96.59\%,这有 +下,我们获得了 96.82\% 的准确率。相比第一章使用二次\gls*{cost-func}的结果 96.59\%,这有 一定的提升。这看起来是一个小小的变化,但是考虑到误差率已经从 3.41\% 下降到3.18\% 了。我们已经消除了原误差的 $1/14$。这其实是可观的改进。 -令人振奋的是交叉熵代价函数给了我们和二次代价相比类似的或者更好的结果。然而,这些 +令人振奋的是交叉熵\gls*{cost-func}给了我们和二次代价相比类似的或者更好的结果。然而,这些 结果并没有能够明确地证明交叉熵是更好的选择。原因在于我花费了少许努力在选择诸如学 习速率,\gls*{mini-batch}大小等等这样的超参数上。为了让这些提升更具说服力,我们 需要对超参数进行深度的优化。然而,这些结果仍然是令人鼓舞的,巩固了我们早先关于交 @@ -407,8 +407,8 @@ \subsection{使用交叉熵来对 MNIST 数字进行分类} 至此,我们已经花了大量篇幅介绍交叉熵。为何对一个只能给我们的 MNIST 结果带来一点 点性能提升的技术花费这么多的精力?后面,我们会看到其他的技术~——~% -\hyperref[sec:overfitting_and_regularization]{规范化},会带来更大的提升效果。所 -以为何要这么细致地讨论交叉熵?部分原因在于交叉熵是一种广泛使用的代价函数,值得深 +\hyperref[sec:overfitting_and_regularization]{\gls*{regularization}},会带来更大的提升效果。所 +以为何要这么细致地讨论交叉熵?部分原因在于交叉熵是一种广泛使用的\gls*{cost-func},值得深 入理解。但是更加重要的原因是神经元的饱和是神经网络中一个关键的问题,整本书都会不 断回归到这个问题上。因此我现在深入讨论交叉熵就是因为这是一种开始理解神经元饱和以 及如何解决这个问题的很好的实验。 @@ -428,7 +428,7 @@ \subsection{交叉熵的含义?源自哪里?} \frac{\partial C}{\partial b } &= (a-y) \label{eq:72}\tag{72} \end{align} -如果我们选择的代价函数满足这些条件,那么它们就能以简单的方式呈现这样的特性:初始 +如果我们选择的\gls*{cost-func}满足这些条件,那么它们就能以简单的方式呈现这样的特性:初始 误差越大,神经元学习得越快。这也能够解决学习速度下降的问题。实际上,从这些公式开 始,现在我们就看看凭着我们数学的直觉推导出交叉熵的形式是可行的。我们来推一下,由 链式法则,我们有 @@ -456,8 +456,8 @@ \subsection{交叉熵的含义?源自哪里?} C = -[y \ln a + (1-y) \ln (1-a)]+ {\rm constant} \label{eq:76}\tag{76} \end{equation} -其中 {\rm constant} 是积分常量。这是一个单独的训练样本 $x$ 对代价函数的贡献。为 -了得到整个的代价函数,我们需要对所有的训练样本进行平均,得到了 +其中 {\rm constant} 是积分常量。这是一个单独的训练样本 $x$ 对\gls*{cost-func}的贡献。为 +了得到整个的\gls*{cost-func},我们需要对所有的训练样本进行平均,得到了 \begin{equation} C = -\frac{1}{n} \sum_x [y \ln a +(1-y) \ln(1-a)] + {\rm constant} \label{eq:77}\tag{77} @@ -484,10 +484,10 @@ \subsection{交叉熵的含义?源自哪里?} \subsection*{问题} \begin{itemize} -\item 我们已经深入讨论了使用二次代价函数的网络中在输出神经元饱和时候学习缓慢的问 +\item 我们已经深入讨论了使用二次\gls*{cost-func}的网络中在输出神经元饱和时候学习缓慢的问 题,另一个可能会影响学习的因素就是在方程~\eqref{eq:61} 中的 $x_j$ 项。由于此项, 当输入 $x_j$ 接近 $0$ 时,对应的\gls*{weight} $w_j$ 会学习得相当缓慢。解释为何 - 不可以通过改变代价函数来消除 $x_j$ 项的影响。 + 不可以通过改变\gls*{cost-func}来消除 $x_j$ 项的影响。 \end{itemize} \subsection{柔性最大值} @@ -601,7 +601,7 @@ \subsection*{问题} \textbf{学习缓慢问题:} 我们现在已经对柔性最大值神经元层有了一定的认识。但是我们 还没有看到一个柔性最大值层会怎么样解决学习缓慢问题。为了理解这点,让我们先定义一 -个\textbf{\gls{log-likelihood}}代价函数。我们使用 $x$ 表示网络的训练输入,$y$ 表 +个\textbf{\gls{log-likelihood}}\gls*{cost-func}。我们使用 $x$ 表示网络的训练输入,$y$ 表 示对应的目标输出。然后关联这个训练输入的\gls*{log-likelihood}\gls*{cost-func}就是 \begin{equation} C \equiv -\ln a^L_y @@ -611,8 +611,8 @@ \subsection*{问题} \gls*{log-likelihood}代价就是 $-\ln a_7^L$。看看这个直觉上的含义,想想当网络表现 很好的时候,也就是确认输入为 $7$ 的时候。这时,他会估计一个对应的概率 $a_7^L$ 跟 $1$ 非常接近,所以代价 $-\ln a_7^L$ 就会很小。反之,如果网络的表现糟糕时,概率 -$a_7^L$ 就变得很小,代价 $-\ln a_7^L$ 随之增大。所以对数似然代价函数也是满足我们 -期待的代价函数的条件的。 +$a_7^L$ 就变得很小,代价 $-\ln a_7^L$ 随之增大。所以对数似然\gls*{cost-func}也是满足我们 +期待的\gls*{cost-func}的条件的。 那关于学习缓慢问题呢?为了分析它,回想一下学习缓慢的关键就是量 $\partial C / \partial w^L_{jk}$ 和 $\partial C / \partial b^L_j$ 的变化情况。我不会显式地给出 @@ -695,9 +695,9 @@ \section{过度拟合和规范化} 让我们通过构造一个网络泛化能力很差的例子使这个问题更清晰。我们的网络有 30 个隐藏 神经元,共 23,860 个参数。但是我们不会使用所有 50,000 幅 MNIST 训练图像。相反,我 们只使用前 1,000 幅图像。使用这个受限的集合,会让泛化的问题突显。我们按照之前同样 -的方式,使用交叉熵代价函数,\gls*{learning-rate}设置为 $\eta = 0.5$ 而\gls*{mini-batch}大小 +的方式,使用交叉熵\gls*{cost-func},\gls*{learning-rate}设置为 $\eta = 0.5$ 而\gls*{mini-batch}大小 设置为 $10$。不过这里我们要训练 400 个\gls*{epoch},比前面的要多一些,因为我们只用了 -少量的训练样本。我们现在使用 \lstinline!network2! 来研究代价函数改变的情况: +少量的训练样本。我们现在使用 \lstinline!network2! 来研究\gls*{cost-func}改变的情况: \begin{lstlisting}[language=Python] >>> import mnist_loader @@ -718,7 +718,7 @@ \section{过度拟合和规范化} \includegraphics[width=.6\textwidth]{overfitting1} \end{center} -这看起来令人振奋,因为代价函数有一个光滑的下降,跟我们预期一致。注意,我只是展示 +这看起来令人振奋,因为\gls*{cost-func}有一个光滑的下降,跟我们预期一致。注意,我只是展示 了 $200$ 到 $399$ \gls*{epoch}的情况。这给出了很好的近距离理解训练后期的情况,也是出 现有趣现象的地方。 @@ -787,8 +787,8 @@ \section{过度拟合和规范化} $10,000$ 幅都不相同。我们会使用 \lstinline!validation_data! 而不是 \lstinline!test_data! 来防止\gls*{overfitting}。我们会为 \lstinline!test_data! 使用和上面 提到的相同的策略。我们在每个\gls*{epoch}的最后都计算在 \lstinline!validation_data! -上的分类准确率。一旦分类准确率已经饱和,就停止训练。这个策略被称为\emph{提前停 - 止}\index{提前停止}。当然,实际应用中,我们不会立即知道什么时候准确率会饱和。 +上的分类准确率。一旦分类准确率已经饱和,就停止训练。这个策略被称为\textbf{提前停 + 止}。当然,实际应用中,我们不会立即知道什么时候准确率会饱和。 相反,我们会一直训练直到我们确信准确率已经饱和\footnote{这里需要一些判定标准来确 定什么时候停止。在我前面的图中,将 280 \gls*{epoch}看成是饱和的地方。可能这有点太 悲观了。因为神经网络有时候会训练过程中处在一个稳定期,然后又开始提升。如果在 @@ -814,7 +814,7 @@ \section{过度拟合和规范化} 最终我们就使用 \lstinline!test_data! 进行准确率测量。这给了我们在 \lstinline!test_data! 上的结果是一个网络泛化能力真正的度量方式的信心。换言之,你 可以将验证集看成是一种特殊的训练数据集能够帮助我们学习好的超参数。这种寻找好的超 -参数的方法有时候被称为\emph{hold out}方法,因为 \lstinline!validation_data! 是从 +参数的方法有时候被称为\textbf{hold out}方法,因为 \lstinline!validation_data! 是从 \lstinline!traning_data! 训练集中留出或者“拿出”的一部分。 在实际应用中,甚至在衡量了 \lstinline!test_data! 的性能后,我们可能也会改变想法并 @@ -851,8 +851,8 @@ \subsection{规范化} 幸运的是,还有其他的技术能够缓解\gls*{overfitting},即使我们只有一个固定的网络和固定的训练集 合。这种技术就是\textbf{\gls{regularization}}。本节,我会给出一种最为常用的\gls*{regularization} -手段~——~有时候被称为\textbf{\gls{weight-decay}}或者 \textbf{\gls{l2-regularization}}。L2 规范化的想法是增加一个额外的 -项到代价函数上,这个项叫做\textbf{\gls{regularization-term}}。下面是规范化的交叉熵: +手段~——~有时候被称为\textbf{\gls{weight-decay}}或者 \textbf{L2 \gls*{regularization}}。L2 \gls*{regularization}的想法是增加一个额外的 +项到\gls*{cost-func}上,这个项叫做\textbf{\gls{regularization-term}}。下面是\gls*{regularization}的交叉熵: \begin{equation} C = -\frac{1}{n} \sum_{xj} \left[ y_j \ln a^L_j+(1-y_j) \ln (1-a^L_j)\right] + \frac{\lambda}{2n} \sum_w w^2 @@ -860,12 +860,12 @@ \subsection{规范化} \end{equation} 其中第一个项就是常规的交叉熵的表达式。第二个现在加入的就是所有\gls*{weight}的平方的和。然 -后使用一个因子 $\lambda / 2n$ 进行量化调整,其中 $\lambda > 0$ 可以称为\emph{规 - 范化参数}\index{规范化参数},而 $n$ 就是训练集合的大小。我们会在后面讨论 -$\lambda$ 的选择策略。需要注意的是,规范化项里面并\emph{不}包含\gls*{bias}。这点我们后 +后使用一个因子 $\lambda / 2n$ 进行量化调整,其中 $\lambda > 0$ 可以称为\textbf{规 + 范化参数},而 $n$ 就是训练集合的大小。我们会在后面讨论 +$\lambda$ 的选择策略。需要注意的是,\gls*{regularization}项里面并\textbf{不}包含\gls*{bias}。这点我们后 面也会再讲述。 -当然,对其他的代价函数也可以进行规范化,例如二次代价函数。类似的规范化的形式如下: +当然,对其他的\gls*{cost-func}也可以进行\gls*{regularization},例如二次\gls*{cost-func}。类似的\gls*{regularization}的形式如下: \begin{equation} C = \frac{1}{2n} \sum_x \|y-a^L\|^2 + \frac{\lambda}{2n} \sum_w w^2 \label{eq:86}\tag{86} @@ -876,17 +876,17 @@ \subsection{规范化} C = C_0 + \frac{\lambda}{2n} \sum_w w^2 \label{eq:87}\tag{87} \end{equation} -其中 $C_0$ 是原始的代价函数。 +其中 $C_0$ 是原始的\gls*{cost-func}。 -直觉地看,规范化的效果是让网络倾向于学习小一点的\gls*{weight},其他的东西都一样的。大的权 -重只有能够给出代价函数第一项足够的提升时才被允许。换言之,规范化可以当做一种寻找 -小的\gls*{weight}和最小化原始的代价函数之间的折中。这两部分之间相对的重要性就由 $\lambda$ -的值来控制了:$\lambda$ 越小,就偏向于最小化原始代价函数,反之,倾向于小的\gls*{weight}。 +直觉地看,\gls*{regularization}的效果是让网络倾向于学习小一点的\gls*{weight},其他的东西都一样的。 +大的\gls*{weight}只有能够给出\gls*{cost-func}第一项足够的提升时才被允许。换言之,\gls*{regularization}可以当做一种寻找 +小的\gls*{weight}和最小化原始的\gls*{cost-func}之间的折中。这两部分之间相对的重要性就由 $\lambda$ +的值来控制了:$\lambda$ 越小,就偏向于最小化原始\gls*{cost-func},反之,倾向于小的\gls*{weight}。 现在,对于这样的折中为何能够减轻\gls*{overfitting}还不是很清楚!但是,实际表现表明了这点。我 -们会在下一节来回答这个问题。但现在,我们来看看一个规范化的确减轻\gls*{overfitting}的例子。 +们会在下一节来回答这个问题。但现在,我们来看看一个\gls*{regularization}的确减轻\gls*{overfitting}的例子。 -为了构造这个例子,我们首先需要弄清楚如何将随机梯度下降算法应用在一个规范化的神经 +为了构造这个例子,我们首先需要弄清楚如何将\gls*{sgd}算法应用在一个\gls*{regularization}的神经 网络上。特别地,我们需要知道如何计算对网络中所有\gls*{weight}和\gls*{bias}的偏导数 $\partial C/\partial w$ 和 $\partial C/\partial b$。对方程~\eqref{eq:87} 进行求偏导数得: \begin{align} @@ -897,7 +897,7 @@ \subsection{规范化} $\partial C_0/\partial w$ 和 $\partial C_0/\partial b$ 可以通过\gls*{bp}进行计算, 正如\hyperref[ch:HowTheBackpropagationAlgorithmWorks]{上一章}中描述的那样。所以 -我们看到其实计算规范化的代价函数的梯度是很简单的:仅仅需要\gls*{bp},然后加上 +我们看到其实计算\gls*{regularization}的\gls*{cost-func}的梯度是很简单的:仅仅需要\gls*{bp},然后加上 $\frac{\lambda}{n} w$ 得到所有\gls*{weight}的偏导数。而\gls*{bias}的偏导数就不要变化,所以\gls*{bias}的 梯度下降学习规则不会发生变化: \begin{equation} @@ -914,35 +914,35 @@ \subsection{规范化} \end{align} 这正和通常的梯度下降学习规则相同,除了通过一个因子 $1-\frac{\eta\lambda}{n}$ 重 -新调整了\gls*{weight} $w$。这种调整有时被称为\emph{\gls*{weight}衰减},因为它使得\gls*{weight}变小。粗看, -这样会导致\gls*{weight}会不断下降到 $0$。但是实际不是这样的,因为如果在原始代价函数中造成 +新调整了\gls*{weight} $w$。这种调整有时被称为\textbf{\gls*{weight}衰减},因为它使得\gls*{weight}变小。粗看, +这样会导致\gls*{weight}会不断下降到 $0$。但是实际不是这样的,因为如果在原始\gls*{cost-func}中造成 下降的话其他的项可能会让\gls*{weight}增加。 -好的,这就是梯度下降工作的原理。那么随机梯度下降呢?正如在没有规范化的随机梯度下 +好的,这就是梯度下降工作的原理。那么\gls*{sgd}呢?正如在没有\gls*{regularization}的随机梯度下 降中,我们可以通过平均 $m$ 个训练样本的\gls*{mini-batch}来估计 $\partial C_0/\partial -w$。因此,为了随机梯度下降的规范化学习规则就变成(参考方程~\eqref{eq:20}) +w$。因此,为了\gls*{sgd}的\gls*{regularization}学习规则就变成(参考方程~\eqref{eq:20}) \begin{equation} w \rightarrow \left(1-\frac{\eta \lambda}{n}\right) w -\frac{\eta}{m} \sum_x \frac{\partial C_x}{\partial w} \label{eq:93}\tag{93} \end{equation} 其中后面一项是在训练样本的\gls*{mini-batch} $x$ 上进行的,而 $C_x$ 是对每个训练样本的(无规 - 范化的)代价。这其实和之前通常的随机梯度下降的规则是一样的,除了有一个\gls*{weight}下降 -的因子 $1-\frac{\eta \lambda}{n}$。最后,为了完整,我给出\gls*{bias}的规范化的学习规则。 -这当然是和我们之前的非规范化的情形一致了(参考公式~\eqref{eq:21}), + 范化的)代价。这其实和之前通常的\gls*{sgd}的规则是一样的,除了有一个\gls*{weight}下降 +的因子 $1-\frac{\eta \lambda}{n}$。最后,为了完整,我给出\gls*{bias}的\gls*{regularization}的学习规则。 +这当然是和我们之前的非\gls*{regularization}的情形一致了(参考公式~\eqref{eq:21}), \begin{equation} b \rightarrow b - \frac{\eta}{m} \sum_x \frac{\partial C_x}{\partial b} \label{eq:94}\tag{94} \end{equation} 这里求和也是在训练样本的\gls*{mini-batch} $x$ 上进行的。 -让我们看看规范化给网络带来的性能提升吧。这里还会使用有 $30$ 个隐藏神经元、\gls*{mini-batch} +让我们看看\gls*{regularization}给网络带来的性能提升吧。这里还会使用有 $30$ 个隐藏神经元、\gls*{mini-batch} 大小为 $10$,\gls*{learning-rate}为 $0.5$,使用交叉熵的神经网络。然而,这次我们会使用规 范化参数为 $\lambda = 0.1$。注意在代码中,我们使用的变量名字为 \lstinline!lmbda!, 这是因为在 Python 中 \lstinline!lambda! 是关键字,有着不相关的含义。我也会再次使 用 \lstinline!test_data!,而不是 \lstinline!validation_data!。不过严格地讲,我们 应当使用 \lstinline!validation_data! 的,因为前面已经讲过了。这里我这样做,是因 -为这会让结果和非规范化的结果对比起来效果更加直接。你可以轻松地调整为 +为这会让结果和非\gls*{regularization}的结果对比起来效果更加直接。你可以轻松地调整为 \lstinline!validation_data!,你会发现有相似的结果。 \begin{lstlisting}[language=Python] @@ -958,7 +958,7 @@ \subsection{规范化} ... monitor_training_cost=True, monitor_training_accuracy=True) \end{lstlisting} -训练集上的代价函数持续下降,和前面无规范化的情况一样的规律\footnote{这个以及下一 +训练集上的\gls*{cost-func}持续下降,和前面无\gls*{regularization}的情况一样的规律\footnote{这个以及下一 个图像由程序 \href{https://github.com/mnielsen/neural-networks-and-deep-learning/blob/master/fig/overfitting.py}{overfitting.py} 生成。}: @@ -971,18 +971,18 @@ \subsection{规范化} \includegraphics[width=.6\textwidth]{regularized2} \end{center} -显然,规范化的使用能够解决\gls*{overfitting}的问题。而且,准确率相当高了,最高处达到了 +显然,\gls*{regularization}的使用能够解决\gls*{overfitting}的问题。而且,准确率相当高了,最高处达到了 87.1\%,相较于之前的 82.27\%。因此,我们几乎可以确信在 400 \gls*{epoch}之后持续训 -练会有更加好的结果。看起来,经实践检验,规范化让网络具有更好的泛化能力,显著地减 +练会有更加好的结果。看起来,经实践检验,\gls*{regularization}让网络具有更好的泛化能力,显著地减 轻了\gls*{overfitting}的影响。 如果我们摆脱人为的仅用 1,000 个训练图像的环境,转而用所有 50,000 图像的训练集, 会发生什么?当然,我们之前已经看到\gls*{overfitting}在大规模的数据上其实不是那么明显了。那规 范化能不能起到相应的作用呢?保持超参数和之前一样,30 \gls*{epoch}, \gls*{learning-rate}为 0.5, -\gls*{mini-batch}大小为 10。不过我们这里需要改变规范化参数。原因在于训练数据的大小已 +\gls*{mini-batch}大小为 10。不过我们这里需要改变\gls*{regularization}参数。原因在于训练数据的大小已 经从 $n=1,000$ 改成了 $n=50,000$,这个会改变\gls*{weight}衰减因子 $1-\frac{\eta\lambda}{n}$。如果我们持续使用 $\lambda = 0.1$ 就会产生很小的\gls*{weight}衰 -减,因此就将规范化的效果降低很多。我们通过修改为 $\lambda = 5.0$ 来补偿这种下降。 +减,因此就将\gls*{regularization}的效果降低很多。我们通过修改为 $\lambda = 5.0$ 来补偿这种下降。 好了,来训练网络,重新初始化\gls*{weight}: @@ -998,14 +998,14 @@ \subsection{规范化} \includegraphics[width=.6\textwidth]{code_samples/fig/regularized_full} \end{center} -这个结果很不错。第一,我们在测试集上的分类准确率在使用规范化后有了提升,从 +这个结果很不错。第一,我们在测试集上的分类准确率在使用\gls*{regularization}后有了提升,从 95.49\% 到 96.49\%。这是个很大的进步。第二,我们可以看到在训练数据和测试数据上的 结果之间的差距也更小了。这仍然是一个大的差距,不过我们已经显著得到了本质上的降低 \gls*{overfitting}的进步。 -最后,我们看看在使用 100 个隐藏神经元和规范化参数为 $\lambda = 5.0$ 相应的测试分 +最后,我们看看在使用 100 个隐藏神经元和\gls*{regularization}参数为 $\lambda = 5.0$ 相应的测试分 类准确率。我不会给出详细分析,纯粹为了好玩,来看看我们使用一些技巧(交叉熵函数和 - L2 规范化)能够达到多高的准确率。 + L2 \gls*{regularization})能够达到多高的准确率。 \begin{lstlisting}[language=Python] >>> net = network2.Network([784, 100, 10], cost=network2.CrossEntropyCost) @@ -1020,16 +1020,16 @@ \subsection{规范化} $\eta=0.1$ 和$\lambda = 5.0$。我们就突破了 98\%,达到了98.04\% 的分类准确 率\label{98percent}。对于 152 行代码这个效果还真不错! -我已经把规范化描述为一种减轻\gls*{overfitting}和提高分类准确率的方法。实际上,这不是仅有的 +我已经把\gls*{regularization}描述为一种减轻\gls*{overfitting}和提高分类准确率的方法。实际上,这不是仅有的 好处。实践表明,在使用不同的(随机)\gls*{weight}初始化进行多次 MNIST 网络训练的时候,我发 -现无规范化的网络会偶然被限制住,明显困在了代价函数的局部最优值处。结果就是不同的 -运行会给出相差很大的结果。对比看来,规范化的网络能够提供更容易复制的结果。 +现无\gls*{regularization}的网络会偶然被限制住,明显困在了\gls*{cost-func}的局部最优值处。结果就是不同的 +运行会给出相差很大的结果。对比看来,\gls*{regularization}的网络能够提供更容易复制的结果。 -为何会这样子?从经验上看,如果代价函数是无规范化的,那么\gls*{weight}向量的长度可能会增长, +为何会这样子?从经验上看,如果\gls*{cost-func}是无\gls*{regularization}的,那么\gls*{weight}向量的长度可能会增长, 而其他的东西都保持一样。随着时间的推移,这会导致\gls*{weight}向量变得非常大。所以会使得权 重向量卡在朝着更多还是更少的方向上变化,因为当长度很大的时候梯度下降带来的变化仅 仅会引起在那个方向发生微小的变化。我相信这个现象让我们的学习算法更难有效地探索权 -重空间,最终导致很难找到代价函数的最优值。 +重空间,最终导致很难找到\gls*{cost-func}的最优值。 \subsection{为何规范化可以帮助减轻过度拟合} @@ -1061,7 +1061,7 @@ \subsection{为何规范化可以帮助减轻过度拟合} % plot(x2,y2,'b') % hold off -我们已经看到了规范化在实践中能够减少\gls*{overfitting}了。这是令人振奋的,不过,这背后的原因 +我们已经看到了\gls*{regularization}在实践中能够减少\gls*{overfitting}了。这是令人振奋的,不过,这背后的原因 还不得而知!通常的说法是:小的\gls*{weight}在某种程度上,意味着更低的复杂性,也就对数据给出了一 种更简单却更强大解释,因此应该优先选择。这虽然很简短,不过暗藏了一些可能看 起来会令人困惑的因素。让我们将这个解释细化,认真地研究一下。现在给一个简单的数据 @@ -1115,11 +1115,11 @@ \subsection{为何规范化可以帮助减轻过度拟合} 让我们从这个观点来看神经网络。假设神经网络大多数有很小的\gls*{weight},这最可能出现在规范 化的网络中。更小的\gls*{weight}意味着网络的行为不会因为我们随便改变了一个输入而改变太大。 -这会让规范化网络学习局部噪声的影响更加困难。将它看做是一种让单个的证据不会影响网 -络输出太多的方式。相对的,规范化网络学习去对整个训练集中经常出现的证据进行反应。 +这会让\gls*{regularization}网络学习局部噪声的影响更加困难。将它看做是一种让单个的证据不会影响网 +络输出太多的方式。相对的,\gls*{regularization}网络学习去对整个训练集中经常出现的证据进行反应。 对比看,大\gls*{weight}的网络可能会因为输入的微小改变而产生比较大的行为改变。所以一个无规 范化的网络可以使用大的\gls*{weight}来学习包含训练数据中的噪声的大量信息的复杂模型。简言之, -规范化网络受限于根据训练数据中常见的模式来构造相对简单的模型,而能够抵抗训练数据 +\gls*{regularization}网络受限于根据训练数据中常见的模式来构造相对简单的模型,而能够抵抗训练数据 中的噪声的特性影响。我们的想法就是这可以让我们的网络对看到的现象进行真实的学习, 并能够根据已经学到的知识更好地进行泛化。 @@ -1159,15 +1159,15 @@ \subsection{为何规范化可以帮助减轻过度拟合} 的工作。第二,即使我们可以做出这样一个判断,简单性也是一个使用时需要相当小心的指 导!第三,对模型真正的测试不是简单性,而是它在新场景中对新的活动中的预测能力。 -所以,我们应当时时记住这一点,规范化的神经网络常常能够比非规范化的泛化能力更强, +所以,我们应当时时记住这一点,\gls*{regularization}的神经网络常常能够比非\gls*{regularization}的泛化能力更强, 这只是一种实验事实(empirical fact)。所以,本书剩下的内容,我们也会频繁地使用规 范化技术。我已经在上面讲过了为何现在还没有一个人能够发展出一整套具有说服力的关于 -规范化可以帮助网络泛化的理论解释。实际上,研究者们不断地在写自己尝试不同的规范化 -方法,然后看看哪种表现更好,尝试理解为何不同的观点表现的更好。所以你可以将规范化 +\gls*{regularization}可以帮助网络泛化的理论解释。实际上,研究者们不断地在写自己尝试不同的\gls*{regularization} +方法,然后看看哪种表现更好,尝试理解为何不同的观点表现的更好。所以你可以将\gls*{regularization} 看做某种任意整合的技术。尽管其效果不错,但我们并没有一套完整的关于所发生情况的理 解,仅仅是一些不完备的启发式规则或者经验。 -这里也有更深的问题,这个问题也是有关科学的关键问题~——~我们如何泛化。规范化能够给我 +这里也有更深的问题,这个问题也是有关科学的关键问题~——~我们如何泛化。\gls*{regularization}能够给我 们一种计算上的魔力帮助神经网络更好地泛化,但是并不会带来原理上理解的指导,甚至不 会告诉我们什么样的观点才是最好的\footnote{这些问题追溯到 \href{http://en.wikipedia.org/wiki/Problem_of_induction}{problem of @@ -1182,8 +1182,8 @@ \subsection{为何规范化可以帮助减轻过度拟合} 的图片,他就能快速地学会认识其他的大象。当然,他们偶尔也会搞错,很可能将一只犀牛 误认为大象,但是一般说来,这个过程会相当准确。所以我们有个系统~——~人的大脑~——~拥有超 大量的自由变量。在受到仅仅少量的训练图像后,系统学会了在其他图像上的推广。某种程度 -上,我们的大脑的规范化做得特别好!怎么做的?现在还不得而知。我期望若干年后,我们 -能够发展出更加强大的技术来规范化神经网络,最终这些技术会让神经网络甚至在小的训练 +上,我们的大脑的\gls*{regularization}做得特别好!怎么做的?现在还不得而知。我期望若干年后,我们 +能够发展出更加强大的技术来\gls*{regularization}神经网络,最终这些技术会让神经网络甚至在小的训练 集上也能够学到强大的泛化能力。 实际上,我们的网络已经比我们预先期望的要好一些了。拥有 100 个隐藏元的网络会有接 @@ -1192,77 +1192,77 @@ \subsection{为何规范化可以帮助减轻过度拟合} 网络实际上却泛化得很好。为什么?这一点并没有很好地理解。这里有个猜想\footnote{在 \href{http://yann.lecun.com/exdb/publis/pdf/lecun-01a.pdf}{Gradient-Based Learning Applied to Document Recognition} 中,作者为 Yann LeCun, Léon Bottou, - Yoshua Bengio, 和 Patrick Haffner (1998)。}:梯度下降学习的动态有一种自规范化的 + Yoshua Bengio, 和 Patrick Haffner (1998)。}:梯度下降学习的动态有一种自\gls*{regularization}的 效应。这真是一个意料之外的巧合,但也带来了对于这种现象本质无知的不安。不过,我们 -还是会在后面依照这种实践的观点来应用规范化技术的。神经网络也是由于这点表现才更好 +还是会在后面依照这种实践的观点来应用\gls*{regularization}技术的。神经网络也是由于这点表现才更好 一些。 -现在我们回到前面留下来的一个细节:L2 规范化没有限制\gls*{bias},以此作为本节的结论。当然 -了,对规范化的过程稍作调整就可以对\gls*{bias}进行规范了。实践看来,做出这样的调整并不会 -对结果改变太多,所以,在某种程度上,对不对\gls*{bias}进行规范化其实就是一种习惯了。然而, +现在我们回到前面留下来的一个细节:L2 \gls*{regularization}没有限制\gls*{bias},以此作为本节的结论。当然 +了,对\gls*{regularization}的过程稍作调整就可以对\gls*{bias}进行规范了。实践看来,做出这样的调整并不会 +对结果改变太多,所以,在某种程度上,对不对\gls*{bias}进行\gls*{regularization}其实就是一种习惯了。然而, 需要注意的是,有一个大的\gls*{bias}并不会像大的\gls*{weight}那样会让神经元对输入太过敏感。所以我 们不需要对大的\gls*{bias}所带来的学习训练数据的噪声太过担心。同时,允许大的\gls*{bias}能够让网 络更加灵活~——~因为,大的\gls*{bias}让神经元更加容易饱和,这有时候是我们所要达到的效果。所 -以,我们通常不会对\gls*{bias}进行规范化。 +以,我们通常不会对\gls*{bias}进行\gls*{regularization}。 \subsection{规范化的其他技术} -除了 L2 外还有很多规范化技术。实际上,正是由于数量众多,我这里也不会将所有的都列 -举出来。在本节,我简要地给出三种减轻\gls*{overfitting}的其他的方法:L1 规范化、弃权和人为 +除了 L2 外还有很多\gls*{regularization}技术。实际上,正是由于数量众多,我这里也不会将所有的都列 +举出来。在本节,我简要地给出三种减轻\gls*{overfitting}的其他的方法:L1 \gls*{regularization}、\gls*{dropout}和人为 增加训练样本。我们不会像上面介绍得那么深入。其实,目的只是想让读者熟悉这些主要的 -思想,然后来体会一下规范化技术的多样性。\\ +思想,然后来体会一下\gls*{regularization}技术的多样性。\\ -\textbf{L1 规范化\index{L1 规范化}:} 这个方法是在未规范化的代价函数上加上一个权 +\textbf{L1 \gls*{regularization}:} 这个方法是在未\gls*{regularization}的\gls*{cost-func}上加上一个权 重绝对值的和: \begin{equation} C = C_0 + \frac{\lambda}{n} \sum_w |w| \label{eq:95}\tag{95} \end{equation} -凭直觉地看,这和 L2 规范化相似,惩罚大的\gls*{weight},倾向于让网络优先选择小的\gls*{weight}。当然, -L1 规范化和 L2 规范化并不相同,所以我们不应该期望从 L1 规范化得到完全同样的行为。 -让我们来试着理解使用 L1 规范化训练的网络和 L2 规范化训练的网络所不同的行为。 +凭直觉地看,这和 L2 \gls*{regularization}相似,惩罚大的\gls*{weight},倾向于让网络优先选择小的\gls*{weight}。当然, +L1 \gls*{regularization}和 L2 \gls*{regularization}并不相同,所以我们不应该期望从 L1 \gls*{regularization}得到完全同样的行为。 +让我们来试着理解使用 L1 \gls*{regularization}训练的网络和 L2 \gls*{regularization}训练的网络所不同的行为。 -首先,我们会研究一下代价函数的偏导数。对 \eqref{eq:95} 求导我们有: +首先,我们会研究一下\gls*{cost-func}的偏导数。对 \eqref{eq:95} 求导我们有: \begin{equation} \frac{\partial C}{\partial w} = \frac{\partial C_0}{\partial w} + \frac{\lambda}{n} \, {\rm sgn}(w) \label{eq:96}\tag{96} \end{equation} 其中 ${\rm sgn}(w)$ 就是 $w$ 的正负号,即 $w$ 是正数时为 $+1$,而 $w$ 为负数时为 -$-1$。使用这个表达式,我们可以轻易地对\gls*{bp}进行修改从而使用基于 L1 规范化的随 -机梯度下降进行学习。对 L1 规范化的网络进行更新的规则就是 +$-1$。使用这个表达式,我们可以轻易地对\gls*{bp}进行修改从而使用基于 L1 \gls*{regularization}的随 +机梯度下降进行学习。对 L1 \gls*{regularization}的网络进行更新的规则就是 \begin{equation} w \rightarrow w' = w-\frac{\eta \lambda}{n} \mbox{sgn}(w) - \eta \frac{\partial C_0}{\partial w} \label{eq:97}\tag{97} \end{equation} 其中和往常一样,我们可以用一个\gls*{mini-batch}的均值来估计 $\partial C_0/\partial w$。对 -比 L2 规范化的更新规则(参见方程~\eqref{eq:93}), +比 L2 \gls*{regularization}的更新规则(参见方程~\eqref{eq:93}), \begin{equation} w \rightarrow w' = w\left(1 - \frac{\eta \lambda}{n} \right) - \eta \frac{\partial C_0}{\partial w} \label{eq:98}\tag{98} \end{equation} -在两种情形下,规范化的效果就是缩小\gls*{weight}。这符合我们的直觉,两种规范化都惩罚大的权 -重。但\gls*{weight}缩小的方式不同。在 L1 规范化中,\gls*{weight}通过一个常量向 $0$ 进行缩小。在 L2 -规范化中,\gls*{weight}通过一个和 $w$ 成比例的量进行缩小的。所以,当一个特定的\gls*{weight}绝对值 -$|w|$ 很大时,L1 规范化的\gls*{weight}缩小得远比 L2 规范化要小得多。相反,当一个特定的权 -重绝对值 $|w|$ 很小时,L1 规范化的\gls*{weight}缩小得要比 L2 规范化大得多。最终的结果就是: -L1 规范化倾向于聚集网络的\gls*{weight}在相对少量的高重要度连接上,而其他\gls*{weight}就会被驱使向 +在两种情形下,\gls*{regularization}的效果就是缩小\gls*{weight}。这符合我们的直觉,两种\gls*{regularization}都惩罚大的权 +重。但\gls*{weight}缩小的方式不同。在 L1 \gls*{regularization}中,\gls*{weight}通过一个常量向 $0$ 进行缩小。在 L2 +\gls*{regularization}中,\gls*{weight}通过一个和 $w$ 成比例的量进行缩小的。所以,当一个特定的\gls*{weight}绝对值 +$|w|$ 很大时,L1 \gls*{regularization}的\gls*{weight}缩小得远比 L2 \gls*{regularization}要小得多。相反,当一个特定的权 +重绝对值 $|w|$ 很小时,L1 \gls*{regularization}的\gls*{weight}缩小得要比 L2 \gls*{regularization}大得多。最终的结果就是: +L1 \gls*{regularization}倾向于聚集网络的\gls*{weight}在相对少量的高重要度连接上,而其他\gls*{weight}就会被驱使向 $0$ 接近。 我在上面的讨论中其实忽略了一个问题~——~在 $w=0$ 的时候,偏导数 $\partial C/\partial w$ 未定义。原因在于函数 $|w|$ 在 $w=0$ 时有个“直角”,事实上,导数是 -不存在的。不过也没有关系。我们下面要做的就是应用通常的(无规范化的)随机梯度下降 -的规则在 $w=0$ 处。这应该不会有什么问题,凭直觉地看,规范化的效果就是缩小\gls*{weight}, +不存在的。不过也没有关系。我们下面要做的就是应用通常的(无\gls*{regularization}的)\gls*{sgd} +的规则在 $w=0$ 处。这应该不会有什么问题,凭直觉地看,\gls*{regularization}的效果就是缩小\gls*{weight}, 显然,不能对一个已经是 $0$ 的\gls*{weight}进行缩小了。更准确地说,我们将会使用方 程~\eqref{eq:96} 和~\eqref{eq:97} 并约定 $\mbox{sgn}(0) = 0$。这样就给出了一种细 -致又紧凑的规则来进行采用 L1 规范化的随机梯度下降学习。\\ +致又紧凑的规则来进行采用 L1 \gls*{regularization}的\gls*{sgd}学习。\\ -\textbf{弃权:} 弃权\index{弃权}(Dropout\index{Dropout})是一种相当激进的技术。 -和 L1、L2 规范化不同,弃权技术并不依赖对代价函数的修改。而是,在弃权中,我们改变 -了网络本身。在介绍它为什么能工作,以及所得到的结果前,让我描述一下弃权基本的工作 +\textbf{\gls{dropout}:} \gls*{dropout}是一种相当激进的技术。 +和 L1、L2 \gls*{regularization}不同,\gls*{dropout}技术并不依赖对\gls*{cost-func}的修改。而是,在\gls*{dropout}中,我们改变 +了网络本身。在介绍它为什么能工作,以及所得到的结果前,让我描述一下\gls*{dropout}基本的工作 机制。 假设我们尝试训练一个网络: @@ -1271,25 +1271,25 @@ \subsection{规范化的其他技术} \end{center} 特别地,假设我们有一个训练数据 $x$ 和 对应的目标输出 $y$。通常我们会通过在网络中 -前向传播 $x$ ,然后进行\gls*{bp}来确定对梯度的贡献。使用弃权技术,这个过程就改了。 +前向传播 $x$ ,然后进行\gls*{bp}来确定对梯度的贡献。使用\gls*{dropout}技术,这个过程就改了。 我们会从随机(临时)地删除网络中的一半的隐藏神经元开始,同时让输入层和输出层的神经元保持 -不变。在此之后,我们会得到最终如下线条所示的网络。注意那些被弃权的神经元,即那些临时被删 +不变。在此之后,我们会得到最终如下线条所示的网络。注意那些被\gls*{dropout}的神经元,即那些临时被删 除的神经元,用虚圈表示在图中: \begin{center} \includegraphics{tikz31} \end{center} 我们前向传播输入 $x$,通过修改后的网络,然后\gls*{bp}结果,同样通过这个修改后的网 -络。在一个\minibatch{小批量}的若干样本上进行这些步骤后,我们对有关的\gls*{weight}和\gls*{bias}进 -行更新。然后重复这个过程,首先重置弃权的神经元,然后选择一个新的随机的隐藏神经元 +络。在一个\gls*{mini-batch}的若干样本上进行这些步骤后,我们对有关的\gls*{weight}和\gls*{bias}进 +行更新。然后重复这个过程,首先重置\gls*{dropout}的神经元,然后选择一个新的随机的隐藏神经元 的子集进行删除,估计对一个不同的\gls*{mini-batch}的梯度,然后更新\gls*{weight}和\gls*{bias}。 通过不断地重复,我们的网络会学到一个\gls*{weight}和\gls*{bias}的集合。当然,这些\gls*{weight}和\gls*{bias}也是在 -一半的隐藏神经元被弃权的情形下学到的。当我们实际运行整个网络时,是指两倍的隐藏神 +一半的隐藏神经元被\gls*{dropout}的情形下学到的。当我们实际运行整个网络时,是指两倍的隐藏神 经元将会被激活。为了补偿这个,我们将从隐藏神经元出去的\gls*{weight}减半。 -这个弃权过程可能看起来奇怪,像是临时安排的。为什么我们会指望这样的方法能够进行规 -范化呢?为了解释所发生的事,我希望你停下来想一下标准(没有弃权)的训练方式。 +这个\gls*{dropout}过程可能看起来奇怪,像是临时安排的。为什么我们会指望这样的方法能够进行规 +范化呢?为了解释所发生的事,我希望你停下来想一下标准(没有\gls*{dropout})的训练方式。 特别地,想象一下我们训练几个不同的神经网络,都使用同一个训练数据。当然,网络可能 不是从同一初始状态开始的,最终的结果也会有一些差异。出现这种情况时,我们可以使用 一些平均或者投票的方式来确定接受哪个输出。例如,如果我们训练了五个网络,其中三个 @@ -1297,9 +1297,9 @@ \subsection{规范化的其他技术} 方式通常是一种强大(尽管代价昂贵)的方式来减轻\gls*{overfitting}。原因在于不同的网络可能会 以不同的方式\gls*{overfitting},平均法可能会帮助我们消除那样的\gls*{overfitting}。 -那么这和弃权有什么关系呢?启发式地看,当我们弃权掉不同的神经元集合时,有点像我们 -在训练不同的神经网络。所以,弃权过程就如同大量不同网络的效果的平均那样。不同的网 -络会以不同的方式\gls*{overfitting}了,所以,弃权过的网络的效果会减轻\gls*{overfitting}。 +那么这和\gls*{dropout}有什么关系呢?启发式地看,当我们\gls*{dropout}掉不同的神经元集合时,有点像我们 +在训练不同的神经网络。所以,\gls*{dropout}过程就如同大量不同网络的效果的平均那样。不同的网 +络会以不同的方式\gls*{overfitting}了,所以,\gls*{dropout}过的网络的效果会减轻\gls*{overfitting}。 一个相关的启发式解释\label{dropout_explanation}在早期使用这项技术的论文中曾经给 出\footnote{\href{https://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf}{ImageNet @@ -1307,30 +1307,30 @@ \subsection{规范化的其他技术} Ilya Sutskever, 和 Geoffrey Hinton (2012)。}:“因为神经元不能依赖其他神经元特 定的存在,这个技术其实减少了复杂的互适应的神经元。所以,强制要学习那些在神经元的 不同随机子集中更加健壮的特征。” 换言之,如果我们将我们的神经网络看做一个进行预 -测的模型的话,我们就可以将弃权看做是一种确保模型对于一部分证据丢失健壮的方式。这 -样看来,弃权和 L1、L2 规范化也是有相似之处的,这也倾向于更小的\gls*{weight},最后让网络对 +测的模型的话,我们就可以将\gls*{dropout}看做是一种确保模型对于一部分证据丢失健壮的方式。这 +样看来,\gls*{dropout}和 L1、L2 \gls*{regularization}也是有相似之处的,这也倾向于更小的\gls*{weight},最后让网络对 丢失个体连接的场景更加健壮。 -当然,弃权技术的真正衡量是它已经在提升神经网络性能上应用得相当成功。原始论 +当然,\gls*{dropout}技术的真正衡量是它已经在提升神经网络性能上应用得相当成功。原始论 文\footnote{\href{http://arxiv.org/pdf/1207.0580.pdf}{Improving neural networks by preventing co-adaptation of feature detectors}, 作者为 Geoffrey Hinton, Nitish Srivastava, Alex Krizhevsky, Ilya Sutskever, 和 Ruslan Salakhutdinov (2012)。 注意这篇论文讨论了许多细微之处,这些我在这个简要的介绍里敷衍地带过了。}介 -绍了将其应用于很多不同任务的技术。对我们来说,特别感兴趣的是他们应用弃权在 MNIST +绍了将其应用于很多不同任务的技术。对我们来说,特别感兴趣的是他们应用\gls*{dropout}在 MNIST 数字分类上,使用了一个和我们之前介绍的那种普通的前向神经网络。这篇论文提及到当前 -最好的结果是在测试集上取得了 98.4\% 的准确率。他们使用弃权和 L2 规范化的组合将其 +最好的结果是在测试集上取得了 98.4\% 的准确率。他们使用\gls*{dropout}和 L2 \gls*{regularization}的组合将其 提高到了 98.7\%。类似重要的结果在其他不同的任务上也取得了一定的成效,包括图像和 -语音识别、自然语言处理。弃权技术在训练大规模深度网络时尤其有用,这样的网络中过度 +语音识别、自然语言处理。\gls*{dropout}技术在训练大规模深度网络时尤其有用,这样的网络中过度 拟合问题经常特别突出。\\ \textbf{人为扩展训练数据:} 我们前面看到了 MNIST 分类准确率在我们使用 1,000 幅训 练图像时候下降到了 80\% 中间的准确率。这种情况并不奇怪,因为更少的训练数据意味着 我们的网络接触更少的人类手写的数字中的变化。让我们训练 30 个隐藏神经元的网络,使 用不同的训练数据集,来看看性能的变化情况。我们使用\gls*{mini-batch}大小为 $10$,% -\gls*{learning-rate}为 $\eta=0.5$,规范化参数是 $\lambda=5.0$,交叉熵代价函数。 +\gls*{learning-rate}为 $\eta=0.5$,\gls*{regularization}参数是 $\lambda=5.0$,交叉熵\gls*{cost-func}。 我们在全部训练数据集合上训练 30 个\gls*{epoch},然后会随着训练数据量的下降而成比 例增加\gls*{epoch}的数量。为了确保\gls*{weight}衰减因子在训练数据集上相同,我们 -会在全部训练集上使用规范化参数为 $\lambda = 5.0$,然后在使用更小的训练集的时候成 +会在全部训练集上使用\gls*{regularization}参数为 $\lambda = 5.0$,然后在使用更小的训练集的时候成 比例地降低$\lambda$ 值\footnote{这个以及下两个图形由程序 \href{https://github.com/mnielsen/neural-networks-and-deep-learning/blob/master/fig/more_data.py}{\lstinline!more_data.py!} 生成}。 @@ -1435,7 +1435,7 @@ \subsection*{练习} 在特定的训练数据集上的应用效果。那些给出基准数据集的人们会拥有更大的研究经费支持, 这样能够获得更好的训练数据。所以,很可能他们由于超赞的技术的性能提升其实在更大的 数据集合上就丧失了。换言之,人们标榜的提升可能就是历史的偶然。所以需要记住的特别 -是在实际应用中,我们想要的是更好的算法\emph{和}更好的训练数据。寻找更好的算法很 +是在实际应用中,我们想要的是更好的算法\textbf{和}更好的训练数据。寻找更好的算法很 好,不过需要确保你在此过程中,没有放弃对更多更好的数据的追求。 \subsection*{问题} @@ -1448,9 +1448,9 @@ \subsection*{问题} 理论上的验证方法吗?如果可以,比较不同的机器学习算法的渐近性能。 \end{itemize} -\textbf{总结:} 我们现在已经介绍完了\gls*{overfitting}和规范化。当然,我们会重回这个问题。 +\textbf{总结:} 我们现在已经介绍完了\gls*{overfitting}和\gls*{regularization}。当然,我们会重回这个问题。 正如我已经提过了几次,\gls*{overfitting}是神经网络中一个重要的问题,尤其是计算机越来越强大, -我们有训练更大的网络的能力时。我们有迫切的需要来开发出强大的规范化技术来减轻过度 +我们有训练更大的网络的能力时。我们有迫切的需要来开发出强大的\gls*{regularization}技术来减轻过度 拟合,而这也是当前研究的极其活跃的领域。 \section{权重初始化} @@ -1459,7 +1459,7 @@ \section{权重初始化} 创建了神经网络后,我们需要进行\gls*{weight}和\gls*{bias}的初始化。到现在,我们一直是根据在% \hyperref[ch:UsingNeuralNetsToRecognizeHandwrittenDigits]{第一章中}介绍的那样进 行初始化。提醒你一下,之前的方式就是根据独立高斯随机变量来选择\gls*{weight}和\gls*{bias},其被归 -一化为均值为 $0$,标准差 $1$。这个方法工作的还不错,但是非常\emph{特别},所以值 +一化为均值为 $0$,标准差 $1$。这个方法工作的还不错,但是非常\textbf{特别},所以值 得去重新探讨它,看看是否能寻找一些更好的方式来设置初始的\gls*{weight}和\gls*{bias},这也许能帮助 我们的网络学习得更快。 @@ -1484,12 +1484,12 @@ \section{权重初始化} -1$。如果是这样,隐藏神经元的输出 $\sigma(z)$ 就会接近 $1$ 或者 $0$。也就表示我 们的隐藏神经元会饱和。所以当出现这样的情况时,在\gls*{weight}中进行微小的调整仅仅会给隐藏 神经元的激活值带来极其微弱的改变。而这种微弱的改变也会影响网络中剩下的神经元,然 -后会带来相应的代价函数的改变。结果就是,这些\gls*{weight}在我们进行梯度下降算法时会学习得 +后会带来相应的\gls*{cost-func}的改变。结果就是,这些\gls*{weight}在我们进行梯度下降算法时会学习得 非常缓慢\footnote{我们在第二章详细讨论过这个问题,其中我们用% \hyperref[sec:the_four_fundamental_equations_behind_backpropagation]{\gls*{bp} 的方程}来显示输入到饱和神经元的\gls*{weight}学习得缓慢。}。这其实和我们在本章前面讨论 的问题差不多,前面的情况是输出神经元在错误的值上饱和导致学习的下降。我们之前通过 -代价函数的选择解决了前面的问题。不幸的是,尽管那种方式在输出神经元上有效,但对于 +\gls*{cost-func}的选择解决了前面的问题。不幸的是,尽管那种方式在输出神经元上有效,但对于 隐藏神经元的饱和却一点作用都没有。 我已经讨论了第一个隐藏层的\gls*{weight}输入。当然,类似的论证也适用于后面的隐藏层:如果后 @@ -1525,7 +1525,7 @@ \subsection*{练习} 们后面还会按照前面的方式来进行初始化。 让我们在 MNIST 数字分类任务上比较一下新旧两种\gls*{weight}初始化方式。同样,还是使用 $30$ -个隐藏元,\minibatch 的大小为 $10$,规范化参数 $\lambda = 5.0$,然后是交叉熵代价 +个隐藏元,\gls*{mini-batch} 的大小为 $10$,\gls*{regularization}参数 $\lambda = 5.0$,然后是交叉熵代价 函数。我们将\gls*{learning-rate}从 $\eta=0.5$ 降到 $0.1$,因为这样会让结果在图像中表 现得更加明显。我们先使用旧的\gls*{weight}初始化方法训练: @@ -1586,7 +1586,7 @@ \subsection*{练习} \subsection*{问题} \begin{itemize} -\item \textbf{将规范化和改进的\gls*{weight}初始化方法结合使用} L2 规范化有时候会自动给我 +\item \textbf{将\gls*{regularization}和改进的\gls*{weight}初始化方法结合使用} L2 \gls*{regularization}有时候会自动给我 们一些类似于新的初始化方法的东西。假设我们使用旧的初始化\gls*{weight}的方法。考虑一个启 发式的观点:(1)假设 $\lambda$ 不太小,训练的第一\gls*{epoch}将会几乎被\gls*{weight}衰减统 治;(2)如果 $\eta \lambda \ll n$,\gls*{weight}会按照因子 $\exp(-\eta \lambda / m)$ 每 @@ -1668,14 +1668,14 @@ \section{再看手写识别问题:代码} \end{lstlisting} 让我们分解一下。第一个看到的是:即使使用的是交叉熵,数学上看,就是一个函数,这里 -我们用 Python 的类而不是 Python 函数实现了它。为什么这样做呢?答案就是代价函数在 +我们用 Python 的类而不是 Python 函数实现了它。为什么这样做呢?答案就是\gls*{cost-func}在 我们的网络中扮演了两种不同的角色。明显的角色就是代价是输出激活值 $a$ 和目标输出 $y$ 差距优劣的度量。这个角色通过 \lstinline!CrossEntropyCost.fn! 方法来扮演。 (注意,\lstinline!np.nan_to_num! 调用确保了 Numpy 正确处理接近 $0$ 的对数值)但 -是代价函数其实还有另一个角色。回想% +是\gls*{cost-func}其实还有另一个角色。回想% \hyperref[sec:the_four_fundamental_equations_behind_backpropagation]{第二章}中运 行\gls*{bp}算法时,我们需要计算网络输出误差,$\delta^L$。这种形式的输出误差依赖于 -代价函数的选择:不同的代价函数,输出误差的形式就不同。对于交叉熵函数,输出误差就 +\gls*{cost-func}的选择:不同的\gls*{cost-func},输出误差的形式就不同。对于交叉熵函数,输出误差就 如公式~\eqref{eq:66}所示: \begin{equation} \delta^L = a^L-y @@ -1686,10 +1686,10 @@ \section{再看手写识别问题:代码} 道如何进行输出误差的计算。然后我们将这两个组合在一个包含所有需要知道的有关代价函 数信息的类中。 -类似地,\lstinline!network2.py! 还包含了一个表示二次代价函数的类。这个是用来和第 +类似地,\lstinline!network2.py! 还包含了一个表示二次\gls*{cost-func}的类。这个是用来和第 一章的结果进行对比的,因为后面我们几乎都在使用交叉函数。代码如下。 \lstinline!QuadraticCost.fn! 方法是关于网络输出 $a$ 和目标输出 $y$ 的二次代价函 -数的直接计算结果。由 \lstinline!QuadraticCost.delta! 返回的值基于二次代价函数的 +数的直接计算结果。由 \lstinline!QuadraticCost.delta! 返回的值基于二次\gls*{cost-func}的 误差表达式~\eqref{eq:30},我们在第二章中得到它。 \begin{lstlisting}[language=Python] class QuadraticCost(object): @@ -1705,14 +1705,14 @@ \section{再看手写识别问题:代码} 现在,我们理解了 \lstinline!network2.py! 和 \lstinline!network.py! 两个实现之间 的主要差别。都是很简单的东西。还有一些更小的变动,下面我们会进行介绍,包含 L2 规 -范化的实现。在讲述规范化之前,我们看看 \lstinline!network2.py! 完整的实现代码。 +范化的实现。在讲述\gls*{regularization}之前,我们看看 \lstinline!network2.py! 完整的实现代码。 你不需要太仔细地读遍这些代码,但是对整个结构尤其是文档中的内容的理解是非常重要的, 这样,你就可以理解每段程序所做的工作。当然,你也可以随自己意愿去深入研究!如果你 迷失了理解,那么请读读下面的讲解,然后再回到代码中。不多说了,给代码: \lstinputlisting[language=Python]{code_samples/src/network2.py} -有个更加有趣的变动就是在代码中增加了 L2 规范化。尽管这是一个主要的概念上的变动, +有个更加有趣的变动就是在代码中增加了 L2 \gls*{regularization}。尽管这是一个主要的概念上的变动, 在实现中其实相当简单。对大部分情况,仅仅需要传递参数 \lstinline!lmbda! 到不同的 方法中,主要是 \lstinline!Network.SGD! 方法。实际上的工作就是一行代码的事在 \lstinline!Network.update_mini_batch! 的倒数第四行。这就是我们改动梯度下降规则来 @@ -1722,7 +1722,7 @@ \section{再看手写识别问题:代码} 范化。概念的理解非常微妙困难。但是添加到程序中的时候却如此简单。精妙复杂的技术可 以通过微小的代码改动就可以实现了。 -另一个微小却重要的改动是随机梯度下降方法的几个标志位的增加。这些标志位让我们可以 +另一个微小却重要的改动是\gls*{sgd}方法的几个标志位的增加。这些标志位让我们可以 对在代价和准确率的监控变得可能。这些标志位默认是 \lstinline!False! 的,但是在我 们例子中,已经被置为 \lstinline!True! 来监控 \lstinline!Network! 的性能。另外, \lstinline!network2.py! 中的 \lstinline!Network.SGD! 方法返回了一个四元组来表示 @@ -1739,7 +1739,7 @@ \section{再看手写识别问题:代码} \end{lstlisting} 所以,比如 \lstinline!evaluation_cost! 将会是一个 $30$ 个元素的列表其 -中包含了每个\gls*{epoch}在验证集合上的代价函数值。这种类型的信息在理解网络行为的过程 +中包含了每个\gls*{epoch}在验证集合上的\gls*{cost-func}值。这种类型的信息在理解网络行为的过程 中特别有用。比如,它可以用来画出展示网络随时间学习的状态。其实,这也是我在前面的 章节中展示性能的方式。然而要注意的是如果任何标志位都没有设置的话,对应的元组中的 元素就是空列表。 @@ -1759,10 +1759,10 @@ \section{再看手写识别问题:代码} \subsection*{问题} \begin{itemize} -\item 更改上面的代码来实现 L1 规范化,使用 L1 规范化使用 $30$ 个隐藏元的神经网络 - 对 MNIST 数字进行分类。你能够找到一个规范化参数使得比无规范化效果更好么? +\item 更改上面的代码来实现 L1 \gls*{regularization},使用 L1 \gls*{regularization}使用 $30$ 个隐藏元的神经网络 + 对 MNIST 数字进行分类。你能够找到一个\gls*{regularization}参数使得比无\gls*{regularization}效果更好么? \item 看看 \href{https://github.com/mnielsen/neural-networks-and-deep-learning/blob/master/src/network.py}{\lstinline!network.py!} 中的 \lstinline!Network.cost_derivative! 方法。 - 这个方法是为二次代价函数写的。怎样修改可以用于交叉熵代价函数上?你能不能想到可 + 这个方法是为二次\gls*{cost-func}写的。怎样修改可以用于交叉熵\gls*{cost-func}上?你能不能想到可 能在交叉熵函数上遇到的问题?在 \lstinline!network2.py! 中,我们已经去掉了 \lstinline!Network.cost_derivative! 方法,将其集成进了 `CrossEntropyCost.delta` 方法中。请问,这样是如何解决你已经发现的问题的? @@ -1771,11 +1771,11 @@ \subsection*{问题} \section{如何选择神经网络的超参数} \label{sec:how_to_choose_a_neural_network's_hyper-parameters} -直到现在,我们还没有解释对诸如\gls*{learning-rate} $\eta$,规范化参数 $\lambda$ 等等 +直到现在,我们还没有解释对诸如\gls*{learning-rate} $\eta$,\gls*{regularization}参数 $\lambda$ 等等 超参数选择的方法。我只是给出那些效果很好的值而已。实践中,当你使用神经网络解决问 题时,寻找好的超参数其实是很困难的一件事。例如,我们要解决 MNIST 问题,开始时对 于选择什么样的超参数一无所知。假设,刚开始的实验中选择前面章节的参数都是运气较好。 -但在使用\gls*{learning-rate} $\eta=10.0$ 而规范化参数 $\lambda=1000.0$。下面是我们的 +但在使用\gls*{learning-rate} $\eta=10.0$ 而\gls*{regularization}参数 $\lambda=1000.0$。下面是我们的 一个尝试: \begin{lstlisting}[language=Python] @@ -1809,12 +1809,12 @@ \section{如何选择神经网络的超参数} 我们分类准确率并不比随机选择更好。网络就像随机噪声产生器一样。 -你可能会说,“这好办,降低\gls*{learning-rate}和规范化参数就好了。”不幸的是,你并不 +你可能会说,“这好办,降低\gls*{learning-rate}和\gls*{regularization}参数就好了。”不幸的是,你并不 先验地知道这些就是需要调整的超参数。可能真正的问题出在 $30$ 个隐藏元中,本身就不 能很有效,不管我们如何调整其他的超参数都没有作用的?可能我们真的需要至少 $100$ 个隐藏神经元?或者是 $300$ 个隐藏神经元?或者更多层的网络?或者不同输出编码方式? 可能我们的网络一直在学习,只是学习的回合还不够?可能 minibatch 设的太小了?可能我 -们需要切换成二次代价函数?可能我们需要尝试不同的\gls*{weight}初始化方法?等等。很容易就在 +们需要切换成二次\gls*{cost-func}?可能我们需要尝试不同的\gls*{weight}初始化方法?等等。很容易就在 超参数的选择中迷失了方向。如果你的网络规模很大,或者使用了很多的训练数据,这种情 况就很令人失望了,因为一次训练可能就要几个小时甚至几天乃至几周,最终什么都没有获 得。如果这种情况一直发生,就会打击你的自信心。可能你会怀疑神经网络是不是适合你所 @@ -1825,7 +1825,7 @@ \section{如何选择神经网络的超参数} 会是一个能够完全解决的问题,也不存在一种通用的关于正确策略的共同认知。总是会有一 些新的技巧可以帮助你提高一点性能。但是本节的启发式想法能帮你开个好头。\\ -\textbf{宽泛策略:} 在使用神经网络来解决新的问题时,一个挑战就是获得\emph{任何} +\textbf{宽泛策略:} 在使用神经网络来解决新的问题时,一个挑战就是获得\textbf{任何} 一种非寻常的学习,也就是说,达到比随机的情况更好的结果。这个实际上会很困难,尤其 是遇到一种新类型的问题时。让我们看看有哪些策略可以在面临这类困难时候尝试。 @@ -1956,9 +1956,9 @@ \section{如何选择神经网络的超参数} 人生中很多情况一样~——~万事开头难。 好了,上面就是宽泛的策略。现在我们看看一些具体的设置超参数的推荐。我会聚焦在学习 -率 $\eta$,L2 规范化参数 +率 $\eta$,L2 \gls*{regularization}参数 $\lambda$,和\gls*{mini-batch}大小。然而,很多的观点同样可以应用在其他的超参数的选择上, -包括一些关于网络架构的、其他类型的规范化和一些本书后面遇到的如 momentum +包括一些关于网络架构的、其他类型的\gls*{regularization}和一些本书后面遇到的如 momentum co-efficient 这样的超参数。\\ \textbf{\gls*{learning-rate}:} 假设我们运行了三个不同\gls*{learning-rate}($\eta=0.025$、 @@ -1969,21 +1969,21 @@ \section{如何选择神经网络的超参数} \includegraphics[width=.6\textwidth]{multiple_eta} \end{center} -使用 $\eta=0.025$,代价函数平滑下降到最后的回合。使用 $\eta=0.25$,代价刚开始下降, +使用 $\eta=0.025$,\gls*{cost-func}平滑下降到最后的回合。使用 $\eta=0.25$,代价刚开始下降, 在大约 $20$ 回合后接近饱和状态,后面就是微小的震荡和随机抖动。最终使 用 $\eta=2.5$ 代价从始至终都震荡得非常明显。为了理解震荡的原因,回想一下随机梯度 -下降其实是期望我们能够逐渐地抵达代价函数的谷底的, +下降其实是期望我们能够逐渐地抵达\gls*{cost-func}的谷底的, \begin{center} \includegraphics{valley_with_ball} \end{center} 然而,如果 $\eta$ 太大的话,步长也会变大可能会使得算法在接近最小值时候又越过了谷 -底。这在 $\eta=2.5$ 时非常可能发生。当我们选择 $\eta=0.25$ 时,初始几步将我们带到 -了谷底附近,但一旦到达了谷底,又很容易跨越过去。而在我们选择 $\eta=0.025$ 时,在 -前 $30$ 回合的训练中不再受到这个情况的影响。当然,选择太小的\gls*{learning-rate},也会 -带来另一个问题~——~随机梯度下降算法变慢了。一种更加好的策略其实是,在开始时使 -用 $\eta=0.25$,随着越来越接近谷底,就换成 -$\eta=0.025$。这种可变\gls*{learning-rate}的方法我们后面会介绍。现在,我们就聚焦在找出 -一个单独的好的\gls*{learning-rate}的选择,$\eta$。 +底。这在 $\eta=2.5$ 时非常可能发生。当我们选择 $\eta=0.25$ 时,初始几步将我们带 +到了谷底附近,但一旦到达了谷底,又很容易跨越过去。而在我们选择 $\eta=0.025$ 时, +在前 $30$ 回合的训练中不再受到这个情况的影响。当然,选择太小的% +\gls*{learning-rate},也会带来另一个问题~——~\gls*{sgd}算法变慢了。一种更加好的 +策略其实是,在开始时使用 $\eta=0.25$,随着越来越接近谷底,就换成$\eta=0.025$。这 +种可变\gls*{learning-rate}的方法我们后面会介绍。现在,我们就聚焦在找出一个单独的 +好的\gls*{learning-rate}的选择,$\eta$。 所以,有了这样的想法,我们可以如下设置 $\eta$。首先,我们选择在训练数据上的代价立 即开始下降而非震荡或者增加时作为 $\eta$ 的阈值的估计。这个估计并不需要太过精确。 @@ -1999,14 +1999,14 @@ \section{如何选择神经网络的超参数} 你更应该使用稍微小一点的值,例如,阈值的一半这样的选择。这样的选择能够允许你训练 更多的回合,不会减慢学习的速度。 -在 MNIST 数据中,使用这样的策略会给出一个关于\gls*{learning-rate} $\eta$ 的一个量级的 -估计,大概是 $0.1$。在一些改良后,我们得到了阈值 $\eta=0.5$。所以,我们按照刚刚 -的取一半的策略就确定了\gls*{learning-rate}为 $\eta=0.25$。实际上,我发现使用 -$\eta=0.5$ 在 $30$ 回合内表现是很好的,所以选择更低的\gls*{learning-rate},也没有什么 -问题。 +在 MNIST 数据中,使用这样的策略会给出一个关于\gls*{learning-rate} $\eta$ 的一个 +量级的估计,大概是 $0.1$。在一些改良后,我们得到了阈值 $\eta=0.5$。所以,我们按 +照刚刚的取一半的策略就确定了\gls*{learning-rate}为 $\eta=0.25$。实际上,我发现使 +用 $\eta=0.5$ 在 $30$ 回合内表现是很好的,所以选择更低的\gls*{learning-rate},也 +没有什么问题。 -这看起来相当直接。然而,使用训练代价函数来选择 $\eta$ 看起来和我们之前提到的通过 -验证集来确定超参数的观点有点矛盾。实际上,我们会使用验证准确率来选择规范化超参数, +这看起来相当直接。然而,使用训练\gls*{cost-func}来选择 $\eta$ 看起来和我们之前提到的通过 +验证集来确定超参数的观点有点矛盾。实际上,我们会使用验证准确率来选择\gls*{regularization}超参数, minibatch 大小,和层数及隐藏元个数这些网络参数,等等。为何对\gls*{learning-rate}要用 不同的方法呢?坦白地说,这些选择其实是我个人美学偏好,个人习惯罢了。原因就是其他 的超参数倾向于提升最终的测试集上的分类准确率,所以将他们通过验证准确率来选择更合 @@ -2015,30 +2015,29 @@ \section{如何选择神经网络的超参数} 其实就是个人的偏好。在学习的前期,如果验证准确率提升,训练代价通常都在下降。所以 在实践中使用那种衡量方式并不会对判断的影响太大。\\ -\textbf{使用提前停止\label{early_stopping}来确定训练的\gls*{epoch}数量:} 正如我们在本章前面讨论 -的那样,提前停止表示在每个回合的最后,我们都要计算验证集上的分类准确率。 -当准确率不再提升,就终止它。这让选择回合数变得很简单。特别地,也意味着我们不再需 -要担心显式地掌握回合数和其他超参数的关联。而且,这个过程还是自动的。另外,提前停止 -也能够帮助我们避免\gls*{overfitting}。尽管在实验前期不采用提前停止,这样可 -以看到任何过匹配的信号,使用这些来选择规范化方法,但提前停止仍然是一件很 -棒的事。 - -我们需要再明确一下什么叫做分类准确率不再提升,这样方可实现提前停止。正如 -我们已经看到的,分类准确率在整体趋势下降的时候仍旧会抖动或者震荡。如果我们在准确 -度刚开始下降的时候就停止,那么肯定会错过更好的选择。一种不错的解决方案是如果分类 -准确率在一段时间内不再提升的时候终止。例如,我们要解决 MNIST 问题。如果分类准确 -度在近 $10$ 个回合都没有提升的时候,我们将其终止。这样不仅可以确保我们不会终止得 -过快,也能够使我们不要一直干等直到出现提升。 +\textbf{使用提前停止\label{early_stopping}来确定训练的\gls*{epoch}数量:} 正如我 +们在本章前面讨论的那样,提前停止表示在每个回合的最后,我们都要计算验证集上的分类 +准确率。当准确率不再提升,就终止它。这让选择回合数变得很简单。特别地,也意味着我 +们不再需要担心显式地掌握回合数和其他超参数的关联。而且,这个过程还是自动的。另外, +提前停止也能够帮助我们避免\gls*{overfitting}。尽管在实验前期不采用提前停止,这样 +可以看到任何过匹配的信号,使用这些来选择\gls*{regularization}方法,但提前停止仍然是一件很棒的事。 + +我们需要再明确一下什么叫做分类准确率不再提升,这样方可实现提前停止。正如我们已经 +看到的,分类准确率在整体趋势下降的时候仍旧会抖动或者震荡。如果我们在准确度刚开始 +下降的时候就停止,那么肯定会错过更好的选择。一种不错的解决方案是如果分类准确率在 +一段时间内不再提升的时候终止。例如,我们要解决 MNIST 问题。如果分类准确度在近 +$10$ 个回合都没有提升的时候,我们将其终止。这样不仅可以确保我们不会终止得过快, +也能够使我们不要一直干等直到出现提升。 这种 $10$ 回合不提升就终止的规则很适合 MNIST 问题的一开始的探索。然而,网络有时 候会在很长时间内于一个特定的分类准确率附近形成平缓的局面,然后才会有提升。如果你 -尝试获得相当好的性能,这个规则可能就会太过激进了~——~停止得太草率。所以,我建议在你 -更加深入地理解网络训练的方式时,仅仅在初始阶段使用 $10$ 回合不提升规则,然后逐步 -地选择更久的回合,比如说:$20$ 回合不提升就终止,$50$ 回合不提升就终止,以此类推。 -当然,这就引入了一种新的需要优化的超参数!实践中,其实比较容易设置这个超参数来获 -得相当好的结果。类似地,对不同于 MNIST 的问题,$10$ 回合不提升就终止的规则会太过 -激进或者太过保守,这都取决于问题本身的特质。然而,进行一些小的实验,发现好的提前 -终止的策略还是非常简单的。 +尝试获得相当好的性能,这个规则可能就会太过激进了~——~停止得太草率。所以,我建议在 +你更加深入地理解网络训练的方式时,仅仅在初始阶段使用 $10$ 回合不提升规则,然后逐 +步地选择更久的回合,比如说:$20$ 回合不提升就终止,$50$ 回合不提升就终止,以此类 +推。当然,这就引入了一种新的需要优化的超参数!实践中,其实比较容易设置这个超参数 +来获得相当好的结果。类似地,对不同于 MNIST 的问题,$10$ 回合不提升就终止的规则会 +太过激进或者太过保守,这都取决于问题本身的特质。然而,进行一些小的实验,发现好的 +提前终止的策略还是非常简单的。 我们还没有在我们的 MNIST 实验中使用提前终止。原因是我们已经比较了不同的学习观点。 这样的比较其实比较适合使用同样的训练回合。但是,在 \lstinline!network2.py! 中实 @@ -2055,31 +2054,31 @@ \subsection*{问题} 证准确率和训练的回合数。 \end{itemize} -\textbf{\gls*{learning-rate}调整:} 我们一直都将\gls*{learning-rate}设置为常量。但是,通常 -采用可变的\gls*{learning-rate}更加有效。在学习的前期,\gls*{weight}可能非常糟糕。所以最好是使 -用一个较大的\gls*{learning-rate}让\gls*{weight}变化得更快。越往后,我们可以降低\gls*{learning-rate}, -这样可以作出更加精良的调整。 +\textbf{\gls*{learning-rate}调整:} 我们一直都将\gls*{learning-rate}设置为常量。 +但是,通常采用可变的\gls*{learning-rate}更加有效。在学习的前期,\gls*{weight}可 +能非常糟糕。所以最好是使用一个较大的\gls*{learning-rate}让\gls*{weight}变化得更 +快。越往后,我们可以降低\gls*{learning-rate},这样可以作出更加精良的调整。 我们要如何设置\gls*{learning-rate}呢?其实有很多方法。一种自然的观点是使用提前终止的 想法。就是保持\gls*{learning-rate}为一个常量直到验证准确率开始变差。然后按照某个量下 降\gls*{learning-rate},比如说按照$10$ 或者 $2$。我们重复此过程若干次,直到% \gls*{learning-rate}是初始值的 $1/1024$(或者$1/1000$)。那时就终止。 -可变\gls*{learning-rate}可以提升性能,但是也会产生大量可能的选择。这些选择会让人头疼 -~——~你可能需要花费很多精力才能优化学习规则。对刚开始实验,我建议使用单一的常量作 -为\gls*{learning-rate}的选择。这会给你一个比较好的近似。后面,如果你想获得更好的性能, -值得按照某种规则进行实验,根据我已经给出的资料。 +可变\gls*{learning-rate}可以提升性能,但是也会产生大量可能的选择。这些选择会让人 +头疼~——~你可能需要花费很多精力才能优化学习规则。对刚开始实验,我建议使用单一的常 +量作为\gls*{learning-rate}的选择。这会给你一个比较好的近似。后面,如果你想获得更 +好的性能,值得按照某种规则进行实验,根据我已经给出的资料。 \subsection*{练习} \begin{itemize} -\item 更改 \lstinline!network2.py! 实现学习规则:每次验证准确率满足$10$ 回合 - 不提升终止策略时改变\gls*{learning-rate};当\gls*{learning-rate}降到初始值的 $1/128$ 时 - 终止。 +\item 更改 \lstinline!network2.py! 实现学习规则:每次验证准确率满足 $10$ 回合不 + 提升终止策略时改变\gls*{learning-rate};当\gls*{learning-rate}降到初始值的 + $1/128$ 时终止。 \end{itemize} -\textbf{规范化参数:} 我建议,开始时不包含规范化($\lambda=0.0$),确定 $\eta$ -的值。使用确定出来的 $\eta$,我们可以使用验证数据来选择好的 $\lambda$。从尝试 +\textbf{\gls*{regularization}参数:} 我建议,开始时不包含\gls*{regularization}($\lambda=0.0$),确定 $\eta$的 +值。使用确定出来的 $\eta$,我们可以使用验证数据来选择好的 $\lambda$。从尝试 $\lambda=1.0$ 开始,然后根据验证集上的性能按照因子 $10$增加或减少其值。一旦我已 经找到一个好的量级,你可以改进 $\lambda$ 的值。这里搞定后,你就可以返回再重新优 化 $\eta$。 @@ -2092,12 +2091,12 @@ \subsection*{练习} \end{itemize} \textbf{在本书前面,我是如何选择超参数的:} 如果你使用本节给出的推荐策略,你会发 -现你自己找到的 $\eta$ 和 $\lambda$ 不总是和我给出的一致。原因在于书本由于叙事篇幅的 -限制,有时候会使得优化超参数变得不现实。想想我们已经做过的使用不同观点学习的对比, -比如说,比较二次代价函数和交叉熵代价函数,比较\gls*{weight}初始化的新旧方法,使不使用规范 -化,等等。为了使这些比较有意义,我通常会将参数在这些方法上保持不变(或者进行合适 - 的尺度调整)。当然,同样超参数对不同的学习观点都是最优的也没有理论保证,所以我 -用的那些超参数常常是折衷的选择。 +现你自己找到的 $\eta$ 和 $\lambda$ 不总是和我给出的一致。原因在于书本由于叙事篇 +幅的限制,有时候会使得优化超参数变得不现实。想想我们已经做过的使用不同观点学习的 +对比,比如说,比较二次\gls*{cost-func}和交叉熵\gls*{cost-func},比较\gls*{weight}初始化的新旧方 +法,使不使用\gls*{regularization},等等。为了使这些比较有意义,我通常会将参数在这些方法上保持不 +变(或者进行合适的尺度调整)。当然,同样超参数对不同的学习观点都是最优的也没有理 +论保证,所以我用的那些超参数常常是折衷的选择。 相较于这样的折衷,其实我本可以尝试优化每个单一的观点的超参数选择。理论上,这可能 是更好更公平的方式,因为那样的话我们可以看到每个观点的最优性能。但是,我们现在依 @@ -2105,19 +2104,21 @@ \subsection*{练习} 使用折衷方式来采用尽可能好(却不一定最优)的超参数选择。\\ \label{mini_batch_size} -\textbf{\gls*{mini-batch}大小:} 我们应该如何设置\gls*{mini-batch}的大小?为了回答这个问题, -让我们先假设正在进行在线学习,也就是说使用大小为 $1$ 的\gls*{mini-batch}。 - -一个关于在线学习的担忧是使用只有一个样本的\gls*{mini-batch}会带来关于梯度的错误估计。 -实际上,误差并不会真的产生这个问题。原因在于单一的梯度估计不需要绝对精确。我们需 -要的是确保代价函数保持下降的足够精确的估计。就像你现在要去北极点,但是只有一个不 -大精确的(差个 $10-20$ 度)指南针。如果你不断频繁地检查指南针,指南针会在平均状 -况下给出正确的方向,所以最后你也能抵达北极点。 - -基于这个观点,这看起来好像我们需要使用在线学习。实际上,情况会变得更加复杂。在 -\hyperref[ch:]{上一章的问题中} 我指出我们可以使用矩阵技术来对所有在\gls*{mini-batch}中 -的样本同时计算梯度更新,而不是进行循环。所以,取决于硬件和线性代数库的实现细节, -这会比循环方式进行梯度更新快好多。也许是 $50$ 和 $100$ 倍的差别。 +\textbf{\gls*{mini-batch}大小:} 我们应该如何设置\gls*{mini-batch}的大小?为了回 +答这个问题,让我们先假设正在进行在线学习,也就是说使用大小为 $1$ 的% +\gls*{mini-batch}。 + +一个关于在线学习的担忧是使用只有一个样本的\gls*{mini-batch}会带来关于梯度的错误 +估计。实际上,误差并不会真的产生这个问题。原因在于单一的梯度估计不需要绝对精确。 +我们需要的是确保\gls*{cost-func}保持下降的足够精确的估计。就像你现在要去北极点,但是只有 +一个不大精确的(差个 $10-20$ 度)指南针。如果你不断频繁地检查指南针,指南针会在 +平均状况下给出正确的方向,所以最后你也能抵达北极点。 + +基于这个观点,这看起来好像我们需要使用在线学习。实际上,情况会变得更加复杂。在% +\hyperref[ch:]{上一章的问题中} 我指出我们可以使用矩阵技术来对所有在 +\gls*{mini-batch}中的样本同时计算梯度更新,而不是进行循环。所以,取决于硬件和线 +性代数库的实现细节,这会比循环方式进行梯度更新快好多。也许是 $50$ 和 $100$ 倍的 +差别。 现在,看起来这对我们帮助不大。我们使用 $100$ 的\gls*{mini-batch}的学习规则如下; \begin{equation} @@ -2130,36 +2131,38 @@ \subsection*{练习} \label{eq:101}\tag{101} \end{equation} 即使它仅仅是 $50$ 倍的时间,结果仍然比直接在线学习更好,因为我们在线学习更新得太 -过频繁了。假设,在\gls*{mini-batch}下,我们将\gls*{learning-rate}扩大了 $100$ 倍,更新规则就是 +过频繁了。假设,在\gls*{mini-batch}下,我们将\gls*{learning-rate}扩大了 $100$ 倍, +更新规则就是 \begin{equation} w \rightarrow w' = w-\eta \sum_x \nabla C_x \label{eq:102}\tag{102} \end{equation} -这看起来像做了 $100$ 次独立的在线学习。但是仅仅花费了 $50$ 次在线学习的时间。 -当然,其实不是同样的 100 次在线学习,因为\gls*{mini-batch}中 $\nabla C_x$ 是都对同样的 -\gls*{weight}进行衡量的,而在线学习中是累加的学习。使用更大的\gls*{mini-batch}看起来还是显著地 -能够进行训练加速的。 - -所以,选择最好的\gls*{mini-batch}大小也是一种折衷。太小了,你不会用上很好的矩阵库的快 -速计算。太大,你是不能够足够频繁地更新\gls*{weight}的。你所需要的是选择一个折衷的值,可以 -最大化学习的速度。幸运的是,\gls*{mini-batch}大小的选择其实是相对独立的一个超参数(网络 - 整体架构外的参数),所以你不需要优化那些参数来寻找好的\gls*{mini-batch}大小。因此, -可以选择的方式就是使用某些可以接受的值(不需要是最优的)作为其他参数的选择,然后 -进行不同\gls*{mini-batch}大小的尝试,像上面那样调整 $\eta$。画出验证准确率的值随时间 -(非回合)变化的图,选择哪个得到最快性能的提升的\gls*{mini-batch}大小。得到了 -\gls*{mini-batch}大小,也就可以对其他的超参数进行优化了。 - -当然,你也发现了,我这里并没有做到这么多。实际上,我们的实现并没有使用到 -\gls*{mini-batch}更新快速方法。就是简单使用了\gls*{mini-batch}大小为 $10$。所以,我们其实可以 -通过降低\gls*{mini-batch}大小来进行提速。我也没有这样做,因为我希望展示\gls*{mini-batch}大于 -$1$ 的使用,也因为我实践经验表示提升效果其实不明显。在实践中,我们大多数情况肯定 -是要实现更快的\gls*{mini-batch}更新策略,然后花费时间精力来优化\gls*{mini-batch}大小,来达到 -总体的速度提升。\\ +这看起来像做了 $100$ 次独立的在线学习。但是仅仅花费了 $50$ 次在线学习的时间。当 +然,其实不是同样的 100 次在线学习,因为\gls*{mini-batch}中 $\nabla C_x$ 是都对同 +样的\gls*{weight}进行衡量的,而在线学习中是累加的学习。使用更大的% +\gls*{mini-batch}看起来还是显著地能够进行训练加速的。 + +所以,选择最好的\gls*{mini-batch}大小也是一种折衷。太小了,你不会用上很好的矩阵 +库的快速计算。太大,你是不能够足够频繁地更新\gls*{weight}的。你所需要的是选择一 +个折衷的值,可以最大化学习的速度。幸运的是,\gls*{mini-batch}大小的选择其实是相 +对独立的一个超参数(网络整体架构外的参数),所以你不需要优化那些参数来寻找好的% +\gls*{mini-batch}大小。因此,可以选择的方式就是使用某些可以接受的值(不需要是最 + 优的)作为其他参数的选择,然后进行不同\gls*{mini-batch}大小的尝试,像上面那样 +调整 $\eta$。画出验证准确率的值随时间(非回合)变化的图,选择哪个得到最快性能的 +提升的\gls*{mini-batch}大小。得到了\gls*{mini-batch}大小,也就可以对其他的超参数 +进行优化了。 + +当然,你也发现了,我这里并没有做到这么多。实际上,我们的实现并没有使用到% +\gls*{mini-batch}更新快速方法。就是简单使用了\gls*{mini-batch}大小为 $10$。所以, +我们其实可以通过降低\gls*{mini-batch}大小来进行提速。我也没有这样做,因为我希望 +展示\gls*{mini-batch}大于$1$ 的使用,也因为我实践经验表示提升效果其实不明显。在 +实践中,我们大多数情况肯定是要实现更快的\gls*{mini-batch}更新策略,然后花费时间 +精力来优化\gls*{mini-batch}大小,来达到总体的速度提升。\\ \textbf{自动技术:} 我已经给出很多在手动进行超参数优化时的启发式规则。手动选择当 然是种理解网络行为的方法。不过,现实是,很多工作已经使用自动化过程进行。通常的技 -术就是\emph{网格搜索}(\emph{grid search}),可以系统化地对超参数的参数空间的网 +术就是\textbf{网格搜索}(\textbf{grid search}),可以系统化地对超参数的参数空间的网 格进行搜索。网格搜索的成就和限制(易于实现的变体)在 James Bergstra 和 Yoshua Bengio $2012$年的论 文\footnote{\href{http://dl.acm.org/citation.cfm?id=2188395}{Random search for @@ -2175,7 +2178,7 @@ \subsection*{练习} \textbf{总结:} 跟随上面的经验并不能帮助你的网络给出绝对最优的结果。但是很可能给 你一个好的开始和一个改进的基础。特别地,我已经非常独立地讨论了超参数的选择。实践 中,超参数之间存在着很多关系。你可能使用 $\eta$ 进行试验,发现效果不错,然后去优 -化$\lambda$,发现这里又和 $\eta$ 混在一起了。在实践中,一般是来回往复进行的,最 +化 $\lambda$,发现这里又和 $\eta$ 混在一起了。在实践中,一般是来回往复进行的,最 终逐步地选择到好的值。总之,启发式规则其实都是经验,不是金规玉律。你应该注意那些 没有效果的尝试的信号,然后乐于尝试更多试验。特别地,这意味着需要更加细致地监控神 经网络的行为,特别是验证集上的准确率。 @@ -2208,11 +2211,11 @@ \subsection*{练习} 设定超参数的挑战让一些人抱怨神经网络相比较其他的机器学习算法需要大量的工作进行参 数选择。我也听到很多不同的版本:“的确,参数完美的神经网络可能会在这问题上获得最 优的性能。但是,我可以尝试一下随机森林(或者 SVM 或者……这里脑补自己偏爱的技术) -也能够工作的。我没有时间搞清楚那个最好的神经网络。” 当然,从一个实践者角度,肯 -定是应用更加容易的技术。这在你刚开始处理某个问题时尤其如此,因为那时候,你都不确 -定一个机器学习算法能够解决那个问题。但是,如果获得最优的性能是最重要的目标的话, -你就可能需要尝试更加复杂精妙的知识的方法了。如果机器学习总是简单的话那是太好不过 -了,但也没有什么理由说机器学习非得这么简单。 +也能够工作的。我没有时间搞清楚那个最好的神经网络。” 当然,从一个实践者角度,肯定 +是应用更加容易的技术。这在你刚开始处理某个问题时尤其如此,因为那时候,你都不确定 +一个机器学习算法能够解决那个问题。但是,如果获得最优的性能是最重要的目标的话,你 +就可能需要尝试更加复杂精妙的知识的方法了。如果机器学习总是简单的话那是太好不过了, +但也没有什么理由说机器学习非得这么简单。 \section{其它技术} \label{sec:other_techniques} @@ -2224,15 +2227,14 @@ \section{其它技术} \subsection{随机梯度下降的变化形式} -通过\gls*{bp}进行的随机梯度下降已经在 MNIST 数字分类问题上有了很好的表现。然而, -还有很多其他的观点来优化代价函数,有时候,这些方法能够带来比在小批量的随机梯度 -下降更好的效果。本节,我会介绍两种观点,Hessian 和 momentum 技术。 -\\ +通过\gls*{bp}进行的\gls*{sgd}已经在 MNIST 数字分类问题上有了很好的表现。然而, +还有很多其他的观点来优化\gls*{cost-func},有时候,这些方法能够带来比在小批量的随机梯度下 +降更好的效果。本节,我会介绍两种观点,Hessian 和 momentum 技术。\\ \textbf{Hessian 技术:} 为了更好地讨论这个技术,我们先把神经网络放在一边。相反, -我直接思考最小化代价函数 $C$ 的抽象问题,其中 $C$ 是多个参数的函 -数,$w=w_1,w_2,...$,所以 $C=C(w)$。借助于泰勒展开式,代价函数可以在点 $w$ 处被近 -似为: +我直接思考最小化\gls*{cost-func} $C$ 的抽象问题,其中 $C$ 是多个参数的函数, +$w=w_1,w_2,\ldots$,所以 $C=C(w)$。借助于泰勒展开式,\gls*{cost-func}可以在点 $w$ 处被近似 +为: \begin{align} C(w+\Delta w) &= C(w) + \sum_j \frac{\partial C}{\partial w_j} \Delta w_j \nonumber \\ & \quad + \frac{1}{2} \sum_{jk} \Delta w_j \frac{\partial^2 C}{\partial w_j \partial w_k} \Delta w_k + \ldots \label{eq:103}\tag{103} @@ -2244,9 +2246,9 @@ \subsection{随机梯度下降的变化形式} \frac{1}{2} \Delta w^T H \Delta w + \ldots \label{eq:104}\tag{104} \end{equation} -其中 $\nabla C$ 是通常的梯度向量,$H$ 就是矩阵形式的 \emph{Hessian 矩阵},其中 -第 $jk$ 项就是 $\partial^2 C/\partial w_j\partial w_k$。假设我们通过丢弃更高阶的 -项来近似 $C$, +其中 $\nabla C$ 是通常的梯度向量,$H$ 就是矩阵形式的 \textbf{Hessian 矩阵},其中第 +$jk$ 项就是 $\partial^2 C/\partial w_j\partial w_k$。假设我们通过丢弃更高阶的项 +来近似 $C$, \begin{equation} C(w+\Delta w) \approx C(w) + \nabla C \cdot \Delta w + \frac{1}{2} \Delta w^T H \Delta w @@ -2254,16 +2256,16 @@ \subsection{随机梯度下降的变化形式} \end{equation} 使用微积分,我们可证明右式表达式可以进行最小化\footnote{严格地说,对此是一个最小 - 值,而不仅仅是一个极值,我们需要假设 Hessian 矩阵是正定的。直观地说,这意味着函 - 数 $C$ 看起来局部像一个山谷,而不是一座山或一个马鞍。},选择: + 值,而不仅仅是一个极值,我们需要假设 Hessian 矩阵是正定的。直观地说,这意味着 + 函数 $C$ 看起来局部像一个山谷,而不是一座山或一个马鞍。},选择: \begin{equation} \Delta w = -H^{-1} \nabla C \label{eq:106}\tag{106} \end{equation} -根据 \eqref{eq:105} 是代价函数的比较好的近似表达式,我们期望从点 $w$ 移动 -到 $w+\Delta w = w - H^{-1}\nabla C$ 可以显著地降低代价函数的值。这就给出了一种优 -化代价函数的可能的算法: +根据~\eqref{eq:105} 是\gls*{cost-func}的比较好的近似表达式,我们期望从点 $w$ 移动到 +$w+\Delta w = w - H^{-1}\nabla C$ 可以显著地降低\gls*{cost-func}的值。这就给出了一种优化 +\gls*{cost-func}的可能的算法: \begin{itemize} \item 选择开始点,$w$ \item 更新 $w$ 到新点 $w' = w - H_{-1}\nabla C$,其中 Hessian $H$ 和 $\nabla C$ @@ -2277,18 +2279,18 @@ \subsection{随机梯度下降的变化形式} 地使用改变量 $\Delta w = -\eta H^{-1} \nabla C$ 来 改变 $w$,其中 $\eta$ 就是 \gls*{learning-rate}。 -这个最小化代价函数的方法常常被称为 \emph{Hessian 技术}或者 \emph{Hessian 优化}。在理 -论上和实践中的结果都表明 Hessian 方法比标准的梯度下降方法收敛速度更快。特别地, -通过引入代价函数的二阶变化信息,可以让 Hessian 方法避免在梯度下降中常碰到的多路 -径(pathologies)问题。而且,\gls*{bp}算法的有些版本也可以用于计算 Hessian。 +这个最小化\gls*{cost-func}的方法常常被称为 \textbf{Hessian 技术}或者 \textbf{Hessian 优化}。 +在理论上和实践中的结果都表明 Hessian 方法比标准的梯度下降方法收敛速度更快。特别 +地,通过引入\gls*{cost-func}的二阶变化信息,可以让 Hessian 方法避免在梯度下降中常碰到的 +多路径(pathologies)问题。而且,\gls*{bp}算法的有些版本也可以用于计算 Hessian。 如果 Hessian 优化这么厉害,为何我们这里不使用它呢?不幸的是,尽管 Hessian 优化有 很多可取的特性,它其实还有一个不好的地方:在实践中很难应用。这个问题的部分原因在 -于 Hessian 矩阵的太大了。假设你有一个 $10^7$ 个\gls*{weight}和\gls*{bias}的网络。那么对应的 -Hessian 矩阵会有 $10^7 \times 10^7=10^{14}$ 个元素。这真的是太大了!所以在实践中, -计算 $H^{-1}\nabla C$ 就极其困难。不过,这并不表示学习理解它没有用了。实际上,有 -很多受到 Hessian 优化启发的梯度下降的变种,能避免产生太大矩阵的问题。让我们看看 -其中一个称为基于 momentum 梯度下降的方法。\\ +于 Hessian 矩阵的太大了。假设你有一个 $10^7$ 个\gls*{weight}和\gls*{bias}的网络。 +那么对应的Hessian 矩阵会有 $10^7 \times 10^7=10^{14}$ 个元素。这真的是太大了!所 +以在实践中,计算 $H^{-1}\nabla C$ 就极其困难。不过,这并不表示学习理解它没有用了。 +实际上,有很多受到 Hessian 优化启发的梯度下降的变种,能避免产生太大矩阵的问题。 +让我们看看其中一个称为基于 momentum 梯度下降的方法。\\ \textbf{基于 momentum 的梯度下降:} 直觉上看,Hessian 优化的优点是它不仅仅考虑了 梯度,而且还包含梯度如何变化的信息。基于 momentum 的梯度下降就基于这个直觉,但是 @@ -2318,24 +2320,24 @@ \subsection{随机梯度下降的变化形式} 每一步速度都不断增大,所以我们会越来越快地达到谷底。这样就能够确保 momentum 技术 比标准的梯度下降运行得更快。当然,这里也会有问题,一旦达到谷底,我们就会跨越过去。 或者,如果梯度本该快速改变而没有改变,那么我们会发现自己在错误的方向上移动太多了。 -这就是在 \eqref{eq:107} 中使用 $\mu$ 这个超参数的原因了。前面提到,$\mu$ 可以控 -制系统中的摩擦力大小;更加准确地说,你应该将 $1-\mu$ 看成是摩擦力的量。当 -$\mu=1$ 时,没有摩擦,速度完全由梯度 $\nabla C$ 决定。相反,若是 $\mu=0$,就存在 -很大的摩擦,速度无法叠加,公式 \eqref{eq:107} \eqref{eq:108} 就变成了通常的梯度 -下降,$w\rightarrow w'=w-\eta \nabla C$。在实践中,使用 $0$ 和 $1$ 之间的 $\mu$ -值可以给我们避免过量而又能够叠加速度的好处。我们可以使用 hold out 验证数据集来选 -择合适的 $\mu$ 值,就像我们之前选择$\eta$ 和 $\lambda$ 那样。 +这就是在~\eqref{eq:107} 中使用 $\mu$ 这个超参数的原因了。前面提到,$\mu$ 可以控 +制系统中的摩擦力大小;更加准确地说,你应该将 $1-\mu$ 看成是摩擦力的量。当$\mu=1$ +时,没有摩擦,速度完全由梯度 $\nabla C$ 决定。相反,若是 $\mu=0$,就存在很大的摩 +擦,速度无法叠加,公式~\eqref{eq:107}~\eqref{eq:108} 就变成了通常的梯度下降, +$w\rightarrow w'=w-\eta \nabla C$。在实践中,使用 $0$ 和 $1$ 之间的 $\mu$值可以 +给我们避免过量而又能够叠加速度的好处。我们可以使用 hold out 验证数据集来选择合适 +的 $\mu$ 值,就像我们之前选择$\eta$ 和 $\lambda$ 那样。 我到现在也没有把 $\mu$ 看成是超参数。原因在于 $\mu$ 的标准命名不大好:它叫做 -\emph{moment co-efficient}。这其实很让人困惑,因为 $\mu$ 并不是物理学那个叫做动 +\textbf{moment co-efficient}。这其实很让人困惑,因为 $\mu$ 并不是物理学那个叫做动 量(momentum)的东西。并且,它更像摩擦力的概念。然而,现在这个术语已经被大家广泛 使用了,所以我们继续使用它。 关于 momentum 技术的一个很好的特点是它基本上不需要改变太多梯度下降的代码就可以实 现。我们可以继续使用\gls*{bp}来计算梯度,就和前面那样,使用随机选择的 minibatch -的方法。这样的话,我们还是能够从 Hessian 技术中学到的优点的~——~使用梯度如何改变的 -信息。也仅仅需要进行微小的调整。实践中,momentum 技术很常见,也能够带来学习速度 -的提升。 +的方法。这样的话,我们还是能够从 Hessian 技术中学到的优点的~——~使用梯度如何改变 +的信息。也仅仅需要进行微小的调整。实践中,momentum 技术很常见,也能够带来学习速 +度的提升。 \subsection*{练习} @@ -2347,41 +2349,40 @@ \subsection*{练习} \subsection*{问题} \begin{itemize} -\item 增加基于 momentum 的随机梯度下降到 \lstinline!network2.py! 中。 +\item 增加基于 momentum 的\gls*{sgd}到 \lstinline!network2.py! 中。 \end{itemize} -\textbf{其他优化代价函数的方法:} 很多其他的优化代价函数的方法也被提出来了,并没 +\textbf{其他优化\gls*{cost-func}的方法:} 很多其他的优化\gls*{cost-func}的方法也被提出来了,并没 有关于哪种最好的统一意见。当你越来越深入了解神经网络时,值得去尝试其他的优化技术, 理解他们工作的原理,优势劣势,以及在实践中如何应用。前面我提到的一篇论 文\footnote{\href{http://yann.lecun.com/exdb/publis/pdf/lecun-98b.pdf}{Efficient BackProp},作者为 Yann LeCun, Léon Bottou, Genevieve Orr and Klaus-Robert Müller (1998)。},介绍并对比了这些技术,包含共轭梯度下降和 BFGS 方法(也可以 -看看 limited memory -BFGS,\href{http://en.wikipedia.org/wiki/Limited-memory_BFGS}{L-BFGS}。另一种近期 -效果很不错技术\footnote{例如,看看 \href{http://www.cs.toronto.edu/~hinton/absps/momentum.pdf}{On the - importance of initialization and momentum in deep learning},作者为 Ilya - Sutskever, James Martens, George Dahl 和 Geoffrey - Hinton (2012)。}是Nesterov 的加速梯度技术,这个技术对 momentum 技术进行了改进。 -然而,对很多问题,标准的随机梯度下降算法,特别当 momentum 用起来后就可以工作得很 -好了,所以我们会继续在本书后面使用随机梯度下算法。 + 看看 limited memory BFGS, + \href{http://en.wikipedia.org/wiki/Limited-memory_BFGS}{L-BFGS}。另一种近期效 + 果很不错技术\footnote{例如,看看 + \href{http://www.cs.toronto.edu/~hinton/absps/momentum.pdf}{On the + importance of initialization and momentum in deep learning},作者为 Ilya + Sutskever, James Martens, George Dahl 和 Geoffrey Hinton (2012)。}是 + Nesterov 的加速梯度技术,这个技术对 momentum 技术进行了改进。然而,对很多问题, + 标准的\gls*{sgd}算法,特别当 momentum 用起来后就可以工作得很好了,所以我们会 + 继续在本书后面使用随机梯度下算法。 \subsection{人工神经元的其他模型} \label{subsec:other_models_of_artificial_neuron} -到现在,我们使用的神经元都是 S 型神经元。理论上讲,从这样类型的神经元构建起 -来的神经网络可以计算任何函数。实践中,使用其他模型的神经元有时候会超过 S 型 -网络。取决于不同的应用,基于其他类型的神经元的网络可能会学习得更快,更好地泛化到 -测试集上,或者可能两者都有。让我们给出一些其他的模型选择,便于了解常用的模型上的 -变化。 +到现在,我们使用的神经元都是 \gls*{sigmoid-neuron}。理论上讲,从这样类型的神经元构建起来的 +神经网络可以计算任何函数。实践中,使用其他模型的神经元有时候会超过 S 型网络。取 +决于不同的应用,基于其他类型的神经元的网络可能会学习得更快,更好地泛化到测试集上, +或者可能两者都有。让我们给出一些其他的模型选择,便于了解常用的模型上的变化。 -可能最简单的变种就是 tanh(发音为 “tanch”)神经元\index{tanh 神经元},使用双曲 -正切(hyperbolic tangent)函数\index{双曲正切函数}替换了 S 型函数。输入为 $x$,权 -重向量为 $w$,\gls*{bias}为 $b$ 的tanh 神经元的输出是 +可能最简单的变种就是\gls{tanh-neuron},使用\gls{hyperbolic-tangent}函数替换了 \gls*{sigmoid-func}。输入为 $x$,权 +重向量为 $w$,\gls*{bias}为 $b$ 的\gls*{tanh-neuron}的输出是 \begin{equation} \tanh(w \cdot x+b) \label{eq:109}\tag{109} \end{equation} -这其实和 S 型神经元关系相当密切。回想一下 $\tanh$ 函数的定义: +这其实和 \gls*{sigmoid-neuron}关系相当密切。回想一下 $\tanh$ 函数的定义: \begin{equation} \tanh(z) \equiv \frac{e^z-e^{-z}}{e^z+e^{-z}} \label{eq:110}\tag{110} @@ -2391,7 +2392,7 @@ \subsection{人工神经元的其他模型} \sigma(z) = \frac{1+\tanh(z/2)}{2} \label{eq:111}\tag{111} \end{equation} -也就是说,$\tanh$ 仅仅是 S 型函数的按比例变化版本。我们同样也能用图像看看 +也就是说,$\tanh$ 仅仅是 \gls*{sigmoid-func}的按比例变化版本。我们同样也能用图像看看 $\tanh$ 的形状: \begin{center} \includegraphics{tanh_function} @@ -2399,13 +2400,13 @@ \subsection{人工神经元的其他模型} 这两个函数之间的一个差异就是 $\tanh$ 神经元的输出的值域是 $(-1, 1)$ 而非 $(0, 1)$。这意味着如果你构建基于 $\tanh$ 神经元,你可能需要正规化最终的输出(取决于应 -用的细节,还有你的输入),跟 sigmoid 网络略微不同。 + 用的细节,还有你的输入),跟 sigmoid 网络略微不同。 -类似于 S 型神经元,基于 tanh 神经元的网络可以在理论上,计算任何将输入映射 -到 $(-1, 1)$ 的函数\footnote{对于 tanh 和 S 型神经元,以及下面要讨论的修正线性神 - 经元,这个说法有一些技术上的预先声明。然而,非正式地,通常可以把神经网络看做可 - 以以任意精度近似任何函数。}。而且,诸如\gls*{bp}和随机梯度下降这样的想法也能够轻 -松地用在tanh 神经元构成的网络上的。 +类似于 \gls*{sigmoid-neuron},基于\gls*{tanh-neuron}的网络可以在理论上,计算任何将输入映射到 +$(-1, 1)$ 的函数\footnote{对于 tanh 和 \gls*{sigmoid-neuron},以及下面要讨论的修正线性神经 + 元,这个说法有一些技术上的预先声明。然而,非正式地,通常可以把神经网络看做可以 + 以任意精度近似任何函数。}。而且,诸如\gls*{bp}和\gls*{sgd}这样的想法也能够轻 +松地用在\gls*{tanh-neuron}构成的网络上的。 \subsection*{练习} @@ -2413,15 +2414,15 @@ \subsection*{练习} \item 证明公式~\eqref{eq:111}。 \end{itemize} -那么你应该在网络中使用什么类型的神经元呢,tanh 还是 sigmoid?实话讲,确实并没有 -先验的答案!然而,存在一些理论论点和实践证据表明 tanh 有时候表现更好\footnote{例 +那么你应该在网络中使用什么类型的神经元呢,\gls*{tanh}还是 S 型?实话讲,确实并没有 +先验的答案!然而,存在一些理论论点和实践证据表明\gls*{tanh}有时候表现更好\footnote{例 如,看看 \href{http://yann.lecun.com/exdb/publis/pdf/lecun-98b.pdf}{Efficient BackProp},作者为 Yann LeCun, Léon Bottou, Genevieve Orr 和 Klaus-Robert Müller (1998),以及 \href{http://jmlr.org/proceedings/papers/v9/glorot10a/glorot10a.pdf}{Understanding the difficulty of training deep feedforward networks},作者为 Xavier Glorot - 和 Yoshua Bengio (2010)。}。让我简要介绍一下其中关于 tanh 的一个理论观点。假 -设我们使用 S 型神经元,所有激活值都是正数。让我们考虑一下\gls*{weight} + 和 Yoshua Bengio (2010)。}。让我简要介绍一下其中关于\gls*{tanh}的一个理论观点。假 +设我们使用 \gls*{sigmoid-neuron},所有激活值都是正数。让我们考虑一下\gls*{weight} $w_{jk}^{l+1}$ 输入到第 $l+1$ 层的第 $j$ 个神经元上。\gls*{bp}的规则(看% \hyperref[eq:bp4]{这里})告诉我们相关的梯度是 $a_k^l\delta_j^{l+1}$。因为所有 的激活值都是正数,所以梯度的符号就和 $\delta_j^{l+1}$ 一致。这意味着如果 @@ -2429,14 +2430,14 @@ \subsection*{练习} 果 $\delta_j^{l+1}$ 为负,那么所有的\gls*{weight} $w_{jk}^{l+1}$ 都会在梯度下降 时增加。换言之,针对同一的神经元的所有\gls*{weight}都会或者一起增加或者一起减少。 这就有问题了,因为某些\gls*{weight}可能需要有相反的变化。这样的话,只能是某些输 -入激活值有相反的符号才可能出现。所以,我们用 tanh 替换就能够达到这个目的。因此, -因为 tanh 是关于 $0$ 对称的,$\tanh(-z) = -\tanh(z)$,我们甚至期望,大概地,隐藏 +入激活值有相反的符号才可能出现。所以,我们用\gls*{tanh}替换就能够达到这个目的。因此, +因为\gls*{tanh}是关于 $0$ 对称的,$\tanh(-z) = -\tanh(z)$,我们甚至期望,大概地,隐藏 层的激活值能够在正负间保持平衡。这样其实可以保证对\gls*{weight}更新没有系统化的 单方面的\gls*{bias}。 我们应当如何看待这个论点?尽管论点是建设性的,但它还只是一个启发式的规则,而非严 -格证明说 tanh 就一定超过 sigmoid 函数。可能 sigmoid 神经元还有其他的特性能够补偿 -这个问题?实际上,对很多任务,tanh 在实践中给出了微小的甚至没有性能提升。不幸的 +格证明说\gls*{tanh}就一定超过 \gls*{sigmoid-func}。可能 \gls*{sigmoid-neuron}还有其他的特性能够补偿 +这个问题?实际上,对很多任务,\gls*{tanh}在实践中给出了微小的甚至没有性能提升。不幸的 是,我们还没有快速准确的规则说哪种类型的神经元对某种特定的应用学习得更快,或者泛 化能力最强。 @@ -2452,33 +2453,32 @@ \subsection*{练习} \end{center} 显然,这样的神经元和 sigmoid 和 tanh 都不一样。然而,ReLU 也是能够用来计算任何函 -数的,也可以使用\gls*{bp}算法和随机梯度下降进行训练。 - -什么时候应该使用 ReLU 而非其他神经元呢?一些近期的图像识别上的研究工 -作\footnote{例如,看 - 看 \href{http://yann.lecun.com/exdb/publis/pdf/jarrett-iccv-09.pdf}{What is - the Best Multi-Stage Architecture for Object Recognition?},作者为 Kevin - Jarrett, Koray Kavukcuoglu, Marc'Aurelio Ranzato和 Yann - LeCun (2009), +数的,也可以使用\gls*{bp}算法和\gls*{sgd}进行训练。 + +什么时候应该使用 ReLU 而非其他神经元呢?一些近期的图像识别上的研究工作 +\footnote{例如,看看 + \href{http://yann.lecun.com/exdb/publis/pdf/jarrett-iccv-09.pdf}{What is the + Best Multi-Stage Architecture for Object Recognition?},作者为 Kevin Jarrett, + Koray Kavukcuoglu, Marc'Aurelio Ranzato和 Yann LeCun (2009), \href{http://www.jmlr.org/proceedings/papers/v15/glorot11a.html}{Deep Sparse Rectifier Neural Networks},作者为 Xavier Glorot,Antoine Bordes, 和 Yoshua Bengio (2011), 以及 \href{https://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf}{ImageNet Classification with Deep Convolutional Neural Networks},作者为 Alex - Krizhevsky, Ilya Sutskever, 和Geoffrey Hinton (2012)。 注意这些论文提供了关 - 于如何设置输出层、代价函数、用修正线性单元规范化网络的重要细节。我在这简短的描 - 述中敷衍地带过了这些细节。这些论文也详细讨论了使用修正线性单元的好处和缺点。另 - 一篇提供有用信息的论文 - 是 \href{https://www.cs.toronto.edu/~hinton/absps/reluICML.pdf}{Rectified - Linear Units Improve Restricted Boltzmann Machines},作者为 Vinod - Nair 和Geoffrey Hinton (2010),其中演示了以不同于神经网络的方法使用修正线性单 - 元的好处。}找到了使用 ReLU 所带来的好处。然而,就像 tanh 神经元那样,我们还没有 -一个关于什么时候 什么原因 ReLU 表现更好的深度的理解。为了让你感受一下这个问题,回 -想起 sigmoid 神经元在饱和时停止学习的问题,也就是输出接近 $0$ 或者 $1$ 的时候。在 -这章我们也反复看到了问题就是 $\sigma'$ 降低了梯度,减缓了学习。Tanh 神经元也有类 -似的问题。对比一下,提高 ReLU 的带权输入并不会导致其饱和,所以就不存在前面那样的 -学习速度下降。另外,当带权输入是负数的时候,梯度就消失了,所以神经元就完全停止了 -学习。这就是很多有关理解 ReLU 何时何故更优的问题中的两个。 + Krizhevsky, Ilya Sutskever, 和Geoffrey Hinton (2012)。 注意这些论文提供了 + 关于如何设置输出层、\gls*{cost-func}、用修正线性单元\gls*{regularization}网络的重要细节。我在这简短的 + 描述中敷衍地带过了这些细节。这些论文也详细讨论了使用修正线性单元的好处和缺点。 + 另一篇提供有用信息的论文是 + \href{https://www.cs.toronto.edu/~hinton/absps/reluICML.pdf}{Rectified Linear + Units Improve Restricted Boltzmann Machines},作者为 Vinod Nair 和Geoffrey + Hinton (2010),其中演示了以不同于神经网络的方法使用修正线性单元的好处。}找到 +了使用 ReLU 所带来的好处。然而,就像\gls*{tanh-neuron}那样,我们还没有一个关于什么时候 +什么原因 ReLU 表现更好的深度的理解。为了让你感受一下这个问题,回想起 sigmoid 神 +经元在饱和时停止学习的问题,也就是输出接近 $0$ 或者 $1$ 的时候。在这章我们也反复 +看到了问题就是 $\sigma'$ 降低了梯度,减缓了学习。Tanh 神经元也有类似的问题。对比 +一下,提高 ReLU 的带权输入并不会导致其饱和,所以就不存在前面那样的学习速度下降。 +另外,当带权输入是负数的时候,梯度就消失了,所以神经元就完全停止了学习。这就是很 +多有关理解 ReLU 何时何故更优的问题中的两个。 我已经给出了一些不确定性的描述,指出我们现在还没有一个坚实的理论来解释如何选择激 活函数。实际上,这个问题比我已经讲过的还要困难,因为其实是有无穷多的可能的激活函 @@ -2486,7 +2486,7 @@ \subsection*{练习} 高的测试准确率?其实现在并没有太多真正深刻而系统的研究工作。理想中,我们会有一个 理论告诉人们,准确细致地,如何选择我们的激活函数。另外,我们不应该让这种缺失阻碍 我们学习和应用神经网络!我们已经有了一些强大的工作,可以使用它们完成很多的研究工 -作。本书剩下的部分中,我会继续使用 S 型神经元作为首选,因为他们其实是强大的也给 +作。本书剩下的部分中,我会继续使用 \gls*{sigmoid-neuron}作为首选,因为他们其实是强大的也给 出了具体关于神经网络核心思想的示例。但是你需要记住的是,这些同样的想法也都可以用 在其他类型的神经元上,有时候的确会有一些性能的提升。 @@ -2513,11 +2513,11 @@ \subsection{有关神经网络的故事} 所遇到的一些最难的问题。进展当然会非常缓慢!但是,听听人们目前正在思考的方式也是 非常有价值的,即使这些尝试不一定会有无可置疑的新进展。 -你可能会注意到类似于“我对你的观点很赞同,但是...”的话语。为了解释我们已经看到 -的情况,我通常会使用“启发式地,...”或者“粗略地讲,...”,然后接上解释某个现象 -或者其他问题的故事。这些故事是可信的,但是实验性的证据常常是不够充分的。如果你通 -读研究文献,你会发现在神经网络研究中很多类似的表达,基本上都是没有太过充分的支撑 -证据的。所以我们应该怎样看待这样的故事呢? +你可能会注意到类似于“我对你的观点很赞同,但是...”的话语。为了解释我们已经看到的 +情况,我通常会使用“启发式地,...”或者“粗略地讲,...”,然后接上解释某个现象或者其 +他问题的故事。这些故事是可信的,但是实验性的证据常常是不够充分的。如果你通读研究 +文献,你会发现在神经网络研究中很多类似的表达,基本上都是没有太过充分的支撑证据的。 +所以我们应该怎样看待这样的故事呢? 在科学的很多分支~——~尤其是那些解决相当简单现象的领域~——~很容易会得到一些关于很一 般的假说的非常扎实非常可靠的证据。但是在神经网络中,存在大量的参数和超参数及其间 @@ -2527,7 +2527,7 @@ \subsection{有关神经网络的故事} 理论在有新的证据出现时,也需要进行调整甚至丢弃。 对这种情况的一种观点是~——~任何启发式的关于神经网络的论点会带来一个挑战。例如,考 -虑我\hyperref[dropout_explanation]{之前引用}的语句,解释 dropout 工作的原 +虑我\hyperref[dropout_explanation]{前文引用}的语句,解释\gls*{dropout}工作的原 因\footnote{源自 \href{https://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf}{ImageNet Classification with Deep Convolutional Neural Networks},作者为 Alex diff --git a/chap4.tex b/chap4.tex index e800557..99f8225 100644 --- a/chap4.tex +++ b/chap4.tex @@ -21,7 +21,7 @@ \chapter{神经网络可以计算任何函数的可视化证明} \includegraphics{vector_valued_network} \end{center} -结果表明神经网络拥有一种\emph{普遍性}。不论我们想要计算什么样的函数,我们都确信存 +结果表明神经网络拥有一种\textbf{普遍性}。不论我们想要计算什么样的函数,我们都确信存 在一个神经网络可以计算它。 而且,这个普遍性定理甚至在我们限制了神经网络只在输入层和输出层之间存在一个中间层 @@ -71,8 +71,8 @@ \section{两个预先声明} 在解释为何普遍性定理成立前,我想要提下关于非正式的表述“神经网络可以计算任何函 数”的两个预先声明。 -第一点,这句话不是说一个网络可以被用来\emph{准确地}计算任何函数。而是说,我们可以 -获得尽可能好的一个\emph{近似}。通过增加隐藏元的数量,我们可以提升近似的精度。例 +第一点,这句话不是说一个网络可以被用来\textbf{准确地}计算任何函数。而是说,我们可以 +获得尽可能好的一个\textbf{近似}。通过增加隐藏元的数量,我们可以提升近似的精度。例 如,\hyperref[basic_network_precursor]{前面}我举例说明一个使用了三个隐藏元的网络 来计算 $f(x)$。对于大多数函数使用三个隐藏元仅仅能得到一个低质量的近似。通过增加隐 藏神经元的数量(比如说,五个),我们能够明显地得到更好的近似: @@ -87,7 +87,7 @@ \section{两个预先声明} $|g(x) - f(x)| < \epsilon$ 从而实现近似计算。换言之,近似对每个可能的输入都是限 制在目标准确度范围内的。 -第二点,就是可以按照上面的方式近似的函数类其实是\emph{连续}函数。如果函数不是连 +第二点,就是可以按照上面的方式近似的函数类其实是\textbf{连续}函数。如果函数不是连 续的,也就是会有突然、极陡的跳跃,那么一般来说无法使用一个神经网络进行近似。这并 不意外,因为神经网络计算的就是输入的连续函数。然而,即使那些我们真的想要计算的函 数是不连续的,一般来说连续的近似其实也足够的好了。如果这样的话,我们就可以用神经 @@ -165,7 +165,7 @@ \section{一个输入和一个输出的普遍性} 为了回答这个问题,试着修改上面图中的权重和偏置(你可能需要向上滚动一点)。你能不 能算出阶跃的位置如何取决于 $w$ 和 $b$?做点工作你应该能够说服自己,阶跃的位置 -和 $b$ \emph{成正比},和 $w$ \emph{成反比}。 +和 $b$ \textbf{成正比},和 $w$ \textbf{成反比}。 实际上,阶跃发生在 $s = -b/w$ 的位置,正如你能在下图中通过修改权重和偏置看到的: \begin{center} @@ -189,12 +189,12 @@ \section{一个输入和一个输出的普遍性} \includegraphics{two_hn_network} \end{center} -右边的绘图是隐藏层的\emph{加权输出} $w_1 a_1 + w_2 a_2$。这里 $a_1$ 和 $a_2$ 各自 +右边的绘图是隐藏层的\textbf{加权输出} $w_1 a_1 + w_2 a_2$。这里 $a_1$ 和 $a_2$ 各自 是顶部和底部神经元的输出\footnote{顺便提一下,注意整个网络的输出是 $\sigma(w_1 a_1+w_2 a_2 + b)$,其中 $b$ 是隐藏层的偏置。很明显,这不同于隐藏层加权后的输出, 也就是我们这里的绘图。我们现在打算专注于隐藏层的加权输出,不一会就会考虑如何把 它关联到整个网络的输出。}。这些输出由 $a$ 表示,是因为它们通常被称为神经元 -的\emph{激活值}(\emph{activations})。 +的\textbf{激活值}(\textbf{activations})。 试着增加和减小顶部隐藏神经元的阶跃点 $s_1$。感受下这如何改变隐藏层的加权输出。尤 其值得去理解当 $s_1$ 经过 $s_2$ 时发生了什么。你会看到这时图形发生了变化,因为我 @@ -256,7 +256,7 @@ \section{一个输入和一个输出的普遍性} 每对神经元有一个值 $h$ 和它联系。记住,神经元输出的连接有权重 $h$ 和 $-h$ (未标 记)。点击一个 $h$ 值,左右拖动鼠标来改变其值。当你这么做的时候,观察函数的变化。 -通过改变输出权重我们实际上在\emph{设计}这个函数。 +通过改变输出权重我们实际上在\textbf{设计}这个函数。 相反地,试着在图形上点击,上下拖动来改变凸起函数的高度。当你改变高度,你能看到相 应的 $h$ 值的变化。而且,虽然没有显示,在对应的输出权重上也有一个变 @@ -303,7 +303,7 @@ \section{一个输入和一个输出的普遍性} 网页}上设计一个神经网络可近似上面显示的目标函数。为了尽可能多地学习,我希望你 能分两次解决这个问题。第一次,请在图形上点击,直接调整不同的凹凸函数的高度。你应 该能发现找到一个很好的匹配的目标函数是很容易的。你做的有多好通过目标函数和网络实 -际计算函数的\emph{平均偏差}来衡量。你的挑战是尽可能\emph{低}的平均偏差。当你将平 +际计算函数的\textbf{平均偏差}来衡量。你的挑战是尽可能\textbf{低}的平均偏差。当你将平 均偏差争取到 $0.40$ 或以下时,你完成了挑战。 一旦你完成了,点击“重置”,随机重新初始化凹凸形状。你第二次解决问题时,忍住在图 @@ -435,7 +435,7 @@ \section{多个输入变量} 试着改变参数 $h$。正如你能看到,这引起输出权重的变化,以及 $x$ 和 $y$ 上凹凸函数 的高度。 -我们构建的有点像是一个\emph{塔型}函数: +我们构建的有点像是一个\textbf{塔型}函数: \begin{center} \includegraphics{tower} \end{center} @@ -495,7 +495,7 @@ \section{多个输入变量} 让我们尝试将两个这样的网络组合在一起,来计算两个不同的塔型函数。为了使这两个子网 络更清楚,我把它们放在如下所示的分开的方形区域:每个方块计算一个塔型函数,使用上 -面描述的技术。右边的图上显示了\emph{第二个}隐藏层的加权输出,即,它是一个加权组 +面描述的技术。右边的图上显示了\textbf{第二个}隐藏层的加权输出,即,它是一个加权组 合的塔型函数。 \begin{center} \includegraphics{the_two_towers} @@ -665,7 +665,7 @@ \section{结论} 尽管这个结果并不能直接用于解释网络,它还是是很重要的,因为它解开了是否使用一个神 经网络可以计算任意特定函数的问题。对这个问题的答案总是“是”。所以需要问的正确问 -题,并不是是否任意函数可计算,而是计算函数的\emph{好的}方法是什么。 +题,并不是是否任意函数可计算,而是计算函数的\textbf{好的}方法是什么。 我们建立的对于普遍性的解释只使用了两个隐藏层来计算一个任意的函数。而且,正如我们 已经讨论过的,只使用单个的隐藏层来取得相同的结果是可能的。鉴于此,你可能想知道为 diff --git a/chap5.tex b/chap5.tex index 72bad2b..2f9a0cb 100644 --- a/chap5.tex +++ b/chap5.tex @@ -127,7 +127,7 @@ \section{消失的梯度问题} 我们设置隐藏层神经元为 30 个,输出层为 10 个神经元,对应于 10 个 MNIST 数字 ('0', '1', '2', ..., 9)。 -让我们训练 30 个完整的\epochs{},使用\minibatch{}大小为 10, 学习率 $\eta = 0.1$, +让我们训练 30 个完整的\gls*{epoch},使用\gls*{mini-batch}大小为 10, 学习率 $\eta = 0.1$, 规范化参数 $\lambda = 5.0$。在训练时,我们也会在 \lstinline!validation_data! 上监 控分类的准确度\footnote{注意网络可能需要花费几分钟来训练,要看你机器的速度。所以 如果你正在运行代码,你可能愿意继续阅读并稍后回来,而不是等待代码完成执行。}: @@ -248,9 +248,7 @@ \section{消失的梯度问题} 现在我们已经有了一项重要的观察结果:至少在某些深度神经网络中,在我们在隐藏层 BP的 时候梯度倾向于变小。这意味着在前面的隐藏层中的神经元学习速度要慢于后面的隐藏层。 这儿我们只在一个网络中发现了这个现象,其实在多数的神经网络中存在着更加根本的导致 -这个现象出现的原因。这个现象也被称作是\emph{消失的梯度问题\index{消失的梯度问 - 题}(vanishing gradient - problem)}\footnote{参见 +这个现象出现的原因。这个现象也被称作是\textbf{消失的梯度问题}\footnote{参见 \href{http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.24.7321}{Gradient flow in recurrent nets: the difficulty of learning long-term dependencies}, 作者为 Sepp Hochreiter,Yoshua Bengio, Paolo Frasconi, 和 Jürgen @@ -262,9 +260,9 @@ \section{消失的梯度问题} 为何消失的梯度问题会出现呢?我们可以通过什么方式避免它?还有在训练深度神经网络时 如何处理好这个问题?实际上,这个问题是可以避免的,尽管替代方法并不是那么有效,同 -样会产生问题~——~在前面的层中的梯度会变得非常大!这也叫做\emph{激增的梯度问 - 题}(\emph{exploding gradient problem}),这也没比消失的梯度问题更好处理。更加 -一般地说,在深度神经网络中的梯度是\emph{不稳定}的,在前面的层中或会消失,或会激增。 +样会产生问题~——~在前面的层中的梯度会变得非常大!这也叫做\textbf{激增的梯度问 + 题},这也没比消失的梯度问题更好处理。更加 +一般地说,在深度神经网络中的梯度是\textbf{不稳定}的,在前面的层中或会消失,或会激增。 这种不稳定性才是深度神经网络中基于梯度学习的根本问题。这就是我们需要理解的东西, 如果可能的话,采取合理的步骤措施解决问题。 @@ -419,7 +417,7 @@ \section{什么导致了消失的梯度问题?深度神经网络中的梯度 而是在前面的层上的梯度是来自后面的层上项的乘积。当存在过多的层次时,就出现了内在 本质上的不稳定场景。唯一让所有层都接近相同的学习速度的方式是所有这些项的乘积都能 得到一种平衡。如果没有某种机制或者更加本质的保证来达成平衡,那网络就很容易不稳定 -了。简而言之,真实的问题就是神经网络受限于\emph{不稳定梯度的问题}。所以,如果我们 +了。简而言之,真实的问题就是神经网络受限于\textbf{不稳定梯度的问题}。所以,如果我们 使用标准的基于梯度的学习算法,在网络中的不同层会出现按照不同学习速度学习的情况。 \subsection*{练习} @@ -456,7 +454,7 @@ \subsection*{问题} 对应的权重 $w_1$,偏差 $b$,输出上的权重 $w_2$。证明,通过合理选择权重和偏差, 我们可以确保 $w_2 \sigma(w_1*x +b)~=x$ 其中$x \in [0, 1]$。这样的神经元可用来作为幺元使用,输出和输入相同(成比例 - )。\emph{提示:可以重写 $x = 1/2 + \Delta$,可以假设 $w_1$ 很小,和在 $w_1 * + )。\textbf{提示:可以重写 $x = 1/2 + \Delta$,可以假设 $w_1$ 很小,和在 $w_1 * \Delta$ 使用泰勒级数展开}。 \end{itemize} diff --git a/chap6.tex b/chap6.tex index 04e7f6a..af6b979 100644 --- a/chap6.tex +++ b/chap6.tex @@ -15,46 +15,46 @@ \chapter{深度学习} 不太紧密,所以如果读者熟悉基本的神经网络的知识,那么可以任意跳到自己最感兴趣的部 分。 -\hyperref[sec:convolutional_networks]{本章主要的部分}是对最为流行神经网络之一的深 -度卷积网络的介绍。我们将细致地分析一个使用卷积网络来解决 MNIST 数据集的手写数字识 -别的例子(包含了代码和讲解): +\hyperref[sec:convolutional_networks]{本章主要的部分}是对最为流行神经网络之一的 +深度卷积网络的介绍。我们将细致地分析一个使用卷积网络来解决 MNIST 数据集的手写数 +字识别的例子(包含了代码和讲解): \begin{center} \includegraphics[width=64pt]{digits} \end{center} 我们将从浅层的神经网络开始来解决上面的问题。通过多次的迭代,我们会构建越来越强大 -的网络。在这个过程中,也将要探究若干强大技术:卷积、pooling、使用GPU来更好地训练、 -训练数据的算法性扩展(避免过度拟合)、dropout 技术的使用(同样为了防止过度拟合现象)、 -网络的 ensemble 使用和其他技术。最终的结果能够接近人类的表现。 -在 10,000 幅 MNIST 测试图像上~——~模型从未在训练中接触的图像~——~该系统最终能够将其 -中 9,967 幅正确分类。这儿我们看看错分的 33 幅图像。注意正确分类是右上的标记;系统 -产生的分类在右下: +的网络。在这个过程中,也将要探究若干强大技术:卷积、\gls*{pooling}、使用 GPU 来 +更好地训练、训练数据的算法性扩展(避免\gls*{overfitting})、\gls*{dropout}技术的 +使用(同样为了防止\gls*{overfitting}现象)、网络的综合使用和其他技术。最终的结果 +能够接近人类的表现。在 10,000 幅 MNIST 测试图像上~——~模型从未在训练中接触的图 +像~——~该系统最终能够将其中 9,967 幅正确分类。这儿我们看看错分的 33 幅图像。注意 +正确分类是右上的标记;系统产生的分类在右下: \begin{center} \includegraphics[width=.75\textwidth]{ensemble_errors} \end{center} -可以发现,这里面的图像使对于人类来说都是非常困难区分的。例如,在第一行的第三幅图。 -我看的话,看起来更像是 “9” 而非 “8”,而 “8” 却是给出的真实的结果。我们的网 -络同样能够确定这个是 “9”。这种类型的“错误”最起码是容易理解的,可能甚至值得我 -们赞许。最后用对最近使用深度(卷积)神经网络在图像识别上的研究进展作为关于图像识 -别的讨论的总结。 +可以发现,这里面的图像即使对于人类来说都是非常困难区分的。例如,在第一行的第三幅 +图。我看的话,看起来更像是 “9” 而非 “8”,而 “8” 却是给出的真实的结果。我们 +的网络同样能够确定这个是 “9”。这种类型的“错误”最起码是容易理解的,可能甚至值 +得我们赞许。最后用对最近使用深度(卷积)神经网络在图像识别上的研究进展作为关于图 +像识别的讨论的总结。 本章剩下的部分,我们将会从一个更加宽泛和宏观的角度来讨论深度学习。概述一些神经网 -络的其他模型,例如 RNN 和 LSTM 网络,以及这些网络如何在语音识别、自然语言处理和其 -他领域中应用的。最后会试着推测一下,神经网络和深度学习未来发展的方向,会 -从 intention-driven user interfaces 谈谈深度学习在人工智能的角色。这章内容建立在 -本书前面章节的基础之上,使用了前面介绍的诸如 BP、规范化、softmax 函数,等等。然而, -要想阅读这一章,倒是不需要太过细致地掌握前面章节中内容的所有的细节。当然读完第一 -章关于神经网络的基础是非常有帮助的。本章提到第二章到第五章的概念时,也会在文中给 -出链接供读者去查看这些必需的概念。 +络的其他模型,例如 RNN 和 LSTM 网络,以及这些网络如何在语音识别、自然语言处理和 +其他领域中应用的。最后会试着推测一下,神经网络和深度学习未来发展的方向,会从% +\gls*{idui}谈谈深度学习在人工智能的角色。这章内容建立在本书前面章节的基础之上, +使用了前面介绍的诸如\gls*{bp}、\gls*{regularization}、\gls*{softmax-func},等等。 +然而,要想阅读这一章,倒是不需要太过细致地掌握前面章节中内容的所有的细节。当然读 +完第一章关于神经网络的基础是非常有帮助的。本章提到第二章到第五章的概念时,也会在 +文中给出链接供读者去查看这些必需的概念。 需要注意的一点是,本章所没有包含的那一部分。这一章并不是关于最新和最强大的神经网 络库。我们也不是想训练数十层的神经网络来处理最前沿的问题。而是希望能够让读者理解 -深度神经网络背后核心的原理,并将这些原理用在一个 MNIST 问题的解决中,方便我们的理 -解。换句话说,本章目标不是将最前沿的神经网络展示给你看。包括前面的章节,我们都是 -聚焦在基础上,这样读者就能够做好充分的准备来掌握众多的不断涌现的深度学习领域最新 -工作。本章仍然在Beta版。期望读者指出笔误,bug,小错和主要的误解。如果你发现了可疑 -的地方,请直接联系 mn@michaelnielsen.org。 +深度神经网络背后核心的原理,并将这些原理用在一个 MNIST 问题的解决中,方便我们的 +理解。换句话说,本章目标不是将最前沿的神经网络展示给你看。包括前面的章节,我们都 +是聚焦在基础上,这样读者就能够做好充分的准备来掌握众多的不断涌现的深度学习领域最 +新工作。本章仍然在Beta版。期望读者指出笔误,bug,小错和主要的误解。如果你发现了 +可疑的地方,请直接联系 mn@michaelnielsen.org。 \section{介绍卷积网络} \label{sec:convolutional_networks} @@ -70,41 +70,39 @@ \section{介绍卷积网络} \includegraphics{tikz41} \end{center} -特别地,对输入图像中的每个像素点,我们将其光强度作为对应输入层神经元的值。对 -于 $28 \times 28$ 像素的图像,这意味着我们的网络有 -$784$($= 28 \times 28$)个输入神经元。我们然后训练网络的权重和偏置,使得网络输出 -能够~——~如我们希望地~——~正确地辨认输入图像:'0', '1', '2', ..., '8', or '9'。 +特别地,对输入图像中的每个像素点,我们将其光强度作为对应输入层神经元的值。对于 +$28 \times 28$ 像素的图像,这意味着我们的网络有 784($= 28 \times 28$)个输入神 +经元。我们然后训练网络的\gls*{weight}和\gls*{bias},使得网络输出能够~——~如我们希望地~——~正确地辨 +认输入图像:'0', '1', '2', $\ldots$, '8', 或 '9'。 我们之前的网络工作得相当好:我们已经\hyperref[98percent]{得到了超过 98\% 的分类 准确率},使用来自 \hyperref[sec:learning_with_gradient_descent]{MNIST 手写数字 数据集}的训练和测试数据。但是仔细推敲,使用全连接层的网络来分类图像是很奇怪的。 原因是这样的一个网络架构不考虑图像的空间结构。例如,它在完全相同的基础上去对待相 距很远和彼此接近的输入像素。这样的空间结构的概念必须从训练数据中推断。但是如果我 -们使用一个设法利用空间结构的架构,而不是从一个\emph{白板状态}的网络架构开始,会 -怎样?在这一节中,我会描述\emph{卷积神经网络}\index{卷积神经网络}\footnote{最初 - 的卷积神经网络要追溯到 1970 年代。但是建立起现代卷积网络学科的开创性论文是一篇 - 1998 年的 +们使用一个设法利用空间结构的架构,而不是从一个\textbf{白板状态}的网络架构开始, +会怎样?在这一节中,我会描述\textbf{\gls{cnn}}\footnote{最初的\gls*{cnn}要追溯到 + 1970 年代。但是建立起现代卷积网络学科的开创性论文是一篇1998 年的 “\href{http://yann.lecun.com/exdb/publis/pdf/lecun-98.pdf}{Gradient-based learning applied to document recognition}”,作者为 Yann LeCun, Léon Bottou, Yoshua Bengio, 和 Patrick Haffner。LeCun 自那以后做了一个关于卷积网 络术语的有趣评论:“以类似卷积网络模型的[生物学上的]神经系统原型是非常牵强的。 - 这正是为什么我把它们称为‘卷积网络’而不是‘卷积神经网络’,以及为什么我们称这 - 些节点为‘单元’而不是‘神经元’”。尽管有这样的评论,卷积网络使用许多我们目前 - 为止学习到的神经网络相同的思想:诸如反向传播,梯度下降,规范化,非线性激活函数 - 等等。所以我们会遵循常规惯例,认为它们是神经网络的一种类型。我会交换地使用术语 - “卷积神经网络”和“卷积网络(行为)”。我也会交换地使用术语“[人工]神经元” - 和“单元”。}。这些网络使用一个特别适用于分类图像的特殊架构。使用这个架构使得 -卷积网络能更快训练。相应的,这帮助我们训练深度的、多层的网络,它非常擅长于分类图 -像。今天,深度卷积网络或者一些近似的变化形式,被用在大多数图像识别的神经网络中。 - -卷积神经网络采用了三种基本概念:\emph{局部感受野}(\emph{local receptive - fields}\index{local receptive fields}),\emph{共享权重}(\emph{shared - weights}\index{shared weights}),和\emph{混 - 合}(\emph{pooling}\index{pooling})。让我们逐个看下:\\ - -\textbf{局部感受野\index{局部感受野}:} 在之前看到的全连接层的网络中,输入被描绘 -成纵向排列的神经元。但在一个卷积网络中,把输入看作是一个 $28 \times 28$ 的方形排 -列的神经元更有帮助,其值对应于我们用作输入的 $28 \times 28$ 的像素光强度: + 这正是为什么我把它们称为‘卷积网络’而不是‘\gls*{cnn}’,以及为什么我们称这些 + 节点为‘单元’而不是‘神经元’”。尽管有这样的评论,卷积网络使用许多我们目前为 + 止学习到的神经网络相同的思想:诸如反向传播,梯度下降,\gls*{regularization}, + 非线性激活函数等等。所以我们会遵循常规惯例,认为它们是神经网络的一种类型。我会 + 交换地使用术语“\gls*{cnn}”和“卷积网络(行为)”。我也会交换地使用术语“[人 + 工]神经元”和“单元”。}。这些网络使用一个特别适用于分类图像的特殊架构。使 +用这个架构使得卷积网络能更快训练。相应的,这帮助我们训练深度的、多层的网络,它非 +常擅长于分类图像。今天,深度卷积网络或者一些近似的变化形式,被用在大多数图像识别 +的神经网络中。 + +\gls*{cnn}采用了三种基本概念:\textbf{\gls{lrf}},\textbf{\gls{shared-weights}}, +和\textbf{\gls{pooling}}。让我们逐个看下:\\ + +\textbf{\gls*{lrf}:} 在之前看到的全连接层的网络中,输入被描绘成纵向排列的神经元。 +但在一个卷积网络中,把输入看作是一个 $28 \times 28$ 的方形排列的神经元更有帮助, +其值对应于我们用作输入的 $28 \times 28$ 的像素光强度: \begin{center} \includegraphics{tikz42} \end{center} @@ -112,73 +110,74 @@ \section{介绍卷积网络} 和通常一样,我们把输入像素连接到一个隐藏神经元层。但是我们不会把每个输入像素连接 到每个隐藏神经元。相反,我们只是把输入图像进行小的,局部区域的连接。 -说的确切一点,第一个隐藏层中的每个神经元会连接到一个输入神经元的一个小区域,例如, -一个 $5 \times 5$ 的区域,对应于 $25$ 个输入像素。所以对于一个特定的隐藏神经元, -我们可能有看起来像这样的连接: +说的确切一点,第一个\gls*{hidden-layer}中的每个神经元会连接到一个输入神经元的一 +个小区域,例如,一个 $5 \times 5$ 的区域,对应于 25 个输入像素。所以对于一个特定 +的隐藏神经元,我们可能有看起来像这样的连接: \begin{center} \includegraphics{tikz43} \end{center} -这个输入图像的区域被称为隐藏神经元的\emph{局部感受野}。它是输入像素上的一个小窗口。 -每个连接学习一个权重。而隐藏神经元同时也学习一个总的偏置。你可以把这个特定的隐藏 -神经元看作是在学习分析它的局部感受野。 +这个输入图像的区域被称为隐藏神经元的\textbf{\gls*{lrf}}。它是输入像素上的一个小 +窗口。每个连接学习一个\gls*{weight}。而隐藏神经元同时也学习一个总的\gls*{bias}。你可以把这个特定 +的隐藏神经元看作是在学习分析它的\gls*{lrf}。 -我们然后在整个输入图像上交叉移动局部感受野。对于每个局部感受野,在第一个隐藏层中 -有一个不同的隐藏神经元。为了正确说明,让我们从左上角开始一个局部感受野: +我们然后在整个输入图像上交叉移动\gls*{lrf}。对于每个\gls*{lrf},在第一个\gls*{hidden-layer}中 +有一个不同的隐藏神经元。为了正确说明,让我们从左上角开始一个\gls*{lrf}: \begin{center} \includegraphics{tikz44} \end{center} -然后我们往右一个像素(即一个神经元)移动局部感受野,连接到第二个隐藏神经元: +然后我们往右一个像素(即一个神经元)移动\gls*{lrf},连接到第二个隐藏神经元: \begin{center} \includegraphics{tikz45} \end{center} -如此重复,构建起第一个隐藏层。注意如果我们有一个 $28 \times 28$ 的输入图像,$5 -\times 5$ 的局部感受野,那么隐藏层中就会有 $24 \times 24$ 个神经元。这是因为在抵 -达右边(或者底部)的输入图像之前,我们只能把局部感受野横向移动 $23$ 个神经元(或 - 者往下 $23$ 个神经元)。 - -我显示的局部感受野每次移动一个像素。实际上,有时候会使用不同的\emph{跨距}。例如, -我可以往右(或下)移动 $2$ 个像素的局部感受野,这种情况下我们使用了 $2$ 个跨距。 -在这章里我们大部分时候会固定使用 $1$ 的跨距,但是值得知道人们有时用不同的跨距试 -验\footnote{正如在前面章节中做过的,如果我们对尝试不同跨距感兴趣,那么我们可以使用 -验证数据来挑选可以提供最佳性能的跨距。详细内容参见前面关于如何选择神经网络的超参数的% -\hyperref[sec:how_to_choose_a_neural_network's_hyper-parameters]{讨论}。 -相同的方法也可以用来选择局部感受野的大小~——~当然,对于使用一个 $5 \times 5$ -的局部感受野没有什么特别的。通常,当输入图像显著大于 $28 \times 28$ 像素的 MNIST 图像时, -更大的局部感受野往往是有益的。}。\\ - -\textbf{共享权重\index{共享权重}和偏置:} 我已经说过每个隐藏神经元具有一个偏置和 -连接到它的局部感受野的 $5 \times 5$ 权重。我没有提及的是我们打算对 $24 \times -24$ 隐藏神经元中的每一个使用\emph{相同的}权重和偏置。换句话说,对第 $j, k$ 个隐 -藏神经元,输出为: +如此重复,构建起第一个\gls*{hidden-layer}。注意如果我们有一个 $28 \times 28$ 的输入图像,$5 +\times 5$ 的\gls*{lrf},那么\gls*{hidden-layer}中就会有 $24 \times 24$ 个神经元。这是因为在抵 +达右边(或者底部)的输入图像之前,我们只能把\gls*{lrf}横向移动 23 个神经元(或 + 者往下 23 个神经元)。 + +我显示的\gls*{lrf}每次移动一个像素。实际上,有时候会使用不同的\textbf{跨距}。例 +如,我可以往右(或下)移动 $2$ 个像素的\gls*{lrf},这种情况下我们使用了 $2$ 个跨 +距。在这章里我们大部分时候会固定使用 $1$ 的跨距,但是值得知道人们有时用不同的跨 +距试验\footnote{正如在前面章节中做过的,如果我们对尝试不同跨距感兴趣,那么我们可 + 以使用验证数据来挑选可以提供最佳性能的跨距。详细内容参见前面关于如何选择神经网 + 络的超参数的% + \hyperref[sec:how_to_choose_a_neural_network's_hyper-parameters]{讨论}。相同的 + 方法也可以用来选择\gls*{lrf}的大小~——~当然,对于使用一个 $5 \times 5$ 的% + \gls*{lrf}没有什么特别的。通常,当输入图像显著大于 $28 \times 28$ 像素的 MNIST + 图像时,更大的\gls*{lrf}往往是有益的。}。\\ + +\textbf{\gls*{shared-weights}和\gls*{bias}:} 我已经说过每个隐藏神经元具有一个\gls*{bias}和连 +接到它的\gls*{lrf}的 $5 \times 5$ \gls*{weight}。我没有提及的是我们打算对 $24 \times 24$ +隐藏神经元中的每一个使用\textbf{相同的}\gls*{weight}和\gls*{bias}。换句话说,对第 $j, k$ 个隐藏 +神经元,输出为: \begin{equation} \sigma\left(b + \sum_{l=0}^4 \sum_{m=0}^4 w_{l,m} a_{j+l, k+m} \right) \label{eq:125}\tag{125} \end{equation} 这里 $\sigma$ 是神经元的激活函数~——~可以是我们在前面章里使用过的 -\hyperref[sec:sigmoid_neurons]{S型函数}。$b$ 是偏置的共享值。$w_{l,m}$ 是一个共 -享权重的 $5 \times 5$ 数组。最后,我们使用 $a_{x, y}$ 来表示位置为 $x, y$ 的输入 -激活值。 +\hyperref[sec:sigmoid_neurons]{\gls*{sigmoid-func}}。$b$ 是\gls*{bias}的共享值。 + $w_{l,m}$ 是一个共享\gls*{weight}的 $5 \times 5$ 数组。最后,我们使用 $a_{x, y}$ 来表示 + 位置为 $x, y$ 的输入激活值。 -这意味着第一个隐藏层的所有神经元检测完全相同的特征\footnote{我还没有精确定义特征 +这意味着第一个\gls*{hidden-layer}的所有神经元检测完全相同的特征\footnote{我还没有精确定义特征 的概念。非正式地,把一个隐藏神经元检测的特征看作一种引起神经元激活的输入模式: 例如,它可能是图像的一条边,或者一些其它的形状。},只是在输入图像的不同位置。 -要明白为什么是这个道理,把权重和偏置设想成隐藏神经元可以挑选的东西,例如,在一个 -特定的局部感受野的垂直边缘。这种能力在图像的其它位置也很可能是有用的。因此,在图 +要明白为什么是这个道理,把\gls*{weight}和\gls*{bias}设想成隐藏神经元可以挑选的东西,例如,在一个 +特定的\gls*{lrf}的垂直边缘。这种能力在图像的其它位置也很可能是有用的。因此,在图 像中应用相同的特征检测器是非常有用的。用稍微更抽象的术语,卷积网络能很好地适应图 -像的平移不变性:例如稍稍移动一幅猫的图像,它仍然是一幅猫的图像\footnote{ -实际上,对于我们已经在学习的 MNIST 数字分类问题,这些图像被置于中心,而且大小被归一化。 -所以 MNIST 比“在自然环境中”找到的图像有更少的平移不变性。很多输入空间中的诸如边缘 -和角点的特征仍然可能是有用的。}。 +像的平移不变性:例如稍稍移动一幅猫的图像,它仍然是一幅猫的图像\footnote{实际上, + 对于我们已经在学习的 MNIST 数字分类问题,这些图像被置于中心,而且大小被归一化。 + 所以 MNIST 比“在自然环境中”找到的图像有更少的平移不变性。很多输入空间中的诸 + 如边缘和角点的特征仍然可能是有用的。}。 -因为这个原因,我们有时候把从输入层到隐藏层的映射称为一个\emph{特征映射}。我们把 -定义特征映射的权重称为\emph{共享权重}。我们把以这种方式定义特征映射的偏置称为% -\emph{共享偏置}。共享权重和偏置经常被称为一个\emph{卷积核}或者\emph{滤波器}。在文献 -中,人们有时以稍微不同的方式使用这些术语,对此我不打算去严格区分;稍后我们会看一 -些具体的例子。 +因为这个原因,我们有时候把从输入层到\gls*{hidden-layer}的映射称为一个\textbf{特征映射}。我们 +把定义特征映射的\gls*{weight}称为\textbf{\gls*{shared-weights}}。我们把以这种方式定义特征 +映射的\gls*{bias}称为\textbf{共享\gls*{bias}}。\gls*{shared-weights}和\gls*{bias}经常被称为一个% +\textbf{卷积核}或者\textbf{滤波器}。在文献中,人们有时以稍微不同的方式使用这些术 +语,对此我不打算去严格区分;稍后我们会看一些具体的例子。 目前我描述的网络结构只能检测一种局部特征的类型。为了完成图像识别我们需要超过一个 的特征映射。所以一个完整的卷积层由几个不同的特征映射组成: @@ -186,90 +185,92 @@ \section{介绍卷积网络} \includegraphics{tikz46} \end{center} -在这个例子中,有 3 个特征映射。每个特征映射定义为一个 $5 \times 5$ 共享权重和单个 -共享偏置的集合。其结果是网络能够检测 3 种不同的特征,每个特征都在整个图像中可检 -测。 +在这个例子中,有 3 个特征映射。每个特征映射定义为一个 $5 \times 5$ +\gls*{shared-weights}和单个共享\gls*{bias}的集合。其结果是网络能够检测 3 种不同的特征, +每个特征都在整个图像中可检测。 -为了让上面的图示简单些,我仅仅展示了 $3$ 个特征映射。然而,在实践中卷积网络可能使 +为了让上面的图示简单些,我仅仅展示了 3 个特征映射。然而,在实践中卷积网络可能使 用很多(也许多得多)的特征映射。一种早期的识别 MNIST 数字的卷积网络,LeNet-5,使 -用 $6$ 个特征映射,每个关联到一个 $5 \times 5$ 的局部感受野。所以上面的插图例子实 +用 6 个特征映射,每个关联到一个 $5 \times 5$ 的\gls*{lrf}。所以上面的插图例子实 际和 LeNet-5 很接近。而在我们在本章后面要开发的例子里,我们将使用具有 20 和40 个 -特征映射的卷积层。让我们快速看下已经学到的一些特征\footnote{图示说明的特征映射来自 -我们最后的训练卷积网络,参见\hyperref[final_conv]{这里}。}: +特征映射的卷积层。让我们快速看下已经学到的一些特征\footnote{图示说明的特征映射来 + 自我们最后的训练卷积网络,参见\hyperref[final_conv]{这里}。}: \begin{center} \includegraphics[width=.65\textwidth]{net_full_layer_0} \end{center} 这 20 幅图像对应于 20 个不同的特征映射(或滤波器、核)。每个映射有一幅 $5 \times -5$ 块的图像表示,对应于局部感受野中的 $5 \times 5$ 权重。白色块意味着一个小(典型 -的,更小的负数)权重,所以这样的特征映射对相应的输入像素有更小的响应。更暗的块意 -味着一个更大的权重,所以这样的特征映射对相应的输入像素有更大的响应。非常粗略地讲, -上面的图像显示了卷基层作出响应的特征类型。 +5$ 块的图像表示,对应于\gls*{lrf}中的 $5 \times 5$ \gls*{weight}。白色块意味着一个小(典 + 型的,更小的负数)\gls*{weight},所以这样的特征映射对相应的输入像素有更小的响应。更暗的 +块意味着一个更大的\gls*{weight},所以这样的特征映射对相应的输入像素有更大的响应。非常粗略 +地讲,上面的图像显示了卷基层作出响应的特征类型。 所以我们能从这些特征映射中得到什么结论?很明显这里有超出了我们期望的空间结构:这 些特征许多有清晰的亮和暗的子区域。这表示我们的网络实际上正在学习和空间结构相关的 东西。然而,除了那个,看清这些特征检测器在学什么是很困难的。当然,我们并不是在学 习(例如)\href{http://en.wikipedia.org/wiki/Gabor_filter}{Gabor 滤波器},它已经 被用在很多传统的图像识别方法中。实际上,现在有许多关于通过卷积网络来更好理解特征 -的工作成果。如果你感兴趣,我建议从 Matthew Zeiler 和 Rob Fergus 的(2013)论 -文 \href{http://arxiv.org/abs/1311.2901}{Visualizing and Understanding +的工作成果。如果你感兴趣,我建议从 Matthew Zeiler 和 Rob Fergus 的(2013)论文 +\href{http://arxiv.org/abs/1311.2901}{Visualizing and Understanding Convolutional Networks} 开始。 -共享权重和偏置的一个很大的优点是,它大大减少了参与的卷积网络的参数。对于每个特征 -映射我们需要 $25 = 5 \times 5$ 个共享权重,加上一个共享偏置。所以每个特征映射需 -要 $26$ 个参数。如果我们有 $20$ 个特征映射,那么总共有 $20 \times 26 = 520$ 个参 -数来定义卷积层。作为对比,假设我们有一个全连接的第一层,具有 $784 = 28 \times -28$ 个输入神经元,和一个相对适中的 $30$ 个隐藏神经元,正如我们在本书之前的很多例 -子中使用的。总共有 $784 \times 30$ 个权重,加上额外的 $30$ 个偏置,共 -有 $23,550$个参数。换句话说,这个全连接的层有多达 $40$ 倍于卷基层的参数。 +\gls*{shared-weights}和\gls*{bias}的一个很大的优点是,它大大减少了参与的卷积网络的参数。 +对于每个特征映射我们需要 $25 = 5 \times 5$ 个\gls*{shared-weights},加上一个共享 +\gls*{bias}。所以每个特征映射需要 $26$ 个参数。如果我们有 $20$ 个特征映射,那么总共有 +$20 \times 26 = 520$ 个参数来定义卷积层。作为对比,假设我们有一个全连接的第一层, +具有 $784 = 28 \times 28$ 个输入神经元,和一个相对适中的 $30$ 个隐藏神经元,正如 +我们在本书之前的很多例子中使用的。总共有 $784 \times 30$ 个\gls*{weight},加上额外的 $30$ +个\gls*{bias},共有 $23,550$个参数。换句话说,这个全连接的层有多达 $40$ 倍于卷基层的参 +数。 当然,我们不能真正做一个参数数量之间的直接比较,因为这两个模型的本质是不同的径。 但是,直观地,使用卷积层的平移不变性似乎很可能减少全连接模型中达到同样性能的参数 数量。反过来,这将导致更快的卷积模型的训练,并最终,将有助于我们使用卷积层建立深 度网络。 -顺便提一下,\emph{卷积}(\textit{convolutional})这一名称源自方 -程~\eqref{eq:125} 中的操作符有时被称为一个\emph{卷积}(\textit{convolution})。稍 -微更精确些,人们有时把这个方程写成 $a^1 = \sigma(b + w * a^0)$,其中 $a^1$ 表示出 -自一个特征映射的输出激活值集合,$a^0$ 是输入激活值的集合,而 $*$ 被称为一个卷积操 -作。我们不打算深入使用卷积数学,所以你不用对此太担心。但是至少值得知道这个名称从何而来。 -\\ - -\textbf{混合层:} 除了刚刚描述的卷积层,卷积神经网络也包含\emph{混合 - 层}(\textit{pooling layers})。混合层通常紧接着在卷积层之后使用。它要做的是 -简化从卷积层输出的信息。 - -详细地说,一个混合层取得从卷积层输出的每一个特征映射\footnote{这里使用的术语是不严格的。 -特别地,我用“特征映射”表示的意思不是通过卷积层计算出的函数,而是隐藏神经元从该层输出的激活值。 -这种术语的轻度滥用在研究文献中是相当普遍的。}并且从它们准 -备一个凝缩的特征映射。例如,混合层的每个单元可能概括了前一层的一个(比如)$2 -\times 2$ 的区域。作为一个具体的例子,一个常见的混合的程序被称为\emph{最大值混 - 合}(\textit{max-pooling})。在最大值混合中,一个混合单元简单地输出 -其 $2 \times 2$ 输入区域的最大激活值,正如下图说明的: +顺便提一下,\textbf{卷积}(\textit{convolutional})这一名称源自方程~\eqref{eq:125} +中的操作符有时被称为一个\textbf{卷积}(\textit{convolution})。稍微更精确些,人 +们有时把这个方程写成 $a^1 = \sigma(b + w * a^0)$,其中 $a^1$ 表示出自一个特征映 +射的输出激活值集合,$a^0$ 是输入激活值的集合,而 $*$ 被称为一个卷积操作。我们不 +打算深入使用卷积数学,所以你不用对此太担心。但是至少值得知道这个名称从何而来。\\ + +\textbf{\gls*{pooling}层:} 除了刚刚描述的卷积层,\gls*{cnn}也包含% +\textbf{\gls*{pooling}层}(\textit{pooling layers})。\gls*{pooling}层通常紧接着 +在卷积层之后使用。它要做的是简化从卷积层输出的信息。 + +详细地说,一个\gls*{pooling}层取得从卷积层输出的每一个特征映射\footnote{这里使用 + 的术语是不严格的。特别地,我用“特征映射”表示的意思不是通过卷积层计算出的函数, + 而是隐藏神经元从该层输出的激活值。这种术语的轻度滥用在研究文献中是相当普遍的。}并 +且从它们准备一个凝缩的特征映射。例如,\gls*{pooling}层的每个单元可能概括了前一层 +的一个(比如)$2 \times 2$ 的区域。作为一个具体的例子,一个常见的\gls*{pooling} +的程序被称为\textbf{最大值混合}(\textit{max-pooling})。在最大值\gls*{pooling} +中,一个\gls*{pooling}单元简单地输出其 $2 \times 2$ 输入区域的最大激活值,正如下 +图说明的: \begin{center} \includegraphics{tikz47} \end{center} -注意既然从卷积层有 $24 \times 24$ 个神经元输出,混合后我们得到 $12 \times -12$ 个神经元。 +注意既然从卷积层有 $24 \times 24$ 个神经元输出,\gls*{pooling}后我们得到 $12 +\times 12$ 个神经元。 -正如上面提到的,卷积层通常包含超过一个特征映射。我们将最大值混合分别应用于每一 -个特征映射。所以如果有三个特征映射,组合在一起的卷积层和最大值混合层看起来像这 -样: +正如上面提到的,卷积层通常包含超过一个特征映射。我们将最大值\gls*{pooling}分别应 +用于每一个特征映射。所以如果有三个特征映射,组合在一起的卷积层和最大值% +\gls*{pooling}层看起来像这样: \begin{center} \includegraphics{tikz48} \end{center} -我们可以把最大值混合看作一种网络询问是否有一个给定的特征在一个图像区域中的哪个 -地方被发现的方式。然后它扔掉确切的位置信息。直观上,一旦一个特征被发现,它的确切 -位置并不如它相对于其它特征的大概位置重要。一个很大的好处是,这样可以有很多被更少 -地混合的特征,所以这有助于减少在以后的层所需的参数的数目。 +我们可以把最大值\gls*{pooling}看作一种网络询问是否有一个给定的特征在一个图像区域 +中的哪个地方被发现的方式。然后它扔掉确切的位置信息。直观上,一旦一个特征被发现, +它的确切位置并不如它相对于其它特征的大概位置重要。一个很大的好处是,这样可以有很 +多被更少地\gls*{pooling}的特征,所以这有助于减少在以后的层所需的参数的数目。 -最大值混合并不是用于混合的仅有的技术。另一个常用的方法是\emph{L2 混合}(\textit{L2 - pooling})。这里我们取 $2 \times 2$ 区域中激活值的平方和的平方根,而不是最大 -激活值。虽然细节不同,但其直观上和最大值混合是相似的:L2 混合是一种凝缩从卷积层输 -出的信息的方式。在实践中,两种技术都被广泛应用。而且有时候人们使用其它混合操作的 -类型。如果你正在尝试优化性能,你可以使用验证数据来比较混合的不同方法,并选择一个 -工作得最好的。但我们不打算惦记这种细节上的优化。\\ +最大值\gls*{pooling}并不是用于\gls*{pooling}的仅有的技术。另一个常用的方法是 +\textbf{L2 \gls*{pooling}}。这里我们取 $2 \times 2$ 区域中激活值的平方和的平方根, +而不是最大激活值。虽然细节不同,但其直观上和最大值\gls*{pooling}是相似的:L2 +\gls*{pooling}是一种凝缩从卷积层输出的信息的方式。在实践中,两种技术都被广泛应用。 +而且有时候人们使用其它\gls*{pooling}操作的类型。如果你正在尝试优化性能,你可以使 +用验证数据来比较\gls*{pooling}的不同方法,并选择一个工作得最好的。但我们不打算惦 +记这种细节上的优化。\\ \textbf{综合在一起:} 我们现在可以把这些思想都放在一起来构建一个完整的卷积神经网 络。它和我们刚看到的架构相似,但是有额外的一层 $10$ 个输出神经元,对应于 $10$ 个 @@ -278,90 +279,97 @@ \section{介绍卷积网络} \includegraphics{tikz49} \end{center} -这个网络从 $28 \times 28$ 个输入神经元开始,这些神经元用于对 MNIST 图像的像素强度 -进行编码。接着的是一个卷积层,使用一个 $5 \times 5$ 局部感受野和 $3$ 个特征映射。 -其结果是一个 $3 \times 24 \times 24$ 隐藏特征神经元层。下一步是一个最大值混合层, -应用于$2 \times 2$ 区域,遍及 $3$ 个特征映射。结果是一个 $3 \times 12 \times 12$ -隐藏特征神经元层。 +这个网络从 $28 \times 28$ 个输入神经元开始,这些神经元用于对 MNIST 图像的像素强 +度进行编码。接着的是一个卷积层,使用一个 $5 \times 5$ \gls*{lrf}和 $3$ 个特征映 +射。其结果是一个 $3 \times 24 \times 24$ 隐藏特征神经元层。下一步是一个最大值% +\gls*{pooling}层,应用于 $2 \times 2$ 区域,遍及 $3$ 个特征映射。结果是一个 $3 +\times 12 \times 12$ 隐藏特征神经元层。 -网络中最后连接的层是一个全连接层。更确切地说,这一层将最大值混合层的\emph{每一个}神 -经元连接到每一个输出神经元。这个全连接结构和我们之前章节中使用的相同。然而,注意 -上面的图示,为了简化,我只使用了一个箭头,而不是显示所有的连接。当然,你可以很容 -易想象到这些连接。 +网络中最后连接的层是一个全连接层。更确切地说,这一层将最大值\gls*{pooling}层的% +\textbf{每一个}神经元连接到每一个输出神经元。这个全连接结构和我们之前章节中使用 +的相同。然而,注意上面的图示,为了简化,我只使用了一个箭头,而不是显示所有的连接。 +当然,你可以很容易想象到这些连接。 这个卷积架构和之前章节中使用的架构相当不同。但是总体的描述是相似的:一个由很多简 -单的单元构成的网络,这些单元的行为由它们的权重和偏置确定。而总体的目标仍然是一样 -的:用训练数据来训练网络的权重和偏置,使得网络可以胜任分类输入数字。 +单的单元构成的网络,这些单元的行为由它们的\gls*{weight}和\gls*{bias}确定。而总体的目标仍然是一样 +的:用训练数据来训练网络的\gls*{weight}和\gls*{bias},使得网络可以胜任分类输入数字。 -特别的,正如本书中前面那样,我们将用随即梯度下降和反向传播训练我们的网络。这大部 -分按照前面章节中完全相同的方式来处理。然而,我们确实需要对反向传播程序做些修改。 -原因是我们之前的\hyperref[ch:HowThebackpropagationalgorithmworks]{反向传播的推导}是 -针对全连接层的网络。幸运的是,针对卷积和最大值混合层的推导是简单的。如果你想理解 -细节,那么我请你完成下面的问题。注意这个问题会花费些时间来完成,除非你确实已经吸 -收了\hyperref[ch:HowThebackpropagationalgorithmworks]{前面的反向传播的推导}(这 - 种情况下是容易的)。 +特别的,正如本书中前面那样,我们将用\gls*{sgd}和\gls*{bp}训练我们的网络。这大部 +分按照前面章节中完全相同的方式来处理。然而,我们确实需要对\gls*{bp}程序做些修改。 +原因是我们之前的\hyperref[ch:HowThebackpropagationalgorithmworks]{\gls*{bp}的推 + 导}是针对全连接层的网络。幸运的是,针对卷积和最大值\gls*{pooling}层的推导是简 +单的。如果你想理解细节,那么我请你完成下面的问题。注意这个问题会花费些时间来完成, +除非你确实已经吸收了\hyperref[ch:HowThebackpropagationalgorithmworks]{前面的反向 + 传播的推导}(这种情况下是容易的)。 \subsection*{问题} \begin{itemize} -\item \textbf{卷积网络中的反向传播}\quad 在一个具有全连接层的网络中,反向传播的 - 核心方程是 \eqref{eq:bp1}--\eqref{eq:bp4}(\hyperref[backpropsummary]{链接})。 - 假设我们有这样一个网络,它包含有一个卷积层,一个最大值混合层,和一个全连接的输 - 出层,正如上面讨论的那样。反向传播的方程式要如何修改? +\item \textbf{卷积网络中的\gls*{bp}}\quad 在一个具有全连接层的网络中,\gls*{bp} + 的核心方程是 \eqref{eq:bp1}--\eqref{eq:bp4}(\hyperref[backpropsummary]{链接})。 + 假设我们有这样一个网络,它包含有一个卷积层,一个最大值\gls*{pooling}层,和一个 + 全连接的输出层,正如上面讨论的那样。\gls*{bp}的方程式要如何修改? \end{itemize} \section{卷积神经网络在实际中的应用} \label{seq:convolutional_neural_networks_in_practice} -我们现在已经明白了卷积神经网络后面的核心思想。让我们通过实现一些卷积网络,并将它 -们应用于 MNIST 数字分类问题,来看看它们如何在实践中工作。我们将使用的程序是 +我们现在已经明白了\gls*{cnn}后面的核心思想。让我们通过实现一些卷积网络,并将它们 +应用于 MNIST 数字分类问题,来看看它们如何在实践中工作。我们将使用的程序是 \lstinline!network3.py!,它是前面章节开发的 \lstinline!network.py! 和 -\lstinline!network2.py! 的强化版本\footnote{注意 \lstinline!network3.py! - 包含了源自 Theano 库文档中关于卷积神经网络(尤其是 \href{http://deeplearning.net/tutorial/lenet.html}{LeNet-5} 的实现) - ,Misha Denil 的\href{https://github.com/mdenil/dropout}{弃权的实现},以及 - \href{http://colah.github.io/}{Chris Olah} 的概念。}。如果你想跟着学,代码可以从 +\lstinline!network2.py! 的强化版本\footnote{注意 \lstinline!network3.py! 包含了 + 源自 Theano 库文档中关于\gls*{cnn}(尤其是 + \href{http://deeplearning.net/tutorial/lenet.html}{LeNet-5} 的实现),Misha + Denil 的\href{https://github.com/mdenil/dropout}{弃权的实现},以及 + \href{http://colah.github.io/}{Chris Olah} 的概念。}。如果你想跟着学,代码可以 +从 \href{https://github.com/mnielsen/neural-networks-and-deep-learning/blob/master/src/network3.py}{GitHub} 上下载。注意我们将在下一节中解决 \lstinline!network3.py! 需要的代码。在这一节中, 我们将把 \lstinline!network3.py! 作为库来构建卷积网络。 -程序 \lstinline!network.py! 和 \lstinline!network2.py! 是用 Python 和矩阵库 -Numpy 实现的。这些程序从最初的原理工作,并致力于反向传播、随即梯度下降等细节。但 -是现在我们已经理解了这些细节,对于 \lstinline!network3.py! 我们打算使用一个称为 -\href{http://deeplearning.net/software/theano/}{Theano} 的机器学习 -库\footnote{参见 \href{http://www.iro.umontreal.ca/~lisa/pointeurs/theano_scipy2010.pdf}{Theano: A CPU and GPU Math Expression Compiler in Python}, -作者为 James Bergstra, Olivier Breuleux, Frederic Bastien, Pascal Lamblin, Ravzan - Pascanu, Guillaume Desjardins, Joseph Turian, David Warde-Farley, 和 Yoshua - Bengio (2010)。 Theano 也是流行的 \href{http://deeplearning.net/software/pylearn2/}{Pylearn2} 和 - \href{http://keras.io/}{Keras} 神经网络库的基础。其它在本文写作时流行的神经网路库包括 - \href{http://caffe.berkeleyvision.org/}{Caffe} 和 \href{http://torch.ch/}{Torch}。}。使用 Theano 使得实现针对卷积神经网络的 -反向传播很容易,因为它自动计算涉及到的映射。Theano 也比我们前面代码更快(那些代 +程序 \lstinline!network.py! 和 \lstinline!network2.py! 是用 Python 和矩阵库Numpy +实现的。这些程序从最初的原理工作,并致力于\gls*{bp}、\gls*{sgd}等细节。但是现在 +我们已经理解了这些细节,对于 \lstinline!network3.py! 我们打算使用一个称为 +\href{http://deeplearning.net/software/theano/}{Theano} 的机器学习库\footnote{参 + 见 + \href{http://www.iro.umontreal.ca/~lisa/pointeurs/theano_scipy2010.pdf}{Theano: + A CPU and GPU Math Expression Compiler in Python},作者为 James Bergstra, + Olivier Breuleux, Frederic Bastien, Pascal Lamblin, Ravzan Pascanu, + Guillaume Desjardins, Joseph Turian, David Warde-Farley, 和 Yoshua Bengio + (2010)。 Theano 也是流行的 + \href{http://deeplearning.net/software/pylearn2/}{Pylearn2} 和 + \href{http://keras.io/}{Keras} 神经网络库的基础。其它在本文写作时流行的神经网 + 路库包括\href{http://caffe.berkeleyvision.org/}{Caffe} 和 + \href{http://torch.ch/}{Torch}。}。使用 Theano 使得实现针对\gls*{cnn}的% +\gls*{bp}很容易,因为它自动计算涉及到的映射。Theano 也比我们前面代码更快(那些代 码是为了容易理解,不是为了运行速度),这使它可实际用于训练更复杂的网络。特别地, -Theano 的一个非常好的特性是它能够运行于 CPU 或者,如果可以,GPU 上。运行于 GPU -上可以提供显著的增速,而且,有助于实际用于更复杂的网络。 - -如果你想要跟着学,你需要可运行在你的系统上的 Theano。按照项 -目\href{http://deeplearning.net/software/theano/}{主页}上的说明来安装 Theano。接 -下来的例子使用 Theano 0.6\footnote{当我发布这一章时,Theano 的当前版本变成了 0.7。 -我实际上已经在 Theano 0.7 版本中重新运行过这些例子并取得了和文中非常相似的结果。} 运行过。 -有些在没有 GPU 支持的 Mac OS X Yosemite 运行过。有些在有 NVIDIA -GPU 支持的 Ubuntu 14.04 中运行过。有些实验在两个系统中都运行过。为了 -让 \lstinline!networks3.py! 运行,你需要(适当地)把 \lstinline!networks3.py! 源 -码中的 \lstinline!GPU! 标志设置为 \lstinline!True! 或者 \lstinline!False!。此外, -为了让 Theano 运行于 GPU 上,你可能会发 -现\href{http://deeplearning.net/software/theano/tutorial/using_gpu.html}{这份指导 - 说明}有帮助。互联网上也有教程,很容易用 Google 搜索到,同样能帮助你让 Theano 工 -作。如果你手上的系统没有可用的 GPU,那么你可能想要看 -下 \href{http://aws.amazon.com/ec2/instance-types/}{Amazon Web Services} EC2 G2 -实例类型。注意即使有 GPU 支持,代码仍然需要一些时间执行。许多实验要花费从几分钟到 -几个小时的时间来运行。在 CPU 上可能需要花费数天时间来运行最复杂的实验。正如前面章 +Theano 的一个非常好的特性是它能够运行于 CPU 或者,如果可以,GPU 上。运行于 GPU上 +可以提供显著的增速,而且,有助于实际用于更复杂的网络。 + +如果你想要跟着学,你需要可运行在你的系统上的 Theano。按照项目% +\href{http://deeplearning.net/software/theano/}{主页}上的说明来安装 Theano。接下 +来的例子使用 Theano 0.6\footnote{当我发布这一章时,Theano 的当前版本变成了 0.7。 + 我实际上已经在 Theano 0.7 版本中重新运行过这些例子并取得了和文中非常相似的结 + 果。} 运行过。有些在没有 GPU 支持的 Mac OS X Yosemite 运行过。有些在有 NVIDIA +GPU 支持的 Ubuntu 14.04 中运行过。有些实验在两个系统中都运行过。为了让 +\lstinline!networks3.py! 运行,你需要(适当地)把 \lstinline!networks3.py! 源码 +中的 \lstinline!GPU! 标志设置为 \lstinline!True! 或者 \lstinline!False!。此外, +为了让 Theano 运行于 GPU 上,你可能会发现% +\href{http://deeplearning.net/software/theano/tutorial/using_gpu.html}{这份指导 + 说明}有帮助。互联网上也有教程,很容易用 Google 搜索到,同样能帮助你让 Theano +工作。如果你手上的系统没有可用的 GPU,那么你可能想要看下 +\href{http://aws.amazon.com/ec2/instance-types/}{Amazon Web Services} EC2 G2实例 +类型。注意即使有 GPU 支持,代码仍然需要一些时间执行。许多实验要花费从几分钟到几 +个小时的时间来运行。在 CPU 上可能需要花费数天时间来运行最复杂的实验。正如前面章 节里说的,我建议让程序运行着,同时继续阅读,偶尔回来检查下代码的输出。如果你用的 -是 CPU,你可能需要对更复杂的实验减少训练\epochs{}的数量,或者整个忽略它们。 - -为了取得一个基线,我们将从一个浅层架构开始,它仅仅使用一个隐藏层,包含 $100$ 个 -隐藏神经元。我们会训练 $60$ \epochs{},使用\learningrate{}为:$\eta = 0.1$, -\minibatch{} 大小为 $10$,没有规范化。这样运行\footnote{本节中的实验代码可以在 -\href{https://github.com/mnielsen/neural-networks-and-deep-learning/blob/master/src/conv.py}{这个脚本中}找到。 -注意,脚本中的代码只是简单地重复并相对于本节中的讨论。}: +是 CPU,你可能需要对更复杂的实验减少训练\\gls*{epoch}的数量,或者整个忽略它们。 + +为了取得一个基线,我们将从一个浅层架构开始,它仅仅使用一个\gls*{hidden-layer},包含 $100$ 个 +隐藏神经元。我们会训练 60 个\gls*{epoch},使用\gls*{learning-rate}为:$\eta = +0.1$,\gls*{mini-batch} 大小为 $10$,没有\gls*{regularization}。这样运 +行\footnote{本节中的实验代码可以在% + \href{https://github.com/mnielsen/neural-networks-and-deep-learning/blob/master/src/conv.py}{% + 这个脚本中}找到。注意,脚本中的代码只是简单地重复并相对于本节中的讨论。}: \begin{lstlisting}[language=Python] >>> import network3 >>> from network3 import Network @@ -376,47 +384,49 @@ \section{卷积神经网络在实际中的应用} \end{lstlisting} 我获得的一个最好的分类准确率是 $97.80$\%。这是 \lstinline!test_data! 上的分类准 -确率,在这个取值的训练\epoch{}的地方,我们在\lstinline!validation_data!上得到了 +确率,在这个取值的训练\gls*{epoch}的地方,我们在\lstinline!validation_data!上得到了 最好的分类准确率。使用验证数据来决定在何时对测试准确率估值有助于避免测试数据上的 -过度拟合(见前面关于验证数据使用的\hyperref[validation_explanation]{讨论})。我 -们将在下面遵循这个习惯。你的结构可能稍有不同,因为网络的权重和偏置是随机初始化 -的\footnote{实际上,在这个实验中我其实对这个架构的网络运行了三次独立的训练。然后我从 -这三次运行中报告了对应于最佳验证准确率的测试准确率。利用多次运行有助于减少结果中的变动, -它在比较许多个架构时是很有用的,正如我们正在做的。除非明确指出,我在下面已经遵循了这个 -程序。在实践中,它对于所获得的结果不会带来什么区别。}。 +\gls*{overfitting}(见前面关于验证数据使用的\hyperref[validation_explanation]{讨 + 论})。我们将在下面遵循这个习惯。你的结构可能稍有不同,因为网络的\gls*{weight}和\gls*{bias} +是随机初始化的\footnote{实际上,在这个实验中我其实对这个架构的网络运行了三次独立 + 的训练。然后我从这三次运行中报告了对应于最佳验证准确率的测试准确率。利用多次运 + 行有助于减少结果中的变动,它在比较许多个架构时是很有用的,正如我们正在做的。除 + 非明确指出,我在下面已经遵循了这个程序。在实践中,它对于所获得的结果不会带来什 + 么区别。}。 这个 $97.80$\% 的准确率接近于\hyperref[chap3_98_04_percent]{第三章}中获得的 $98.04$\% 的准确率,使用一个相似的网络架构和学习超参数。特别地,两个例子都使用一 -个浅层网络,具有单个包含有 $100$ 个隐藏神经元的隐藏层。两者都训练 $60$ 个 -\epochs{},\minibatch{}大小为 $10$,\learningrate{} 为 $\eta = 0.1$。 +个浅层网络,具有单个包含有 $100$ 个隐藏神经元的\gls*{hidden-layer}。两者都训练 $60$ 个 +\gls*{epoch},\gls*{mini-batch}大小为 $10$,\gls*{learning-rate} 为 $\eta = 0.1$。 然而,在之前的网络中有两个不同的地方。首先,我们% -\hyperref[sec:overfitting_and_regularization]{规范化}了之前的网络,来帮助降低过 -度拟合带来的影响。规范化当前的网络确实可以提高准确率,但是得到的只是很小,所以我 -们将推迟到后面再来惦记规范化。第二,虽然之前的网络中的最终层使用了S型激活值和交 -叉熵代价函数,当前网络使用一个柔性最大值的最终层,以及对数似然代价函数。正如第三 -章中\hyperref[subsec:softmax]{解释}的,这不是一个大的改变。我没有为了任何特别深 -刻的原因来做出这样的改变~——~主要是因为柔性最大值和对数似然代价在现代的图像分类网 -络中很常见。 +\hyperref[sec:overfitting_and_regularization]{\gls*{regularization}}了之前的网络, +来帮助降低\gls*{overfitting}带来的影响。\gls*{regularization}当前的网络确实可以 +提高准确率,但是得到的只是很小,所以我们将推迟到后面再来惦记% +\gls*{regularization}。第二,虽然之前的网络中的最终层使用了S型激活值和交叉熵代价 +函数,当前网络使用一个\gls*{softmax}的最终层,以及对数似然代价函数。正如第三章中% +\hyperref[subsec:softmax]{解释}的,这不是一个大的改变。我没有为了任何特别深刻的 +原因来做出这样的改变~——~主要是因为\gls*{softmax}和\gls*{log-likelihood}代价在现 +代的图像分类网络中很常见。 我们能用一个更深的网络架构来做得比这些结果更好吗? 让我们从在网络开始位置的右边插入一个卷积层开始。我们将使用 $5 \times 5$ 局部感受 -野,跨距为 $1$,$20$ 个特征映射。我们也会插入一个最大值混合层,它用一个 $2 -\times 2$ 的混合窗口来合并特征。所以总体的网络架构看起来很像上一节讨论的架构,但 -是有一个额外的全连接层: +野,跨距为 $1$,$20$ 个特征映射。我们也会插入一个最大值\gls*{pooling}层,它用一 +个 $2 \times 2$ 的\gls*{pooling}窗口来合并特征。所以总体的网络架构看起来很像上一 +节讨论的架构,但是有一个额外的全连接层: \begin{center} \includegraphics{simple_conv} \end{center} -在这个架构中,我们可以把卷积和混合层看作是在学习输入训练图像中的局部感受野,而后 -面的全连接层则在一个更抽象的层次学习,从整个图像整合全局信息。这是一种常见的卷积 -神经网络模式。 +在这个架构中,我们可以把卷积和\gls*{pooling}层看作是在学习输入训练图像中的% +\gls*{lrf},而后面的全连接层则在一个更抽象的层次学习,从整个图像整合全局信息。这 +是一种常见的卷积神经网络模式。 -让我们训练这样的一个网络,看看它表现怎样\footnote{这里我继续使用一个大小为 -$10$ 的\minibatch{}。正如我们\hyperref[mini_batch_size]{前面讨论过的}, -使用更大的\minibatch{}可能提高训练速度。我继续使用相同的\minibatch{},主要是 -为了和前面章节中的实验保持一致。}: +让我们训练这样的一个网络,看看它表现怎样\footnote{这里我继续使用一个大小为 $10$ + 的\gls*{mini-batch}。正如我们\hyperref[mini_batch_size]{前面讨论过的},使用更 + 大的\gls*{mini-batch}可能提高训练速度。我继续使用相同的\gls*{mini-batch},主要 + 是为了和前面章节中的实验保持一致。}: \begin{lstlisting}[language=Python] >>> net = Network([ ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28), @@ -428,26 +438,27 @@ \section{卷积神经网络在实际中的应用} validation_data, test_data) \end{lstlisting} -我们得到了 $98.78$\% 的准确率,这是相当大的改善,超过了我们以前结构的任何一个。事 -实上,我们已经减少了超过三分之一的错误率,这是一个很大的进步。 +我们得到了 $98.78$\% 的准确率,这是相当大的改善,超过了我们以前结构的任何一个。 +事实上,我们已经减少了超过三分之一的错误率,这是一个很大的进步。 -在指定网络结构时,我把卷积和混合层作为一个单一层对待。不管他们是被视为分开的层还 -是作为一个单一的层在一定程度上是一个个人喜好的问题。\lstinline!network3.py! 视他 -们为单个层,因为它使得 \lstinline!network3.py! 的代码更紧凑。然而,如果需要的话, -很容易修改 \lstinline!network3.py! 使得这些层可以单独指定。 +在指定网络结构时,我把卷积和\gls*{pooling}层作为一个单一层对待。不管他们是被视为 +分开的层还是作为一个单一的层在一定程度上是一个个人喜好的问题。 +\lstinline!network3.py! 视他们为单个层,因为它使得 \lstinline!network3.py! 的代 +码更紧凑。然而,如果需要的话,很容易修改 \lstinline!network3.py! 使得这些层可以 +单独指定。 \subsection*{练习} \begin{itemize} -\item 如果你删除了全连接层,只使用卷积--混合层和柔性最大值层,你得到了什么样的分 - 类准确率?全连接层的加入有帮助吗? +\item 如果你删除了全连接层,只使用卷积--\gls*{pooling}层和\gls*{softmax}层,你得 + 到了什么样的分类准确率?全连接层的加入有帮助吗? \end{itemize} 我们能改进 $98.78$\% 的分类准确率吗? -让我们试着插入第二个卷积--混合层。把它插在已有的卷积--混合层和全连接隐藏层之间。 -我们再次使用一个 $5 \times 5$ 局部感受野,混合 $2 \times 2$ 的区域。让我们看看用 -前面相似的超参数训练会发生什么: +让我们试着插入第二个卷积--\gls*{pooling}层。把它插在已有的卷积--\gls*{pooling}层 +和全连接\gls*{hidden-layer}之间。我们再次使用一个 $5 \times 5$ \gls*{lrf},\gls*{pooling} $2 +\times 2$ 的区域。让我们看看用前面相似的\gls*{hyper-params}训练会发生什么: \begin{lstlisting}[language=Python] >>> net = Network([ ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28), @@ -464,59 +475,61 @@ \subsection*{练习} 再一次,我们得到了改善:现在我们达到了 $99.06$\% 的分类准确率。 -在这里有两个很自然想到的问题。第一个问题是:应用第二个卷积--混合层意味着什么?实 -际上,你可以认为第二个卷积--混合层输入 $12 \times 12$ 幅“图像”,其“像素”代表 -原始输入图像中特定的局部特征的存在(或不存在)。所以你可以认为这一层输入原始输入 -图像的一个版本。这个版本是经过抽象和凝缩过的,但是仍然有大量的空间结构,所以使用 -第二个卷积--混合层是有意义的。 +在这里有两个很自然想到的问题。第一个问题是:应用第二个卷积--\gls*{pooling}层意味 +着什么?实际上,你可以认为第二个卷积--\gls*{pooling}层输入 $12 \times 12$ 幅“图 +像”,其“像素”代表原始输入图像中特定的局部特征的存在(或不存在)。所以你可以认 +为这一层输入原始输入图像的一个版本。这个版本是经过抽象和凝缩过的,但是仍然有大量 +的空间结构,所以使用第二个卷积--\gls*{pooling}层是有意义的。 这是一个令然满意的观点,但是引出了第二个问题。从前面层的输出涉及 $20$ 个独立的特 -征映射,所以对第二个卷积--混合层有 $20 \times 20 \times 12$ 个输入。就好像我们有 -$20$ 幅单独的图像输入给卷积--混合层,而不是第一个卷积--混合层情况下的单幅图像。 -第二个卷积--混合层里的神经元应该如何响应这些多重的输入图像呢?实际上,我们将允许 -这一层中的每个神经元从它的局部感受野中的\emph{所有} $20 \times 5 \times 5$ 输入 -神经元学习。更非正式的:第二个卷积--混合层中的特征检测器可访问\emph{所有}前面层 -的特征,但仅在其特定的局部感受野中\footnote{ -如果输入图像是有颜色的,这个问题会在第一层中出现。在这种情况下,对于每一个像素我们会有 3 -个输入特征,对应于输入图像中的红色、绿色和蓝色通道。因此我们将允许特征检测器可访问所有颜色信息, -但仅仅在一个给定的局部感受野中。}。 +征映射,所以对第二个卷积--\gls*{pooling}层有 $20 \times 20 \times 12$ 个输入。就 +好像我们有 $20$ 幅单独的图像输入给卷积--\gls*{pooling}层,而不是第一个卷 +积--\gls*{pooling}层情况下的单幅图像。第二个卷积--\gls*{pooling}层里的神经元应该 +如何响应这些多重的输入图像呢?实际上,我们将允许这一层中的每个神经元从它的% +\gls*{lrf}中的\textbf{所有} $20 \times 5 \times 5$ 输入神经元学习。更非正式的: +第二个卷积--\gls*{pooling}层中的特征检测器可访问\textbf{所有}前面层的特征,但仅 +在其特定的\gls*{lrf}中\footnote{如果输入图像是有颜色的,这个问题会在第一层中出现。 + 在这种情况下,对于每一个像素我们会有 3 个输入特征,对应于输入图像中的红色、绿 + 色和蓝色通道。因此我们将允许特征检测器可访问所有颜色信息,但仅仅在一个给定的% + \gls*{lrf}中。}。 \subsection*{问题} \begin{itemize} -\item \textbf{使用 tanh 激活函数}\quad 在本书前面我已经几次提起 - 过\hyperref[subsec:other_models_of_artificial_neuron]{tanh 函数}可以是一个 - 比 S型函数更好的激活函数。我们还没有实际采用过这些建议,因为我们已经用 S 型取得 - 了大量进展。但现在让我们试试一些用 tanh 作为我们激活函数的实验。试着训练卷积和 - 全连接层中具有 tanh 激活值的网络\footnote{注意你可以 - 将 \lstinline!activation_fn=tanh! 作为一个参数传递 - 给 \lstinline!ConvPoolLayer! 和 \lstinline!FullyConnectedLayer! 类。}。开始时 - 使用 S 型网络中使用的相同的超参数,但是训练 $20$ 个\epochs{},而不是 $60$个。你 - 的网络表现得怎么样?如果你继续训练到 $60$ 个\epochs{}会怎样?试着将tanh和 S型网 - 络的每个\epoch{}的验证准确率都绘制出来,都绘制到 $60$ 个\epochs{}。如果你的结果 - 和我的相似,你会发现 tanh 网络训练得稍微快些,但是最终的准确率非常相似。你能否 - 解释为什么 tanh 网络可以训练得更快?你能否用 S型取得一个相似的训练速度,也许通 - 过改变\learningrate{},或者做些调整\footnote{你也许可以回想 $\sigma(z) = - (1+\tanh(z/2))/2$ 来找灵感。}?试着用五六个迭代学习超参数和网络架构,寻 - 找 tanh 优于 S 型的方面。\emph{注意:这是一个开放式问题。就我个人而言,我并没有 - 找到太多切换为 tanh 的优势,虽然我没全面地做过实验,也许你会找到一个方法。无 - 论如何,我们马上会发现切换到修正线性激活函数的一个优势,所以我们不会去深入使 - 用 tanh 函数。}。 +\item \textbf{使用\gls*{tanh}激活函数}\quad 在本书前面我已经几次提起过% + \hyperref[subsec:other_models_of_artificial_neuron]{\gls*{tanh-func}}可以是一 + 个比 \gls*{sigmoid-func}更好的激活函数。我们还没有实际采用过这些建议,因为我们 + 已经用 S 型取得了大量进展。但现在让我们试试一些用\gls*{tanh}作为我们激活函数的 + 实验。试着训练卷积和全连接层中具有\gls*{tanh}激活值的网络\footnote{注意你可以 + 将 \lstinline!activation_fn=tanh! 作为一个参数传递给 + \lstinline!ConvPoolLayer! 和 \lstinline!FullyConnectedLayer! 类。}。开始时使 + 用 S 型网络中使用的相同的超参数,但是训练 $20$ 个\gls*{epoch},而不是 $60$个。你 + 的网络表现得怎么样?如果你继续训练到 $60$ 个\gls*{epoch}会怎样?试着将\gls*{tanh} + 和 S 型网络的每个\gls*{epoch}的验证准确率都绘制出来,都绘制到 $60$ 个\gls*{epoch}。如 + 果你的结果和我的相似,你会发现\gls*{tanh}网络训练得稍微快些,但是最终的准确率 + 非常相似。你能否解释为什么\gls*{tanh}网络可以训练得更快?你能否用 S型取得一个 + 相似的训练速度,也许通过改变\gls*{learning-rate},或者做些调整\footnote{你也许可以 + 回想 $\sigma(z) = (1+\tanh(z/2))/2$ 来找灵感。}?试着用五六个迭代学习超参数 + 和网络架构,寻找\gls*{tanh}优于 S 型的方面。\textbf{注意:这是一个开放式问题。 + 就我个人而言,我并没有找到太多切换为\gls*{tanh}的优势,虽然我没全面地做过实 + 验,也许你会找到一个方法。无论如何,我们马上会发现切换到修正线性激活函数的一 + 个优势,所以我们不会去深入使用\gls*{tanh}函数。}。 \end{itemize} -\textbf{使用修正线性单元:} 到现在为止,我们开发的网络实际上是一篇开创性的 1998论 -文\footnote{\href{http://yann.lecun.com/exdb/publis/pdf/lecun-98.pdf}{"Gradient-based learning applied to document recognition"},作者为 Yann - LeCun, Léon Bottou, Yoshua Bengio, 和 Patrick Haffner (1998)。细节上有很多不 - 同,但大体上讲,我们的网络和论文中描述的网络非常相似。} 中使用的众多网络中一种 -的变化形式,这个网络被称为 LeNet-5,并引入了 MNIST 问题。这为进一步实验并构筑理解 -和直观感受打下很好的基础。特别是,有很多种我们可以改变网络来改善结果的方式。 +\textbf{使用\gls*{relu}:} 到现在为止,我们开发的网络实际上是一篇开创性的 1998论 +文\footnote{\href{http://yann.lecun.com/exdb/publis/pdf/lecun-98.pdf}{"Gradient-based + learning applied to document recognition"},作者为 Yann LeCun, Léon Bottou, + Yoshua Bengio, 和 Patrick Haffner (1998)。细节上有很多不同,但大体上讲,我 + 们的网络和论文中描述的网络非常相似。} 中使用的众多网络中一种的变化形式,这个网 +络被称为 LeNet-5,并引入了 MNIST 问题。这为进一步实验并构筑理解和直观感受打下很 +好的基础。特别是,有很多种我们可以改变网络来改善结果的方式。 作为开始,让我们改变我们的神经元,我们使用% \hyperref[sec:other_models_of_artificial_neuron]{修正线性单元}而不是 S 型激活函 -数。确切地说,我们将使用激活函数 $f(z) \equiv \max(0, z)$。我们将训练 $60$ 个 -\epochs{},\learningrate{}为$\eta = 0.03$。我也发现用一些 -\hyperref[sec:overfitting_and_regularization]{L2 规范化}也有点帮助,使用规范化参 -数 $\lambda = 0.1$: +数。确切地说,我们将使用激活函数 $f(z) \equiv \max(0, z)$。我们将训练 $60$ 个% +\gls*{epoch},\gls*{learning-rate}为$\eta = 0.03$。我也发现用一些 +\hyperref[sec:overfitting_and_regularization]{L2 \gls*{regularization}}也有点帮 +助,使用\gls*{regularization}参数 $\lambda = 0.1$: \begin{lstlisting}[language=Python] >>> from network3 import ReLU >>> net = Network([ @@ -538,7 +551,7 @@ \subsection*{问题} 实验中我发现基于修正线性单元的网络,其性能始终优于基于 S 型激活函数的网络。似乎对 于这个问题切换到修正线性单元确实有收益。 -是什么使得修正线性激活函数好于 S 型或者 tanh 函数?目前,我们对这个问题的答案有一 +是什么使得修正线性激活函数好于 S 型或者\gls*{tanh}函数?目前,我们对这个问题的答案有一 个很差的理解。实际上,修正线性单元只在过去几年才开始被广泛使用。最近才采用的原因 是以经验为依据的:一些人经常基于直觉或者启发式的理由试着用修正线性单 元\footnote{一个通常的理由是 $\max(0, z)$ 在 $z$ 取最大极限时不会饱和,不像 S 型 @@ -562,10 +575,10 @@ \subsection*{问题} 运行这个程序取得 $50,000$ 幅 MNIST 训练图像并扩展为具有 $250,000$ 幅训练图像的训练集。然后我们可以使用这些训练图像来训练我们的网络。我们 -将使用和上面一样的具有修正线性单元的网络。在我初始的实验中我减少了训练\epochs{}的 +将使用和上面一样的具有修正线性单元的网络。在我初始的实验中我减少了训练\gls*{epoch}的 数量~——~这讲得通,因为我们在训练 $5$ 倍的数据。但是实际上,扩展数据结果是相当多地 -减少了过度拟合的影响。所有,在做了一些实验后,我最终回到训练 -$60$ 个\epochs{}。不管怎样,让我们训练: +减少了\gls*{overfitting}的影响。所有,在做了一些实验后,我最终回到训练 +$60$ 个\gls*{epoch}。不管怎样,让我们训练: \begin{lstlisting}[language=Python] >>> expanded_training_data, _, _ = network3.load_data_shared( "../data/mnist_expanded.pkl.gz") @@ -592,7 +605,7 @@ \subsection*{问题} Practices for Convolutional Neural Networks Applied to Visual Document Analysis},作者为 Patrice Simard, Dave Steinkraus, 和 John Platt (2003)。} 使用一个神经网络改进了他们的 MNIST 性能,达到了 -$99.6$\%,这个网络以其它方式和我们的非常相似,使用两个卷积--混合层,跟着一个具 +$99.6$\%,这个网络以其它方式和我们的非常相似,使用两个卷积--\gls*{pooling}层,跟着一个具 有 $100$ 个神经元的隐藏的全连接层。在他们的架构中有一些细节上的不同~——~例如他们没 有利用修正线性单元~——~但是他们改进性能的关键是扩展训练数据。他们通过旋转,位移和 扭曲 MNIST 训练图像来扩展。他们还开发了一个“弹性扭曲”的流程,一种模拟当一个人写 @@ -637,7 +650,7 @@ \subsection*{问题} \label{final_conv} 这里发生了什么事?扩展的,或者额外的全连接层真的对 MNIST 没帮助吗?或者说,我们 的网络有能力做得更好,但我们在用错误的方式学习?例如,也许我们可以用更强有力的规 -范化技术来减小过度拟合的趋势。一种可能性是第三章介绍的% +范化技术来减小\gls*{overfitting}的趋势。一种可能性是第三章介绍的% \hyperref[sec:other_techniques_for_regularization]{弃权}技术。回想弃权的基本思想 是在训练网络时随机地移除单独的激活值。这使得模型对单独依据的丢失更为强劲,因此不 太可能依赖于训练数据的特质。让我们试着应用弃权到最终的全连接层: @@ -666,10 +679,10 @@ \subsection*{问题} 有两个值得注意的变化。 -首先,我减少了训练\epochs{}的数量到 $40$:弃权减少了过度拟合,所以我们学习得更 +首先,我减少了训练\gls*{epoch}的数量到 $40$:弃权减少了\gls*{overfitting},所以我们学习得更 快。 -其次,全连接隐藏层有 $1,000$ 个神经元,不是之前使用的 $100$ 个。当然,在训练时弃 +其次,全连接\gls*{hidden-layer}有 $1,000$ 个神经元,不是之前使用的 $100$ 个。当然,在训练时弃 权有效地忽略了很多神经元。所以一些扩充是可以预期的。实际上,我试过 用 $300$ 和 $1,000$ 个隐藏神经元的实验,用 $1,000$ 个隐藏神经元(非常略微地)取得 了更好的验 @@ -705,9 +718,9 @@ \subsection*{问题} \textbf{为什么我们只对全连接层应用弃权:} 如果你仔细看上面的代码,你会注意到我们 只在网络的全链接部分应用了弃权,而不是卷积层。原则上我们可以在卷积层上应用一个类 -似的程序。但是,实际上那没必要:卷积层有相当大的先天的对于过度拟合的抵抗。原因是 -共享权重意味着卷积滤波器被强制从整个图像中学习。这使他们不太可能去选择在训练数据 -中的局部特质。于是就很少有必要来应用其它规范化,例如弃权。\\ +似的程序。但是,实际上那没必要:卷积层有相当大的先天的对于\gls*{overfitting}的抵抗。原因是 +\gls*{shared-weights}意味着卷积滤波器被强制从整个图像中学习。这使他们不太可能去选择在训练数据 +中的局部特质。于是就很少有必要来应用其它\gls*{regularization},例如弃权。\\ \textbf{进一步:} 仍然有可能在 MNIST 上提高性能。Rodrigo Benenson 汇编了一份% \href{http://rodrigob.github.io/are_we_there_yet/build/classification_datasets_results.html}{% @@ -723,13 +736,13 @@ \subsection*{问题} Meier,Luca Maria Gambardella, 和 Jürgen Schmidhuber (2010)。}。我喜欢这篇 论文的地方是它是如此简单。其中的网络是一个许多层的神经网络,仅使用全连接层(没有 卷积层)。他们最成功的网络有分别包含 -有 $2,500$,$2,000$,$1,500$,$1,000$ 和 $500$ 神经元的隐藏层。他们使用和 Simard +有 $2,500$,$2,000$,$1,500$,$1,000$ 和 $500$ 神经元的\gls*{hidden-layer}。他们使用和 Simard 等人类似的想法来扩展他们的训练数据。除了这些,他们没有使用其它的技巧,包括没有卷 积层:这是一个清晰的,简单的网络,这样的网络如果有足够的耐心,可以在 80 年代将其 训练(如果 MNIST 数据集已经有了),假设那时有足够的计算能力。他们达到了一 个99.65\% 的分类准确率,或多或少和我们一样。其关键是使用一个非常大,非常深的网络, -并且使用一块 GPU 来加速训练。这让他们训练了很多个\epochs{}。他们也利用了他们的长 -训练时间来逐渐地将\learningrate{}从 $10^{-3}$ 减小到 $10^{-6}$。试着用一个相似的 +并且使用一块 GPU 来加速训练。这让他们训练了很多个\gls*{epoch}。他们也利用了他们的长 +训练时间来逐渐地将\gls*{learning-rate}从 $10^{-3}$ 减小到 $10^{-6}$。试着用一个相似的 架构来匹配他们的结果是个很有趣的联系。\\ \textbf{为什么我们能够训练?} 我们在\hyperref[ch:WhyHardToTrain]{上一章}看到了深 @@ -741,19 +754,19 @@ \subsection*{问题} 当然,答案是我们没有回避这些结果。相反,我们已经做了一些事情,帮助我们继续进行。 特别地:(1)使用卷积层极大地减少了这些层中的参数的数目,使学习的问题更容易;(2) -使用更多强有力的规范化技术(尤其是弃权和卷积层)来减少过度拟合,否则它在更复杂的 +使用更多强有力的\gls*{regularization}技术(尤其是弃权和卷积层)来减少\gls*{overfitting},否则它在更复杂的 网络中是更多的问题;(3)使用修正线性单元而不是 S 型神经元,来加速训练~——~依据经 验通常是 $3$--$5$ 倍;(4)使用 GPU 并愿意长时间的训练。特别是,在我们最后的实验 -中,我们训练了 $40$ 个\epochs{},使用一个 $5$ 倍于未经处理的 MNIST 训练数据的数据 -集。在本书前面,我们主要用原始训练数据训练了 $30$ 个\epochs{}。结合因素(3)和 +中,我们训练了 $40$ 个\gls*{epoch},使用一个 $5$ 倍于未经处理的 MNIST 训练数据的数据 +集。在本书前面,我们主要用原始训练数据训练了 $30$ 个\gls*{epoch}。结合因素(3)和 (4),仿佛我们训练了比以前 $30$ 倍长的时间。 你的反应可能是:“就这样?这就是我们为了训练深度网络所要做的全部事情?为什么要小 题大做?” -当然,我们也已经使用了其它主意:利用充分大的数据集(为了避免过度拟合);使用正确 +当然,我们也已经使用了其它主意:利用充分大的数据集(为了避免\gls*{overfitting});使用正确 的代价函数(为了避免\hyperref[sec:the_cross-entropy_cost_function]{学习减速}); -使用\hyperref[how_to_choose_a_neural_network's_hyper-parameters]{好的权重初始 +使用\hyperref[how_to_choose_a_neural_network's_hyper-parameters]{好的\gls*{weight}初始 化}(也是为了避免因为神经元饱和引起的学习减速);% \hyperref[sec:other_techniques_for_regularization]{以算法形式扩展训练数据}。我们 在前面章节中讨论了这些和其它想法,并在本章中的大部分已经可以重用这些想法了,而不 @@ -762,10 +775,10 @@ \subsection*{问题} 有了这样说法,这真的是一套相当简单的想法。在组合使用时简单,但功能强大。入门深度 学习变得非常容易!\\ -\textbf{这些网络有多深?} 把卷积--混合层算作一个层,我们最终的架构有 $4$ 个隐藏层。 -这样的一个真的应该被称为一个深度网络吗?当然,$4$ 个隐藏层远远多于我们前面学习的 -浅层网络。那些网络大部分只有一个隐藏层,或者偶尔有 $2$ 个隐藏层。另一方面,2015年 -使用最先进技术的深度网络有时候有几十个隐藏层。我偶尔听到有人采取“更比你更深”的 +\textbf{这些网络有多深?} 把卷积--\gls*{pooling}层算作一个层,我们最终的架构有 $4$ 个\gls*{hidden-layer}。 +这样的一个真的应该被称为一个深度网络吗?当然,$4$ 个\gls*{hidden-layer}远远多于我们前面学习的 +浅层网络。那些网络大部分只有一个\gls*{hidden-layer},或者偶尔有 $2$ 个\gls*{hidden-layer}。另一方面,2015年 +使用最先进技术的深度网络有时候有几十个\gls*{hidden-layer}。我偶尔听到有人采取“更比你更深”的 态度,认为如果你没有跟上在隐层数目方面的攀比,那么你真的没有在做深度学习。我不赞 同这样的态度,部分因为它使得深度学习的定义像是时刻就有结果的事。深度学习中实际的 突破是认识到它超过浅的 $1$、$2$ 层的网络是切实可行的,这样的浅层网络直到 2000 年 @@ -774,7 +787,7 @@ \subsection*{问题} 实现其他目标工具~——~例如更好的分类精确率。 \\ -\textbf{一些按部就班的话:} 在这一节中,我们从单个隐藏层的浅层网络顺利转换到多层 +\textbf{一些按部就班的话:} 在这一节中,我们从单个\gls*{hidden-layer}的浅层网络顺利转换到多层 卷积网络。这一切似乎很容易!我们做了一个改变,其中大部分,我们得到了改进。如果你 开始尝试,我可以保证事情不会总是那么顺利。原因是,我呈现的是一个清理过的叙述,省 略了许多实验~——~包括许多失败的实验。这个清理过的叙述,希望能帮助你清楚认识基本思 @@ -829,13 +842,13 @@ \section{卷积网络的代码} \end{lstlisting} \lstinline!__init__! 方法中的大部分都是可以自解释的,这里再给出一些解释。我们根据 -正态分布随机初始化了权重和偏差。代码中对应这个操作的一行看起来可能很吓人,但其实 -只在进行载入权重和偏差到 Theano 中所谓的共享变量中。这样可以确保这些变量可 +正态分布随机初始化了\gls*{weight}和偏差。代码中对应这个操作的一行看起来可能很吓人,但其实 +只在进行载入\gls*{weight}和偏差到 Theano 中所谓的共享变量中。这样可以确保这些变量可 在 GPU中进行处理。对此不做过深的解释。如果感兴趣,可以查 看 \href{http://deeplearning.net/software/theano/index.html}{Theano 的文档}。而这 种初始化的方式也是专门为 sigmoid 激活函数设计的(参 -见\hyperref[sec:weight_initialization]{这里})。理想的情况是,我们初始化权重和偏 -差时会根据不同的激活函数(如 tanh 和 Rectified Linear Function)进行调整。这个在 +见\hyperref[sec:weight_initialization]{这里})。理想的情况是,我们初始化\gls*{weight}和偏 +差时会根据不同的激活函数(如\gls*{tanh}和 Rectified Linear Function)进行调整。这个在 下面的问题中会进行讨论。初始方法 \lstinline!__init__! 以 \lstinline!self.params = [self.W, self.b]! 结束。这样将该层所有需要学习的参数都归在一起。后 面,\lstinline!Network.SGD! 方法会使用 \lstinline!params! 属性来确定网络实例中什 @@ -863,9 +876,9 @@ \section{卷积网络的代码} 积、max-pooling和 softmax 函数。 不大明显的,在我们引入\hyperref[sec:softmax]{softmax layer} 时,我们没有讨论如何 -初始化权重和偏差。其他地方我们已经讨论过对 sigmoid 层,我们应当使用合适参数的正态 -分布来初始化权重。但是这个启发式的论断是针对 sigmoid 神经元的(做一些调整可以用 -于 tanh 神经元上)。但是,并没有特殊的原因说这个论断可以用在 softmax 层上。所以没 +初始化\gls*{weight}和偏差。其他地方我们已经讨论过对 sigmoid 层,我们应当使用合适参数的正态 +分布来初始化\gls*{weight}。但是这个启发式的论断是针对 sigmoid 神经元的(做一些调整可以用 +于\gls*{tanh}神经元上)。但是,并没有特殊的原因说这个论断可以用在 softmax 层上。所以没 有一个先验的理由应用这样的初始化。与其使用之前的方法初始化,我这里会将所有权值和 偏差设置为 $0$。这是一个 ad hoc 的过程,但在实践使用过程中效果倒是很不错。 @@ -904,7 +917,7 @@ \section{卷积网络的代码} 这里不是 Theano 的教程,所以不会深度讨论这些变量指代什么东西。但是粗略的想法就是 这些代表了数学变量,而非显式的值。我们可以对这些变量做通常需要的操作:加减乘除, -作用函数等等。实际上,Theano 提供了很多对符号变量进行操作方法,如卷积、最大值混合 +作用函数等等。实际上,Theano 提供了很多对符号变量进行操作方法,如卷积、最大值\gls*{pooling} 等等。但是最重要的是能够进行快速符号微分运算,使用反向传播算法一种通用的形式。这 对于应用随机梯度下降在若干种网络结构的变体上特别有效。特别低,接下来几行代码定义 了网络的符号输出。我们通过下面这行 @@ -915,7 +928,7 @@ \section{卷积网络的代码} 设置初始层的输入。 -请注意输入是以每次一个 mini-batch 的方式进行的,这就是\minibatch{}大小为何要指定 +请注意输入是以每次一个 mini-batch 的方式进行的,这就是\gls*{mini-batch}大小为何要指定 的原因。还需要注意的是,我们将输入 \lstinline!self.x! 传了两次:这是因为我们我们 可能会以两种方式(有dropout和无dropout)使用网络。\lstinline!for! 循环将符号变 量 \lstinline!self.x! 通过 \lstinline!Network! 的层进行前向传播。这样我们可以定义 @@ -1008,7 +1021,7 @@ \section{卷积网络的代码} print("Corresponding test accuracy of {0:.2 \end{lstlisting} -前面几行很直接,将数据集分解成 x 和 y 两部分,并计算在每个数据集中\minibatch{}的 +前面几行很直接,将数据集分解成 x 和 y 两部分,并计算在每个数据集中\gls*{mini-batch}的 数量。接下来的几行更加有意思,这也体现了 Theano 有趣的特性。那么我们就摘录详解一 下: @@ -1022,7 +1035,7 @@ \section{卷积网络的代码} for param, grad in zip(self.params, grads)] \end{lstlisting} -这几行,我们符号化地给出了规范化的对数似然代价函数,在梯度函数中计算了对应的导数, +这几行,我们符号化地给出了\gls*{regularization}的对数似然代价函数,在梯度函数中计算了对应的导数, 以及对应参数的更新方式。Theano 让我们通过这短短几行就能够获得这些效果。唯一隐藏的 是计算 \lstinline!cost! 包含一个对输出层 \lstinline!cost! 方法的调用;该代码 在 \lstinline!network3.py! 中其他地方。但是,总之代码很短而且简单。有了所有这些定 @@ -1033,7 +1046,7 @@ \section{卷积网络的代码} 对这些函数进行平均,我们可以计算整个验证集和测试数据集上的准确度。 \lstinline!SGD! 方法剩下的就是可以自解释的了~——~我们对次数进行迭代,重复使用训练 -数据的\minibatch{}来训练网络,计算验证集和测试集上的准确度。 +数据的\gls*{mini-batch}来训练网络,计算验证集和测试集上的准确度。 好了,我们已经理解了 \lstinline!network3.py! 代码中大多数的重要部分。让我们看看整 个程序,你不需过分仔细地读下这些代码,但是应该享受粗看的过程,并随时深入研究那些 @@ -1049,33 +1062,33 @@ \section{卷积网络的代码} \subsection*{问题} \begin{itemize} -\item 目前,\lstinline!SGD! 方法要求用户手动选择用于训练\epochs{}的数量。前文中, +\item 目前,\lstinline!SGD! 方法要求用户手动选择用于训练\gls*{epoch}的数量。前文中, 我们讨论了一种自动选择训练次数的方法,也就是\hyperref[early_stopping]{提前停止}。 修改 \lstinline!network3.py! 以实现提前停止。 \item 增加一个 \lstinline!Network! 方法来返回在任意数据集上的准确度。 -\item 修改 \lstinline!SGD! 方法来允许学习率 $\eta$ 可以是训练次数的函数。\emph{提 +\item 修改 \lstinline!SGD! 方法来允许学习率 $\eta$ 可以是训练次数的函数。\textbf{提 示:在思考这个问题一段时间后,你可能会 在\href{https://groups.google.com/forum/\#!topic/theano-users/NQ9NYLvleGc}{这 个链接}找到有用的信息。} \item 在本章前面我曾经描述过一种通过应用微小的旋转、扭曲和变化来扩展训练数据的方 - 法。改变 \lstinline!network3.py! 来加入这些技术。\emph{注意:除非你有充分多的内 + 法。改变 \lstinline!network3.py! 来加入这些技术。\textbf{注意:除非你有充分多的内 存,否则显式地产生整个扩展数据集是不大现实的。所以要考虑一些变通的方法。} \item 在 \lstinline!network3.py! 中增加 \lstinline!load! 和 \lstinline!save! 方 法。 \item 当前的代码缺点就是只有很少的用来诊断的工具。你能想出一些诊断方法告诉我们网 络过匹配到什么程度么?加上这些方法。 -\item 我们已经对修正线性单元及 S 型和 tanh 函数神经元使用了同样的初始方法。正 +\item 我们已经对\gls*{relu}及 S 型和\gls*{tanh-func}神经元使用了同样的初始方法。正 如\hyperref[sec:weight_initialization]{这里所说},这种初始化方法只是适用 - 于sigmoid 函数。假设我们使用一个全部使用 ReLU 的网络。试说明以常数 $c$ 倍调整网 - 络的权重最终只会对输出有常数 $c$ 倍的影响。如果最后一层是 softmax,则会发生什么 - 样的变化?对 ReLU 使用 sigmoid 函数的初始化方法会怎么样?有没有更好的初始化方 - 法?\emph{注意:这是一个开放的问题,并不是说有一个简单的自包含答案。还有,思考 + 于 \gls*{sigmoid-func}。假设我们使用一个全部使用 ReLU 的网络。试说明以常数 $c$ 倍调整网 + 络的\gls*{weight}最终只会对输出有常数 $c$ 倍的影响。如果最后一层是 softmax,则会发生什么 + 样的变化?对 ReLU 使用\gls*{sigmoid-func}的初始化方法会怎么样?有没有更好的初始化方 + 法?\textbf{注意:这是一个开放的问题,并不是说有一个简单的自包含答案。还有,思考 这个问题本身能够帮助你更好地理解包含 ReLU 的神经网络。} \item 我们对于不稳定梯度问题 的% \hyperref[sec:what_is_causing_the_vanishing_gradient_problem_unstable_gradients_in_deep_neural_nets]{% 分析}实际上是针对 sigmoid 神经元的。如果是 ReLU,那分析又会有什么差异?你能够 - 想出一种使得网络不太会受到不稳定梯度问题影响的好方法么?\emph{注意:“好”这个 + 想出一种使得网络不太会受到不稳定梯度问题影响的好方法么?\textbf{注意:“好”这个 词实际上就是一个研究性问题。实际上有很多容易想到的修改方法。但我现在还没有研 究足够深入,能告诉你们什么是真正的好技术。} \end{itemize} @@ -1090,7 +1103,7 @@ \section{图像识别领域中的近期进展} 在这一节中,我简要介绍一些最近的使用神经网络的图像识别成果。 这一节和本书的大部分内容不同。贯穿本书我集中在可能引起持久兴趣的思想~——~如反向传 -播、规范化、卷积网络。正如我写过的,我曾试图避免那些流行一时的成绩,但其长期价值 +播、\gls*{regularization}、卷积网络。正如我写过的,我曾试图避免那些流行一时的成绩,但其长期价值 是未知的。在科学界,这样的成绩往往是生命短促的事物,它们逐渐消失,很少有持久的影 响。鉴于此,一个怀疑论者可能会说:“嗯,当真最近在图像识别中的进步是流行一时的例 子吗?在另外两年或三年中,一切会继续前进。所以当真这些成绩仅仅提起一些想要在绝对 @@ -1099,7 +1112,7 @@ \section{图像识别领域中的近期进展} 这样的一个怀疑论者是对的,最近论文的一些出色的细节,其被认为的重要性会逐渐减弱。 但过去几年已经看到了使用深度网络攻克极度困难的图像识别任务的非凡的进步。想象一下 一个科学历史学家在 2100 年写关于计算机视觉的东西。它们会把 2011 年至 2015 年(也 -许超过几年)鉴定为由卷积神经网络驱动的取得重大突破的时间。那并不意味着深度卷积网 +许超过几年)鉴定为由\gls*{cnn}驱动的取得重大突破的时间。那并不意味着深度卷积网 络在 2100 年仍然被使用,更不用提诸如弃权、修正线性单元等细节概念。但是它确实意味 着一个重要的变革正在发生,就现在,在知识的历史中。它有点像观察原子的发现,或者抗 生素的发明:具有历史意义的发明和探索。所以尽管我们不会深入挖掘细节,了解一些目 @@ -1112,7 +1125,7 @@ \section{图像识别领域中的近期进展} Corrado,Jeff Dean, 和 Andrew Ng (2012)。注意这篇论文中使用的网络架构在很多 细节上和我们已经学习的深度卷积网络不同。然而,一般地说,LRMD 基于很多类似的思 想。}开始。我把这篇论文称为 LRMD,取自前四位作者的姓。LRMD 使用一个神经网络来分 -类\href{http://www.image-net.org/}{ImageNet} 的图像,一个非常具有挑战性的图像识别 +类 \href{http://www.image-net.org/}{ImageNet} 的图像,一个非常具有挑战性的图像识别 问题。他们使用的 2011 年 ImageNet 数据包含 16,000,000 幅全彩色图像,有 20,000个种 类。这些图像收集自开放的网络,由亚马逊的 Mechanical Turk 服务部门的员工分类。这里 有一些 ImageNet 图像\footnote{这些取自 2014 年的数据集,其相比 2011 年的有点改变。 @@ -1144,7 +1157,7 @@ \section{图像识别领域中的近期进展} \footnote{\href{http://www.cs.toronto.edu/~fritz/absps/imagenet.pdf}{ImageNet classification with deep convolutional neural networks},作者为 Alex Krizhevsky,Ilya Sutskever, 和 Geoffrey E. Hinton (2012)。} 的 2012 年论文追 -随。KSH 训练和测试一个深度卷积神经网络,它使用 ImageNet数据的一个有限的子集。他们 +随。KSH 训练和测试一个深度\gls*{cnn},它使用 ImageNet数据的一个有限的子集。他们 使用的子集来自一个流行的机器学习竞赛~——~ImageNet Large-Scale Visual Recognition Challenge(ILSVRC)。使用一个竞赛用的数据集给了他们一个很好的和其它领先技术比较的 途径。ILSVRC-2012 训练集包含有大约 1,200,000 幅 ImageNet 图像,取自 1,000 个种类。 @@ -1159,12 +1172,12 @@ \section{图像识别领域中的近期进展} 准,KSH 的网络达到了 $63.3$\% 的准确率。 既然 KSH 网络激励了随后的成果,值得对它简要描述一下。正如我们看到的,尽管要更精细, -它非常接近于这一章中我们之前训练的网络。KSH 使用一个深度卷积神经网络,在两 +它非常接近于这一章中我们之前训练的网络。KSH 使用一个深度\gls*{cnn},在两 个 GPU上训练。他们使用两块 GPU 是因为当时使用的特定的 GPU 型号(一块 NVIDIA GeForce GTX 580)没有足够的片上存储器来保存整个网络。所以他们用两块 GPU 把网络分 成两个部分。 -KSH 网络有 $7$ 个隐藏神经元层。前 $5$ 个隐藏层是卷积层(有些具有最大值混合),而 +KSH 网络有 $7$ 个隐藏神经元层。前 $5$ 个\gls*{hidden-layer}是卷积层(有些具有最大值\gls*{pooling}),而 接下来的 $2$ 层是全连接层。输出层是一个 $1,000$ 个单元的柔性最大值层,对应于 那$1,000$ 个图像类别。这是这个网络的一个草图,取自 KSH 论文\footnote{感谢 Ilya Sutskever。}。细节在下面讨论。注意许多层被分成 $2$ 个部分,对应于 $2$ 块 GPU。 @@ -1177,7 +1190,7 @@ \section{图像识别领域中的近期进展} 为一个神经网络的输入层通常是固定大小。KSH 通过缩放每幅图像使得长和宽中短的长度 为 $256$ 来处理。然后他们从缩放后的图像中裁剪出一个 $256 \times 256$ 的区域。最 后,KSH 从 $256 \times 256$ 的图像中随机提取出 $224 \times 224$ 的子图像(和水平 -反射)。他们把这个随机的裁剪用作扩展训练数据的方式,这样减少过度拟合。这在一个例 +反射)。他们把这个随机的裁剪用作扩展训练数据的方式,这样减少\gls*{overfitting}。这在一个例 如 KSH 的大的网络中尤其有帮助。正是这些 $224 \times 224$ 图像被用于网络的输入。在 大多数情况下裁剪后的图像仍然包含有未改动图像的主要物体。 @@ -1236,7 +1249,7 @@ \section{图像识别领域中的近期进展} 和认定正确的类别究竟是什么”。 这些都是令人惊奇的结果。确实,在这项成果后,很多团队也报告 top-5 错误率实际 -上\emph{好}过 5.1\%。这有时候在媒体上被报道成系统有超过人类的视觉。尽管这些结构是 +上\textbf{好}过 5.1\%。这有时候在媒体上被报道成系统有超过人类的视觉。尽管这些结构是 很振奋人心的,但是这样的报道只能算是一种误解,认为系统在视觉上超过了人类,事实上 并非这样。ILSVRC 竞赛问题在很多方面都是受限的~——~在公开的网络上获得图像并不具备在 实际应用中的代表性!而且 top-5 标准也是非常人工设定的。我们在图像识别,或者更宽泛 @@ -1318,16 +1331,16 @@ \section{其他的深度学习模型} 所以推荐你学会搜索最新的研究成果。尽管这样,我还是很期待众多本质的想法能够受到足 够久的关注。\\ -\textbf{递归神经网络(RNN):} 在前馈神经网络中,单独的输入完全确定 + \textbf{\gls*{rnn}(RNNs):} 在前馈神经网络中,单独的输入完全确定 了剩下的层上的神经元的激活值。可以想象,这是一幅静态的图景:网络中的所有事物都被 固定了,处于一种“冰冻结晶”的状态。但假如,我们允许网络中的元素能够以动态方式不 断地比那话。例如,隐藏神经元的行为不是完全由前一层的隐藏神经元,而是同样受制于更 早的层上的神经元的激活值。这样肯定会带来跟前馈神经网络不同的效果。也可能隐藏和输 出层的神经元的激活值不会单单由当前的网络输入决定,而且包含了前面的输入的影响。 -拥有这类时间相关行为特性的神经网络就是\emph{递归神经网络},常写作 \emph{RNN}。当 +拥有这类时间相关行为特性的神经网络就是\textbf{\gls{rnn}},常写作 \textbf{RNN(s)}。当 然有不同的方式来从数学上给出 RNN 的形式定义。你可以参 -考\href{http://en.wikipedia.org/wiki/Recurrent_neural_network}{维基百科上的RNN介 +考\href{http://en.wikipedia.org/wiki/Recurrent_neural_network}{维基百科上的 RNN 介 绍}来看看 RNN。在我写作本书的时候,维基百科上介绍了超过 13 种不同的模型。但除了 数学细节,更加一般的想法是,RNN 是某种体现出了随时间动态变化的特性的神经网络。也 毫不奇怪,RNN 在处理时序数据和过程上效果特别不错。这样的数据和过程正是语音识别和 @@ -1342,7 +1355,7 @@ \section{其他的深度学习模型} 排序和复制。 不过正如在文中提到的,这些例子都是极其简单的模型。学会执 -行 \lstinline!print(398345+42598)! 并不能让网络称为一个正常的python解释器!对于 +行 \lstinline!print(398345+42598)! 并不能让网络称为一个正常的 Python 解释器!对于 这些想法,我们能推进得多远也是未知的。结果都充满了好奇。历史上,神经网络已经在传 统算法上失败的模式识别问题上取得了一些成功。另外,传统的算法也在神经网络并不擅长 的领域里占据上风。今天没有人会使用神经网络来实现 Web 服务器或者数据库程序。研究出 @@ -1364,7 +1377,7 @@ \section{其他的深度学习模型} 改来训练 RNN。还有其他一些在前馈神经网络中的想法,如正规化技术,卷积和代价函数等 都在 RNN 中非常有效。还有我们在书中讲到的很多技术都可以适配一下 RNN 场景。\\ -\textbf{长短期记忆单元(Long short-term memory units,LSTMs):} 影响 RNN 的一个挑战是前期的模型会 +\textbf{\gls{lstm}(LSTMs):} 影响 RNNs 的一个挑战是前期的模型会 很难训练,甚至比前馈神经网络更难。原因就是我们在\hyperref[ch:WhyHardToTrain]{上一 章}提到的不稳定梯度的问题。回想一下,这个问题的通常表现就是在反向传播的时候梯度 越变越小。这就使得前期的层学习非常缓慢。在 RNN 中这个问题更加糟糕,因为梯度不仅仅 @@ -1376,8 +1389,8 @@ \section{其他的深度学习模型} 单, 很多近期的论文(包括我在上面给出的那些)都是用了 LSTM 或者相关的想法。\\ -\textbf{深度信念网络,生成式模型和 Boltzmann 机:} 对深度学习的兴趣产生于 2006 年, -最早的论文就是解释如何训练称为 深度信念网络 (DBN)的网络\footnote{参见 Geoffrey +\textbf{深度信念网络,生成式模型和玻尔兹曼机:} 对深度学习的兴趣产生于 2006 年, +最早的论文就是解释如何训练称为\textbf{\gls{dbn}}(DBN)的神经网络\footnote{参见 Geoffrey Hinton, Simon Osindero 和 Yee-Whye Teh 在 2006 年的 \href{http://www.cs.toronto.edu/~hinton/absps/fastnc.pdf}{A fast learning algorithm for deep belief nets}, 及 Geoffrey Hinton 和 Ruslan @@ -1387,7 +1400,7 @@ \section{其他的深度学习模型} 但近些年前馈网络和 RNN 的流行,盖过了 DBN 的风头。尽管如此,DBN 还是有几个有趣的 特性。 -一个就是 DBN 是一种生成式模型。在前馈网络中,我们指定了输入的激活函数,然后这些激 +一个就是 DBN 是一种\textbf{生成式模型}。在前馈网络中,我们指定了输入的激活函数,然后这些激 活函数便决定了网络中后面的激活值。而像 DBN 这样的生成式模型可以类似这样使用,但是 更加有用的可能就是指定某些特征神经元的值,然后进行“反向运行”,产生输入激活的值。 具体讲,DBN 在手写数字图像上的训练同样可以用来生成和手写数字很像的图像。换句话 @@ -1406,12 +1419,12 @@ \section{其他的深度学习模型} 他们的研究长期的价值非常重要。我个人的观点是 DBN 和其他的生成式模型应该获得更多的 注意。并且我对今后如果 DBN 或者相关的模型超过目前流行的模型也毫不诧异。欲了 解 DBN,参考这 -个\href{http://www.scholarpedia.org/article/Deep_belief_networks}{DBN 综述}。还 +个 \href{http://www.scholarpedia.org/article/Deep_belief_networks}{DBN 综述}。还 有\href{http://www.cs.toronto.edu/~hinton/absps/guideTR.pdf}{这篇文章}也很有用。 虽然没有主要地将 DBN,但是已经包含了很多关于 DBN 核心组件的受限 Boltzmann 机的有 价值的信息。\\ -\textbf{其他想法:} 在神经网络和深度学习中还有其他哪些正在进行的研究?恩,其实还 +\textbf{其他想法:} 在神经网络和深度学习中还有其他哪些正在进行的研究?嗯,其实还 有其他大量的有极有吸引力的工作。热门的领域包含使用神经网络来 做\href{http://machinelearning.org/archive/icml2008/papers/391.pdf}{自然语言处 理}(see \href{http://arxiv.org/abs/1103.0398}{also @@ -1436,14 +1449,13 @@ \section{神经网络的未来} \label{sec:on_the_future_of_neural_networks} \textbf{意图驱动的用户接口:} 有个很古老的笑话是这么说的:“一位不耐烦的教授对一个 -困惑的学生说道,‘不要光听我说了什么,要听懂我说的\emph{含义}。’”。历史上,计算机 +困惑的学生说道,‘不要光听我说了什么,要听懂我说的\textbf{含义}。’”。历史上,计算机 通常是扮演了笑话中困惑的学生这样的角色,对用户表示的完全不知晓。而现在这个场景发 生了变化。我仍然记得自己在 Google 搜索的打错了一个查询,搜索引擎告诉了我“你是否要 的是[这个正确的查询]?”,然后给出了对应的搜索结果。Google 的 CEO Larry Page曾经描 述了最优搜索引擎就是准确理解用户查询的含义,并给出对应的结果。 -这就是\emph{意图驱动的用户接口}\index{意图驱动的用户接口}\index{Intention-driven - User Interface}的愿景。在这个场景中,不是直接对用户的查询词进行结果的反馈,搜索 +这就是\textbf{\gls{idui}}的愿景。在这个场景中,不是直接对用户的查询词进行结果的反馈,搜索 引擎使用机器学习技术对大量的用户输入数据进行分析,研究查询本身的含义,并通过这些 发现来进行合理的动作以提供最优的搜索结果。 @@ -1548,7 +1560,7 @@ \section{神经网络的未来} 多深刻的新想法,对任何一个人来说都是太多以至于难以掌握所有的想法。所以,这个领域 的社会结构就开始重新组织,围绕着这些想法分离。我们现在看到的就是领域被不断地细分, 子领域按照一种复杂的、递归的、自指的社会结构进行分解,而这些组织关系也反映了最深 -刻的那些想法之间的联系。\emph{因此,知识结构形成了科学的社会组织关系。但这些社会 +刻的那些想法之间的联系。\textbf{因此,知识结构形成了科学的社会组织关系。但这些社会 关系反过来也会限制和帮助决定那些可以发现的事物}。这就是 Conway 法则在科学上变体 版本。 diff --git a/cjkfonts.sty b/cjkfonts.sty index ddba545..11c0070 100644 --- a/cjkfonts.sty +++ b/cjkfonts.sty @@ -111,16 +111,68 @@ UprightFont=NotoSansMonoCJKsc-Bold, AutoFakeSlant=true]{NotoSansMonoSCBold} +\newCJKfontfamily[NotoSerifSC]\NotoSerifSC[ + Extension=.otf, + Path=\cjkfonts@path/, + UprightFont=NotoSerifCJKsc-\cjkfonts@mdseries, + BoldFont=NotoSerifCJKsc-\cjkfonts@bfseries, + ItalicFont=NotoSerifCJKsc-\cjkfonts@mdseries, + BoldItalicFont=NotoSerifCJKsc-\cjkfonts@bfseries, + ItalicFeatures=FakeSlant, + BoldItalicFeatures=FakeSlant]{NotoSerifSC} + +\newCJKfontfamily[NotoSerifSCThin]\NotoSerifSCThin[ + Extension=.otf, + Path=\cjkfonts@path/, + UprightFont=NotoSerifCJKsc-Thin, + AutoFakeSlant=true]{NotoSerifSC} + +\newCJKfontfamily[NotoSerifSCLight]\NotoSerifSCLight[ + Extension=.otf, + Path=\cjkfonts@path/, + UprightFont=NotoSerifCJKsc-Light, + AutoFakeSlant=true]{NotoSerifSC} + +\newCJKfontfamily[NotoSerifSCDemiLight]\NotoSerifSCDemiLight[ + Extension=.otf, + Path=\cjkfonts@path/, + UprightFont=NotoSerifCJKsc-DemiLight, + AutoFakeSlant=true]{NotoSerifSC} + +\newCJKfontfamily[NotoSerifSCRegular]\NotoSerifSCRegular[ + Extension=.otf, + Path=\cjkfonts@path/, + UprightFont=NotoSerifCJKsc-Regular, + AutoFakeSlant=true]{NotoSerifSC} + +\newCJKfontfamily[NotoSerifSCMedium]\NotoSerifSCMedium[ + Extension=.otf, + Path=\cjkfonts@path/, + UprightFont=NotoSerifCJKsc-Medium, + AutoFakeSlant=true]{NotoSerifSC} + +\newCJKfontfamily[NotoSerifSCBold]\NotoSerifSCBold[ + Extension=.otf, + Path=\cjkfonts@path/, + UprightFont=NotoSerifCJKsc-Bold, + AutoFakeSlant=true]{NotoSerifSC} + +\newCJKfontfamily[NotoSerifSCBlack]\NotoSerifSCBlack[ + Extension=.otf, + Path=\cjkfonts@path/, + UprightFont=NotoSerifCJKsc-Black, + AutoFakeSlant=true]{NotoSerifSC} + \ifthenelse{\boolean{cjkfonts@default}}{ \setCJKmainfont[Extension=.otf, Path=\cjkfonts@path/, - UprightFont=NotoSansCJKsc-\cjkfonts@mdseries, - BoldFont=NotoSansCJKsc-\cjkfonts@bfseries, - ItalicFont=NotoSansCJKsc-\cjkfonts@mdseries, - BoldItalicFont=NotoSansCJKsc-\cjkfonts@bfseries, + UprightFont=NotoSerifCJKsc-\cjkfonts@mdseries, + BoldFont=NotoSerifCJKsc-\cjkfonts@bfseries, + ItalicFont=NotoSerifCJKsc-\cjkfonts@mdseries, + BoldItalicFont=NotoSerifCJKsc-\cjkfonts@bfseries, ItalicFeatures=FakeSlant, - BoldItalicFeatures=FakeSlant]{NotoSansSC} + BoldItalicFeatures=FakeSlant]{NotoSerifSC} \setCJKsansfont[ Extension=.otf, @@ -141,9 +193,10 @@ BoldItalicFont=NotoSansMonoCJKsc-Bold, ItalicFeatures=FakeSlant, BoldItalicFeatures=FakeSlant]{NotoSansMonoSC} - -}{} +}{} + %% This must be the last command \endinput + diff --git a/glossaries.tex b/glossaries.tex index df9a804..5d6302d 100644 --- a/glossaries.tex +++ b/glossaries.tex @@ -8,12 +8,12 @@ } \newglossaryentry{sigmoid-neuron}{ - name={\ S 型神经元}, + name={S 型神经元}, description={\emph{Sigmoid Neuron}} } \newglossaryentry{sigmoid-func}{ - name={\ S 型函数}, + name={S 型函数}, description={\emph{Sigmoid Function}} } @@ -57,9 +57,14 @@ description={\emph{Multilayer Perceptron}} } +\newglossaryentry{rnn}{ + name={递归神经网络}, + description={\emph{Recurrent Neural Network(s)}} +} + \newglossaryentry{cost-func}{ name={代价函数}, - description={Cost Function} + description={\emph{Cost Function}} } \newglossaryentry{learning-rate}{ @@ -99,22 +104,22 @@ \newglossaryentry{softmax}{ name={柔性最大值}, - description={Softmax} + description={\emph{Softmax}} } \newglossaryentry{softmax-func}{ name={柔性最大值函数}, - description={Softmax Function} + description={\emph{Softmax Function}} } \newglossaryentry{log-likelihood}{ name={对数似然}, - description={Log-likelihood} + description={\emph{Log-likelihood}} } \newglossaryentry{regularization}{ name={规范化}, - description={Regularization} + description={\emph{Regularization}} } \newglossaryentry{weight-decay}{ @@ -122,16 +127,71 @@ description={\emph{Weight Decay}} } -\newglossaryentry{l2-regularization}{ - name={\ L2 规范化}, - description={\emph{L2 regularization}} -} - \newglossaryentry{regularization-term}{ name={规范化项}, description={\emph{Regularization Term}} } +\newglossaryentry{lrf}{ + name={局部感受野}, + description={\emph{Local Receptive Fields}} +} + +\newglossaryentry{shared-weights}{ + name={共享权重}, + description={\emph{Shared Weights}} +} + +\newglossaryentry{pooling}{ + name={混合}, + description={\emph{Pooling}} +} + +\newglossaryentry{cnn}{ + name={卷积神经网络}, + description={\emph{Convolutional Neural Networks}} +} + +\newglossaryentry{idui}{ + name={意图驱动的用户接口}, + description={\emph{Intention-driven User Interface}} +} + +\newglossaryentry{tanh}{ + name={双曲正切}, + description={\emph{Tanh},(发音为 “tanch”)} +} + +\newglossaryentry{tanh-func}{ + name={双曲正切函数}, + description={\emph{Tanh Function}} +} + +\newglossaryentry{tanh-neuron}{ + name={双曲正切神经元}, + description={\emph{Tanh Neuron}} +} + +\newglossaryentry{hyperbolic-tangent}{ + name={双曲正切}, + description={\emph{Hyperbolic Tangent}} +} + +\newglossaryentry{hyper-params}{ + name={超参数}, + description={\emph{Hyper-parameters}} +} + +\newglossaryentry{lstm}{ + name={长短期记忆单元}, + description={\emph{Long Short-term Memory Units}} +} + +\newglossaryentry{dbn}{ + name={深度信念网络}, + description={\emph{Deep Belief Network(s)}} +} + %% ------ \newglossaryentry{logistic-regression}{ diff --git a/history.tex b/history.tex index 94edff1..428b972 100644 --- a/history.tex +++ b/history.tex @@ -8,6 +8,15 @@ \chapter{修订历史} \toprule \textbf{更新时间} & \textbf{修订内容}\\ \midrule + 2017-04-09 & \vspace{-1.5ex} + \begin{compactitem} + \item Version 0.5 + \item 使用\href{https://www.google.com/get/noto/\#serif-hans}{宋体}作为默认中文字体搭配默认西文字体 + \item 使用 glossaries 组织中英文术语,注意 chapter, section 中无法使用 gls 命令 + \item 合并 \href{mailto:gengchao@foxmail.com}{GC555} 对第一章部分的修订意见 + \item 修正 \href{https://github.com/zhanggyb/nndl/issues/61}{1.6 中的代码错误} + \end{compactitem}\\ + \midrule 2016-11-25 & Version 0.4: 修正错别字及翻译错误 \\ \midrule 2016-08-08 & Version 0.3: 错别字修正,增加致谢,更新为 Tex2016 编译 \\ diff --git a/localization.tex b/localization.tex index da39538..0772f15 100644 --- a/localization.tex +++ b/localization.tex @@ -1,7 +1,7 @@ % file: localization.tex % load cjkfonts and set Source Han Sans SC as the default CJK fonts: -\usepackage[default,mdseries=Light,bfseries=Medium]{cjkfonts} +\usepackage[default,mdseries=Regular,bfseries=Bold]{cjkfonts} \usepackage{titlesec} diff --git a/main.tex b/main.tex index f788b0d..fd15fb6 100644 --- a/main.tex +++ b/main.tex @@ -14,8 +14,8 @@ % ------------------------------------------------------------------------------ % Index package % ------------------------------------------------------------------------------ -\usepackage{makeidx} -\makeindex +% \usepackage{makeidx} +% \makeindex % ------------------------------------------------------------------------------ % load hyperref to use hyperlinks @@ -123,7 +123,6 @@ % Set Roboto and Source Code Pro, which are installed with TexLive, for western % fonts: \input{westernfonts.tex} -\setdefaultwesternfonts{} \usepackage{setspace} \onehalfspacing{} @@ -133,8 +132,6 @@ \usepackage{pgfplots} % load PDFPlots %\pgfplotsset{compat=yourversion} -\input{terms} - % ------------------------------------------------------------------------------ % Load glossaries % ------------------------------------------------------------------------------ @@ -197,7 +194,7 @@ \backmatter{} -\printindex +% \printindex \printglossary[title={中英对照术语表}] \end{document} diff --git a/terms.tex b/terms.tex deleted file mode 100644 index a24764a..0000000 --- a/terms.tex +++ /dev/null @@ -1,29 +0,0 @@ -% file: terms.tex - -% epoch, epochs -% -% In the neural network terminology: -% -% one epoch = one forward pass and one backward pass of all the training -% examples -% -% batch size = the number of training examples in one forward/backward pass. The -% higher the batch size, the more memory space you'll need. -% -% number of iterations = number of passes, each pass using [batch size] number -% of examples. To be clear, one pass = one forward pass + one backward pass (we -% do not count the forward pass and backward pass as two different passes). -% -% Example: if you have 1000 training examples, and your batch size is 500, then -% it will take 2 iterations to complete 1 epoch. -% -% and: http://stackoverflow.com/questions/25887205/what-is-an-epoch-in-anns-and-how-does-it-translate-into-code-in-matlab -\newcommand{\epoch}[1][迭代期]{#1} -\newcommand{\epochs}[1][迭代期]{#1} - -% mini-batch -\newcommand{\minibatch}[1][小批量数据]{#1} - -% learning rate -\newcommand{\learningrate}[1][学习速率]{#1} - diff --git a/title.tex b/title.tex index 9822afb..2af130f 100644 --- a/title.tex +++ b/title.tex @@ -7,7 +7,7 @@ % title of this document {\fontsize{36pt}{40pt}\NotoSansSCBold{} 神经网络与深度学习}\\ \vspace{1em} - {\LARGE\RobotoRegular{} \href{http://neuralnetworksanddeeplearning.com/index.html}{Neural Networks and Deep Learning}}\\ + {\LARGE\href{http://neuralnetworksanddeeplearning.com/index.html}{Neural Networks and Deep Learning}}\\ \vspace{1cm} \includegraphics{cayley}\\ \vspace{1cm} diff --git a/translation.tex b/translation.tex index 8524ea4..a5568e6 100644 --- a/translation.tex +++ b/translation.tex @@ -47,3 +47,9 @@ \section*{翻译团队} & \bfseries\href{https://github.com/jiefangxuanyan}{jiefangxuanyan} \\ \end{tabular} \end{itemize} + +\section*{已知问题} +\label{sec:KnownIssues} + +请在 GitHub 项目的 \href{https://github.com/zhanggyb/nndl/issues}{Issues} 页面查阅 +已知的翻译问题。 \ No newline at end of file diff --git a/westernfonts.tex b/westernfonts.tex index f20ed02..f474c9a 100644 --- a/westernfonts.tex +++ b/westernfonts.tex @@ -1,13 +1,8 @@ % file: westernfonts.tex -\newcommand{\robotodir}[0]{fonts/} \newcommand{\sourcecodeprodir}[0]{fonts/} \newcommand{\sourceserifprodir}[0]{fonts/} -\newcommand{\robotomd}[0]{Light} -\newcommand{\robotobf}[0]{Medium} -\newcommand{\robotoit}[0]{LightItalic} -\newcommand{\robotobi}[0]{MediumItalic} \newcommand{\codepromd}[0]{Light} \newcommand{\codeprobf}[0]{Medium} \newcommand{\codeproit}[0]{LightIt} @@ -15,14 +10,6 @@ \newcommand{\serifpromd}[0]{Light} \newcommand{\serifprobf}[0]{Semibold} -\newfontfamily\Roboto{Roboto}[ - Extension=.ttf, - Path=\robotodir, - UprightFont=*-Regular, - BoldFont=*-Bold, - ItalicFont=*-Italic, - BoldItalicFont=*-BoldItalic] - \newfontfamily\SourceCodePro{SourceCodePro}[ Extension=.otf, Path=\sourcecodeprodir, @@ -38,68 +25,11 @@ BoldFont=*-\serifprobf] \newcommand{\serif}[0]{\SourceSerifPro} - -\newfontfamily\RobotoThin{Roboto}[ - Extension=.ttf, - Path=\robotodir, - UprightFont=*-Thin, - ItalicFont=*-ThinItalic] - -\newfontfamily\RobotoLight{Roboto}[ - Extension=.ttf, - Path=\robotodir, - UprightFont=*-Light, - ItalicFont=*-LightItalic] - -\newfontfamily\RobotoRegular{Roboto}[ - Extension=.ttf, - Path=\robotodir, - UprightFont=*-Regular, - ItalicFont=*-Italic] - -\newfontfamily\RobotoMedium{Roboto}[ - Extension=.ttf, - Path=\robotodir, - UprightFont=*-Medium, - ItalicFont=*-MediumItalic] - -\newfontfamily\RobotoBold{Roboto}[ - Extension=.ttf, - Path=\robotodir, - UprightFont=*-Bold, - ItalicFont=*-BoldItalic] - -\newfontfamily\RobotoBlack{Roboto}[ - Extension=.ttf, - Path=\robotodir, - UprightFont=*-Black, - ItalicFont=*-BlackItalic] - -\newcommand{\setdefaultwesternfonts}[0]{ - - \setmainfont{Roboto}[ - Extension=.ttf, - Path=\robotodir, - UprightFont=*-\robotomd, - BoldFont=*-\robotobf, - ItalicFont=*-\robotoit, - BoldItalicFont=*-\robotobi] - - \setsansfont{Roboto}[ - Extension=.ttf, - Path=\robotodir, - UprightFont=*-\robotomd, - BoldFont=*-\robotobf, - ItalicFont=*-\robotoit, - BoldItalicFont=*-\robotobi] - - \setmonofont{SourceCodePro}[ - Extension=.otf, - Path=\sourcecodeprodir, - UprightFont=*-\codepromd, - BoldFont=*-\codeprobf, - ItalicFont=*-\codeproit, - BoldItalicFont=*-\codeprobi] - -} - + +\setmonofont{SourceCodePro}[ + Extension=.otf, + Path=\sourcecodeprodir, + UprightFont=*-\codepromd, + BoldFont=*-\codeprobf, + ItalicFont=*-\codeproit, + BoldItalicFont=*-\codeprobi]