【Tensorflow】深度学习实战01——Tensorflow实现简单的卷积网络(MNIST)

fishing-panhttps://blog.csdn.net/u013921430转载请注明出处】

前言

       现在深度学习可以说很是热门,自己也非常感兴趣,之前有看过吴恩达老师的课程,也看过一些书和微信推送的文章,但是一直只停留在理论阶段,自己总觉得需要实践一下,毕竟不实战的话,学习的东西都会慢慢忘记,而自己永远只能纸上谈兵。所以花了一天的时间,自己照着书上的内容实现了这个简单的卷积网络,算是打响了自己实战深度学习的第一枪。由于是第一篇,所以也写的比较细一点,基本上对每句话都进行了分析。

代码设计

1.  读入数据&创建会话

from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf
mnist=input_data.read_data_sets("MNIST_data/",one_hot=True)
sess=tf.InteractiveSession()

      最开始,我很好奇这些数据我并没有下载下来,也并没有指明数据路径,编译器是如何获得数据的呢,后来我打开了input_data.py,代码如下;

"""Functions for downloading and reading MNIST data."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

# pylint: disable=unused-import
import gzip
import os
import tempfile

import numpy
from six.moves import urllib
from six.moves import xrange  # pylint: disable=redefined-builtin
import tensorflow as tf
from tensorflow.contrib.learn.python.learn.datasets.mnist import read_data_sets
# pylint: enable=unused-import

     就如备注中所示,这个脚本的功能是下载和读入MNIST数据,但是并没有看到链接,接着,打开mnist.py,可以看到一个read_data_sets函数。

def read_data_sets(train_dir,
                   fake_data=False,
                   one_hot=False,
                   dtype=dtypes.float32,
                   reshape=True,
                   validation_size=5000,
                   seed=None,
                   source_url=DEFAULT_SOURCE_URL):

      在这个函数中,有这样的语句;

local_file =base.maybe_download(TRAIN_LABELS, train_dir, source_url + TRAIN_LABELS)

       这里的source_url是由下面这句话而来;

DEFAULT_SOURCE_URL= 'https://storage.googleapis.com/cvdf-datasets/mnist/'

       TRAIN_LABELS如下;

TRAIN_LABELS ='train-labels-idx1-ubyte.gz'

       把两个字符串拼接起来,输入到浏览器地址栏中,这时候你会发现开始下载一个文件了。到这里就很清楚了,Tensorflow自动帮我们下载了MNIST的数据。  

      至于创建会话(Session),首先要知道Tensorflow使用图 (graph) 来表示计算任务,然后通过启动会话来执行图,所以我们需要创建会话。常见的创建会话的方式有两种InteractiveSession()和Session()。

      tf.Session():需要在启动session之前构建整个计算图,然后调用 **Session.run()**方法执行操作,启动该计算图。

     tf.InteractiveSession():它能让你在运行图的时候,插入一些计算图,这些计算图是由某些操作(operations)构成的。使用 Tensor.eval() 和 Operation.run() 方法代替 Session.run()。这对于工作在交互式环境中的人们来说非常便利,比如使用IPython。

2.  定义权重和偏差的初始化函数

      因为实现卷积网络需要创建许多的权重和偏置,所以先定义好初始化函数,方便直接调用;

def weight_variable(shape):
    initial=tf.truncated_normal(shape,stddev=0.1)
    return tf.Variable(initial)

def bias_variable(shape):
    initial=tf.constant(0.1,shape=shape)
    return tf.Variable(initial)

       这里需要说明,在设置权重时使用截断的正态分布添加了一些随机噪声,打破了完全对称,这样做是为了防止所有节点的初始化值都是一样的,增加网络的鲁棒性,试想,如果网络中同一层的节点都是一样的,那么一个卷积层内所有节点的输出都是一样的,设置再多的节点都没有意义。

      此外,给偏置增加了一个0.1的值,来避免激活函数ReLU的死亡节点。为什么会有死亡节点?因为ReLU函数在函数输入小于0时,导数是为0的,此时权重和偏置都不会再更新,这个节点就“死掉”了。


图 1  ReLU激活函数

       特别是在反向传播的过程中,大的梯度更新,可能会导致参数W变化过大,出现小于0的情况,此时ReLU函数的输入很可能小于0。(这里只做简单的说明,想了解的朋友们可以自行学习激活函数

3.  创建卷积&池化函数

     卷积层和池化层也是需要重复使用的,所以也事先定义好函数,方便后期直接调用;

def conv2d(x,W):
    return tf.nn.conv2d(x,W,strides=[1,1,1,1],padding='SAME')

def max_pool_2x2(x):
    return tf.nn.max_pool(x,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')

      tf.nn.conv2d是Tensorflow中的2维卷积函数,x是输入图像或数据,W是卷积核相关的参数,strides代表移动步长,对应的矩阵中的四个参数分别表示[batch,height,width,channels],[1,1,1,1]表示的是在单张图像上做卷积,每次移动的步长为1,以及卷积核通道数为1,因为这里的图像数据是单通道的。padding代表的是边界处理方式,做过卷积计算的朋友都应该知道,卷积对边界的处理方式有很多,比如边界扩展,边界等同,边界缩小等,这里使用的是边界等同'SAME'

     tf.nn.max_pool是Tensorflow中的最大值池化函数,ksize表示的是池化核的大小,矩阵中的四个参数也同样分别表示[batch,height,width,channels]。这里[1,2,2,1]表示池化核的大小为1维中大小为2X2的卷积核,通道数为1。

4.  创建Placeholder(占位符)

       在正式设计卷积网络之前,需要先定义输入的placeholder;placeholder是TensorFlow的占位符节点,可以将placeholder理解为一种形参。

x=tf.placeholder(tf.float32,[None,784])

y_=tf.placeholder(tf.float32,[None,10])
x_image=tf.reshape(x,[-1,28,28,1])

       其中x是输入的数据,因为此卷积神经网络会用到图像的二维信息,所以使用reshape函数将数据转换为尺寸为28X28大小的数据,前面的-1代表样本数量不固定。y_代表真实的图像标签,即手写数字0-9的真实标签。

5.  定义卷积层

       在前期的准备工作都结束后,首先定义第一个卷积层。使用前面已经定义好的函数进行参数初始化。

W_conv1=weight_variable([5,5,1,32])
b_conv1=bias_variable([32])
h_conv1=tf.nn.relu(conv2d(x_image,W_conv1)+b_conv1)
h_pool1=max_pool_2x2(h_conv1)


W_conv2=weight_variable([5,5,32,64])
b_conv2=bias_variable([64])
h_conv2=tf.nn.relu(conv2d(h_pool1,W_conv2)+b_conv2)
h_pool2=max_pool_2x2(h_conv2)

       参数中首先定义的是weightsbias,[5,5,1,32]代表卷积核的尺寸为5x5,单通道数据,并且生成32个不同的卷积核。使用定义好的conv2d将图像与卷积核进行卷积再加上偏置。然后使用ReLU函数进行激活。最后对激活函数的输出进行最大值池化。

       第二个卷积层也用同样的方式定义,只是单一图像与32个卷积核进行卷积后,生成了32张图像。所以第二层中的卷积核的通道数为32。

6.  定义全连接层

       经过两个卷积层和最大值池化后,已经提取了图像的部分特征,现在将图像转换成1维的图像。经过两次最大值池化后图像的大小已经只有原来的1/16,即现在图像的大小为7x7,而第二个卷积层的卷积核数量为64,所以输出的tensor大小为7x7x64,隐含节点数目为1024;后面也使用一个ReLU激活函数。

W_fc1=weight_variable([7*7*64,1024])
b_fc1=bias_variable([1024])
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)

7.  减轻过拟合(Dropout)&Softmax回归

       神经网络在进行学习的过程中有个几乎不可能规避的问题,就是过拟合。2012年GEHinton教授提出了一种Dropout的方法来减轻过拟合,Dropout就是在每一次训练过程中随机扔掉一部分神经元。也就是让某个神经元的激活值以一定的概率p被停止工作,这次训练过程中不更新权值,也不参加神经网络的计算。但是它的权重得保留下来(只是暂时不更新而已),下次样本输入时它可能又得工作了。

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

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)

      上面的代码定义了一个Dropout层,并且将其输出连接到一个Softmax层,得出网络判别的分类标签。这里得到的y_conv跟y一样是一个分类标签,是一个1行10列的,比如分类标签是7,那么tensor为[0,0,0,0,0,0,0,1,0,0];

8.  定义损失函数和学习速率

       这里的损失函数使用的是交叉熵(cross entropy),它描述了两个概率分布之间的距离,当交叉熵越小说明二者之间越接近。交叉熵的定义如下;


cross_entropy=tf.reduce_mean(-tf.reduce_sum(y_*tf.log(y_conv),reduction_indices=[1]))
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, tf.float32))

        argmax(y_,1)就是查找最大值在该行中的位置,由于tensor是由0,1组成,且只有一个1,所以在行方向上找到1的位置就能判断标签。tf.equal( )是进行对比的函数,如果两个输入的值相同,返回true,否则返回false。tf.cast( )是类型转换函数,在这里将bool型转换成浮点数。然后使用tf.reduce_mean( )函数求取平均值。

       上述代码给了一个固定的比较小的学习率。在训练模型时,在模型的初期的时候,往往设置为较大的学习速率比较好,因为距离极值点比较远,较大的学习速率可以快速靠近极值点;而在训练后期,由于已经靠近极值点,模型快要收敛了,此时采用较小的学习速率较好,较大的学习速率,容易导致在真实极值点附近来回波动,就是无法抵达极值点。

9.  开始训练

       前面所有准备工作都做完了,也就是我们的Graph已经设计完成,可以开始使用会话运行图了。在开始训练前初始化所有参数,设置训练时的Dropout为0.5,测试的过程中关闭了Dropout,进行20000次训练,每进行100次训练都会测评一次准确性。当训练完成后,再用测试集对分类的准确率进行评价。

tf.global_variables_initializer().run()
for i in range(20000):
    batch = mnist.train.next_batch(50)
    if i % 100 == 0:
        train_accuracy = accuracy.eval(feed_dict = {x: batch[0], y_: batch[1], keep_prob: 1.0})
        print("step %d, training accuracy %g"%(i, train_accuracy))
    train_step.run(feed_dict = {x: batch[0], y_: batch[1], keep_prob: 0.5})
    
print("test accuracy %g"%accuracy.eval(feed_dict = {x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))

代码

        在此提供所有代码,方便大家使用

# -*- coding: utf-8 -*-
"""
Created on Tue Apr 17 10:04:21 2018

@author: most_pan
"""

from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf
mnist =input_data.read_data_sets("MNIST_data/",one_hot=True)
sess=tf.InteractiveSession()


def weight_variable(shape):
    initial=tf.truncated_normal(shape,stddev=0.1)
    return tf.Variable(initial)

def bias_variable(shape):
    initial=tf.constant(0.1,shape=shape)
    return tf.Variable(initial)

def conv2d(x,W):
    return tf.nn.conv2d(x,W,strides=[1,1,1,1],padding='SAME')

def max_pool_2x2(x):
    return tf.nn.max_pool(x,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')



x=tf.placeholder(tf.float32,[None,784])

y_=tf.placeholder(tf.float32,[None,10])
x_image=tf.reshape(x,[-1,28,28,1])

W_conv1=weight_variable([5,5,1,32])
b_conv1=bias_variable([32])
h_conv1=tf.nn.relu(conv2d(x_image,W_conv1)+b_conv1)
h_pool1=max_pool_2x2(h_conv1)


W_conv2=weight_variable([5,5,32,64])
b_conv2=bias_variable([64])
h_conv2=tf.nn.relu(conv2d(h_pool1,W_conv2)+b_conv2)
h_pool2=max_pool_2x2(h_conv2)


W_fc1=weight_variable([7*7*64,1024])
b_fc1=bias_variable([1024])
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)

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

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)

cross_entropy=tf.reduce_mean(-tf.reduce_sum(y_*tf.log(y_conv),reduction_indices=[1]))
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, tf.float32))

tf.global_variables_initializer().run()
for i in range(20000):
    batch = mnist.train.next_batch(50)
    if i % 100 == 0:
        train_accuracy = accuracy.eval(feed_dict = {x: batch[0], y_: batch[1], keep_prob: 1.0})
        print("step %d, training accuracy %g"%(i, train_accuracy))
    train_step.run(feed_dict = {x: batch[0], y_: batch[1], keep_prob: 0.5})
    
print("test accuracy %g"%accuracy.eval(feed_dict = {x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))

运行结果

    这里最后分类的准确率为0.9933,还是很不错的。


参考书籍

      《Tensorflow 实战》黄文坚等著;

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值