美文网首页TensorFlow数据科学TensorFlow
简单卷积神经网络的tensorflow实现

简单卷积神经网络的tensorflow实现

作者: 西风酹江月 | 来源:发表于2017-07-09 20:45 被阅读248次

    卷积神经网络概述

    一般一个卷积神经网络由多个卷积层构成,在卷基层内部通常会有如下几个操作:
    1)图像通过多个卷积核滤波,添加偏置,提取局部特征每个卷积核会映射出一个新的2D图像。
    2)卷积核的滤波结果输出到激活函数中,激活函数通常选ReLU【书,参考37】
    3)对激活函数的结果进行池化操作,池化就是对图像分割成不同的小区域后取平均值或最大值。一般取最大值。
    上述几个步骤就构成了最常见的卷积层。在池化的后面还可以加上batch normalization等操作。
    一个卷积层中可以有不同的卷积核,而每一个卷积核都对应一个滤波后映射出的新图像,同一个新图像的每一个像素都来自完全相同的卷积核。这种卷积核的权值共享可以有效降低模型负责度,减轻过拟合,减少计算量。

    tensorflow实现

    卷积神经网络结构

    建立卷积神经网络对手写数字识别问题进行优化,构建由两个卷积层(包含池化层),两个全连接层构成的卷积神经网络。输入图像是28×28的单通道数据,输出是10×1的one_hot编码的向量。
    第一层:卷积核大小是[5,5],输入通道1,输出通道32,padding选择SAME模式,激活函数为relu。
    第二层:池化层,池化核大小是[2,2],步长[2,2]。
    第三层:卷积核大小是[5,5],输入通道32,输出通道64,padding选择SAME模式,激活函数为relu。
    第四层:池化层,设置同上。
    第五层:全连接层,首先将图像数据矩阵flatten化,变成1维向量,输出维度是1024, 之后dropout掉一定的数据防止过拟合
    第六层:全连接层,输出维度10,激活函数为softmax
    损失函数:交叉熵

    具体实现如下:

    导入包和数据

    首先导入需要的包和数据

    import tensorflow as tf
    from tensorflow.examples.tutorials.mnist import input_data
    mnist = input_data.read_data_sets('MNIST_DATA/', one_hot=True)
    

    定义卷积核

    卷积核是指定维数的张量,卷积核的大小为:[filter_height, filter_width, in_channels, out_channels]
    这里用tf.Variable()定义卷积核,定义的时候会指定初始化的操作。

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

    定义偏置

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

    对输入做卷积操作

    def conv_op(in_tensor, kernel, strides=[1,1,1,1], padding=’SAME’, name=’conv’):
        conv_out = tf.nn.conv2d(in_tensor,kernel,strides, padding,name=name)
    return conv_out
    

    输入的张量维度是[batch, in_height,in_width,in_channels]
    在具体操作中,程序会将输入张量展开为[batch, out_height, out_width, filter_hightfilter_widthin_channels]
    并将卷积核展开为[filter_heightfilter_widthin_channels, out_channels ]
    输入张量展开后与卷积核的展开相乘,得到的结果的维度是:[batch, out_height, out_width, out_channels],和输入张量的维度保持一致。也就是说,在tensorflow构建的图中,张量始终都是以始终是:[batch, height,width,channels]的维度流动。
    步长strides通常定义为strides=[1, dh, dw, 1], 也就是strides[0]=strides[3]=1. dh和dw分别是垂直和水平方向卷积核移动的步长。
    padding就是是否要做边界填充,如果要,padding=‘SAME’,如果不用,padding=‘VALID’

    叠加上偏置并输出到激活函数

    h_conv1 = tf.nn.relu(conv_op(x_image, W_conv1)+b_conv1)
    之后输入到激活函数,直接调用tensorflow中的tf.nn.relu()函数就可以,激活函数的输出依然是:[batch, out_height, out_width, out_channels]

    tf.nn.relu(features, name = None)
    

    输入参数:

    • features: 一个Tensor。数据类型必须是:float32,float64,int32,int64,uint8,int16,int8。
    • name: (可选)为这个操作取一个名字。

    输出参数:

    • 一个Tensor,数据类型和features相同。

    在tensorflow中,加法运算有好几种,例如专门的偏置叠加tf.bias_add,还有tf.add,也可以直接用a+b,具体区别见后。

    池化层

    池化操作是:对输入的张量进行区域扫描求最大值或者平均值。一般求最大值。tensorflow有tf.nn.max_pool()函数可以方便地实现池化操作。池化尺寸kh×kw, 步长是dh×dw,padding设置为‘SAME’。

    def max_pool_2x2(in_tensor, ksize=[1,2,2,1], strides=[1,2,2,1],padding=’SAME’,name=’max_pooling’):
        max_pool = tf.nn.max_pool(in_tensor, ksize, strides, padding, name=name)
    return max_pool
    

    这里ksize=[1,kh,kd,1], strides=[1,dh,dw,1]

    目标函数:交叉熵

     cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_*tf.log(y_conv),reduction_indices=[1]))
    

    自此,就定义好了前向计算的所有操作。后向计算采用AdamOptimizer函数,学习率1e-4

    最后,给出卷积操作的实现代码:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    """
    Created on Sun Jul  9 12:16:52 2017
    mnist by simple cnn
    @author: 
    """
    
    import tensorflow as tf
    from tensorflow.examples.tutorials.mnist import input_data
    
    mnist = input_data.read_data_sets('MNIST_DATA/', one_hot=True)
    
    sess = tf.InteractiveSession()
    
    # ======================================================== #
    #                       SIMPLE CNN
    # ======================================================== #
    
    # define conv kernel
    def weight_variable(shape):
        initial = tf.truncated_normal(shape,stddev=0.1)
        weight = tf.Variable(initial_value=initial)
        return weight
    
    # define conv bias
    def bias_variable(shape):
        initial = tf.constant(0.1,shape=shape)
        bias = tf.Variable(initial_value=initial)
        return bias
    
    # define a simple conv operation 
    def conv_op(in_tensor, kernel, strides=[1,1,1,1], padding='SAME'):
        conv_out = tf.nn.conv2d(in_tensor, kernel, strides=strides, padding=padding)
        return conv_out
    
    # define max pooling operation
    def max_pool_2x2(in_tensor,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME'):
        max_pool = tf.nn.max_pool(in_tensor, ksize, strides, padding)
        return max_pool
        
    def simple_cnn():
        x = tf.placeholder(tf.float32, [None, 784])
        y_ = tf.placeholder(tf.float32, [None, 10])
        x_image = tf.reshape(x,[-1, 28, 28, 1]) # 28*28 pic of 1 channel
        
        # cnn structure
        w1 = [5,5,1,32]
        b1 = [32]
        w2 = [5,5,32,64]
        b2 = [64]
        wfc1 = [7*7*64,1024]
        bfc1 = [1024]
        wfc2 = [1024,10]
        bfc2 = [10]
        
        # 1st layer
        W_conv1 = weight_variable(w1)
        b_conv1 = bias_variable(b1)
        h_conv1 = tf.nn.relu(conv_op(x_image, W_conv1)+b_conv1)
        h_pool1 = max_pool_2x2(h_conv1)
        # 2nd layer
        W_conv2 = weight_variable(w2)
        b_conv2 = bias_variable(b2)
        h_conv2 = tf.nn.relu(conv_op(h_pool1, W_conv2)+b_conv2)
        h_pool2 = max_pool_2x2(h_conv2)
        # fc1
        h_pool2_flat = tf.reshape(h_pool2,[-1,7*7*64])
        W_fc1 = weight_variable(wfc1)
        b_fc1 = bias_variable(bfc1)
        h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1)+b_fc1)
        # drop out
        keep_prob = tf.placeholder(tf.float32)
        h_fc1_drop = tf.nn.dropout(h_fc1,keep_prob)
        # fc2
        W_fc2 = weight_variable(wfc2)
        b_fc2 = bias_variable(bfc2)
        y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2)+b_fc2)
        # loss function
        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)
        
        # estimate accuarcy
        correct_prediction = tf.equal(tf.arg_max(y_conv,1), tf.arg_max(y_,1))
        accuarcy = 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 = accuarcy.eval(feed_dict={x:batch[0],y_:batch[1],keep_prob:1.0}) # testify, or prediction
                print('step %d, training accuarcy %g'%(i, train_accuracy))
            train_step.run(feed_dict={x:batch[0],y_:batch[1],keep_prob:0.5})
        
        print("test accuracy %g"%accuarcy.eval(feed_dict={x:mnist.test.images,y_:mnist.test.labels,keep_prob:1.0}))
        return
    
    if __name__ == '__main__':
        simple_cnn()
    

    tensorflow的一些细节点:

    padding

    padding的选项:

    • valid就是指卷积核是以整个卷积核为基础从边沿对齐图像的边沿开始,任何边沿到达图像另一边时就停止卷积运算;
    • same则是以卷积核的中间点为基础,中间点扫过真个图像才结束。自然,same的模式下,卷积后的输出和原输入的维度是一致的。

    tf.cast

    估计模型的准确度时,用到了函数tf.cast
    tf.cast(x, dtype, name=None)数据类型转换,将x或者x.values转换为dtype
    a=tf.constant(value=[1.1,2.3],dtype=tf.float32)
    b=tf.cast(a,dtype=tf.int32)
    sess = tf.Session()
    print(sess.run(a)) #输出[ 1.10000002 2.29999995]
    print(sess.run(b)) #输出[1 2]

    tf.add(a,b)、tf.bias_add(a,b)和 a+b

    1) tf.add(a, b) 与 a+b

    在神经网络前向传播的过程中,经常可见如下两种形式的代码:
    tf.add(tf.matmul(x, w), b)
    tf.matmul(x, w) + b
    简而言之,就是 tf.add(a, b) 与 a + b二者的区别,类似的也有,tf.assign 与 =(赋值运算符)的差异。
    在计算精度上,二者并没有差别。运算符重载的形式a+b,会在内部转换为,a.add(b),而a.add(b)会再一次地映射为tf.add。

    2. tf.nn.bias_add 与 tf.add

    tf.nn.bias_add 是 tf.add 的一个特例,也即 tf.add 支持的操作比 tf.nn.bias_add 更多。二者均支持 broadcasting(广播机制),也即两个操作数最后一个维度保持一致。
    除了支持最后一个维度保持一致的两个操作数相加外,tf.add 还支持第二个操作数是一维的情况。
    参考:
    《tensorflow实战》
    http://blog.csdn.net/wangjian1204/article/details/53291619
    In tensorflow what is the difference between tf.add and operator (+)?(https://stackoverflow.com/questions/37900780/in-tensorflow-what-is-the-difference-between-tf-add-and-operator

    相关文章

      网友评论

        本文标题:简单卷积神经网络的tensorflow实现

        本文链接:https://www.haomeiwen.com/subject/ygdehxtx.html