CNN卷积神经网络简单介绍与详细搭建注释-识别mnist手写黑白数字

卷积神经网络(CNN)是在计算机视觉中经常用到的的学习模型。(详细注释版代码在后面)

CNN是深度神经网络(DNN)的改进版本,加上了convolution卷积层)和pooling(池化层)以及padding(填充)操作。

注:下面引用的图片均来自台大李宏毅老师

                                               CNN卷积神经网络简单介绍与详细搭建注释-识别mnist手写黑白数字_第1张图片

卷积神经网络的结构如上图所示,一个三层张量(RGB图像)的输入,通过多次卷积层和池化层变成具有很多层的张量,再拉平成矩阵输入到全连接层(传统神经网络)。

                                                CNN卷积神经网络简单介绍与详细搭建注释-识别mnist手写黑白数字_第2张图片

卷积神经网络的三大优势如上图所示,可以有效浓缩和减少feature。

                                                   CNN卷积神经网络简单介绍与详细搭建注释-识别mnist手写黑白数字_第3张图片

卷积层演示如上图:这是演示的一个黑白图像(矩阵)和一个卷积核(filter),步长(stride)=1的情况。因为filter是3X3,所以红色框框的大小也是3X3,与filter做内积(位置相同的元素做乘积最后相加),然后红框每次移动一格。6x6的矩阵就成了4x4.

                                                    CNN卷积神经网络简单介绍与详细搭建注释-识别mnist手写黑白数字_第4张图片           

filter的大小和个数,stride的大小都可以自己定义,有2个卷积核所以最后合成的是2层张量。     

                                                       CNN卷积神经网络简单介绍与详细搭建注释-识别mnist手写黑白数字_第5张图片

三层的输入(RGB图像)则每个filter本身就是三层,不像之前的filter本身只有一层,这里的filter把3层的东西变成一个点;最后输出的模型有几层就代表有几个卷积核。

                                                     CNN卷积神经网络简单介绍与详细搭建注释-识别mnist手写黑白数字_第6张图片

通过这样的方式,就不需要全连接和有效减少参数了。

                                                 CNN卷积神经网络简单介绍与详细搭建注释-识别mnist手写黑白数字_第7张图片

池化层(pooling)就是起到压缩的方式,常见的有取最大的(max)和取平均值(average),例如在2x2的池化层中,有[-1,1,0,3],用最大的方式压缩就只剩下[3],pooling同样有stride,通常是卷积核stride的+1。

填充操作(padding)就是为了防止在步长移动超过原有数据集设计的,valid的方式是舍弃最后一部分数据而same是在周围补0是补步长可以移动。

                                                 CNN卷积神经网络简单介绍与详细搭建注释-识别mnist手写黑白数字_第8张图片

最后把这个已经有很多层的输出拉平成矩阵,输入传统DNN。

以上就是CNN的快速入门,接下来就开始代码实践了!

卷积神经网络搭建演示:

实验环境:jupyter notebook, python3, tensorflow(cpu版), Anaconda, mnist数据集

import tensorflow as tf
import matplotlib.pyplot as plt
%matplotlib inline

from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets('MNIST_data',one_hot=True) #读取数据0~9

#########################################
###函数名:computer_accuracy         ###
###作用: 计算模型的正确率            ###
###输入:验证集的sample和label       ###
###输出:模型的正确率                ###
#########################################
def computer_accuracy(v_xs,v_ys):
    global prediction
    #keep_prob是dropout剩下的比例
    y_pre = sess.run(prediction,feed_dict={xs:v_xs,keep_prob:1})  
    #下行代码因为结果是一个10个数(表示对于数字的概率)的向量
    correct_prediction = tf.equal(tf.argmax(y_pre,1),tf.argmax(v_ys,1)) 
    accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32)) #记得先转化数据格式
    result = sess.run(accuracy,feed_dict={xs:v_xs,ys:v_ys,keep_prob:1})
    return result

#########################################
###函数名:weight_variable           ###
###作用:随机初始化权重且维度相同      ###
###输入:输入张量(这里是一维的)      ### 
###输出:一个具有初始值的权重变量      ###
#########################################
def weight_variable(shape):
    initial = tf.truncated_normal(shape,stddev = 0.1) 
    #从截断的正态分布中输出随机值,stddev是正态分布的标准差
    return tf.Variable(initial)  #通过构造variable 类的实例来添加一个变量到图中

#########################################
###函数名:bias_variable             ###
###作用: 随机初始化偏差且维度相同     ###
###输入: 输入张量                   ###
###输出:一个初始值为0.1的偏差变量    ###
#########################################
def bias_variable(shape):
    initial = tf.constant(0.1,shape=shape)  #y一个常量张量的维度等于输入张量的维度,值为0.1
    return tf.Variable(initial)

#########################################
###函数名:conv2d                    ###
###作用:2维的卷积层                  ###
###输入:数据信息 和 权重             ###
###输出:卷积过的数据集               ###
#########################################
def conv2d(x,W):
    #步长=[1,x_movement,y_movement,1],padding填充周围有valid和same可选择
    return tf.nn.conv2d(x,W,strides=[1,1,1,1],padding='SAME')  

#########################################
###函数名:max_pool_2x2              ###
###作用:用最大值方法压缩的池化层      ###
###输入:卷积过的数据集               ###
###输出:压缩过的数据集               ###
#########################################
def max_pool_2x2(x):
    #ksize是池化窗口的大小=[1,height,width,1],一般height=width=池化窗口的步长
    #池化窗口的步长一般是比卷积核多移动一位
    return tf.nn.max_pool(x,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')        


#定义placeholder,便于神经网络输入数据
xs = tf.placeholder(tf.float32,[None,784])  #不限sample的个数,每个图片是28x28=784
ys = tf.placeholder(tf.float32,[None,10])
keep_prob = tf.placeholder(tf.float32)
#下行代码中-1表示不限导入图片个数,会自动计算==placeholder里面的None,最后的1表示channel=1,说明#是灰白图像,3则是彩色图像
x_image = tf.reshape(xs,[-1,28,28,1])   
#print(x_image.shape) #[number_samples,28,28,1]

##############################网络层搭建-start#######################
##conv1 layer##
#[5,5,1,32]就是输入的张量,5X5是卷积核的大小,1表示输入的通道数为1是黑白的,32是操作后输出图片的
#厚度,表明有32个卷积核
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) #加上激活函数非线性化处理  #convolution 卷积层
h_pool1 = max_pool_2x2(h_conv1) #池化层 pooling 
#h_conv1的outsize 28x28x32 ,  h_pool1的outsize 14x14x32  因为填充方式padding都是same,但是
#conv的步长是1,而pool的步长是2

##conv2 layer##
W_conv2 = weight_variable([5,5,32,64])#卷积核还是5x5,但是传入的张量维度从1变成了32
#传出的张量维度改为64,表明有64个卷积核
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1,W_conv2) + b_conv2) #加上激活函数非线性化处理  #convolution 卷积层
h_pool2 = max_pool_2x2(h_conv2)  #池化层 pooling 
#h_conv2的outsize 14x14x64 ,  h_pool2的outsize 7x7x64

#s上面的卷积层想设置几个设置几个,下面开始写全连接层代码。

##func1 layer##
W_fc1 = weight_variable([7*7*64,1024])  #表明一维张量全连接1024个神经元
b_fc1 = bias_variable([1024])  #要是[卷积核或者神经元个数],[]不可以少因为输入的是张量
#下行代码实现拉平成二维张量(矩阵)   [n_samples,7,7,64]->>[n_samples,7*7*64]
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)    #矩阵相乘加上bias,result也是一维张量
h_fc1_drop = tf.nn.dropout(h_fc1,keep_prob)  #减少overfit
                                
##func2 layer##
W_fc2 = weight_variable([1024,10]) #因为是识别10个数字
b_fc2= bias_variable([10])
prediction = tf.nn.softmax(tf.matmul(h_fc1_drop,W_fc2) + b_fc2)  #最后的输出层

############################网络层搭建-end###############################

#通过优化器减少预测值和真实值之间的交叉熵达到训练模型的目的
cross_entropy =tf.reduce_mean(-tf.reduce_sum(ys*tf.log(prediction),reduction_indices=[1]))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy) #学习率设置的很小

#可视化为了画折线图
step_list = list(range(10)) # [0,1,2,……,9]
cnn_list = []
fig = plt.figure() #建立可视化图像框
ax = fig.add_subplot(1,1,1) #z子图总行数、列数,位置
ax.set_title('cnn_accuracy ', fontsize=14,y=1.02)

#初始化,如果存在变量则是必不可少的操作
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    
    for step in range(500):  #训练500次
        batch_xs,batch_ys = mnist.train.next_batch(100) 
        #小批量训练法,每次训练用其中的100个sample,
        sess.run(train_step,feed_dict={xs:batch_xs,ys:batch_ys,keep_prob:0.5})
        if step%50 ==0:
            #下列计算不需要sess.run是因为computer_accuracy函数里面已经有sess.run了
            cnn_accuracy = computer_accuracy(mnist.test.images,mnist.test.labels)   #mnist.test.images[:1000]
            cnn_list.append(cnn_accuracy)
            print(cnn_accuracy)
            
ax.plot(step_list,cnn_list)
            
###题外话:正确率可以达到95%以上,CPU版本的TF可以让电脑满负荷100%CPU的跑……###

实验结果:

                                          CNN卷积神经网络简单介绍与详细搭建注释-识别mnist手写黑白数字_第9张图片

参考资料:

莫烦tensorflow视频教程 :https://www.bilibili.com/video/av16001891

李宏毅卷积神经网络原理:https://www.bilibili.com/video/av23593949?from=search&seid=11913427503734628359

 

你可能感兴趣的:(深度学习)