首先简单过一遍什么是卷积神经网络?
![a65d4c907bd2e087bdf3e447ad7977e9.png](https://i-blog.csdnimg.cn/blog_migrate/9258d0395b0b6c68ba5a087e9c2de695.jpeg)
以上是维基百科给出的定义。
用我这种新人菜鸟的思维去理解,卷积网络就是比普通的神经网络多了一个用于过滤特征的卷积层和用于降采样的池化层。
这个卷积层具体来说就是一个批量的过滤器(filter),不断的在图片上滚动收集图片的信息,而每一次搜集都只收集一小块像素区域,比如图片颜色的深浅和轮廓,而使用不同的过滤器则可以提取图像中不同的特征。
而池化层就是强化这个特征提取的能力,它会将最有用的信息筛选出来,然后交给下一个层去分析。
对于图像识别来说,CNN所学习的是图片中特定的一个部分,而不是像普通网络那样使用整个图片,这也就意味着无论什么是图片,只要图片中有这个部分,那么CNN就都可以识别出来,而普通网络就做不到,而这还有一个很显而易见的优点,那就是CNN可以使用更少的参数,这将大大减少神经网络的计算负担
至于更深层的推导原理,大家可以去这里看一下吴恩达老师的深度学习的公开课,讲的非常的好。https://study.163.com/courses-search?keyword=%E5%90%B4%E6%81%A9%E8%BE%BE
剩下的细节,我会在代码中讲出来。
Import data
首先导入所需要的包
![91af17d564fb0b8390018769e348f858.png](https://i-blog.csdnimg.cn/blog_migrate/334587d7f2da40dbccb08febc5644250.jpeg)
导入数据,这个数据集中的图片大小是28x28的,是灰色图像,只有一个颜色通道,所以每一张图片有28x28x1=784个像素
![aca1a94aeb904ddb96663e5aadcf4221.png](https://i-blog.csdnimg.cn/blog_migrate/6faaefb39d0fe70a33f6452da2c34d00.jpeg)
Data Preprocessing
检查一下缺失值
![8c2341efd00d76199fa5334ad3700793.png](https://i-blog.csdnimg.cn/blog_migrate/8ffff982e33f9be4acc1715474513789.jpeg)
很好,没有缺失值,接着我们把数据分成特征集和标签
![d4e6ada763e99811d0dcac5892232e74.png](https://i-blog.csdnimg.cn/blog_migrate/583e2cd1378c4e22512264aa53804cd6.png)
观察一下各个类别之间是否平衡
![b4ea0169e4007de3ad390b92664ce5cc.png](https://i-blog.csdnimg.cn/blog_migrate/7e4a5fc435cc560d23df53cc69fea35d.jpeg)
之后我们要对图像进行一个灰度的标准化,这可以减少照明对图片的影响,而且CNN计算[0,1]之间的数据要比[0,255]的数据要快一些。而这个操作也很简单,除以255就可以了
![3630aa472a8b76662945ae492655a38b.png](https://i-blog.csdnimg.cn/blog_migrate/68d0852b45c2966124e74e5330863124.png)
在建模之前,我们先看看我们的数字是长什么样的
![554d6f92ce09fc1010035177e1b01225.png](https://i-blog.csdnimg.cn/blog_migrate/a5414e9e26e156027a58751dc0ee5a01.jpeg)
嗯,写的还算规矩。之后我们要对样本标签进行重新编码,将其转换为一个用0和1代表的独热向量
![b0b5bb154aedebc4df039d980c8b157d.png](https://i-blog.csdnimg.cn/blog_migrate/f6a1cc2a400e08ca16f911d7850d749a.jpeg)
最后我们将数据分割成训练集和测试集
![a7eaf0605e5f89e225592ecbfa3ad6e3.png](https://i-blog.csdnimg.cn/blog_migrate/ffcbbd7b67dab509f4d878cd06d3d1e1.png)
Define function
首先需要定义几个用于搭建CNN的比较重要的函数,分别是定义神经网络所需要的权重,偏置和以及CNN中特有的卷积层与池化层
![fd4f243fcaaa82b3097cd9b72f56999b.png](https://i-blog.csdnimg.cn/blog_migrate/06a1e96b66876fc4e0526ec77f516e02.png)
![29359d3bca51b8a8f70ed709236e8527.png](https://i-blog.csdnimg.cn/blog_migrate/5c862741d11476e314e3cb2bbfe7d68d.png)
定义卷积层这里有几个参数需要说明一下,x代表输入数据,W代表对应的过滤器权重,strides则表示filter每次移动的步长为1,而padding则表示是否用0填充卷积后的数据,因为每一次进行卷积数据都会有一部分损失掉了,具体表现就是图片变小了,那这时就可以使用zero-padding填充图片大小,这里有两种模式,一种是‘SAME’也就是每次都将数据填充为和卷积前一样大的数据,还有一种就是‘VALID’也就是不进行填充,这里选择‘SAME’模式
![b50638f4a198c0d6b38aae2e411863b0.png](https://i-blog.csdnimg.cn/blog_migrate/cdc77f9721be656095d1107c8402be1f.png)
然后就是定义池化层,有两种池化方式,一种是平均池化,也就是对池化内核所选区域取平均数,但这种方式已经很少使用了,另一种就是最大池化,选择所选区域最大的数,这里我们用最大池化。ksize表示池化内核的大小,我们使用2x2的内核,步长控制为2,同样填充方式选择‘SAME’。
还有一点需要注意,那就是池化层并没有使用激活函数,他只是一个单纯的线性变化,同样也没有偏置这个参数的设置。
![d36cf98bc3679e9d6ebee0c83a898c7a.png](https://i-blog.csdnimg.cn/blog_migrate/e5c7d8abf8ac690667eb5d8eebd93e2a.png)
Build Model
好,定义好关键的函数之后,我们就要开始搭建模型了。
我们这次搭建的CNN的结构是这样的,卷积层(relu)->池化层->卷积层(relu)->池化层->全连接层(relu)->输出层(softmax)。这里的relu和softmax代表对应层所使用的激活函数。
首先使用tf.placeholder为我们将要输入的数据和标签进行占位,这个就是TensorFlow的特别之处了,并不是在一开始就传入数据,而是在执行的时候在赋予值。
![002b3e9ed16a1fedc6b4fd69da319b77.png](https://i-blog.csdnimg.cn/blog_migrate/11a97d7ca7fc40620af6fcf2656cbc6f.png)
layer1
我们先定第一层卷积层,W_c1代表该卷积层所使用的过滤器,[5,5,1,32]就代表这是32个大小为5x5x1的过滤器,b_c1为每个过滤器对应的偏置
![71216c3e078af660e85054743abffacc.png](https://i-blog.csdnimg.cn/blog_migrate/080dc65491ed285acfad6df4a22dfad8.png)
为了进行卷积,我们还要改变数据的形状,我们要把现在一维的数据变为一个4D的张量,我们使用tf.shape方法,[-1,28,28,1]这里我需要解释一下,-1代表无视行数,也就是你不需要刻意的输入对应的行数,传入的数据有多少行这里就是多少行;28,28代表图片的高度和宽度,1是因为这里使用的图片只有一个颜色通道,如果是RGB图片将会有3个颜色通道。
对传入数据进行卷积操作后使用一个relu函数作为激活函数,接着进行一步池化,到这里第一层卷积层就结束了。
![34bc43ccbc1b5a5be6a78547bfa1a21d.png](https://i-blog.csdnimg.cn/blog_migrate/9f280ddfcdaeece3185a46b86cce0fc1.png)
layer2
第二层与第一层差不多,区别是这一层我们使用64个5x5x32的过滤器,连带着偏置也是64个
![9ad30a0b32b39c3a8012d0b36ac08fc8.png](https://i-blog.csdnimg.cn/blog_migrate/7ab6dd061bebc41b337cc0b48dfe52bf.png)
full-connected layer
这里就是全连接层了,但是这时经过两次卷积之后,数据由原来的一维数据变成了一个7x7x64的三维数据,所以在喂入全连接层之前我们需要先将数据铺平
![b570bc79906b36584b62f8ecba5dab55.png](https://i-blog.csdnimg.cn/blog_migrate/349e81cc99e064c3c77ddaa6a84abb9a.png)
接着就是常规操作了,这一层我们使用了1024个神经元,先对数据进行线性变换,然后使用一个relu的激活函数。
![ec73092604e7b534a3da8acf8b47a00d.png](https://i-blog.csdnimg.cn/blog_migrate/12554631c32737d5a5f500d03bb03e79.png)
![9098896274ed78594eac33b3fbfd7810.png](https://i-blog.csdnimg.cn/blog_migrate/f6eb764fd6e27e8b55343301e0fb82c9.png)
dropout
dropout是指在深度学习网络的训练过程中,对于神经网络单元,按照一定的概率将其暂时从网络中丢弃。而这样做的好处就是可以很有效的防止神经网络的过拟合,具体的细节大家可以自己去查找一下。
使用起来也很方便,TensorFlow对dropout也进行了很好的封装
![dc7cfa3b1ad200dc7847138b2b0b0941.png](https://i-blog.csdnimg.cn/blog_migrate/90408449e112e3a189d864d91f40161c.png)
output layer
这一层就是输出层了,这个手写字对应着10个类别,所以这里也有10个输出,最后一层使用softmax作为激活函数,这时的y_predict就是你的神经网络所预测出来的类别了。
![3672ebe217dfbfc6a10c6d4a6cc18a43.png](https://i-blog.csdnimg.cn/blog_migrate/61c950b5fde1b9a348e88a52e2eb50c7.png)
cost function and optimizer
但是一开始会有很大的误差,为了衡量这种误差就需要定义一个损失函数,而我们所谓的优化神经网络也就是让这个损失函数收敛到最小,而梯度下降就是一种常见的优化方法,而这些方法在TensorFlow中都有很好的封装,这里我们选择Adam作为我们的优化器
![e3c593ffe19fa6e0afd9092f70597147.png](https://i-blog.csdnimg.cn/blog_migrate/93c5b8592f04d37fd5001ae2b005d9c8.png)
evaluation
选择好损失函数和优化器之后,还需要有一个用于评价我们神经网络性能的指标,我们就使用分类模型最常用的准确率(accuracy)作为评价指标,而这里的predict也就是神经网络用于预测新数据的方法
![26cdf6893b06339418af55a058ad59c0.png](https://i-blog.csdnimg.cn/blog_migrate/080dd08dbdba579744d173d74dbb26cd.png)
get batch
在正式开始训练神经网络之前,还有一件事需要去做,要知道一次性将所有数据都扔进这个CNN中,那个计算量实在是太大了,很有可能产生内存问题,所以这里有一个方法,我们可以减少每一次迭代所需要的数据量,这个数据量的大小我们称为batch_size,而且在TensorFlow中也有相应的封装方法,这里我就不一一介绍了,这里有一篇文章对这些方法有很好的介绍https://blog.csdn.net/sinat_35821976/article/details/82668555
![fdeb9788d697d6ae4bfad3caa27ae5b8.png](https://i-blog.csdnimg.cn/blog_migrate/88b546f2419c54fac645c3f25c962ce4.png)
之后在定义几个空的列表,用于之后的可视化
![af33f3a9161a6b98ccae1bbad5ee4681.png](https://i-blog.csdnimg.cn/blog_migrate/3619089277ffd7d84d363f27ae3b33cf.png)
begin session
之后就要开启会话,初始化tf中所定义好的变量,这里我们每一个batch_size大小是50,训练集大小除以50,大概迭代750次,这里设置每10次打印一次准确率,具体的代码都在下面了
![9a4fddfddc87e23eb400cb1f82498624.png](https://i-blog.csdnimg.cn/blog_migrate/36efc4dc4fc2b42e4d4ac566a735dd0a.jpeg)
![019e95c4db7ea34c8cd9fccea3b15a73.png](https://i-blog.csdnimg.cn/blog_migrate/538eae57ffe698a9bfb7e42450e2d7e6.jpeg)
![af6121fcf6839203677e3317c03a2b6a.png](https://i-blog.csdnimg.cn/blog_migrate/042a9b3bcf8ccc8e233258f3e5f44c29.jpeg)
打印的结果太多,这里我只截取了一小部分,但是可以看出神经网络的准确率随着他迭代次数的增加会变的越来越高,这里测试集的准确率达到了96%,但这绝不是最好的效果,实际上继续增加迭代次数,性能可以继续攀升,只是这里我偷了一个懒,有兴趣的小伙伴可以自己去尝试一下其他参数,看看能达到什么样的效果。
最后我们看一下训练集和测试集的准确率变化情况
![ea239785aa2d18086e6b890b0fcac863.png](https://i-blog.csdnimg.cn/blog_migrate/61cf8a746ce605628602fe08e5fa3e57.jpeg)
可以看出,该CNN模型正确率提升的还是比较符合常理的,前期准确率增加很快,随着准确率越来越高,增速也随之放缓,等步入平稳期之后,准确率就随着训练迭代次数的增多缓慢提升,这个模型的效果还是不错的,并没有出现明显的过拟合现象。
嗯,这篇文章到这里就算结束了,对了,还有一点不要忘
别忘记关闭会话!
![b901ab9f6ae1b3f3327aed46b9227391.png](https://i-blog.csdnimg.cn/blog_migrate/914c878ee0357c2012e062f7b9e7feef.png)