前言
在前几讲里已经介绍了卷积神经网络中常用的一些单元,例如,
本文会介绍最后一个卷积神经网络中常用的单元Dropout,可以称之为“丢弃法”,或者“随机失活”。它在2012年由Alex Krizhevsky、Geoffrey Hinton提出的那个大名鼎鼎的卷积神经网络模型AlexNet中首次被使用,Dropout的使用也是AlexNet与20世纪90年代提出的LeNet的最大不同之处。随后,Krizhevsky和Hinton在文章《Dropout: A Simple Way to Prevent Neural Networks from Overtting》又详细的介绍了介绍了Dropout的原理。发展至今,Dropout已经成为深度学习领域一个举足轻重的技术,它的价值主要体现在解决模型的过拟合问题,虽然它不是唯一的解决过拟合的手段,但它却是兼备轻量化和高效两点做的最好的一个手段。
“丢弃法”,从字面意思很好理解,就是丢弃网络中的一些东西。丢弃的是什么?神经元,有放回的随机丢弃一些神经元。
很多刚接触或者使用过Dropout的同学都会觉得“这有什么好讲的?这是一个非常简单的东西啊”,如果仅仅从使用角度来讲,这的确非常简单。以目前主流的机器学习平台而言,tensorflow、mxnet、pytorch,均是传入一个概率值即可,一行代码即可完成。但是,我认为学习深度学习如果仅仅是为了会使用,那么真的没什么可以学习的,抽空把tensorflow教程看一下既可以称得上入门深度学习。如果剖开表象看一下Dropout的原理,会发现,它的理论基础是非常深的,从作者先后用《Improving neural networks by preventing co-adaptation of feature detectors》《Dropout: A Simple Way to Prevent Neural Networks from Overtting》等多篇文章来阐述这个算法就可以看出它的不可小觑的价值。
和往常先讲理论再讲用法不同,本文先介绍一下它在tensorflow中的用法,然后做一个了解后带着问题去介绍它的理论知识,本文主要包括如下几块内容,
- tensorflow中Dropout的使用
- 优化与机器学习的区别
- 过拟合
- Dropout理论知识
tensorflow中Dropout的使用
在tensorflow中Dropout的函数为,
tf.nn.dropout(x, keep_prob, noise_shape=None, seed=None, name=None)
函数中的参数分别是:
x:Dropout的输入,即为上一层网络的输出
keep_prob:和x尺寸相同的张量(多维向量),它用于定义每个神经元的被保留的概率,假如keep_prob=0.8,那么它被保留的概率为0.8,换个角度说,它有20%的概率被丢弃。
noise_shape:表示随机生成的保存/删除标志的形状,默认值为None。
seed:一个用于创建随机种子的整数,默认值为None。
name:运算或操作的名称,可自行定义,默认值为None。
上述5个参数中x和keep_prob为必须参数,其他很少用到,所以不多介绍。x不难理解,就是上一层网络的输出。这里主要提示一下keep_prob,它是Dropout使用中最为重要的参数。
注意:keep_prob是网络中每个神经元被保留的概率,并非是神经网络中神经元被保留个数的概率。举个例子,加入有个3层神经网络,共100个神经元,定义keep_prob=0.6,那么并不是说要保留60个神经元而丢弃40个。而是每个神经元将会有60%的概率被保留,40%的概率被丢弃,所以最终剩余的神经元并不是确切的60个,可能多于60也可能少于60。
策略:深度学习是一门经验主义非常重的方向,Dropout的使用同样需要很多经验。一般情况下建议靠近输入层的把keep_prob设置大一些,就是靠近输入层经历多保留神经元。
优化与机器学习的区别
讲完Dropout的使用,话说回来,为什么要用Dropout?
提到这个问题,就不得不先做一下铺垫,先谈一下优化与机器学习的区别。
机器学习主要包括如下几块内容:
- 数据集
- 模型
- 损失函数
- 优化算法
其中优化算法直接决定着最终模型效果的好坏,因此,很多人都肆意的扩大优化算法的价值,认为“机器学习就是优化算法”。
我认为这是不严谨的说法机器学习与优化算法有着本质的区别。优化算法主要用于已知或未知数学模型的优化问题,它主要关注的是在既定模型上的误差,而不关注它的泛化误差。而机器学习则不同,它是在训练集上训练书模型,训练过程中与优化算法类似,考虑在训练集上的误差,但是它对比于优化算法还要多一步,要考虑在测试集上的泛化误差。
过拟合
(图片截取自吴恩达《深度学习工程师》)
在训练集和测试集这两个数据集上的精确度就引出几种情况:
- 在训练集上效果不好,在测试集上效果也不好:欠拟合(上图中第一种情况)
- 在训练集上效果很好,在测试集上效果不好:过拟合(上图中第三种情况)
在实际项目中,这两种情况是非常常见的,显然,这并不是我们想要的,我们追求的是第三种情况:
- 在训练集和测试集上的效果都相对较好(上图中第二种情况)
但是,事与愿违,欠拟合和过拟合是机器学习中非常常见的现象,尤其是过拟合。
过拟合的形成原因主要包括如下几点:
- 训练数据集太少
- 参数过多
- 特征维度过高
- 模型过于复杂
- 噪声多
很多研究者把目光和精力都聚焦在解决过拟合这个问题上,目前用于解决过拟合问题的算法有很多,例如,
- 权重衰减
- Early stopping
- 批量归一化(没错,就是前一讲讲的BN,它也带有一些正则化的功能)
在解决过拟合问题的算法中最为知名的词汇莫过于正则化。
提到正则化,很多同学会想到L1、L2正则化,其实它是一类方法的统称,并非仅限于L1、L2正则化,目前用于结果过拟合的正则化方法主要包括如下几种:
- 数据扩充
- L1、L2正则化
- Dropout
没错,Dropout也是正则化方法中的一种!铺垫这么多,终于引出本文的主角了。
数据扩充解决过拟合,这一点不难理解,因为数据缺少是引起过拟合的主要原因之一,由于数据的缺失导致模型学习过程中不能学到全局特征,只能通过少量数据学习到一个类别的部分特征,通过数据的扩充能够让模型学习到全局特征,减少过拟合现象。
L1、L2正则化主要为损失函数添加一个L1或L2的正则化惩罚项,防止学习过程中过于偏向于某一个方向引起过拟合。
最后就轮到Dropout,下面来详细讲一下Dropout的原理。
Dropout
如何使用Dropout?
(图片来自于《Dropout: A Simple Way to Prevent Neural Networks from Overtting》)
上图中左图为一个标准的神经网络,右图是采用Dropout之后的神经网络,其中的区别一目了然,就是丢弃了一些神经元。
前面已经讲解了在tensorflow中如何使用Dropout,已经清楚,对于Dropout最为核心的就是保留概率或者丢弃概率,简述它的原理就是:遍历神经网络的每一层中每一个神经元,以一定概率丢弃或保留某个神经元。
用数学语言描述如下,
假设某一个神经元的输入有4个,那么神经元的计算表达式为,
其中x是输入,w是权重,b是偏差。
假设保留概率为p,那么丢弃改为就为1-p,那么神经元h就有1-p的概率被丢弃,那么经过Dropout运算后的神经元为,
其为0或者1,它为0或者1的概率分别为1-p或p如果为0,则这个神经元被清零,临时被丢弃,一定要注意,是临时的丢弃,Dropout是有放回的采样。在一轮训练中前向或反向传播都会用丢弃后的神经网络,下一轮训练又会随机丢弃,用一个新的网络去训练。
编程实现Dropout其实只需要几行代码,下面结合代码来解释,会更容易理解,
def dropout(X, keep_prob): assert 0 < keep_prob < 1 if keep_prob == 0: return np.zeros(X.shape) mask = np.random.uniform(0, 1, X.shape) < keep_prob return mask * X / keep_prob
输入参数为上一层的激活值和保留概率,
第3行:如果保留概率为0,也就是不保留的话,则全部元素都丢弃。
第5行:生成一个随机的掩码,掩码和输入X形状相同,每个位置非0即1,然后用这个掩码与X相乘,如果对应位置乘以0,则这个神经元被丢弃,反之保留。
Dropout为什么起作用?
(图片来自《深度学习》)
这里不得不提一下Bagging集成集成学习方法,在Bagging集成学习方法中,预先会定义k的模型,然后采用k个训练集,然后分别训练k个模型,然后以各种方式去评价、选取最终学习的模型。
Dropout的训练过程中与Bagging集成学习方法类似,以上图为例,有一个三层神经网络(两个输入神经元、两个隐藏神经元、一个输出神经元),从这个基础的网络中随机删除非输出神经元来构建一些子集。这样每一次训练就如同在这些子集中随机选择一个不同的网络模型进行训练,最后通过"投票"或者平均等策略而选择出一个最好的模型。
其实这个思想并不陌生,在传统机器学习中Adaboost、随机森林都是采用集成学习的思想,效果非常好。采用Dropout后的深度神经网络和这类思想也是类似的,这样能够结合不同网络模型的训练结果对最终学习的模型进行评价,能够综合多数,筛选掉少数,即便是某个网络模型出现了过拟合,最终经过综合也会过滤掉,效果自然会好很多。
需要说明一点的是,虽然采用Dropout和其他集成学习方法思想有异曲同工之处,但是也有一些细节的差异。在Bagging中所有模型都是独立的,但是在Dropout中所有模型是共享参数的,每个子模型会继承父网络的网络参数,这样可以有效的节省内存的占用。
需要注意的点
到这里,Dropout的内容就讲解完了,总结一些本文,需要有几点需要注意,
- keep_prob是每个神经元被保留的概率
- Dropout和L1、L2、数据扩充都属于正则化方法
- Dropout的丢弃是有放回的