Tensorflow的MNIST进阶教程CNN网络参数理解
- 背景
- 问题说明
- 分析
- 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比如输入的x
None*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网络参数理解相关推荐
- PyTorch中CNN网络参数计算和模型文件大小预估
前言 在深度学习CNN构建过程中,网络的参数量是一个需要考虑的问题.太深的网络或是太大的卷积核.太多的特征图通道数都会导致网络参数量上升.写出的模型文件也会很大.所以提前计算网络参数和预估模型文件大小 ...
- c++类指针赋值表达式必须是可修改的左值_C++进阶教程系列:全面理解C++中的类...
原标题:C++进阶教程系列:全面理解C++中的类 关注Linux公社 最近刷了一些题,也面试了一些公司,把关于C++中关于类的一些概念总结了一下. 在这里也反思一下,面试前信心满满自以为什么都懂,毫无 ...
- AI:IPPR的数学表示-CNN结构/参数分析
前言:CNN迎接多类的挑战 特定类型的传统PR方法特征提取的方法是固定的,模式函数的形式是固定的,在理论上产生了特定的"局限性" 的,分类准确度可以使用PAC学习理论的方法计算出来 ...
- TensorFlow基于cifar10数据集实现进阶的卷积网络
TensorFlow基于cifar10数据集实现进阶的卷积网络 学习链接 CIFAR10模型及数据集介绍 综述 CIFAR10数据集介绍 CIFAR10数据集可视化 CIFAR10模型 CIFAR10 ...
- TensorFlow人工智能引擎入门教程之二 CNN卷积神经网络的基本定义理解。
摘要: 这一章节我们将 tensorFlow怎么实现卷积神经网络 ,CNN ,其实CNN可以用来训练声音的,不过效果一般,所以一般都用于图像方面,因为图像像素局部共享 参数共享 ,图像金字塔法则.可以 ...
- TensorFlow学习笔记(二):快速理解Tutorial第一个例子-MNIST机器学习入门 标签: 机器学习SoftmaxTensorFlow教程 2016-08-02 22:12 3729人阅
TensorFlow学习笔记(二):快速理解Tutorial第一个例子-MNIST机器学习入门 标签: 机器学习SoftmaxTensorFlow教程 2016-08-02 22:12 3729人阅读 ...
- 【Pytorch分布式训练】在MNIST数据集上训练一个简单CNN网络,将其改成分布式训练
文章目录 普通单卡训练-GPU 普通单卡训练-CPU 分布式训练-GPU 分布式训练-CPU 租GPU服务器相关 以下代码示例基于:在MNIST数据集上训练一个简单CNN网络,将其改成分布式训练. 普 ...
- 【深度学习系列】用PaddlePaddle和Tensorflow实现经典CNN网络AlexNet
上周我们用PaddlePaddle和Tensorflow实现了图像分类,分别用自己手写的一个简单的CNN网络simple_cnn和LeNet-5的CNN网络识别cifar-10数据集.在上周的实验表现 ...
- CNN网络实现手写数字(MNIST)识别 代码分析
CNN网络实现手写数字(MNIST)识别 代码分析(自学用) Github代码源文件 本文是学习了使用Pytorch框架的CNN网络实现手写数字(MNIST)识别 #导入需要的包 import num ...
最新文章
- PyQ4标准输入框——QInputDialog(一)
- c语言创建学生成绩表,C语言创建信息链表,求助
- 《假如编程是魔法之零基础看得懂的Python入门教程 》——(二)魔法实习生第一步了解魔杖的使用
- JAVA入门级教学之(面向过程和面向对象的区别)
- Java hdfs连接池_Java使用连接池管理Hdfs连接
- 【转】8 个效果惊人的 WebGL/JavaScript 演示
- list选取多个元素 python_python基础篇:list列表的操作大盘点
- 整个线上营销_而言,最重要的是什么?
- 设置 Web 服务器控件颜色属性 转
- python 自动回收机制
- File Cabinet Pro for Mac(菜单栏快捷文件管理软件)
- windows权限了解
- 全球及中国在线教育行业发展态势与营利模式咨询报告2022版
- 一元线性回归方程的参数估计
- 图片上传被旋转,golang Exif 判断.jepg图片原始信息Orientation方向,并旋转,
- 异常与处理--python
- 实现短链接跳转、批量域名跳转、批量域名转发重定向的解决方案
- ANSYS CMD
- java 流水线_用工厂流水线的方式来理解 RxJava 的概念
- 为懒惰的你送上GTA的作弊~~