腾讯云海外购

卷积神经网络处理图像识别(二)

本篇介绍卷积神经网络之前向传播的基本实现。

本篇中卷积神经网络的结构为:

卷积层->池化层->卷积层->池化层->全连接层->全连接层

其中的全连接层还引入了dropout的概念。dropout在训练时会随机将部分节点的输出改为0(使神经元以一定概率失活)。dropout可以避免过拟合(overfitting)问题。

代码和注释中有详细的介绍:

import  tensorflow as tf #基于MNIST 数据集,稍作更改便可应用于其他数据集。#MNIST 数据集信息,IMAGE_HEIGHT = 28IMAGE_WIDTH = 28NUM_CHANNELS = 1#颜色通道数NUM_LABELS =10#神经网络参数#INPUT_NODE = IMAGE_HEIGHT*IMAGE_WIDTHOUTPUT_NODE = NUM_LABELS#第1层卷积层的尺寸(5x5)和深度CONV1_SIZE = 5CONV1_DEEP = 32#第2层卷积层的尺寸(5x5)和深度CONV2_SIZE = 5CONV2_DEEP = 64#第一个全连接层的节点个数FC1_SIZE = 520 def inference(input_tensor, train, regularizer, avg_class, reuse = True):    '''卷积神经网络前向传播,参数train用于区分训练过程和测试过程'''       #第一层,卷积层    with tf.variable_scope('layer1-conv1', reuse =reuse):        #权重矩阵        conv1_weights = tf.get_variable("weight", [CONV1_SIZE, CONV1_SIZE, NUM_CHANNELS, CONV1_DEEP],                                        initializer = tf.truncated_normal_initializer(stddev = 0.1))        #偏置矩阵        conv1_biases = tf.get_variable("bias", [CONV1_DEEP], initializer = tf.constant_initializer(0.0))               #过滤器x,y向步长均为1 (strides的第2,3个元素)。strides的第1,第4个元素只能为1        #全0填充 padding='SAME';不填充则用padding='VALID'        conv1 = tf.nn.conv2d(input_tensor, conv1_weights, strides =[1,1,1,1], padding='SAME')        #添加偏置        conv1 = tf.nn.bias_add(conv1, conv1_biases)        #ReLU引入非线性        relu1 = tf.nn.relu(conv1)        #输出矩阵shape 为 IMAGE_HEIGHTXIMAGE_WIDTHXCONV1_DEEP, 即28x28x32          #第二层,池化层(下采样)    with tf.variable_scope('layer2-pool1', reuse =reuse):        #使用最大池化层,过滤器的尺寸为2X2, 过滤器x,y向步长均为2,全零填充        pool1 = tf.nn.max_pool(relu1, ksize=(1,2,2,1), strides =[1,2,2,1], padding='SAME')#输出矩阵shape 为 14x14x32$$$$           #第三层,卷积层    with tf.variable_scope('layer3-conv2', reuse =reuse):        #权重矩阵        conv2_weights = tf.get_variable("weight", [CONV2_SIZE, CONV2_SIZE, CONV1_DEEP, CONV2_DEEP],                                        initializer = tf.truncated_normal_initializer(stddev = 0.1))        #偏置矩阵        conv2_biases = tf.get_variable("bias", [CONV2_DEEP], initializer = tf.constant_initializer(0.0))               #过滤器x,y向步长均为1 (strides的第2,3个元素)。strides的第1,第4个元素只能为1        #全0填充 padding='SAME';不填充则用padding='VALID'        conv2 = tf.nn.conv2d(pool1, conv2_weights, strides =[1,1,1,1], padding='SAME')        #添加偏置        conv2 = tf.nn.bias_add(conv2, conv2_biases)        #ReLU引入非线性        relu2 = tf.nn.relu(conv2)        #输出矩阵shape ,我们仅改变了深度,即14X14X64             #第四层,池化层(下采样)    with tf.variable_scope('layer4-pool2', reuse =reuse):         #使用最大池化层,过滤器的尺寸为2X2, 过滤器x,y向步长均为2,全零填充        pool2 = tf.nn.max_pool(relu2, ksize=(1,2,2,1), strides =[1,2,2,1], padding='SAME')#        #输出矩阵shape 为 7X7X64
    #第五层,(第1个)全连接层    #全连接层的输入格式为向量,张量需要展平。    #get_shape().as_list()可以计算(最后一个池化层)各维度大小,返回到一个列表    # 因每层神经网络的输入输出都为一个batch(一同训练的一批样本)的张量,pool_shape[0]为一个batch中样本的个数    #而inference函数前面都不用考虑batch    pool_shape = pool2.get_shape().as_list()    nodes =  pool_shape[1] * pool_shape[2] * pool_shape[3] #得到第一个全连接层的输入节点数    #注意    #pool_shape[0]这里不如用-1 。    # -1(自动调整)可以适应 placeholder中batch size 为None,测试集,验证集batch不匹配的问题(测试集batch为batchsize,验证集为整个集样本数)    #reshaped = tf.reshape(pool2,[pool_shape[0], nodes]) # X的第一维size传入None时会报错    reshaped = tf.reshape(pool2,[-1, nodes]) #注意这里的中括号,与numpy.reshape()不同    #这里引入了dropout的概念。dropout在训练时会随机将部分节点的输出改为0。dropout可以避免过拟合(overfitting)问题    #dropout 一般只在全连接层使用    with tf.variable_scope('layer5_fc1', reuse =reuse):        #权重        fc1_weights =  tf.get_variable("weight", [nodes, FC1_SIZE, ],                                       initializer = tf.truncated_normal_initializer(stddev = 0.1))               #只有全连接层的权重需要加入正则化,也是为了避免过拟合        if regularizer != None:            tf.add_to_collection('losses', regularizer(fc1_weights))                   #偏置        fc1_biases = tf.get_variable('bias', [FC1_SIZE], initializer=tf.constant_initializer(0.0))        #平均移动模型        if avg_class != None:            fc1_weights = avg_class.average(fc1_weights)            fc1_biases = avg_class.average(fc1_biases)        #加权        fc1 = tf.matmul(reshaped, fc1_weights) + fc1_biases        #ReLU激活        fc1 = tf.nn.relu(fc1)        if train:            fc1 = tf.nn.dropout(fc1, 0.5) #每个神经元 以 50% 概率被抑制          #第六层,(第2个,最后一个)全连接层    #输出长度为10 (因为只有十个类别)的向量。之后无需引入激活函数。输出通过Softmax()就得到了分类的结果    with tf.variable_scope('layer6_fc2', reuse =reuse):        #权重        fc2_weights =  tf.get_variable("weight", [FC1_SIZE, NUM_LABELS, ],                                       initializer = tf.truncated_normal_initializer(stddev = 0.1))               #只有全连接层的权重需要加入正则化,也是为了避免过拟合        if regularizer is not None:            tf.add_to_collection('losses', regularizer(fc2_weights))                   #偏置        fc2_biases = tf.get_variable('bias', [NUM_LABELS], initializer=tf.constant_initializer(0.0))               #平均移动模型        if avg_class is not None:            fc2_weights = avg_class.average(fc2_weights)            fc2_biases = avg_class.average(fc2_biases)                   #加权        logit = tf.matmul(fc1, fc2_weights) + fc2_biases     #返回最后一层全连接层的输出    return logit    # 通过tf.argmax()即可得到的分类结果

由于此卷积神经网络的待训练参数比较多,所以训练(卷积神经网络的训练下篇会介绍)起来比较慢。若是电脑性能不太好,可以适当减少参数数量,比如可以增大卷积层和池化层的过滤器的尺寸和移动步长,以及减少全连接层的节点数。