TensorFlow 入门 4--卷积神经网络介绍

来源:互联网 发布:程序员改bug 动态图 编辑:程序博客网 时间:2024/06/05 12:43

如下所示是一个用于图像分类问题的一种卷积神经网络架构图。
这里写图片描述
在卷积神经网络的前几层中,每一层的节点都被组织成一个三维矩阵。比如处理Cifar-10 数据集中的图片时,可以将输入层组织成一个32x32泣的三维矩阵。图中虚线部分展示了卷积神经网络的一个连接示意图,从图中可以看出卷积神经网络中前几层中每一个节点只和上一层中部分的节点相连。一个卷积神经网络主要由以下5 种结构组成:

  1. 输入层。输入层是整个神经网络的输入,在处理图像的卷积神经网络中,它一般代表了一张图片的像素矩阵。比如在图中, 最左侧的三维矩阵就可以代表一张图片。其中三维矩阵的长和宽代表了图像的大小,而三维矩阵的深度代表了图像的色彩通道Cchannel ) 。比如黑白图片的深度为1,而在RGB 色彩模式下,图像的深度为3 。从输入层开始, 卷积神经网络通过不同的神经网络结构将上一层的三维矩阵转化为下一层的三维矩阵,直到最后的全连接层。
  2. 卷积层。从名字就可以看出,卷积层是一个卷积神经网络中最为重要的部分。和传统全连接层不同,卷积层中每一个节点的输入只是上一层神经网络的一小块,这个小块常用的大小有3X3 或者5X 5。卷积层试图将神经网络中的每一小块进行更加深入地分析从而得到抽象程度更高的特征。一般来说,通过卷积层处理过的节点矩阵会变得更深,所以在图中可以看到经过卷积层之后的节点矩阵的深度会增加。
  3. 池化层C Pooling) 。池化层神经网络不会改变三维矩阵的深度,但是它可以缩小矩阵的大小。池化操作可以认为是将一张分辨率较高的图片转化为分辨率较低的图片。通过地化层,可以进一步缩小最后全连接层中节点的个数,从而达到减少整个神经网络中参数的目的。
  4. 全连接层。如图所示,在经过多轮卷积层和池化层的处理之后,在卷积神经网络的最后一般会是由1到2个全连接层来给出最后的分类结果。经过几轮卷积层和池化层的处理之后,可以认为图像中的信息已经被抽象成了信息含量更高的特征。我们可以将卷积层和池化层看成自动图像特征提取的过程。在特征提取完成之后,仍然需要使用全连接层来完成分类任务。
  5. Softmax 层。Softmax层主要用于分类问题。通过Softmax层,可以得到当前样例属于不同种类的概率分布情况。

卷积神经网络常用结构

上文已经大致介绍了卷积层和池化层的概念,在本节中将具体介绍这两种网络结构。在下面的两个小节中将分别介绍卷积层和池化层的网络结构以及前向传播的过程, 并通过TensorFlow 实现这些网络结构。[参考:深度学习之卷积神经网络(CNN)的模型结构]

卷积层

下图显示了卷积层神经网络结构中最重要的部分,这个部分被称之为过滤器( filter) 或者内核(kernel) 。因为TensorFlow 文档中将这个结构称之为过滤器( filter ) ,所以在本文中将统称这个结构为过滤器。如图所示,过滤器可以将当前层神经网络上的一个子节点矩阵转化为下一层神经网络上的一个单位节点矩阵。单位节点矩阵指的是一个长和宽都为1, 但深度不限的节点矩阵。
卷积层过滤器(filter)结构示意
在一个卷积层中,过滤器所处理的节点矩阵的长和宽都是由人工指定的,这个节点矩阵的尺寸也被称之为过滤器的尺寸。常用的过滤器尺寸有3X3 或5X5 。因为过滤器处理的矩阵深度和当前层神经网络节点矩阵的深度是一致的,所以虽然节点矩阵是三维的,但过滤器的尺寸只需要指定两个维度。过滤器中另外一个需要人工指定的设置是处理得到的单位节点矩阵的深度,这个设置称为过滤器的深度。注意过滤器的尺寸指的是一个过滤器输入节点矩阵的大小,而深度指的是输出单位节点矩阵的深度。如图所示,左侧小矩阵的尺寸为过滤器的尺寸,而右侧单位矩阵的深度为过滤器的深度。
这里写图片描述
TensorFlow 对卷积神经网络提供了非常好的支持,下面的程序实现了一个卷积层的前向传播过程。从以下代码可以看出,通过TensorFlow 实现卷积层是非常方便的。

#通过tf.get_variable 的方式创建过滤器的权重变量和偏置项变量。上面介绍了卷积层#的参数个数只和过滤器的尺寸、深度以及当前层节点矩阵的深度有关,所以这里声明的参数变# 量是一个四维矩阵,前面两个维度代表了过滤器的尺寸,第三个维度表示当前层的深度,第四#个维度表示过滤器的深度。filter_weights = tf.get_variable(                    "weights",                     shape=[5,5,3,16],                    initializer = tf.truncated_initializer(stddv=0.1))#和卷积层的权重类似, 当前层矩阵上不同位置的偏置项也是共享的,所以总共有下一层深度个不#同的偏置项。本样例代码中16 为过滤器的深度,也是神经网络中下一层节点矩阵的深度。filter_biases = tf.get_variable(                    "biases",                    shape=[16],                    initializer = tf.constant_initializer(0.1))# tf.nn.conv2d提供了一个非常方便的函数来实现卷积层前向传播的算法。这个函数的第一个输#入为当前层的节点矩阵。注意这个矩阵是一个四维矩阵,后面三个维度对应一个节点矩阵,第一#维对应一个输入batch,比如在输入层,input[0, : , : , : ]表示第一张图片, input[1 ,:,:, :]#表示第二张图片,以此类推。tf.nn.conv2d第二个参数提供了卷积层的权重,第三个参数为不#同维度上的步长。虽然第三个参数提供的是一个长度为4 的数组,但是第一维和最后一维的数字#要求一定是1。这是因为卷积层的步长只对矩阵的长和宽有效。最后一个参数是填充(padding)#的方法, TensorFlow 中提供SAME或是VALID 两种选择。其中S血但表示添加全0 填充,#"VALID" 表示不添加conv = tf.nn.conv2d(input, filter_weight,                 strides=[1,1,1,1], padding='SAME')# tf.nn.bias_add 提供了一个方便的函数给每一个节点加上偏置项。注意这里不能直接使用加#法,因为矩阵上不同位置上的节点都需要加上同样的偏置项。虽然下一层神经网络的大小为2x2#但是偏置项只有一个数(因为深度为1 ), 而2 x2 矩阵中的每一个值都需#要加上这个偏置项。bias = tf.nn.bias_add(conv, biases)#将计算的结果通过ReLU激活函数完成去线性化actived_conv = tf.nn.relu(bias) 

池化层

池化层可以非常有效地缩小矩阵的尺寸,从而减少最后全连接层中的参数。使用池化层既可以加快计算速度也有防止过拟合问题的作用。
池化层前向传播的过程也是通过移动一个类似过滤器的结构完成的。不过池化层过滤器中的计算不是节点的加权和,而是采用更加简单的最大值或者平均值运算。使用最大值操作的池化层被称之为最大池化层(max pooling) ,这是被使用得最多的池化层结构。使用平均值操作的池化层被称之为平均池化层( average pooling) 。其他池化层在实践中使用的比较少。
与卷积层的过滤器类似,池化层的过滤器也需要人工设定过滤器的尺寸、是否使用全0 填充以及过滤器移动的步长等设置,而且这些设置的意义也是一样的。卷积层和池化层中过滤器移动的方式是相似的,唯一的区别在于卷积层使用的过滤器是横跨整个深度的,而池化层使用的过滤器只影响一个深度上的节点。所以池化层的过滤器除了在长和宽两个维度移动之外,它还需要在深度这个维度移动。下图展示了一个最大地化层前向传播计
算过程。

3X3 X 2 节点矩阵经过全0 填充且步长为2 的最大池化层前向传播过程示意图。
3X3X2 节点矩阵经过全0 填充且步长为2 的最大池化层前向传播过程示意图。
下面的TensorFlow 程序实现了最大池化层的前向传播算法。

#tf.nn.max_pool 实现了最大油化层的前向传播过程,它的参数和tf.nn.conv2d函数类似。#ksize 提供了过滤器的尺寸, strides提供了步长信息, padding提供了是否使用全0填充。pool = tf.nn.max_pool(actived_conv, ksize=[1,3,3,1],                    strides=[1,2,2,1],                    padding="SAME")

对比池化层和卷积层前向传播在TensorFlow 中的实现,可以发现函数的参数形式是相似的。在tf.nn.max_pool 函数中,首先需要传入当前层的节点矩阵,这个矩阵是一个四维矩阵,格式和tf.nn.conv2d 函数中的第一个参数一致。第二个参数为过滤器的尺寸。虽然给出的是一个长度为4 的一维数组,但是这个数组的第一个和最后一个数必须为1。这意味着池化层的过滤器是不可以跨不同输入样例或者节点矩阵深度的。在实际应用中使用得最多的池化层过滤器尺寸为[1 ,2 ,2 , 1 ]或者[1 , 3 , 3 , 1]。
tf.nn.max_pool 函数的第三个参数为步长,它和tf.nn.conv2d 函数中步长的意义是一样的,而且第一维和最后一维也只能为1 。这意味着在TensorFlow 中,池化层不能减少节点矩阵的深度或者输入样例的个数。tf.nn.max_pool 函数的最后一个参数指定了是否使用全0填充。这个参数也只有两种取值一VALID 或者SAME,其中V丛ID 表示不使用全0 填充, SAME 表示使用全0 填充。TensorFlow 还提供了tf.nn.avg_pool 来实现平均池化层。tf.nn.avg_pool 函数的调用格式和tf.nn.max_pool 函数是一致的。

经典卷积网络模型

[参考:详解CNN五大经典模型:Lenet,Alexnet,Googlenet,VGG,DRL]

LeNet-5

import tensorflow as tfINPUT_NODES = 784OUTPUT_NODES = 10def get_weights_variables(shape, regularize = None):    w = tf.get_variable("weights", shape=shape, initializer=tf.truncated_normal_initializer(stddev=0.1), dtype=tf.float32)    if regularize is not None:        tf.add_to_collection("loss", regularize(w))    return wdef get_biases_variables(shape):    return tf.get_variable("biases", initializer=tf.constant_initializer(0), shape=shape, dtype=tf.float32)N_C_0 = 1F_1 = 5N_C_1 = 32S_1 = 1MAX_POOL_1 = 2F_2 = 5N_C_2 = 64S_2 = 1MAX_POOL_2 = 2FC_3_NODES = 120FC_4_NODES = 84FC_5_NODES = OUTPUT_NODESdef FP_LeNet_5(x, regularize = None, reuse = False, dropout = True):    '''这里实现LeNet-5模型,总共有7层    input layer:image shape = [m, 28,28,1]    1th layer(conv): filter = [5,5,1,32], padding = "VALID", strides = [1,1,1,1]    2th layer(pool): filter = [1,2,2,1], strides=[1,2,2,1]    3th layer(conv): filter = [5,5,32,64], padding = "VALID", strides=[1,1,1,1]    4th layer(pool): filter = [1,2,2,1], strides=[1,2,2,1]    5th layer(FC): nodes = 120    6th layer(FC): nodes = 84    7th layer(FC): nodes = 10    dropout : 用来设置是否使用dropout正则化    '''    # 1th layer    with tf.variable_scope("conv_1", reuse=reuse):        filter_w = get_weights_variables([F_1, F_1, N_C_0, N_C_1], regularize=regularize)        filter_b = get_biases_variables([N_C_1])        conv_1 = tf.nn.conv2d(input=x, filter=filter_w, strides=[1,S_1,S_1,1], padding="VALID")        add_bias = tf.nn.bias_add(conv_1, filter_b)        actived_conv_1 = tf.nn.relu(add_bias)    with tf.variable_scope("pool_1", reuse=reuse):        actived_pool_1 = tf.nn.max_pool(actived_conv_1,                                         ksize=[1,MAX_POOL_1,MAX_POOL_1,1],                                         strides=[1,MAX_POOL_1,MAX_POOL_1,1],                                        padding="VALID")    with tf.variable_scope("conv_2", reuse=reuse):        filter_w = get_weights_variables([F_2,F_2,N_C_1,N_C_2], regularize=regularize)        filter_b = get_biases_variables([N_C_2])        conv_2 = tf.nn.conv2d(input=actived_pool_1, filter=filter_w, strides=[1,S_2,S_2,1], padding="VALID")        add_bias = tf.nn.bias_add(conv_2, filter_b)        actived_conv_2 = tf.nn.relu(add_bias)    with tf.variable_scope("pool_2", reuse=reuse):        actived_pool_2 = tf.nn.max_pool(actived_conv_2,                                         ksize=[1,MAX_POOL_2,MAX_POOL_2,1],                                         strides=[1,MAX_POOL_2,MAX_POOL_2,1],                                        padding="VALID")    with tf.variable_scope("FC_3", reuse=reuse):        pool_2_shape = actived_pool_2.get_shape().as_list()        actived_pool_2 = tf.reshape(actived_pool_2, [pool_2_shape[0], -1])        w = get_weights_variables([actived_pool_2.get_shape().as_list()[1], FC_3_NODES], regularize=regularize)        b = get_biases_variables([FC_3_NODES])        actived_fc_3 = tf.nn.relu(tf.matmul(actived_pool_2, w) + b)        if dropout :             actived_fc_3 = tf.nn.dropout(actived_fc_3, 0.7)    with tf.variable_scope("FC_4", reuse=reuse):        w = get_weights_variables([FC_3_NODES, FC_4_NODES], regularize=regularize)        b = get_biases_variables([FC_4_NODES])        actived_fc_4 = tf.nn.relu(tf.matmul(actived_fc_3, w) + b)        if dropout :             actived_fc_4 = tf.nn.dropout(actived_fc_4, 0.7)    with tf.variable_scope("FC_5", reuse=reuse):        w = get_weights_variables([FC_4_NODES, FC_5_NODES], regularize=regularize)        b = get_biases_variables([FC_5_NODES])        output = tf.matmul(actived_fc_4, w) + b     return output

前向传播

深度学习之DNN与前向传播算法
深度学习之DNN与反向传播算法
干货 | 深度学习之损失函数与激活函数的选择
干货 | 深度学习之DNN的多种正则化方式
深度学习之卷积神经网络(CNN)的前向传播算法详解

反向传播

干货 | 深度学习之CNN反向传播算法详解

原创粉丝点击