译文 | 与TensorFlow的第一次接触 第五章:多层神经网络

本章中,我们继续使用之前章节中的MNIST数字识别问题,与读者一起编码实现一个简单的深度学习神经网络。

如我们所了解的,一个深度学习神经网络由相互叠加的多层组成。特别的,本章中我们会建立一个卷积神经网络---典型的深度学习样例。卷积神经网络由Yann LeCunn及其它人一起在1998年发明并流行起来。这些卷积网络在最近的图像识别中引领了最高性能表现;例如,在这个数字识别的例子中,它就达到了高于99%的精度。

本章的其余部分,我会通过示例代码来讲解神经网络中最重要的两个概念:卷积和池化,关于它们参数的细节超出了本书的讨论范围。但是,读者可以运行本章中的所有代码,也希望读者能从中理解卷积网络背后的整体认识。

卷积神经网络

卷积神经网络(CNN)是深度学习一个特殊示例,它在计算机视觉有非常重要的影响。

CNN的一个典型特性就是它们的输入基本全是图片,这可以有很高效的实现并减少需要的参数。让我们回顾一下MNIST数字识别样例:读入MNIST数据并定义placeholders后:

我们可以对输入照片数据的shape进行重建,代码如下:

x_image=tf.reshape(x,[-1,28,28,1])

在这我们把输入shape转换成了4D tensor,第二与第三维度对应的是照片的宽度与高度,最后一个维度是颜色通道数,本例子中是1。

我们可把神经网络的输入看作2维空间中大小为28*28的神经元:

定义卷积神经网络有两个基本概念:filters与characteristic maps。这些概念可被表述为一组特殊的神经元,稍后我们就会看到。最后要的是我们先简明介绍这两个概念对CNN的重要性。

直观地来说,卷积层的主要目的就是检测图像中的特征或可见特性,如边缘,线,块,颜色等等。这些是由隐藏层负责的,它们会与输入层相连接。在CNN中,输入数据并不是与隐藏层中的神经元全连接;而只是很小的包含图片像素值的局部空间相连。如下图所示:

更精确地来说,在本例子中,隐藏层的每一个神经元是与一个输入层中5*5小区域(25个神经元)相连接。

我们可看成一个5*5大小的窗口在包含整个照片的输入层(28*28)上滑行。窗口滑过整层的所有神经元。对于每一位置的窗口,隐藏层中都有一个神经元来处理这部分信息。

可理解为窗口从图片的左上角开始;这部分信息输入给隐藏层中的第一个神经元。然后窗口向右滑动一个像素;该部分的信息与隐藏层中的第二个神经元相连接。一直持续这个动作直到整体平面从上到下,从左到右被窗口全部转换:

分析这个具体例子,可以发现输入照片大小为28*28,窗口为5*5,会产生第一层隐藏层中24*24排列的神经元,这是因为我们需要从上到下移动窗口23次,从左到右移动窗口23次来覆盖整个输入图片。在这里是假设窗口每次只移动一个像素,所以新窗口与上次的窗口会有重叠。

在卷积层中每次移动超过一个像素是可能的,这个参数叫作步长。另外一个扩展是对边缘填充0(或其它值),所以窗口可滑过图像的边缘,这可能会产生更好的结果。控制这个特性的参数是padding,你可以用它来决定padding的大小。探讨这两个参数的实现细节同样超出了本书的讨论范围。

基于我们研究的样例,遵循之前章节的形式,我们需要一个bias b与一个5*5的矩阵W来连接输入层与隐藏层的神经元。CNN的一个关键特性是这个权重矩阵W与bias b是隐藏层中所有神经元间共享的。本例子中是24*24(576)个神经元。读者将会发现与全连接的神经网络相比,这会大大减少权重参数的数量。具体来说,由于共享了权重矩阵W,参数数量会从14000(5*5*24*24)降到25(5*5)。

这个共享的矩阵W与bias b在CNN中经常被叫作kernel或filter。这些filters与图像处理程序中润色图片的那些是类似的,此处是用来找出有鉴别性的feature。建议读者查阅GIMP手册中的例子来了解卷积是如何工作的。

一个矩阵与bias定义了一个kernel。一个kernel仅仅只检测图像中一个特定相关的feature,所以建议使用多个kernels,每个想检测的特征一个kernel。这就意味着CNN中一个完整的卷积层包含了很多kernels。描述这些kernels的一个常用方法如下:

隐藏层的第一层包含了多个kernels。此处,我们使用了32个kernels,每一个定义了5*5的权重矩阵W和bias b,这两个参数也是隐藏层间共享的。

为了简化代码,定义了如下两个函数来表示权重W与bias b:

为了不深入到细节,可通过自定义用一些随机值来初始化权重与一些正值来初始化化bias。

对于我们描述的卷积层,经常在卷积层后面加一个池化层。池化层简单的浓缩卷积层的输出结果并创建一个压缩版本的信息并输出。本例子中,我们使用2*2区域的卷积层,通过池化将其压缩成一个点:

有多种不同的实现方式来池化浓缩信息;本例子中,我们使用的方法叫max-pooling。这种方法中通过只保留2*2区域中的最大值来压缩信息。

如上面所提到的,卷积层中包含了很多kernels,我们会对每一个应用max-pooling。一般来说,会有很多池化层跟卷积层:

通过池化层将24*24的卷积结果转变成了12*12排列,该排列中的每一元素都是来源于2*2区域。不像是卷积层,这里的数据是平的,并不是由一个滑动窗口创建。

直观地来说,池化就是找出某一特征是否在图片中出现,该特征的确切位置不如其它特征的相关位置重要。

实现模型

本节中,会分析如何基于之前的例子(Deep MNIST)来实现CNN,该例子可在TensorFlow官网上找到。如之前所说,参数的实现细节与理论概念要远比本书中说明的复杂。因此只分析代码的整体,并不会深入到TensorFlow参数的很多细节中。

我们需要定义几个参数来表示卷积与池化层。在每个维度中我们使用步长为1(滑动窗口步长),并用0来padding的模型。使用的池化层是在2*2区域上。为了简单计算,建议使用如下两个函数来实现卷积跟池化:

现在可以实现第一个卷积层与池化层。此处我们使用了32个filters,每一个都有一个大小为5*5的窗口。我们必须定义一个tensor来保存shape为[5,5,1,32]权重矩阵W:前两个参数是窗口的大小,第三个参数是channel的数量。最后一个定义了我们想使用多少个特征。更进一步,还需要为每一个权重矩阵定义bias。使用之前定义的函数来创建变量:

W_conv1=weight_variable([5,5,1,32])

b_conv1=bias_variable([32])

ReLU(Rectified Linear unit)激活函数最近变成了神经网络中隐藏层的默认激活函数。这个简单的函数包含了返回max(0,x),所以对于负值,它会返回0,其它返回x。在我们的例子中,我们会在卷积层后的隐藏层中使用这个激活函数。

代码中首先对输入图像x_image计算卷积,计算得到的结果保存在2D tensor W_conv1中,然后与bias求和,接下来应用ReLU激活函数。最后一步,对输出应用max-pooling:

h_conv1=tf.nn.relu(conv2d(x_image,W_conv1)+b_conv1)

h_pool1=max_pool_2x2(h_conv1)

当构建一个深度神经网络时,可以相互叠加很多层。为演示如何做到这样,我会创建64个filters且窗口大小为5*5的第二层卷积层。此时我们会需传递32个channels,因为这是前一层的输出结果:

当我们对12*12排列应用5*5的窗口,步长为1时,卷积层的输出结果是7*7维度。下一步是对输出的7*7增加一个全连接层,最终结果输入给softmax层。

我们使用包含1024个神经元的一层来处理整个图片。权重与bias的tensor如下:

W_fc1=weight_variable([7*7*64,1024])

b_fc1=bias_variable([1024])

tensor的第一维度表示第二层卷积层的输出,大小为7*7带有64个filters,第二个参数是层中的神经元数量,我们可自由设置。

接下来,我们将tensor打平到vector中。我们在之前章节中已经看到softmax需要将图片打平成一个vector任为输入。通过打平后的vector乘以权重矩阵W_fc1,再加上bias b_fc1,最后应用ReLU激活函数后就能实现:

h_pool2_flat=tf.reshape(h_pool2,[-1,7*7*64])

h_fc1=tf.nn.relu(tf.matmul(h_pool2_flat,W_fc1)+b_fc1)

下一步是使用神经网络中叫做dropout的技术来减少有效参数的数量。这包含了移除结点及它们相关的输入、输出连接。决定哪一个神经元丢弃,哪一个神经元保留是随机的。为了选择神经元比较一致,对神经元是否被丢弃赋予一个概率。

不多介绍dropout的太多细节,但dropout可以降低模型过拟合数据的风险。当隐藏层中有大量神经元时,就会导致过于表现的模型;此时就会发生过拟合,尤其模型的参数数量超过输入的维度时,更容易产生过拟合。最好要避免产生过拟合,因为过拟合的模型预测非常不准。

在本模型中,我们采用dropout,它会在softmax层前先调用tf.nn.dropout函数。我们还需要创建一个placholder来保存神经元被保留的概率:

keep_prob=tf.placeholder("float")

h_fc1_drop=tf.nn.dropout(h_fc1,keep_prob)

最后,在模型中添加softmax层。Softmax函数返回输入属于每一个分类(本例子中是数字)的概率且概率总和为1。Softmax层如下:

W_fc2=weight_variable([1024,10])

b_fc2=bias_variable([10])

y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop,W_fc2)+b_fc2)

训练与评估模型

现在我们已经准备好训练模型,模型会在卷积层中调整所有参数,一个全连接层来获得预测照片的结果。如果我们想知道模型表现如何,就需要之前的测试集。

接下来的实现代码与上一章节中的十分类似,但有一个例外:将原来的梯度下降优化算法替换为ADAM优化算法,因为这个算法根据文献中的说明,它实现一种更有效的优化算法。

同样还需要在feed_dict参数中提供keep_prob,用来控制dropout层保留神经元的概率。

cross_entropy=-tf.reduce_sum(y_*tf.log(y_conv))

train_step=tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)

correct_prediction=tf.equal(tf.argmax(y_conv,1),tf.argmax(y_,1))

accuracy=tf.reduce_mean(tf.cast(correct_prediction,"float"))

sess=tf.Session()

sess.run(tf.initialize_all_variables())

foriinrange(20000):

batch=mnist.train.next_batch(50)

ifi%100==0:

train_accuracy=sess.run(accuracy,feed_dict={x:batch[0],y_:batch[1],keep_prob:1.0})

print("step %d, training accuracy %g"%(i,train_accuracy))

sess.run(train_step,feed_dict={x:batch[0],y_:batch[1],keep_prob:0.5})

print("test accuracy %g"%sess.run(accuracy,feed_dict={x:mnist.test.images,y_:mnist.test.labels,keep_prob:1.0}))

本模型的所有实现代码同样可以在github上获得,我们可以发现该模型的精度达到了99.2%。

现在简要介绍如何使用TensorFlow来建立,训练,评估深度神经网络作为结尾。如果读者已经可以运行本代码,会发现训练这个神经网络的时间要远远长于上一章节中的;可以设想神经网络层数越多,训练时间也会越久。建议读者阅读下一章节,讲述如何使用GPU训练模型,这会大大缩短训练时间。

本章中的完代码可在github上的CNN.py下载到,下图中的代码供研究的需要:

原文发布于微信公众号 - 人工智能LeadAI(atleadai)

原文发表时间:2017-12-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏魏晓蕾的专栏

【深度学习】深度学习中的一些数学公式

5、交叉熵损失(cross-entropy loss)函数:

19610
来自专栏AI研习社

一文详解 Word2vec 之 Skip-Gram 模型(训练篇)

第一部分我们了解 skip-gram 的输入层、隐层、输出层。在第二部分,会继续深入讲如何在 skip-gram 模型上进行高效的训练。 在第一部分讲解完成后,...

4395
来自专栏机器学习从入门到成神

交叉熵代价函数定义及其求导推导(读书笔记)

神经元的输出就是 a = σ(z),其中z=\sum w_{j}i_{j}+b是输⼊的带权和。

44818
来自专栏机器学习原理

机器学习(4)——逻辑回归Logistic回归softmax回归

前言:这里是分类问题,之所以放到线性回归的下面介绍,是因为逻辑回归的步骤几乎是和前面一样的,写出目标函数,找到损失函数,求最小值,求解参数,建立模型,模型评估。...

3408
来自专栏机器学习算法全栈工程师

SSD源码解析

- SSD理论总结(SSD: Single Shot MultiBox Detector)

1774
来自专栏专知

使用SSD进行目标检测:目标检测第二篇

【导读】近日,CV-Tricks.com发布了一篇文章,使用SSD进行目标检测,SSD是当前最流行的目标检测算法之一。作者从检测的基本概念、滑动窗口检测、减少滑...

4005
来自专栏https://www.cnblogs.com/L

【神经网络篇】--RNN递归神经网络初始与详解

由图可知,比如第二个节点的输入不仅依赖于本身的输入U1,而且依赖上一个节点的输入W0,U0,同样第三个节点依赖于前两个节点的输入,

492
来自专栏人工智能LeadAI

卷积,特征图,转置卷积和空洞卷积的计算细节

最近在做姿态估计的项目,在定制和实现卷积网络的时候发现自己对里面的一些计算细节还不够了解,所以整理了该文章,内容如下:

1003
来自专栏机器学习从入门到成神

交叉熵代价函数定义及其求导推导(读书笔记)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_35512245/articl...

762
来自专栏刘笑江的专栏

Factorization Machine

1244

扫描关注云+社区