依照个人口味做了剔除),大家早已应用规范的高斯分布早先化了连年第2隐藏层的权重

(本文是基于
neuralnetworksanddeeplearning
那本书的第壹章Improving the way neural networks
learn
收拾而成的读书笔记,依照个人口味做了删除)

Neil Zhu,简书ID Not_GOD,University AI 创办人 & Chief
Scientist,致力于促进世界人工智能化进度。制定并进行 UAI
中短时间增加战略和目标,教导团队高效成长为人工智能领域最规范的能力。
用作行业老董,他和UAI一起在二〇一四年创制了TASA(中国最早的人造智能协会),
DL Center(深度学习知识基本满世界市值网络),AI
growth(行业智库培训)等,为神州的人造智能人才建设输送了大气的血流和滋养。其它,他还参预依然进行过各样国际性的人造智能峰会和运动,暴发了巨大的影响力,书写了60万字的人造智能精品技艺内容,生产翻译了天下第贰本深度学习入门书《神经互连网与深度学习》,生产的内容被多量的业内垂直公众号和媒体转发与连载。曾经受邀为国内一流大学制定人工智能学习陈设和任课人工智能前沿课程,均受学生和名师好评。

上一章,大家介绍了神经互联网简单并发的过拟合难点,并学习了最常用的正则化方法,以及其余一些技能,后天,大家将介绍本章节最终五个难题:权重起初化超参数的取舍

权重初叶化


创立了神经互连网后,我们须要开展权重和错误的先导化。到方今,大家平素是依据在首先章中介绍的这样举办初始化。提示您须臾间,此前的方式就是依据独立的均值为
$$0$$,标准差为 $$1$$
的高斯随机变量随机采样作为权重和谬误的开始值。那个格局工作的基本上能用,可是丰富ad
hoc,所以我们须求摸索一些更好的法门来安装大家网络的伊始化权重和谬误,那对于赞助网络学习进度的升官很有价值。

结果申明,大家得以比使用正式的高斯分布效果更好。为何?借使我们拔取2个居多的输入神经元,比如说
$$一千$$。假使,大家早就采纳专业的高斯分布开头化了连年第1隐藏层的权重。今后自作者将注意力集中在这一层的连接权重上,忽略互联网其余一些:

图片 1

我们为了简化,假如,大家采用练习样本 x 其中八分之四的神经元值为
$$0$$,另3/6为
$$1$$。上边的眼光也是可以更进一步广阔地行使,可是你可以从特例中赢得背后的合计。让大家考虑带权和
$$z=\sum_j w_j x_j + b$$ 的隐藏元输入。其中 $$500$$
个项消去了,因为对应的输入 $$x_j=0$$。所以 $$z$$ 是 $$501$$
个标准的高斯随机变量的和,包括 $$500$$ 个权重项和额外的 $$1$$
个谬误项。因而 $$z$$ 本人是3个均值为 $$0$$ 标准差为
$$\sqrt{501}\approx 22.4$$ 的分布。$$z$$
其实有二个老大宽的高斯分布,不是老大尖的形制:

图片 2

进而是,大家可以从那幅图中看出 $$|z|$$ 会变得老大的大,比如说 $$z\gg1$$
或者 $$z\ll 1$$。倘若是这么,输出 $$\sigma(z)$$ 就会接近 $$1$$ 恐怕$$0$$。也就意味着大家的隐藏元会饱和。所以当出现这么的气象时,在权重中展开微小的调动仅仅会给隐藏元的激活值带来最好微弱的变动。而那种微弱的改动也会影响网络中多余的神经细胞,然后会带来相应的代价函数的改观。结果就是,这几个权重在我们开展梯度下落算法时会学习得那多少个缓慢。那实质上和大家眼下议论的难点大多,前面的意况是出口神经元在错误的值上饱和导致学习的降低。大家此前经过代价函数的挑选化解了前边的题材。不幸的是,即使这种格局在输出神经元上有效,但对此隐藏元的饱和却一点功力都并未。

小编曾经研究了第三隐藏层的权重输入。当然,类似的判断也对前面的隐藏层有效:借使权重也是用专业的高斯分布举办初步化,那么激活值将会类似
$$0$$ 大概 $$1$$,学习进度也会一定迟缓。

再有可以协理大家开展更好地起初化么,可以幸免那种类型的饱和,最后防止学习进度的暴跌?借使大家有1个有
$$n_{in}$$ 个输入权重的神经细胞。大家会拔取均值为 $$0$$ 标准差为
$$1/\sqrt{n_{in}}$$
的高斯分布开头化那一个权重。也就是说,大家会向下挤压高斯分布,让大家的神经细胞更不容许饱和。大家会继续使用均值为
$$0$$ 标准差为 $$1$$
的高斯分布来对错误举办初阶化,前面会报告您原因。有了这个设定,带权和
$$z=\sum_j w_j x_j + b$$ 还是是三个均值为 $$0$$
但是有很陡的高峰的高斯分布。借使,大家有 $$500$$ 个值为 $$0$$
的输入和$$500$$ 个值为 $$1$$ 的输入。那么很容表明 $$z$$ 是言听计从均值为
$$0$$ 标准差为 $$\sqrt{3/2} = 1.22$$
的高斯分布。那图像要比原先陡得多,所以尽管自身已经对横坐标举办削减为了拓展更直观的比较:

图片 3

这般的3个神经元更不容许饱和,因而也不大可能际遇学习进程回落的标题。

权重初始化

到方今截止,大家都是用归一化高斯分布来初步化权值,但是,大家很想领会是或不是有其余伊始化方法可以让互连网陶冶得更好。

实质上,确实存在比高斯分布更好的主意。但是,大家须求先通晓高斯分布的初步化会存在怎么着缺点。

比方大家有如下的网络布局,其中蕴含 一千 个输入神经元:

图片 4

将来,大家聚焦于隐藏层第1个神经元。要是输入中,有3/6的神经细胞是
0,3/6的神经细胞是 1。输入到隐藏层的权重和为 \(z=\sum_j{w_j x_j}+b\)。由于有二分之一的
\(x_j=0\),所以 \(z\) 约等于是 50三个归一化的高斯分布随机变量的和。因而,\(z\) 本身也是壹个高斯分布,其均值为
0,标准差为 \(\sqrt{501} \approx
22.4\)。那是3个很「宽」的遍布:

图片 5

也等于说,大部分景况下 \(z \gg 1\)
或者 \(z \ll 1\)。对于利用 sigmoid
函数的 \(\sigma(z)\)
来说,那就象征隐藏层或许曾经没有了(所谓没有,就是陶冶起先变缓或终止了,而造成没有的来头在于,偏导中的
\(\sigma'(z)\) 在 \(|z|>1\) 时趋于
0,这样梯度降低就无可怎么着更新参数了)。此前大家用交叉熵函数消除了输出层中学习率低的题材,但对于中等的隐藏层并不曾效应。而且,前一层隐藏层的输出如若也成高斯分布,那么再今后的隐藏层也会没有。

创新这种题材的点子也很不难,既然难点来自在于高斯分布太「宽」,那么大家就想艺术让它变「窄」,相当于标准差要变小。借使1个神经元有
\(n_{in}\)
个输入权值,那么我们只要求将全体权值依据均值为 0,标准差为 \(1/\sqrt{n_{in}}\)
的高斯分布
开首化即可。那样得到的新的高斯分布就会「瘦高」得多。对于以前的例证,在
500 个输入为 0,500 个为 1 的动静下,新高斯分布的均值为 0,标准差为
\(\sqrt{3/2}=1.22…\),如下图所示:

图片 6

这样一来,\(z\) 的值普遍在 \([0, 1]\)
内,隐藏层过早消灭的境况也就全部缓解了。

大家再通过一组实验来看望分裂早先化方法的功效:

图片 7

个中,橙线是用地点提及的新的高斯分布初叶化,而蓝线则是一般的高斯分布。从结果来看,新的伊始化方法可以加速网络的教练,但最终的准确率两者相当。然而在好几意况下,\(1/\sqrt{n_{in}}\)
的初叶化格局会抓实准确率,在下一章中,我们将见到类似的例证。

要留意的少数是,以上的起初化都是本着权值 weight 的,对不是 bias
的先河化不影响互联网的练习(原因临时没想明白)。

练习

  • 验证 $$z=\sum_j w_j x_j + b$$ 标准差为
    $$\sqrt{3/2}$$。上边两点只怕会有帮带:(a)
    独立随机变量的和的方差是各种独立随就算方差的和;(b)方差是标准差的平方。

本身在上头提到,大家利用同一的不二法门对错误举办开端化,就是使用均值为 $$0$$
标准差为 $$1$$
的高斯分布来对不是举办开首化。这实际是实用的,因为这么并不会让我们的神经互联网更便于饱和。实际上,其实早已防止了饱和的难题来说,怎样开首化偏差影响不大。某个人将装有的偏向起始化为
$$0$$,依赖梯度下跌来读书合适的谬误。可是因为距离不是很大,大家前面还会按照前面的主意来展开开首化。

让大家在 MNIST
数字分类任务上相比一下新旧三种权重先导化方式。同样,照旧使用 $$30$$
个隐藏元,minibatch 的尺寸为 $$30$$,规范化参数
$$\lambda=5.0$$,然后是穿插熵代价函数。我们将学习率从 $$\eta=0.5$$
调整到
$$0.1$$,因为如此会让结果在图像中突显得尤其扎眼。大家先利用旧的开端化方法磨练:

>>> import mnist_loader
>>> training_data, validation_data, test_data = \
... mnist_loader.load_data_wrapper()
>>> import network2
>>> net = network2.Network([784, 30, 10], cost=network2.CrossEntropyCost)
>>> net.large_weight_initializer()
>>> net.SGD(training_data, 30, 10, 0.1, lmbda = 5.0,
... evaluation_data=validation_data, 
... monitor_evaluation_accuracy=True)

咱俩也选择新措施来展开权重的起头化。那实际还要更简短,因为 network2’s
暗中认可方式就是接纳新的方式。那代表大家得以丢弃
net.large_weight_initializer() 调用:

>>> net = network2.Network([784, 30, 10], cost=network2.CrossEntropyCost)
>>> net.SGD(training_data, 30, 10, 0.1, lmbda = 5.0,
... evaluation_data=validation_data, 
... monitor_evaluation_accuracy=True)

将结果用图展现出来,就是:

图片 8

二种景况下,我们在 96%
的准确度上重叠了。最后的归类准确度大概统统平等。可是新的先河化技术带来了快慢的升迁。在首先种开头化格局的分类准确度在
87% 一下,而新的不二法门已经大致达到了
93%。看起来的景况就是我们新的关于权重开首化的办法将磨练带到了3个新的地步,让大家可以越发飞速地拿到好的结果。同样的情景在
$$100$$ 个神经元的设定中也出现了:

图片 9

在这几个场地下,五个曲线并不曾重合。然则,小编做的尝试发现了事实上就在局地相当的回合后(那里没有显得)准确度其实也是大概千篇一律的。所以,基于这几个试验,看起来提高的权重开首化仅仅会加快练习,不会转移网络的属性。不过,在第伍张,大家会看出部分例证里面使用
$$1/\sqrt{n_{in}}$$
权重初始化的一劳永逸运转的结果要鲜明更优。由此,不仅仅可以牵动练习进程的�加速,有时候在结尾品质上也有很大的升级。

$$1/\sqrt{n_{in}}$$
的权重先河化方法援助大家升高了神经互联网学习的法子。其他的权重开首化技术一样也有,很多都以根据那几个主旨的思索。作者不会在此处给出其他的主意,因为
$$1/\sqrt{n_{in}}$$
已经得以干活得很好了。假设你对此外的沉思感兴趣,我引进您看看在 $$2011$$
年的 Yoshua Bengio 的杂谈的 $$14$$ 和 $$15$$ 页,以及有关的参考文献。

Practical Recommendations for Gradient-Based Training of Deep
Architectures
,
by Yoshua Bengio (2012).

怎么着抉择超参数

到近来截至,大家都尚未仔细探究超参数该怎样挑选(如读书率 \(\eta\),正则化参数 \(\lambda\)
等等)。超参数的选项对互联网的教练和质量都会生出震慑。由于神经互联网的错综复杂,一旦互连网出现难点,大家将很难定位难点的发源,搞不清楚到底是网络布局有毛病,依然多少集有失水准,依旧超参数本人没选好。由此,这一节我们将学习一些摘取超参数的「灵感」大概「准则」,减弱在超参数采用上的失误。

问题

  • 将规范化和改进的权重先河化方法结合使用 L2
    规范化有时候会自行给大家有个别接近于新的早先化方法的东西。假使大家应用旧的初阶化权重的办法。考虑三个启发式的视角:(1)倘若$$\lambda$$
    不太小,练习的率先回合将会差点被权重下降统治。;(2)假如$$\eta\lambda \ll n$$,权重会依据因子 $$exp(-\eta\lambda/m)$$
    每一回合降低;(3)假使 $$\lambda$$ 不太大,权重下跌会在权重降到
    $$1/\sqrt{n}$$ 的时候保持住,其中 $$n$$
    是网络中权重的个数。用论述那个标准都早就满意本节给出的例子。

广大的国策

为此称之为宽泛,是因为那种方针不报告怎么着调整超参数,而是让您尽量快地获取报告。唯有及早把握网络的求学状态,我们才有耐心和新闻接轨
debug(总不大概每调整五次要等个十来分钟才出结果吗)。小编要幸好 debug
互联网的时候也时不时应用那么些做法,比如,只用很小的数量集锻炼,或然将互联网的社团变小等等。那几个做法唯有二个目标:让网络尽只怕快地报告结果,不管结果好坏,这是大家能继承调试下去的前提。在反复调试后,我们一再能博得部分「灵感」,之后再逐步将标题变的更扑朔迷离一些,然后继续调试。

好了,上边我们针对学习率 \(\eta\)、L2 正则化参数 \(\lambda\)
和批陶冶的数额集大小学习某些比较有效的准则。

再看手写识别难点:代码


让大家兑现本章切磋过的那个想法。大家将写出一个新的主次,network2.py,那是三个对第3章中支出的
network.py 的千锤百炼版本。借使你未曾仔细看过
network.py,那您大概会须求重读前面关于那段代码的座谈。仅仅 $$74$$
行代码,也很易懂。

network.py 一样,紧要部分就是 Network
类了,我们用那个来表示神经互联网。使用八个 sizes
的列表来对每种对应层进行初阶化,暗许使用交叉熵作为代价 cost 参数:

class Network(object):

    def __init__(self, sizes, cost=CrossEntropyCost):
        self.num_layers = len(sizes)
        self.sizes = sizes
        self.default_weight_initializer()
        self.cost=cost

__init__ 方法的和 network.py
中相同,可以轻易弄懂。然而下边两行是新的,大家必要领悟她们到底做了何等。

咱俩先看看 default_weight_initializer
方法,使用了咱们最新立异后的开端化权重方法。如大家已经看到的,使用了均值为
$$0$$ 而标准差为 $$1/\sqrt{n}$$,$$n$$
为对应的输入连接个数。大家利用均值为 $$0$$ 而标准差为 $$1$$
的高斯分布来初叶化偏差。下边是代码:

def default_weight_initializer(self):
        self.biases = [np.random.randn(y, 1) for y in self.sizes[1:]]
        self.weights = [np.random.randn(y, x)/np.sqrt(x) 
                        for x, y in zip(self.sizes[:-1], self.sizes[1:])]

为了了然那段代码,须求精通 np 就是举办线性代数运算的 Numpy
库。大家在程序的上马会 import
Numpy。同样大家从没对第①层的神经细胞的差错举行开始化。因为第壹层其实是输入层,所以不须求引入任何的过错。我们在
network.py 中做了截然一样的事体。

作为 default_weight_initializer 的补偿,大家一样含有了二个
large_weight_initializer
方法。这么些措施应用了第贰章中的观点初始化了权重和谬误。代码也就单单是和default_weight_initializer差点点了:

def large_weight_initializer(self):
        self.biases = [np.random.randn(y, 1) for y in self.sizes[1:]]
        self.weights = [np.random.randn(y, x) 
                        for x, y in zip(self.sizes[:-1], self.sizes[1:])]

我将 larger_weight_initializer
方法包罗进来的原故也等于驱动跟第三章的结果更易于相比较。小编并不曾设想太多的推介应用这么些方式的其实情形。

开头化方法 __init__ 中的第2个新的事物就是大家开端化了 cost
属性。为了知道那么些工作的原理,让大家看一下用来代表交叉熵代价的类:

class CrossEntropyCost(object):

    @staticmethod
    def fn(a, y):
        return np.sum(np.nan_to_num(-y*np.log(a)-(1-y)*np.log(1-a)))

    @staticmethod
    def delta(z, a, y):
        return (a-y)

让我们诠释一下。第3个看到的是:即便使用的是穿插熵,数学上看,就是1个函数,那里大家用
Python 的类而不是 Python
函数完成了它。为何这么做啊?答案就是代价函数在大家的互连网中扮演了三种不一致的脚色。明显的角色就是代价是出口激活值
$$a$$ 和目的输出 $$y$$ 差异优劣的心地。那一个角色通过
CrossEntropyCost.fn 方法来饰演。(注意,np.nan_to_num 调用确保了
Numpy 正确处理接近 $$0$$
的对数值)可是代价函数其实还有另三个剧中人物。回看第叁章中运作反向传播算法时,大家须求统计网络出口误差,$$\delta^L$$。那种方式的出口误差看重于代价函数的选择:不相同的代价函数,输出误差的款式就不一致。对于交叉熵函数,输出误差就好像公式(66)所示:

图片 10

于是,我们定义了第二个法子,CrossEntropyCost.delta,目标就是让互连网领会怎么着开展输出误差的盘算。然后大家将那八个组成在3个暗含全数须要通晓的有关代价函数音信的类中。

类似地,network2.py
还带有了1个意味一遍代价函数的类。那个是用来和第①章的结果进行比较的,因为前面我们大概都在行使交叉函数。代码如下。QuadraticCost.fn
方法是有关互连网出口 $$a$$ 和对象输出 $$y$$
的三回代价函数的直白总结结果。由 QuadraticCost.delta
再次来到的值就是三遍代价函数的误差。

class QuadraticCost(object):

    @staticmethod
    def fn(a, y):
        return 0.5*np.linalg.norm(a-y)**2

    @staticmethod
    def delta(z, a, y):
        return (a-y) * sigmoid_prime(z)

以往,大家精通了 network2.pynetwork.py
多少个完毕之间的重中之重差别。都以很粗略的事物。还有部分更小的改观,上边大家会展开介绍,包蕴L2 规范化的已毕。在描述规范化以前,大家看看 network2.py
完整的落到实处代码。你不须要太仔细地读遍这么些代码,可是对总体结构更为是文档中的内容的明亮是特别重大的,这样,你就足以精通每段程序所做的行事。当然,你也可以随自身希望去深刻钻研!假如你迷失了接头,那么请读读上面的上课,然后再重临代码中。不多说了,给代码:

"""network2.py
~~~~~~~~~~~~~~

An improved version of network.py, implementing the stochastic
gradient descent learning algorithm for a feedforward neural network.
Improvements include the addition of the cross-entropy cost function,
regularization, and better initialization of network weights.  Note
that I have focused on making the code simple, easily readable, and
easily modifiable.  It is not optimized, and omits many desirable
features.

"""

#### Libraries
# Standard library
import json
import random
import sys

# Third-party libraries
import numpy as np


#### Define the quadratic and cross-entropy cost functions

class QuadraticCost(object):

    @staticmethod
    def fn(a, y):
        """Return the cost associated with an output ``a`` and desired output
        ``y``.

        """
        return 0.5*np.linalg.norm(a-y)**2

    @staticmethod
    def delta(z, a, y):
        """Return the error delta from the output layer."""
        return (a-y) * sigmoid_prime(z)


class CrossEntropyCost(object):

    @staticmethod
    def fn(a, y):
        """Return the cost associated with an output ``a`` and desired output
        ``y``.  Note that np.nan_to_num is used to ensure numerical
        stability.  In particular, if both ``a`` and ``y`` have a 1.0
        in the same slot, then the expression (1-y)*np.log(1-a)
        returns nan.  The np.nan_to_num ensures that that is converted
        to the correct value (0.0).

        """
        return np.sum(np.nan_to_num(-y*np.log(a)-(1-y)*np.log(1-a)))

    @staticmethod
    def delta(z, a, y):
        """Return the error delta from the output layer.  Note that the
        parameter ``z`` is not used by the method.  It is included in
        the method's parameters in order to make the interface
        consistent with the delta method for other cost classes.

        """
        return (a-y)


#### Main Network class
class Network(object):

    def __init__(self, sizes, cost=CrossEntropyCost):
        """The list ``sizes`` contains the number of neurons in the respective
        layers of the network.  For example, if the list was [2, 3, 1]
        then it would be a three-layer network, with the first layer
        containing 2 neurons, the second layer 3 neurons, and the
        third layer 1 neuron.  The biases and weights for the network
        are initialized randomly, using
        ``self.default_weight_initializer`` (see docstring for that
        method).

        """
        self.num_layers = len(sizes)
        self.sizes = sizes
        self.default_weight_initializer()
        self.cost=cost

    def default_weight_initializer(self):
        """Initialize each weight using a Gaussian distribution with mean 0
        and standard deviation 1 over the square root of the number of
        weights connecting to the same neuron.  Initialize the biases
        using a Gaussian distribution with mean 0 and standard
        deviation 1.

        Note that the first layer is assumed to be an input layer, and
        by convention we won't set any biases for those neurons, since
        biases are only ever used in computing the outputs from later
        layers.

        """
        self.biases = [np.random.randn(y, 1) for y in self.sizes[1:]]
        self.weights = [np.random.randn(y, x)/np.sqrt(x)
                        for x, y in zip(self.sizes[:-1], self.sizes[1:])]

    def large_weight_initializer(self):
        """Initialize the weights using a Gaussian distribution with mean 0
        and standard deviation 1.  Initialize the biases using a
        Gaussian distribution with mean 0 and standard deviation 1.

        Note that the first layer is assumed to be an input layer, and
        by convention we won't set any biases for those neurons, since
        biases are only ever used in computing the outputs from later
        layers.

        This weight and bias initializer uses the same approach as in
        Chapter 1, and is included for purposes of comparison.  It
        will usually be better to use the default weight initializer
        instead.

        """
        self.biases = [np.random.randn(y, 1) for y in self.sizes[1:]]
        self.weights = [np.random.randn(y, x)
                        for x, y in zip(self.sizes[:-1], self.sizes[1:])]

    def feedforward(self, a):
        """Return the output of the network if ``a`` is input."""
        for b, w in zip(self.biases, self.weights):
            a = sigmoid(np.dot(w, a)+b)
        return a

    def SGD(self, training_data, epochs, mini_batch_size, eta,
            lmbda = 0.0,
            evaluation_data=None,
            monitor_evaluation_cost=False,
            monitor_evaluation_accuracy=False,
            monitor_training_cost=False,
            monitor_training_accuracy=False):
        """Train the neural network using mini-batch stochastic gradient
        descent.  The ``training_data`` is a list of tuples ``(x, y)``
        representing the training inputs and the desired outputs.  The
        other non-optional parameters are self-explanatory, as is the
        regularization parameter ``lmbda``.  The method also accepts
        ``evaluation_data``, usually either the validation or test
        data.  We can monitor the cost and accuracy on either the
        evaluation data or the training data, by setting the
        appropriate flags.  The method returns a tuple containing four
        lists: the (per-epoch) costs on the evaluation data, the
        accuracies on the evaluation data, the costs on the training
        data, and the accuracies on the training data.  All values are
        evaluated at the end of each training epoch.  So, for example,
        if we train for 30 epochs, then the first element of the tuple
        will be a 30-element list containing the cost on the
        evaluation data at the end of each epoch. Note that the lists
        are empty if the corresponding flag is not set.

        """
        if evaluation_data: n_data = len(evaluation_data)
        n = len(training_data)
        evaluation_cost, evaluation_accuracy = [], []
        training_cost, training_accuracy = [], []
        for j in xrange(epochs):
            random.shuffle(training_data)
            mini_batches = [
                training_data[k:k+mini_batch_size]
                for k in xrange(0, n, mini_batch_size)]
            for mini_batch in mini_batches:
                self.update_mini_batch(
                    mini_batch, eta, lmbda, len(training_data))
            print "Epoch %s training complete" % j
            if monitor_training_cost:
                cost = self.total_cost(training_data, lmbda)
                training_cost.append(cost)
                print "Cost on training data: {}".format(cost)
            if monitor_training_accuracy:
                accuracy = self.accuracy(training_data, convert=True)
                training_accuracy.append(accuracy)
                print "Accuracy on training data: {} / {}".format(
                    accuracy, n)
            if monitor_evaluation_cost:
                cost = self.total_cost(evaluation_data, lmbda, convert=True)
                evaluation_cost.append(cost)
                print "Cost on evaluation data: {}".format(cost)
            if monitor_evaluation_accuracy:
                accuracy = self.accuracy(evaluation_data)
                evaluation_accuracy.append(accuracy)
                print "Accuracy on evaluation data: {} / {}".format(
                    self.accuracy(evaluation_data), n_data)
            print
        return evaluation_cost, evaluation_accuracy, \
            training_cost, training_accuracy

    def update_mini_batch(self, mini_batch, eta, lmbda, n):
        """Update the network's weights and biases by applying gradient
        descent using backpropagation to a single mini batch.  The
        ``mini_batch`` is a list of tuples ``(x, y)``, ``eta`` is the
        learning rate, ``lmbda`` is the regularization parameter, and
        ``n`` is the total size of the training data set.

        """
        nabla_b = [np.zeros(b.shape) for b in self.biases]
        nabla_w = [np.zeros(w.shape) for w in self.weights]
        for x, y in mini_batch:
            delta_nabla_b, delta_nabla_w = self.backprop(x, y)
            nabla_b = [nb+dnb for nb, dnb in zip(nabla_b, delta_nabla_b)]
            nabla_w = [nw+dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]
        self.weights = [(1-eta*(lmbda/n))*w-(eta/len(mini_batch))*nw
                        for w, nw in zip(self.weights, nabla_w)]
        self.biases = [b-(eta/len(mini_batch))*nb
                       for b, nb in zip(self.biases, nabla_b)]

    def backprop(self, x, y):
        """Return a tuple ``(nabla_b, nabla_w)`` representing the
        gradient for the cost function C_x.  ``nabla_b`` and
        ``nabla_w`` are layer-by-layer lists of numpy arrays, similar
        to ``self.biases`` and ``self.weights``."""
        nabla_b = [np.zeros(b.shape) for b in self.biases]
        nabla_w = [np.zeros(w.shape) for w in self.weights]
        # feedforward
        activation = x
        activations = [x] # list to store all the activations, layer by layer
        zs = [] # list to store all the z vectors, layer by layer
        for b, w in zip(self.biases, self.weights):
            z = np.dot(w, activation)+b
            zs.append(z)
            activation = sigmoid(z)
            activations.append(activation)
        # backward pass
        delta = (self.cost).delta(zs[-1], activations[-1], y)
        nabla_b[-1] = delta
        nabla_w[-1] = np.dot(delta, activations[-2].transpose())
        # Note that the variable l in the loop below is used a little
        # differently to the notation in Chapter 2 of the book.  Here,
        # l = 1 means the last layer of neurons, l = 2 is the
        # second-last layer, and so on.  It's a renumbering of the
        # scheme in the book, used here to take advantage of the fact
        # that Python can use negative indices in lists.
        for l in xrange(2, self.num_layers):
            z = zs[-l]
            sp = sigmoid_prime(z)
            delta = np.dot(self.weights[-l+1].transpose(), delta) * sp
            nabla_b[-l] = delta
            nabla_w[-l] = np.dot(delta, activations[-l-1].transpose())
        return (nabla_b, nabla_w)

    def accuracy(self, data, convert=False):
        """Return the number of inputs in ``data`` for which the neural
        network outputs the correct result. The neural network's
        output is assumed to be the index of whichever neuron in the
        final layer has the highest activation.

        The flag ``convert`` should be set to False if the data set is
        validation or test data (the usual case), and to True if the
        data set is the training data. The need for this flag arises
        due to differences in the way the results ``y`` are
        represented in the different data sets.  In particular, it
        flags whether we need to convert between the different
        representations.  It may seem strange to use different
        representations for the different data sets.  Why not use the
        same representation for all three data sets?  It's done for
        efficiency reasons -- the program usually evaluates the cost
        on the training data and the accuracy on other data sets.
        These are different types of computations, and using different
        representations speeds things up.  More details on the
        representations can be found in
        mnist_loader.load_data_wrapper.

        """
        if convert:
            results = [(np.argmax(self.feedforward(x)), np.argmax(y))
                       for (x, y) in data]
        else:
            results = [(np.argmax(self.feedforward(x)), y)
                        for (x, y) in data]
        return sum(int(x == y) for (x, y) in results)

    def total_cost(self, data, lmbda, convert=False):
        """Return the total cost for the data set ``data``.  The flag
        ``convert`` should be set to False if the data set is the
        training data (the usual case), and to True if the data set is
        the validation or test data.  See comments on the similar (but
        reversed) convention for the ``accuracy`` method, above.
        """
        cost = 0.0
        for x, y in data:
            a = self.feedforward(x)
            if convert: y = vectorized_result(y)
            cost += self.cost.fn(a, y)/len(data)
        cost += 0.5*(lmbda/len(data))*sum(
            np.linalg.norm(w)**2 for w in self.weights)
        return cost

    def save(self, filename):
        """Save the neural network to the file ``filename``."""
        data = {"sizes": self.sizes,
                "weights": [w.tolist() for w in self.weights],
                "biases": [b.tolist() for b in self.biases],
                "cost": str(self.cost.__name__)}
        f = open(filename, "w")
        json.dump(data, f)
        f.close()

#### Loading a Network
def load(filename):
    """Load a neural network from the file ``filename``.  Returns an
    instance of Network.

    """
    f = open(filename, "r")
    data = json.load(f)
    f.close()
    cost = getattr(sys.modules[__name__], data["cost"])
    net = Network(data["sizes"], cost=cost)
    net.weights = [np.array(w) for w in data["weights"]]
    net.biases = [np.array(b) for b in data["biases"]]
    return net

#### Miscellaneous functions
def vectorized_result(j):
    """Return a 10-dimensional unit vector with a 1.0 in the j'th position
    and zeroes elsewhere.  This is used to convert a digit (0...9)
    into a corresponding desired output from the neural network.

    """
    e = np.zeros((10, 1))
    e[j] = 1.0
    return e

def sigmoid(z):
    """The sigmoid function."""
    return 1.0/(1.0+np.exp(-z))

def sigmoid_prime(z):
    """Derivative of the sigmoid function."""
    return sigmoid(z)*(1-sigmoid(z))

有个进一步有意思的改观就是在代码中扩大了 L2
规范化。尽管这是3个重点的定义上的改变,在已毕中其实一定简单。对超过一半气象,仅仅必要传递参数
lmbda 到区其他章程中,重如若 Network.SGD
方法。实际上的劳作就是单排代码的事在 Network.update_mini_batch
的尾数第④行。那就是我们改变梯度下跌规则来拓展权重下跌的地点。尽管改动很小,但其对结果影响却很大!

实际那种情景在神经互连网中贯彻部分新技巧的大面积现象。大家费用了近千字的字数来研商规范化。概念的知情万分神秘困难。可是添加到程序中的时候却如此简单。精妙复杂的技能能够透过微小的代码改动就可以兑现了。

另八个细微却相当主要的改变是随意梯度下跌方法的多少个标志位的扩大。那几个标志位让大家可以对在代价和准确度的督察变得只怕。这几个标志位暗中同意是
False 的,然则在我们例子中,已经被置为 True 来监控 Network
的性能。另外,network2.py 中的 Network.SGD
方法重返了一个四元组来表示监控的结果。大家得以那样使用:

>>> evaluation_cost, evaluation_accuracy, 
... training_cost, training_accuracy = net.SGD(training_data, 30, 10, 0.5,
... lmbda = 5.0,
... evaluation_data=validation_data,
... monitor_evaluation_accuracy=True,
... monitor_evaluation_cost=True,
... monitor_training_accuracy=True,
... monitor_training_cost=True)

所以,比如 evaluation_cost 将会是三个 $$30$$
个因素的列表其中包涵了各类回合在表达集合上的代价函数值。那体系型的音讯在明亮网络行为的长河中特地有用。比如,它可以用来画出呈现互连网随时间学习的情况。其实,那也是本身在面前的章节中显得性质的办法。然则要留心的是假如别的标志位都并未安装的话,对应的元组中的成分就是空列表。

另3个日增项就是在 Network.save 方法中的代码,用来将 Network
对象保存在磁盘上,还有三个载回内存的函数。那八个点子都以拔取 JSON
举办的,而非 Python 的 pickle 或者 cPickle 模块——这么些平常是 Python
中常见的保存和装载对象的格局。使用 JSON
的原因是,假诺在以后某天,大家想改变 Network 类来允许非 sigmoid
的神经细胞。对这几个改变的落到实处,大家最只怕是改变在 Network.__init__
方法中定义的属性。若是大家大概地 pickle 对象,会导致 load
函数出错。使用 JSON 进行体系化可以显式地让老的 Network 仍旧能够 load

其余也还有局地微小的更改。可是那三个只是 network.py
的微调。结果就是把程序从 $$74$$ 行增进到了 $$152$$ 行。

学习率

至于学习率的选料,Andrew Ng 在他的 Machine
Learning

课程中有过详尽的执教。这一个中最关键的是要防止学习率过大给梯度下跌牵动「抖动」的题材,如下图中的橙线所示。在装置学习率时,大家得以先安装2个小一些的数值,如
0.1,假使这几个数值太大,则调低3个数额级到 0.01,甚至
0.001…如若发现学习进程中代价函数没有出现「抖动」的景观,再体面增强学习率,如由原本的
0.1 进步到 0.二 、0.5…但最终无法超越造成「抖动」的阈值。

图片 11

问题

  • 变动上面的代码来贯彻 L1 规范化,使用 L1 规范化使用 $$30$$
    个隐藏元的神经网络对 MNIST
    数字进行归类。你可知找到二个规范化参数使得比无规范化效果更好么?
  • 看看 network.py 中的 Network.cost_derivative
    方法。那么些格局是为1遍代价函数写的。怎么样修改可以用于交叉熵代价函数上?你能或不能够体悟恐怕在交叉熵函数上遇见的难题?在
    network2.py 中,大家曾经去掉了 Network.cost_derivative
    方法,将其集成进了 CrossEntropyCost.delta
    方法中。请问,那样是哪些缓解您早就意识的难题的?

early stopping 选拔训练轮数

在神经互连网中,并不是教练得更多越好,此前早已涉及过,锻炼太多轮或许导致过拟合。因而,大家要动用尽只怕方便的操练轮数。early
stopping
的具体做法是:在每一轮训练后观看验证集上的准确率,当验证集准确率不再上涨时,就止住陶冶。那里的准确率不再上涨指的是,在一连几轮(比如
10 轮)的教练后,准确率都不再有新的突破,始终维持在三个稳定性的数值。

调动学习率

后面说过,学习率过大只怕造成梯度降低出现「抖动」,过小又会促成互联网陶冶太慢。在实际进程中,大家平常会境遇那样的题材:当网络早先训练时,由于
weights
不够好,那些时候加大学习率可以快速改革互联网;当互联网锻练一段时间后,梯度降低开首到达最低点,这么些时候小一些的学习率可防止治其通过最低点而产出「抖动」。因而,在教练进度中,更好的法子不是原则性壹个学习率,而是基于表达集上的准确率景况,逐步调整学习率(比如一起来设为
0.1,当准确率上升到 五分之四 后,调低到 0.01,上升到 九成后,再持续调低,直到学习率唯有开端值的斑斑了结)。

正则化参数

刚起始练习时,最好将正则化参数 \(\lambda\) 设为
0.0,等学习率明确并且互联网可以健康磨练后,再安装 \(\lambda\)。具体该装置为何,没有通用的守则,只好依据实际意况判断,可以是
1.0,大概 0.1,或许 10.0。可想而知,要基于表明集上的准确率来判断。

批训练的数据集大小

理论上,我们全然可以在每趟练习时只用三个样书,但这么会促成磨练进度13分久远,而七个样本举办批练习,在于今电脑的飞快矩阵运算下并不比单个样本慢,那样相当于同时训练七个样本的时间和单个样本一样(当然,将全数样本都用来练习依旧会潜移默化速度,所以才会使用专断梯度训练的批样书)。其它,个人认为,综合五个样本再取均值举行训练,可以抵消部分噪音样本的熏陶。

参考