卷积神经网络CNN理解

虽然在机器学习和深度学习这块投入了不少精力,可是近一段时间在看论文的过程中,对一些细节方面和算法深层次的原因方面总感觉有些不得其意,所以回过头来,将基础部分查漏补缺。

首先,我们解释一下为什么卷积神经网络要比传统的神经网络要好。图像是由一个个像素点组成的,以灰色28*28大小的图像为例,如果使用全连接的网络结构,网络中的神经元与相邻层上的每个神经元均连接,如下图所示,那么输入层就会有28*28=784个神经元,假若隐含层采用15个神经元,大约会需要784*15=11760个权重,加上输出层的10个神经元,整个三层神经网络总共需要784*15*10=117600个权重(除去偏置项),参数是如此巨大,计算复杂,使得调参变得很困难,所以不建议使用传统神经网络。而CNN采用了局部连接,权值共享和下采样三种方法减少参数量。

卷积神经网络(CNN)是一种多层的监督学习神经网路,包括卷积层(Conv)、池化层(Pooling),全连接层(FC),其中卷积层和池化层是实现卷积网络特征提取功能的核心模块。通常情况下,CNN的常见架构模式为:

INPUT-->[[Conv]*N-->Pool?]*M-->[FC]*K

也就是N个卷积层叠加,然后可选叠加一个Pooling层,重复这个子结构M次,最后叠加K个全连接层。

卷积神经网络包含了一个由卷积层和池化层构成的特征提取器,在卷积层中,一个神经元只与部分邻层神经元相连【局部连接】。在CNN中的卷积层中包含多个feature map, 每个feature map 由一些矩形排列的神经元组成(例如某个feature map的大小为m*n,那么该map共有m*n个神经元),同一个feature map的神经元通共享权值(卷积核)【权值共享】。共享权值带来的直接好处就是减少网络各层之间的连接,同时降低过拟合的风险。子采样也是池化,通常有均值子采样(mean pooling)和最大值子采样(max pooling)两种形式,通过池化来降低卷积层输出的特征向量【池化】。卷积和池化大大简化了模型复杂度,减少了模型的参数。

卷积运算

对于一个尺寸大小为N*N的feature map,如果步幅大小为S,filter的尺寸大小为F*F,Zero Padding的数量是P,那么经过卷积运算得到M*M大小的feature map,其中。为了更好的描述卷积计算过程,我们假设输入map为X,filter为W,输出的map为A,用表示X中第i行第j列的元素,用表示W中第m行第n列的权重,用表示filter的偏置项,用表示A中第p行第q列中的元素,f为激活函数,则计算卷积的公式为。举一个小例子,设有一个5*5的feature map,使用一个3*3的filter进行卷积,步幅为S=1,无Zero Padding,那么就得到一个3*3的feature map, 下面的动图很好的展示了计算过程。

前面讲的是深度为1的卷积层的计算方法,如果深度大于1,卷积的运算应该是什么样的?我也一直都很纠结这个问题,其实只要把上面的公式进行扩展一下即可。,其中D是深度。每个卷积层有多个filter,每个filter和原始图像进行卷积后,就可以得到一个feature map,卷积后的feature map的深度和卷积层的filter的个数是相同的。下面的动图展示了包含两个filter的卷积层的计算。输入map为7*7*3,步幅S=2, 在输入元素的周围补了一圈0,P=1,经过两个3*3*3filter的卷积,得到了3*3*2的输出map,输出map的深度是和卷积核的个数是一致的。

Tensorflow代码实现

tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None, name=None)

函数作用:对输入数据进行二维卷积操作

参数说明:

  • input:输入,A 4-D tensor,shape为[batch_size, input_height, input_width, input_channels],数据类型必须为float32或者float64
  • filter: 卷积核,A 4-D tensor,shape为[filter_height, filter_width, input_channels, output_channels], 和input有相同的数据类型
  • strides: 步长,一个长度为4的一维列表,每一位对应input中每一位对应的移动步长,在使用中,由于input的第一维和第四维不参与卷积运算,所以strides一般为[1, X, X, 1]
  • padding: 填充方式,一个字符串,取值‘SAME’或者‘VALID’. 当padding = ‘SAME’时,tensorflow会自动对原图像进行补零操作,从而使得输入和输出的图像大小一致;当padding = ‘VALID’时, 采用不填充的方式,会根据计算公式缩小原图像的大小,计算公式为(W-F+2P)/S+1, W是图像大小,F为卷积核大小,P是填充数量,S是步长。
  • use_cudnn_on_gpu=None : 可选布尔值,默认为True

池化

池化层的主要目的是通过降采样的方式,在不影响图像质量的情况下,压缩图片,减少参数。子采样有两种形式,一种是均值子采样,即是对一个区域里的元素求平均值;另一种就是最大值采样,即是在一个区域中寻找最大值,如下图所示。

# 平均池化
tf.nn.avg_pool(value, ksize, strides, padding, name=None)
# 最大化池化
tf.nn.max_pool(value, ksize, strides, padding, name=None)

函数作用:对输入进行池化操作

参数说明:

  • value:池化输入,A 4-D tensor,shape为[batch_size, input_height, input_width, input_channels],数据类型为float32,float64,qint8,quint8,qint32
  • ksize:池化窗口大小,A 4-D tensor,一般为[1, X, X, 1]
  • strides:步长,一般为[1, X, X, 1],同上述卷积运算的strides
  • padding:填充,一个字符串取值‘SAME’或者‘VALID’,同上述卷积运算的padding

局部响应归一化LRN(Local Response Normalization)

局部归一的动机:在神经生物学中有一个概念叫做侧抑制,指的是被刺激的神经元抑制相邻神经元。归一化(normalization)的目的是“抑制”,局部响应归一化就是借鉴侧抑制的思想来实现局部抑制,尤其当我们使用ReLU时这种侧抑制很有效。

在AlexNet论文中,Hinton给出的具体的计算公式如下:

a_{x,y}^{i}表示第i个卷积核在位置(x,y)运用激活函数ReLU后的输出,n是同一个 位置上临近的kernal map 的数目,N是kernal的总数,在论文中,设置k=2,n=5,\alpha =10^{-4}\beta =0.75

tf.nn.lrn(input, depth_radius, bias, alpha, beta, name=None)

官方API给出如下解释:

The 4-D input tensor is treated as a 3-D array of 1-D vector(along the last dimension), and each vector is normalized independently. Within a given vector, each component is divided by the weighted, squared sum of inputs within depth_radius.

具体细节如下:

sqrt_sum[a, b, c, d] = sum(input[a, b, c, d-depth_radius : d + depth_radius + 1] ** 2)

output = input / (bias + alpha * sqrt_sum) ** beta

Batch Normalization

机器学习领域有个很重要的假设:IID独立同分布假设,就是假设训练数据和测试数据是满足相同分布的,这是通过训练数据获得的模型能够在测试集上取得好效果的一个基本保障。而BatchNorm的作用就是在深度神经网络训练过程中使得每一层的输入保持相同分布。

BN来自论文《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》。深度学习包含多个隐含层的网络结构,在训练过程中,因为各层参数不停在变化,所以每个隐含层都会面临covariate shift的问题,也就是在训练过程中,隐含层的输入分布会变来变去,BN的提出就是解决‘Internal Covariate Shift’的。BN通过一定的规范化手段,把每层神经网络任意神经元输入值的分布强行拉回到均值为0方差为1的标准正态分布,使得激活函数的输入值落在非线性函数对输入值敏感的区域,这样输入小的变化将会导致损失函数较大的变化,通俗点讲就是梯度变大,所以收敛速度变快,大大加快训练速度。BN的具体计算步骤如下所示:

那么如何对每个隐层神经元的激活值做BN?BN操作位于权重加权求和之后,激活函数之前,相当于在每个隐含层中加上了一层BN操作层。

# x输入,[batch_size, height, width, channel], axes表示在哪个维度上求解,是个list
batch_mean, batch_var = tf.nn.moments(x, axes, name=None, keep_dims=False)
# offset和scale一般需要训练,其中offset一般初始为0,scale一般初始为1。
# variance_epsilon 是一个很小的浮点数避免避免分母为0
tf.nn.batch_normalization(x, mean=batch_mean, variance=batch_var, offset, scale, variance_epsilon, name=None)

对某一隐含层进行BN处理,示例代码如下:

conv2_w = tf.Variable(w_initial) # 参数
hidden_layer2 = tf.matmul(hidden_layer1, conv2_w) # 加权求和
batch_mean2, batch_var2 = tf.nn.moments(hidden_layer2, [0])
offset2 = tf.Variable(tf.ones([100]))
scale2 = tf.Variable(tf.zeros([100]))
BN2 = tf.nn.batch_normalization(hidden_layer2, batch_mean2, batch_var2, offset2, scale2, epsilon) # BN处理
layer2_output = tf.nn.relu(BN2) # 激活函数

卷积神经网络的架构,可以参考我的另一篇博客,点这里,介绍了最常用的一些CNN网络架构,在这里就不在赘述。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

马鹤宁

谢谢

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值