CNN 的数值实验
前面我们聊过了 Caffe 的整体架构,相信各位对 Caffe 的结构已经比较熟悉了,下面我们就来看看 CNN 中的一些细节问题,也顺着前面的思路进一步进行。
序
在好久之前介绍 ReLU 的时候,我们曾经提到 ReLU 相比于 Sigmoid 的一些优势:
- Sigmoid 整体的梯度偏小,在外向传导的过程中会不断让梯度的幅度变小;
- Sigmoid 存在一个梯度饱和问题:那就是如果非线性部分的输出靠近 0 或者 1,那么反向传导到了这里,Sigmoid 的梯度会接近于 0,这样就会导致梯度到这里就传导不下去了。从前面我们的那张图中可以看得出来。
有了这两个原因,我们认为 ReLU 这样的非线性函数在前向后向的传递性方面效果更好,如果 CNN 的网络层数比较深,ReLU 更能够保证梯度的无损传递。
当我们的前辈完成了这个重大发现之后,大家都觉得“这次终于稳了”,AlexNet 也横空出世,让 CNN 大放异彩。那个时候 AlexNet 已经算是非常"Deep"的网络了……
可是后来,大家对 CNN 模型能力的诉求越来越高,大家通过不断地实验,慢慢发现,现在的模型还不够深,想要模型够牛逼,模型的深度还得继续增加啊!
可是随着后面模型的深度不断增加,我们发现模型的训练过程变得越来越不可控,有时候一个不留神模型就训练溢出了,有时候模型训练着训练着就不动了。训练模型也变得越来越像炼丹了……
炼丹不是一件让人开心的事情,能炼好固然让人开心,但是炼不好就会让宝宝有情绪了。于是各位前辈又重新出发,试图找出新的方法解决这些恼人的问题。
当年众位大神齐聚一堂,开始讨论究竟什么样招式能破解这个困局。有的大神选择从数值的方向入手,有的大神选择从模型结构的方向入手,有的大神选择从模型结构入手,还有的大神走了其他的方法。一场“拯救大兵 CNN”的好戏也由此上演……
一个数值的小实验
好了,让我们回到 MNIST 这种比较简单的问题上。我们也曾经展示过 Caffe 中 MNIST 的例子,以下这个模型是我们曾经用过的模型:
- Conv 20*5*5 - ReLU - Pooling2*2,stride2
- Conv 50*5*5 - ReLU - Pooling2*2,stride2
- Ip 500 - ReLU
- Ip 10
这一次我们将 debug_info 打开,记录一下网络中每一层数据的数值信息。Caffe 中的 debug_info 默认可以给出所有数据的 L1 norm,也就是绝对值的和。这个数值可以在一定程度上反映数据的幅度。如果数据比较大,那么说明整体的数值比较大。
完成了 10000 轮模型的训练,模型最终在 test 集合得到了 0.991 的精度,可以说精度已经很高了。那么各个层的数据表现如何呢?这里我们主要关注以下的数值:
- 两个 conv 层和两个 ip 层的 top data 信息
- 两个 conv 层和两个 ip 层参数 w 的 data 信息
- 两个 conv 层和两个 ip 层参数 w 的 diff 信息
- 所有参数的 data 和 diff 的 L1 norm 和 L2 norm
首先是 top data
可以看出除了一开始的数值波动,后面的迭代中数值整体表现比较稳定,稳中有升。
其次是 w 的 data
可以看出 w 整体表现不算稳定,不过考虑整个优化的过程就是在改变它,所以它的大幅波动也是可以理解的。
接下来是 w 的 diff
可以看出 diff 在数值上表现得也比较稳定,基本上处于一个小的区间之中。
最后是 L1 norm 和 L2 norm
norm 和 diff 的表现总体上和前面展现的一致。
总体来说,topdata 和 w 的 diff 数值没有太大的波动,保持在一个稳定幅度上,所以整体的数值比较稳定,无论是前向的 top data 还是后向的 diff data 没有出现太大的数值波动或者数值幅度问题。
看完了这个成功案例,我们再看看失败案例,当然这个失败案例一般不会出现,只是做个效果而已,不过这个效果也足以说明一些问题。
这个失败案例直接来自于刚才的例子。我们在刚才的模型上只修改一个地方:四个核心层(conv1,conv2,ip1,ip2)的 w 参数的初始化方法。
原来的初始化方法是:
weight_filler {
type: "xavier"
}
我们将它们改成:
weight_filler {
type: "gaussian"
}
这样的改法是比较脑残的,这相当于对参数采取均值为 0,方差为 1 的高斯分布对参数进行初始化……
不用说,这个模型会死的很难看,10000 轮训练结束后,最终的精度为 0.1135。要知道一共只有 10 个数字,猜也可以达到 0.1 的精度。这个模型采用了各种高科技,结果却只比猜稍微好一点,简直弱到家了。
那么它在数值上的表现是怎样呢?直接来看图:
从图中可以看出,各个数据在幅度上的差距都非常大。尤其是最后一张图,data 和 diff 在数值上的差距拉得非常大,那么小量的 diff 对大量的 data 完全起不到改变的作用。这么看来一定是初始化的锅了。
那真的完全是初始化的锅么?众人问:初始化同学,你还有没有什么要补充的?
初始化同学:其实是因为我和 ReLU 同学不熟,你们换 Sigmoid 试试……
众人:当真?
于是乎我们开始了第三个实验……
第三个实验
为了洗刷初始化的清白,我们在保持初始化的基础上,将 3 个非线性的部分重新换成 sigmoid,由 sigmoid 再度出马。sigmoid 表示这种小场面它 Hold 住……
于是我们来看看 sigmoid 的最终结果:0.9538,相关的数值图在这里就不全贴了,只贴一张 L1 L2 norm 的图:
从这个图上来看,虽然 data 的幅度很大,但是 diff 的幅度也很大,基本上算旗鼓相当。
这个最终的精确率不能算特别高,但是起码说明模型还是学到了很多有用的知识。而且和 0.11 的精度相比,简直是一个天上一个地下,完全没得比啊!大家纷纷表示关键时候还得靠老将才能扛得住,初始化同学是被冤枉了。
这时候众人重新以怀疑的眼光看着 ReLU,原来真正的问题出你这啊!你的表现有点浪啊!
ReLU 委屈地表示:我还能再说一句么?
众人表示:这次的版面已经不够了,有话下次再说……
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论