TensorFlow学习笔记(7) - 经典CNN模型
上一节学习了 CNN 的结构 - 卷积层和池化层. 然而通过这些网络结构任意组合得到的神经网络有无限多种, 那么怎样的神经网络更能解决图像问题呢. 大神们已经总结出了几种经典的模型, 通过学习这些模型可以总结出一些 CNN 结构设计的一些模式.
LeNet-5 模型
LeNet-5 模型时 Yann LeCun 教授于1998年在论文 Gradient-based learning applied to document recognition 中提出的. 它是第一个成功应用于数字识问题的 CNN. 在 MNIST 数据集上, LeNet-5 模型可以达到99.2%的正确率.
LeNet-5模型总共有7层:
1.卷积层
- 输入层大小 32X32X1.
- 过滤器尺寸为 5X5, 深度为 6.
- 不使用全0填充
- 步长为1.
由上面数据可以看出, 输出层=32-5+1=28, 深度=6.参数数量=5X5X1X6+6=156.
输出的节点=28X28X6=4704, 与5X5=25个过滤器(卷积核)上的点连接, 连接数=4704*(25+1)=122304个连接.1是偏置项.
2.池化层
- 输入 28X28X6.
- 过滤器尺寸=2X2, 步长=2
输出矩阵大小=14X14X6.
3.卷积层
- 输入 14X14X6.
- 过滤器大小 5X5, 深度 16.
- 不使用全0填充
- 步长为1.
输出矩阵大小=10X10X16, 参数数量=5X5X6X16+16=2416, 连接数=10X10X16X(5X5+1)=41600个连接
4. 池化层
- 输入 10X10X16
- 过滤器大小 2X2
- 步长 2
输出 5X5X16.
5.全连接层/池化层
原论文中是5X5的池化层, 等同于全连接.
- 输入&输出 5X5X16
- 输出节点 120
参数数量=5X5X16X120+120=48120
6.全连接层
- 输入节点 120
- 输出节点 84
参数数量=120X84+84=10164
7.全连接层
- 输入84
- 输出 10
参数数量=84X10+10=850
到这里实际编码的时候发现对 Padding 的方式有疑惑, 后来又回去看了下总结出padding的公式:
- 输入尺寸 W, 输出尺寸 NW, 过滤器尺寸 F, 步长 S
- 如果不填充, NW = (W - F + 1) / S , 向上取整
- 如果全0填充, NW = W / S, 向上取整
- 需要 Padding 的尺寸PW = (NW - 1) X S + F - W
简单说就是如果移动步长后, 在卷积的时候如果剩下的输入数据不够过滤器的Size, 则差多少Size就填充多少.
除了 LeNet-5模型, 2012年的 ImageNet ILSVRC 图像分类大赛第一名 AlexNet 模型, 2013年 第一名 ZF Net 模型, 2014年第二名 VGGNet 模型都满足上面7层结构.
从这些模型中看出, 过滤器尺寸通常在2~3,一般不超过5,少有的一些会使用到7甚至11.
深度上, 大多都采用逐层递增或乘以2.
卷积层步长一般为1, 有的为2~3.
池化层尺寸和步长一般都为2~3.
TensorFlow 编码时, 卷积层和池化层实现和上一节学习的一样的, 需要注意的是在第二个池化结束后, 输出的是一个 Height x Width x Deep
的矩阵, 第一个全连接层输入需要的是一个向量, 所以这里需要将矩阵拉直成一个向量.
pool2.get_shape
可以得到矩阵的维度.- 每层输入输出都为1个 batch 的矩阵, 故获得的维度也包含了1个 batch 中数据的个数.
tf.reshape
可以将输出编程一个 batch 的向量.
1 | # ... 略 |
Inception-v3模型
Inception 结构和 LeNet-5结构完成不同.
在 LeNet-5 中, 不同卷积层通过串联连接在一起, 而 Inception-v3模型中的 Inception 结构是并联的方式结合在一起.
简单说就是 输入矩阵->多个尺寸的过滤器->输出的结果组合成一个更深的矩阵.多个过滤器使用1步长和0填充,就保证了输出的矩阵大小不变.
合并使用函数tf.concat
.
Inception-v3 模型总共46层, 由11个 Inception 模块组成, Inception-v3一共有96个卷积层. 如果按照之前卷积层的实现代码, 一个 Inception 结构就需要至少5行代码. 总共需要480行代码实现.
为了简化代码, TensorFlow 提供了TensorF-Slim
工具来更加简洁的实现一个卷积层.
以下为普通实现和 Slim 实现一个卷积层的代码:
1 | # Normal |
Slim 还提供 slim.arg_scope
方法 设置指定默认参数, 如步长和填充, 使用此方法, 在其上下文环境中的 conv2d
参数默认值会与之相同, 进一步简化代码.
1 | with slim.arg_scope([slim.conv2d, slim.max_pool2d, slim.avg_pool2d], stride=1, padding='SAME'): |