• 背景
  • 问题说明
  • 分析
    • LeNet5参数
    • MNIST程序参数
  • 遗留问题
  • 小结

背景

之前博文中关于CNN的模型训练功能上是能实现,但是研究CNN模型内部结构的时候,对各个权重系数ww,偏差bb的shape还是存在疑惑,为什么要取1024,为什么取7*7*64,最近找到了一些相关资料,对这个问题有了新的理解,下面和大家分享一下。


问题说明

# Input Layer
x = tf.placeholder('float',[None,784])
y_ = tf.placeholder('float',[None,10])
# First Layer
W_conv1 = weight_variable([5,5,1,32])
b_conv1 = bias_variable([32])x_image = tf.reshape(x,[-1,28,28,1])
h_conv1 = tf.nn.relu(conv2d(x_image,W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)
# Second Layer
W_conv2 = weight_variable([5,5,32,64])
b_conv2 = bias_variable([64])h_conv2 = tf.nn.relu(conv2d(h_pool1,W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)
# Connect Layer
W_fc1 = weight_variable([7*7*64,1024])
b_fc1 = bias_variable([1024])h_pool2_flat = tf.reshape(h_pool2,[-1,7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat,W_fc1) + b_fc1)
# Dropout Layer
keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1,keep_prob)
# Output Layer
W_fc2 = weight_variable([1024,10])
b_fc2 = bias_variable([10])

这段程序,是mnist_test2.py中关于Tensorflow Graph建立的代码,里面为所有使用到的Variables都分配了指定的大小。例如x通过placeholder预留了None*784大小的shape(None会随着batch获取的数据个数而改变),第一层的卷积系数W_conv1是5*5*1*32大小的shape,后面不再赘述。
有些shape比如输入的xNone*784的784是28*28个8位灰度值的shape,y_是None*10个数字分类的shape,输出的b_fc2是对应10个输出类别的偏差。但是对于其他参数的话,我就不禁要问:①这么做是为什么呢?②如果不这么设置参数行不行呢?③如果我换一套数据集,参数还是这么设置么?

先回答第二个问题吧,不行。如果随意更换参数,换的对可能能跑通,但是影响性能,但是更多的时候会是下面的结果:


分析

先看看LeNet5

在回答第一个问题之前,建议大家阅读一下2012年NIPS上做ImageNet的文献1和解释LaNet5的CNN模型的PPT,如果时间不够用的话,也可以直接看一下qiaofangjie的中文参数解析。里面可以先对LeNet5的参数有个大概了解。下面我对照着经典的图,大体归纳一下:

CNN里的层,除了输入/输出层之外,把隐含层又分成了卷积层、池化层、以及全连接层。三者的作用及介绍也可以参考小新的猫的理解。卷积层会对原始输入数据做加权求和的处理,学过通信的同学一定对一维线性卷积里面“错位相乘相加”很熟悉(原谅我强行奶一口原来的专业),这里就是线性卷积在二维里面的推广。池化层是为了减小模型复杂度,通过降采样(又是通信里有概念我会乱讲?)缩小特征图的尺寸。全连接层是将最终高度抽象化的特征图与输出直接进行的全连接,这与入门级MNIST库的模型训练做的事儿是一样的,一个简单的全映射。
具体的看,第一个卷积层C1对32*32的输入数据(假设成一个矩阵,其中每个元素代表当前位置像素点灰度值的强弱)进行二维加权相加
yl+1m,n=∑i,jwli,jxli,j+bl+1y_{m,n}^{l + 1} = \sum\limits_{i,j}^{} {w_{i,j}^lx_{i,j}^l + b_{}^{l + 1}}
其中上标ll表示层数,下标i,ji,j表示对应的像素点位置,ww、xx、b<script type="math/tex" id="MathJax-Element-8">b</script>分别表示滤波器系数,输入值,偏差值。
(此处补零采用了VALID模式,这个问题后面会接着说)。我们假设卷积器的权重矩阵是一个5*5的,相当于对5*5的输入数据,与5*5的卷积器卷积后,得到了1*1的数值,如果是5*6的原始数据,卷积后会得到1*2的输出,所以推广到32*32的数据,输出会得到28*28的尺寸。再推广一下就会得到:

输出宽度=输入宽度-(卷积器宽度-1)
输出高度=输入高度-(卷积器高度-1)

至于为什么一幅输入会得到6组对应的卷积输出呢?这个问题对我来说还说不清楚,只能暂时理解为一幅输入的6个不同的特征(Hinton老师讲家谱树的时候就用了6个对应特征表示是英国人还是意大利,是爷爷辈儿还是孙子辈儿等),比如输入是否有圆圈形状,边缘是否明确,是否有纹理特征等。这个问题后面的话有可能会可视化验证一下,目前只能这么理解了。
接下来第二个池化层S2,对6*28*28的特征图进行下采样得到了6*14*14的池化层特征图,主要是因为其中采用每2*2的块儿进行一次容和统计,并且块与块儿直接不交叠,掐指一算还真是(28/2*28/2),池化操作并没有增加特征维度,只是单纯的下采样。
在后面的那层卷积层C3的shape也可以理解了,对14*14的特征输入,输出10*10的特征图,并且此时把6个特征抽象到了更多的16个特征。下面的S4就不说了。
看一下C5,这里又来了问题了,暂时还不能理解,为什么会出现120这个数字,暂且搁置。一般CNN中的全连接层,我见到的都是1-2层,并且两个层的shape是一致的。比如这里都是120,文献1中全是2048。

MNIST库程序

ok,以上就是对LeNet5的参数理解,下面我们来看一下MNIST程序中的各个参数。首先,建议使用如下代码,在跑通的程序中,方便查看每个变量的shape,具体代码如下:

def showSize():print "x size:",batch[0].shape#print "x_value",batch[0]print "y_ size:",batch[1].shape#print "y_example",batch[1][0]#print "y_example",batch[1][9]#print "y_example",batch[1][10]print "w_conv1 size:",W_conv1.eval().shapeprint "b_conv1 size:",b_conv1.eval().shapeprint "x_image size:",x_image.eval(feed_dict={x:batch[0]}).shapeprint "h_conv1 size:",h_conv1.eval(feed_dict={x:batch[0],y_:batch[1],keep_prob:1.0}).shapeprint "h_pool1 size:",h_pool1.eval(feed_dict={x:batch[0],y_:batch[1],keep_prob:1.0}).shapeprint "w_conv2 size:",W_conv2.eval().shapeprint "b_conv2 size:",b_conv2.eval().shapeprint "h_conv2 size:",h_conv2.eval(feed_dict={x:batch[0],y_:batch[1],keep_prob:1.0}).shapeprint "h_pool2 size:",h_pool2.eval(feed_dict={x:batch[0],y_:batch[1],keep_prob:1.0}).shapeprint "w_fc1 size:",W_fc1.eval().shapeprint "b_fc1 size:",b_fc1.eval().shapeprint "h_pool2_flat size:",h_pool2_flat.eval(feed_dict={x:batch[0],y_:batch[1],keep_prob:1.0}).shapeprint "h_fc1 size:",h_fc1.eval(feed_dict={x:batch[0],y_:batch[1],keep_prob:1.0}).shapeprint "h_fc1_drop size:",h_fc1_drop.eval(feed_dict={x:batch[0],y_:batch[1],keep_prob:1.0}).shapeprint "w_fc2 size:",W_fc2.eval().shapeprint "b_fc2 size:",b_fc2.eval().shapeprint "y_conv size:",y_conv.eval(feed_dict={x:batch[0],y_:batch[1],keep_prob:1.0}).shape


像这样插在每次循环中,然后,我们可以试运行一下看看结果

这个小函数写的时候要注意,对于Tensor类型的Variable,并没有shape这个属性,必须在每个Tensor.eval()才可以使用.shape属性,而Tensor.eval()的话,就需要feed_dict一个依赖值才可以获得tensor实例,否则Tensor对象只是一个代号而已。
在输入50个batch的时候可以看到输入,输出shape不说了,w_conv1涉及到tf.conv2d的用法,具体可以参考官方文档,这里的前两个5对应的卷及系数shape,第三个参数1表示输入通道,对应输入数据只有灰度值一个特征,或者说是对应LeNet分析中一幅图,最后一个32是输出通道数,表示输出32组特征。

稍等,对于MNIST的28*28的输入数据,经过第一层卷积C1,为何输出的h_conv1还是28*28的维度,而不是刚刚推论中的24*24呢?这我也研究了好久,原因在于tf.conv2d函数的参数指定了padding类型为“SAME”,这个参数的作用在官方文档中直接决定了输出的shape,截选如下:

这只是数值上说明了,具体的原因小新的猫的理解说的非常明白,他还对比了Matlab和Tensorflow对于Padding参数的不同选项,可以视具体的应用环境在做测试。

最后看一下全连接层的参数w_fc1,对接上面的卷基层输出,第一个参数是池化后7*7的尺寸*64组特征,第二个参数与LeNet的120一致,是输出给全连接的参数。


遗留问题

现在看来,与卷积,池化相关的shape变化应该是可以理解了,不过还有两种参数不理解:
①是卷积层的特征数,为什么LeNet里面两层卷积层的特征会是6和16,而MNIST是32和64,按道理MNIST是28*28的数据输入,还小于LeNet的32*32呢,却有着更高的特征维度。
②全连接层的参数,LeNet里面用的2个120参数的,而MNIST里面全连接却有1024个参数。
这些问题也问过Tensorflow群里群友,问到的都说是经验问题,需要自己把握,真的是这样哇?这两个参数的选择难道只是个工程经验问题嘛?我目前的理解的话只能一边继续看资料,一边工程做实验测试一下模型收敛速度啦。结合TensorBoard可视化应该后者会给我个答案。


小结

这篇文章,对CNN的网络中参数的shape做了分析,希望能对大家理解起来有所帮助,至于文中说到的两个参数遗留问题,也希望有大神能不吝指教呀。

PS:我必须要吐槽!CSDN为什么只能保存一个草稿,我写第一篇博文的时候突然觉得应该把这个问题剥离出来先讲一讲,结果之前那篇的草稿就没有了!那也是1个半小时的思路整理啊!不过也怪自己没有备份。TAT


Tensorflow的MNIST进阶教程CNN网络参数理解相关推荐

  1. PyTorch中CNN网络参数计算和模型文件大小预估

    前言 在深度学习CNN构建过程中,网络的参数量是一个需要考虑的问题.太深的网络或是太大的卷积核.太多的特征图通道数都会导致网络参数量上升.写出的模型文件也会很大.所以提前计算网络参数和预估模型文件大小 ...

  2. c++类指针赋值表达式必须是可修改的左值_C++进阶教程系列:全面理解C++中的类...

    原标题:C++进阶教程系列:全面理解C++中的类 关注Linux公社 最近刷了一些题,也面试了一些公司,把关于C++中关于类的一些概念总结了一下. 在这里也反思一下,面试前信心满满自以为什么都懂,毫无 ...

  3. AI:IPPR的数学表示-CNN结构/参数分析

    前言:CNN迎接多类的挑战 特定类型的传统PR方法特征提取的方法是固定的,模式函数的形式是固定的,在理论上产生了特定的"局限性" 的,分类准确度可以使用PAC学习理论的方法计算出来 ...

  4. TensorFlow基于cifar10数据集实现进阶的卷积网络

    TensorFlow基于cifar10数据集实现进阶的卷积网络 学习链接 CIFAR10模型及数据集介绍 综述 CIFAR10数据集介绍 CIFAR10数据集可视化 CIFAR10模型 CIFAR10 ...

  5. TensorFlow人工智能引擎入门教程之二 CNN卷积神经网络的基本定义理解。

    摘要: 这一章节我们将 tensorFlow怎么实现卷积神经网络 ,CNN ,其实CNN可以用来训练声音的,不过效果一般,所以一般都用于图像方面,因为图像像素局部共享 参数共享 ,图像金字塔法则.可以 ...

  6. TensorFlow学习笔记(二):快速理解Tutorial第一个例子-MNIST机器学习入门 标签: 机器学习SoftmaxTensorFlow教程 2016-08-02 22:12 3729人阅

    TensorFlow学习笔记(二):快速理解Tutorial第一个例子-MNIST机器学习入门 标签: 机器学习SoftmaxTensorFlow教程 2016-08-02 22:12 3729人阅读 ...

  7. 【Pytorch分布式训练】在MNIST数据集上训练一个简单CNN网络,将其改成分布式训练

    文章目录 普通单卡训练-GPU 普通单卡训练-CPU 分布式训练-GPU 分布式训练-CPU 租GPU服务器相关 以下代码示例基于:在MNIST数据集上训练一个简单CNN网络,将其改成分布式训练. 普 ...

  8. 【深度学习系列】用PaddlePaddle和Tensorflow实现经典CNN网络AlexNet

    上周我们用PaddlePaddle和Tensorflow实现了图像分类,分别用自己手写的一个简单的CNN网络simple_cnn和LeNet-5的CNN网络识别cifar-10数据集.在上周的实验表现 ...

  9. CNN网络实现手写数字(MNIST)识别 代码分析

    CNN网络实现手写数字(MNIST)识别 代码分析(自学用) Github代码源文件 本文是学习了使用Pytorch框架的CNN网络实现手写数字(MNIST)识别 #导入需要的包 import num ...

最新文章

  1. PyQ4标准输入框——QInputDialog(一)
  2. c语言创建学生成绩表,C语言创建信息链表,求助
  3. 《假如编程是魔法之零基础看得懂的Python入门教程 》——(二)魔法实习生第一步了解魔杖的使用
  4. JAVA入门级教学之(面向过程和面向对象的区别)
  5. Java hdfs连接池_Java使用连接池管理Hdfs连接
  6. 【转】8 个效果惊人的 WebGL/JavaScript 演示
  7. list选取多个元素 python_python基础篇:list列表的操作大盘点
  8. 整个线上营销_而言,最重要的是什么?
  9. 设置 Web 服务器控件颜色属性 转
  10. python 自动回收机制
  11. File Cabinet Pro for Mac(菜单栏快捷文件管理软件)
  12. windows权限了解
  13. 全球及中国在线教育行业发展态势与营利模式咨询报告2022版
  14. 一元线性回归方程的参数估计
  15. 图片上传被旋转,golang Exif 判断.jepg图片原始信息Orientation方向,并旋转,
  16. 异常与处理--python
  17. 实现短链接跳转、批量域名跳转、批量域名转发重定向的解决方案
  18. ANSYS CMD
  19. java 流水线_用工厂流水线的方式来理解 RxJava 的概念
  20. 为懒惰的你送上GTA的作弊~~

热门文章

  1. ae学习笔记2(快捷键)
  2. 文案能力应该这样练习
  3. 微信朋友圈广告投放,怎么样帮助商家实现用户转化?
  4. 2022浙江省税务系统事业单位招聘经典试题及答案
  5. 开源任务式问答机器人系列之rasa篇
  6. 贫富人家的孩子在思维方式上有差异吗?
  7. Apple 开发者账号的分类
  8. CN_@DNS@HTTP
  9. win10打印机服务器属性修改,大师教你win10如何修改打印机名称的完全处理办法...
  10. 使用USB TTL模块 为STC最小系统板下载程序的步骤