个人总结:CNN、tf.nn.conv2d(卷积)与 tf.nn.conv2d_transpose(反卷积)

为什么出现了卷积神经网络

假如有一幅1000x1000的图像,如果把整副图片作为特征进行输入的话,向量的长度为1000000。假设使用普通的NN来对图像进行训练。

假设隐藏层的神经元个数和输入一样,也是1000000;那么,从输入层到隐藏层的参数数据量有10^6 x 10^6 = 10^12,实在是太高了。

局部连接

局部感受野:一般认为人对外界的认知是从局部到全局。因此每个神经元没有必要对全局图像进行感知,只需对局部进行感知,然后在更高层将局部的信息综合起来得到了全局的信息(更高层指的是后面提到的多层卷积)。

比如每个神经元只和10x10个像素值连接。左图为原始,右图为新连接方式。

这样使得参数量变为了1000000x100,变为了原来的万分之一。而这10x10=100个参数,其实就对应的是卷积核的参数,对他们对应相乘相加就是卷积操作。

权值共享

如果刚刚这1000000个输入神经元对应的100个参数都是相等的,那最后总的参数量就变成了100。将这个100(10x10)个参数看成特征提取的方式,与位置无关。隐含的原理是“图像的一部分的统计特征与其他部分是一样的”。意味着在这一部分学习特征的方式也能用到另一部分上。这就是CNN的思想,这个就是最终的卷积核。

每个卷积都是一种特征提取方式,就像一个筛子,将图像中符合条件的(激活值越大越符合条件)的部分筛选出来。

 

多卷积核

上述提到的只是一个10x10的卷积核,提取的只是一种特征,而不同的卷积核可以学到不同的特征。比如两个卷积核就可以生成两种图像,可以看做不同的通道。开始提到了卷积核的数量,也就是卷积核通道数。不同的卷积核可以提取不同的特征,一个卷积核只能提取一种特征,32个卷积核就可以提取32种特征。例如卷积核的维度由 3x3x5x1变为3x3x5x32。最后就输出了32个通道。

多卷积层

实际应用中,往往使用多层卷积,然后再使用全连接层进行训练,多层卷积的目的是一层卷积学到的特征往往是局部的,而层数越高,学到的特征就越全局化。

池化

一个96x96的图像,如果用一个8x8大小的卷积核,每一个特征的维度为(96-8+1)x(96-8+1)(假设使用VALID,步长为1)。定义400个特征(通道),最后的维度即为7921x400=3168400 大小的向量。最后再使用全连接进行分类的话,最后是三百万的卷积特征输入,由于维度太高十分容易出现过拟合。这时就需要用到池化。

对不同位置的特征进行聚合统计。例如可以计算一个区域上某个特定特征的平均值(average_pooling),或者最大值(max_pooling)。最大值池化是最经常使用的池化方式,选取区域的最大值能够很好地保持原图的特征。在这一步操作过后不仅能够得到低得多的维度,还会增强泛化性能。

池化的做法压缩了特征矩阵,简化了接下来的计算。池化在图像识别中应用较多,但在一些网络模型的应用比如图像重建等并没有采用池化。

通过卷积和池化就分别进行了特征提取以及降维的目的。

如何应对CNN中的过拟合的问题?

  1. 添加更多数据
  2. 使用数据增强(旋转图像、放大图像、添加颜色滤波器等)
  3. 简化模型,降低模型复杂度,使用泛化性能更佳的模型结构
  4. 添加正则化(L1/L2正则化)
  5. dropout超参数
  6. 提前终止训练

Dropout为什么需要缩放?

因为训练的时候随机丢弃了一些神经元,每次迭代只更新一部分参数,但是预测的时候就没办法随机丢弃了。如果丢弃一些神经元,会带来结果不稳定的问题,也就是给定一个测试数据,有时输出a有时输出b,结果不稳定。那么一种补偿方案就是每个神经元的权重都乘以一个p,这样在“总体上”使得测试数据和训练数据是大致一样的。比如一个神经元的输出是x,那么在训练的时候它有p的概率参与训练,1-p的概率丢弃(所以也可以选择在输出除以1-p扩大,后面就不需要乘了),那么它的输出期望为px+(1-p)*0=px。所以在预测的时候,将使用所有的神经元,把这些神经元的权重乘以p可以得到同样的期望。

官网中对于卷积tf.nn.conv2d的描述

tf.nn.conv2d(
    input,
    filter,
    strides,
    padding,
    use_cudnn_on_gpu=True,
    data_format='NHWC',
    dilations=[1, 1, 1, 1],
    name=None
)

首先看看input和filter。假设input的size为[1x7x7x3],1为batch_size的大小(一个批次中有多少张图或者数据),7x7为图像的分辨率,3为图像的通道数量。所以为batch_size x map_size x map_size x channels。

而filter,假设为[3x3x3x1],前面的3x3为卷积核的kernel_size,第3个3为卷积核的通道数,这个通道数需要和输入的数据通道数一致,1为输出的通道数,也是卷积核的数量。所以为 kernel_size x kernel_size x input_channels x output_channels。

输出的大小还需要看步长strides的大小,通常strides的大小为[1 x step_size x step_size x 1];以及padding填充方式。两个step_size分别指的横和竖时的步长,一般设为一致。

padding的“VALID”指的是不进行填充:

“SAME”指的是用0对两边进行填充,可能出现不对称的情况,如果两边都补0的数量为奇数,则再往右边补0:

当步长为1:

“SAME”:输出的大小和输入大小一致。这是因为进行填充后,卷积核的窗可以滑出输入的map。

一般计算方法:

“SAME”:输出的大小会变为

out_height = ceil(float(in_height) / float(strides[1]))
out_width  = ceil(float(in_width) / float(strides[2]))

“VALID”:输出的大小会变为

out_height = ceil(float(in_height - filter_height + 1) / float(strides[1]))
out_width  = ceil(float(in_width - filter_width + 1) / float(strides[2]))

一般来说会用步长为1同时填充方式为SAME使用到图像的所有信息,但是这样导致的结果是分辨率不会下降。所以运用池化降低分辨率(一般是max_pooling,也有average_pooling),去掉padding产生的信息。

 

对于反卷积tf.nn.conv2d_transpose的描述

tf.nn.conv2d_transpose(
    value,
    filter,
    output_shape,
    strides,
    padding='SAME',
    data_format='NHWC',
    name=None
)

注意要点:

关于tf.nn.conv2d:

  1. tf.nn.conv2d的input的shape,假设为[A, B, C, D]。那filter的shape一定为[K, K, D, E],其中A为batch_size, D为输入的channel,K为感受野大小,E为输出的channel。
  2. strides如果为[1, 1, 1, 1]。(strides如果改变的话只能改中间两个数,例如改为[1, 2, 2, 1]此时strides为2)。同时padding为same的时候输出的大小不发生改变。(padding为same指的是在输入维度不够的情况下进行补0,若为valid的话在输入维度不够的情况下将未卷积的部分直接舍弃。更详细的内容请翻看其他博客,这里不再赘述。)
  3. dilation指的是空洞卷积,默认为1。关于空洞卷积可以查看知乎上大佬的https://www.zhihu.com/question/54149221/answer/192025860 我认为还是比较容易理解。

关于tf.nn.conv2d_transpose:

  1. 这也是想写这篇博文的原因。提醒自己不要再犯相同的错误。conv2d_transpose顾名思义其实本质上是转置卷积,所以它和卷积一定有一些细微的差别。在tensorflow中,若value为[A, B, C, D],D为输入的channel。则它的filter为[A, F, E, D],其中D为输入channel,此时与前面conv2d就有了差别,这个地方就体现了所谓的transpose。E为output_shape所决定的输出的channel,F与strides有关。
  2. 为什么一定要有output_channel呢。这是因为假如卷积之前我的两个输入的维度分别为[batch_size, 10, 10, 3], [batch_size, 9, 9, 3], 我采用padding=same, strides = [1, 2, 2, 1]的方式, 卷积后得到的维度竟然是相同的[batch_size, 5, 5, output_channels]!这就是为什么我们在反卷积的时候需要输入我们想要得到的维度output_shape
  3. 一定要区分清楚卷积tf.nn.conv2d与反卷积tf.nn.conv2d_transpose的strides,它和卷积的strides是相反的,怎么理解呢?可以这么说,卷积的strides是在前向传播时使用,而反卷积的strides在反向传播时使用。假设A[batch_size, 32, 32, chan_1]通过stride[1, 2, 2, 1], padding = SAME, 卷积的结果变为了B[batch_size, 16, 16, chan_2]. 这个时候前向传播,卷积的stride为2. 当使用反卷积时,若A[batch_size, 32, 32, chan_1],stride[1, 2, 2, 1],则反卷积的结果变成了C[batch_size, 64, 64, chan_2],这是因为这个时候我们要从反向传播的角度去看,从C[batch_size, 64, 64, chan_2]采用stride[1, 2, 2, 1]卷积为了A[batch_size, 32, 32, chan_1]!
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值