微信号:ZTEdeveloper

介绍:中兴开发者社区是一站式云端产品研发社区,为开发者和项目提供项目管理、开源协作、代码托管、持续交付、资源服务、技术交流等服务.让开发更简单,让创新更便捷!

干货丨零基础入门深度学习-神经网络和反向传播算法(下)

2017-05-04 18:30 韩炳涛

作者的话:

在上一篇文章中(《零基础入门深度学习-线性单元和梯度下降》没看过的同学回复“深度学习”查看),我们已经掌握了机器学习的基本套路,对模型、目标函数、优化算法这些概念有了一定程度的理解,而且已经会训练单个的感知器或者线性单元了。在这篇文章中,我们将把这些单独的单元按照一定的规则相互连接在一起形成神经网络,从而奇迹般的获得了强大的学习能力。我们还将介绍这种网络的训练算法:反向传播算法。最后,我们依然用代码实现一个神经网络。如果您能坚持到本文的结尾,将会看到我们用自己实现的神经网络去识别手写数字。现在请做好准备,您即将双手触及到深度学习的大门。


(接上文,点击查看:干货丨零基础入门深度学习-神经网络和反向传播算法(上)

隐藏层权值训练

首先,我们需要定义节点j的所有直接下游节点的集合Downstream(j)。例如,对于节点4来说,它的直接下游节点是节点8、节点9。可以看到netj只能通过影响Downstream(j)再影响Ed。设netk是节点j的下游节点的输入,则Ednetk的函数,而netknetj的函数。因为netk有多个,我们应用全导数公式,可以做出如下推导:

上式就是式4。

——数学公式警报解除——

至此,我们已经推导出了反向传播算法。需要注意的是,我们刚刚推导出的训练规则是根据激活函数是sigmoid函数、平方和误差、全连接网络、随机梯度下降优化算法。如果激活函数不同、误差计算方式不同、网络连接结构不同、优化算法不同,则具体的训练规则也会不一样。但是无论怎样,训练规则的推导方式都是一样的,应用链式求导法则进行推导即可。


神经网络的实现

现在,我们要根据前面的算法,实现一个基本的全连接神经网络,这并不需要太多代码。我们在这里依然采用面向对象设计。

首先,我们先做一个基本的模型:

‍‍如上图,可以分解出5个领域对象来实现神经网络:‍‍‍‍

  • ●Network 神经网络对象,提供API接口。它由若干层对象组成以及连接对象组成。

  • ‍‍‍‍‍‍●Layer 层对象,由多个节点组成。

  • ●Node 节点对象计算和记录节点自身的信息(比如输出值a、误差项 δ 等),以及与这个节点相关的上下游的连接。

  • ●Connection 每个连接对象都要记录该连接的权重。

  • ●Connections 仅仅作为Connection的集合对象,提供一些集合操作。‍‍‍‍

‍‍‍‍

Node实现如下:

Layer对象,负责初始化一层。此外,作为Node的集合对象,提供对Node集合的操作。

Connection对象,主要职责是记录连接的权重,以及这个连接所关联的上下游节点。

Connections对象,提供Connection集合操作。

Network对象,提供API。

至此,实现了一个基本的全连接神经网络。可以看到,同神经网络的强大学习能力相比,其实现还算是很容易的。


梯度检查

怎么保证自己写的神经网络没有BUG呢?事实上这是一个非常重要的问题。一方面,千辛万苦想到一个算法,结果效果不理想,那么是算法本身错了还是代码实现错了呢?定位这种问题肯定要花费大量的时间和精力。另一方面,由于神经网络的复杂性,我们几乎无法事先知道神经网络的输入和输出,因此类似TDD(测试驱动开发)这样的开发方法似乎也不可行。

办法还是有滴,就是利用梯度检查来确认程序是否正确。梯度检查的思路如下:

对于梯度下降算法:

那么根据导数定义应该等于

然后同我们神经网络代码中计算出来的梯度值进行比较。如果两者的差别非常的小,那么就说明我们的代码是正确的。

至此,会推导、会实现、会抓BUG,你已经摸到深度学习的大门了。接下来还需要不断的实践,我们用刚刚写过的神经网络去识别手写数字。


神经网络实战——手写数字识别

针对这个任务,我们采用业界非常流行的MNIST数据集。MNIST大约有60000个手写字母的训练样本,我们使用它训练我们的神经网络,然后再用训练好的网络去识别手写数字。

手写数字识别是个比较简单的任务,数字只可能是0-9中的一个,这是个10分类问题。


超参数的确定

我们首先需要确定网络的层数和每层的节点数。关于第一个问题,实际上并没有什么理论化的方法,大家都是根据经验来拍,如果没有经验的话就随便拍一个。然后,你可以多试几个值,训练不同层数的神经网络,看看哪个效果最好就用哪个。嗯,现在你可能明白为什么说深度学习是个手艺活了,有些手艺很让人无语,而有些手艺还是很有技术含量的。

不过,有些基本道理我们还是明白的,我们知道网络层数越多越好,也知道层数越多训练难度越大。对于全连接网络,隐藏层最好不要超过三层。那么,我们可以先试试仅有一个隐藏层的神经网络效果怎么样。毕竟模型小的话,训练起来也快些(刚开始玩模型的时候,都希望快点看到结果)。

输入层节点数是确定的。因为MNIST数据集每个训练数据是28*28的图片,共784个像素,因此,输入层节点数应该是784,每个像素对应一个输入节点。

输出层节点数也是确定的。因为是10分类,我们可以用10个节点,每个节点对应一个分类。输出层10个节点中,输出最大值的那个节点对应的分类,就是模型的预测结果。

隐藏层节点数量是不好确定的,从1到100万都可以。下面有几个经验公式:

‍‍‍‍‍‍因此,我们可以先根据上面的公式设置一个隐藏层节点数。如果有时间,我们可以设置不同的节点数,分别训练,看看哪个效果最好就用哪个。我们先拍一个,设隐藏层节点数为300吧。

对于3层