深度学习基础系列(九)| Dropout VS Batch Normalization? 是时候放弃Dropout了
Dropout是过去几年非常流行的正则化技术,可有效防止过拟合的发生。但从深度学习的发展趋势看,Batch Normalizaton(简称BN)正在逐步取代Dropout技术,特别是在卷积层。本文将首先引入Dropout的原理和实现,然后观察现代深度模型Dropout的使用情况,并与BN进行实验比对,从原理和实测上来说明Dropout已是过去式,大家应尽可能使用BN技术。
一、Dropout原理
根据wikipedia定义,dropout是指在神经网络中丢弃掉一些隐藏或可见单元。通常来说,是在神经网络的训练阶段,每一次迭代时,都会随机选择一批单元,让其被暂时忽略掉,所谓的忽略是不让这些单元参与前向推理和后向传播。
上图是标准的神经网络,经过dropout后,则变成如下图:

一般来说,我们在可能发生过拟合的情况下才会使用dropout等正则化技术。那什么时候可能会发生呢?比如神经网络过深,或训练时间过长,或没有足够多的数据时。那为什么dropout能有效防止过拟合呢?可以理解为,我们每次训练迭代时,随机选择一批单元不参与训练,这使得每个单元不会依赖于特定的前缀单元,因此具有一定的独立性;同样可以看成我们拿同样的数据在训练不同的网络,每个网络都有可能过拟合,但迭代多次后,这种过拟合会被抵消掉。
要注意的是,dropout是体现在训练环节,训练完成后,我们认为所有的单元都被训练好了,在验证或测试阶段,我们是拿完整的神经网络去验证或测试。
二、Dropout具体实现
以keras为例,其代码为:keras.backend.dropout(x, level, noise_shape=None, seed=None),其中x指的是输入参数,level则是keep-prob,也就是这个单元有多少概率会被设置为0。
import tensorflow.keras.backend as K
input = K.random_uniform_variable(shape=(3, 3), low=0, high=1)
print("dropout with keep-prob 0.5:", K.eval(K.dropout(input, 0.5)))
print("dropout with keep-prob 0.2:", K.eval(K.dropout(input, 0.2)))
print("dropout with keep-prob 0.8:", K.eval(K.dropout(input, 0.8)))
看看输出结果:
dropout with keep-prob 0.5:
[[1.190095 0. 1.2999489]
[0. 0.3164637 0. ]
[0. 0. 0. ]]
dropout with keep-prob 0.2:
[0.74380934 0.67237484 0.81246805]
[0.8819132 0.19778982 1.2349881 ]
[1.0369372 0.5945368 0. ]]
dropout with keep-prob 0.8:
[[0. 0. 0. ]
[0. 0. 4.9399524]
[4.147749 2.3781471 0. ]]
可以看出,level值越大,每个单元成为0的概率也就越大。
在具体的keras应用中,dropout通常放在激活函数后,比如:
model=keras.models.Sequential()
model.add(keras.layers.Dense(150, activation="relu"))
model.add(keras.layers.Dropout(0.5))
三、Dropout正在被抛弃
随着深度学习的发展,Dropout在现代卷积架构中,已经逐步被BN(想要了解BN,大家可以参见我之前写的 深度学习基础系列(七)| Batch Normalization 一文,这里不再赘述)取代,BN也同样拥有不亚于Dropout的正则化效果。
“We presented an algorithm for constructing, training, and performing inference with batch-normalized networks. The resulting networks can be trained with saturating nonlinearities, are more tolerant to increased training rates, and often do not require Dropout for regularization.” -Ioffe and Svegedy 2015
至于为何Dropout不再受青睐,原因如下:
- Dropout在卷积层的正则效果有限。相比较于全连接层,卷积层的训练参数较少,激活函数也能很好地完成特征的空间转换,因此正则化效果在卷积层不明显;
- Dropout也过时了,能发挥其作用的地方在全连接层,可当代的深度网络中,全连接层也在慢慢被全局平均池化曾所取代,不但能减低模型尺寸,还可以提升性能。
事实上,我们可以看看keras实现的现代经典模型,就可以窥之dropout目前的处境。打开keras的地址:https://github.com/keras-team/keras-applications

纵观无论是VGG、ResNet、Inception、MobileNetV2等模型,都不见了Dropout踪影。唯独在MobileNetV1模型里,还可以找到Dropout,但不是在卷积层;而且在MobileNetV2后,已经不再有全连接层,而是被全局平均池化层所取代。如下图所示:

其他模型也类似,纷纷抛弃了Dropout和全连接层。
四、Dropout VS BatchNormalization
我们需要做一个简单实验来验证上述理论的成立,实验分五种测试模型:
- 没有使用Dropout,也没有使用BN;
- 使用了Dropout,不使用BN,使训练单元为0的概率为0.2;
- 使用了Dropout,不使用BN,使训练单元为0的概率为0.5;
- 使用了Dropout,不使用BN,使训练单元为0的概率为0.8;
- 使用了BN,不使用Dropout
代码如下:
import keras
from keras.datasets import cifar10
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten, BatchNormalization
from keras.layers import Conv2D, MaxPooling2D
from matplotlib import pyplot as plt
import numpy as np # 为保证公平起见,使用相同的随机种子
np.random.seed(7)
batch_size = 32
num_classes = 10
epochs = 40
data_augmentation = True # The data, split between train and test sets:
(x_train, y_train), (x_test, y_test) = cifar10.load_data() # Convert class vectors to binary class matrices.
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes) x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255 def model(bn=False, dropout=False, level=0.5):
model = Sequential()
model.add(Conv2D(32, (3, 3), padding='same', input_shape=x_train.shape[1:]))
if bn:
model.add(BatchNormalization())
model.add(Activation('relu'))
model.add(Conv2D(32, (3, 3)))
if bn:
model.add(BatchNormalization())
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
if dropout:
model.add(Dropout(level)) model.add(Conv2D(64, (3, 3), padding='same'))
if bn:
model.add(BatchNormalization())
model.add(Activation('relu'))
model.add(Conv2D(64, (3, 3)))
if bn:
model.add(BatchNormalization())
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
if dropout:
model.add(Dropout(level)) model.add(Flatten())
model.add(Dense(512))
if bn:
model.add(BatchNormalization())
model.add(Activation('relu'))
if dropout:
model.add(Dropout(level))
model.add(Dense(num_classes))
model.add(Activation('softmax'))
if bn:
opt = keras.optimizers.rmsprop(lr=0.001, decay=1e-6)
else:
opt = keras.optimizers.rmsprop(lr=0.0001, decay=1e-6) model.compile(loss='categorical_crossentropy',
optimizer=opt,
metrics=['accuracy']) # 使用数据增强获取更多的训练数据
datagen = ImageDataGenerator(width_shift_range=0.1, height_shift_range=0.1, horizontal_flip=True)
datagen.fit(x_train)
history = model.fit_generator(datagen.flow(x_train, y_train, batch_size=batch_size), epochs=epochs,
validation_data=(x_test, y_test), workers=4)
return history no_dropout_bn_history = model(False, False)
dropout_low_history = model(False, True, 0.2)
dropout_medium_history = model(False, True, 0.5)
dropout_high_history = model(False, True, 0.8)
bn_history = model(True, False) # 比较多种模型的精确度
plt.plot(no_dropout_bn_history.history['val_acc'])
plt.plot(dropout_low_history.history['val_acc'])
plt.plot(dropout_medium_history.history['val_acc'])
plt.plot(dropout_high_history.history['val_acc'])
plt.plot(bn_history.history['val_acc'])
plt.title('Model accuracy')
plt.ylabel('Validation Accuracy')
plt.xlabel('Epoch')
plt.legend(['No bn and dropout', 'Dropout with 0.2', 'Dropout with 0.5', 'Dropout with 0.8', 'BN'], loc='lower right')
plt.grid(True)
plt.show() # 比较多种模型的损失率
plt.plot(no_dropout_bn_history.history['val_loss'])
plt.plot(dropout_low_history.history['val_loss'])
plt.plot(dropout_medium_history.history['val_loss'])
plt.plot(dropout_high_history.history['val_loss'])
plt.plot(bn_history.history['val_loss'])
plt.title('Model loss')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend(['No bn and dropout', 'Dropout with 0.2', 'Dropout with 0.5', 'Dropout with 0.8', 'BN'], loc='upper right')
plt.grid(True)
plt.show()
各模型的验证准确率如下图:

各模型的验证损失率如下:
由上图可知,Dropout在不同概率下,其表现差异较大,相对来说,Dropout with 0.2的表现接近于 No bn and dropout(可以理解为Dropout的keep-prob为1的版本)。总体来说,BN在准确率和损失率上表现要优于Dropout,比如准确率上BN能达到85%,而Dropout接近为79%。
五、结论
无论是理论上的分析,还是现代深度模型的演变,或者是实验的结果,BN技术已显示出其优于Dropout的正则化效果,我们也是时候放弃Dropout,投入BN的怀抱了。
深度学习基础系列(九)| Dropout VS Batch Normalization? 是时候放弃Dropout了的更多相关文章
- 深度学习基础系列(七)| Batch Normalization
Batch Normalization(批量标准化,简称BN)是近些年来深度学习优化中一个重要的手段.BN能带来如下优点: 加速训练过程: 可以使用较大的学习率: 允许在深层网络中使用sigmoid这 ...
- 深度学习基础系列(五)| 深入理解交叉熵函数及其在tensorflow和keras中的实现
在统计学中,损失函数是一种衡量损失和错误(这种损失与“错误地”估计有关,如费用或者设备的损失)程度的函数.假设某样本的实际输出为a,而预计的输出为y,则y与a之间存在偏差,深度学习的目的即是通过不断地 ...
- 深度学习基础系列(十一)| Keras中图像增强技术详解
在深度学习中,数据短缺是我们经常面临的一个问题,虽然现在有不少公开数据集,但跟大公司掌握的海量数据集相比,数量上仍然偏少,而某些特定领域的数据采集更是非常困难.根据之前的学习可知,数据量少带来的最直接 ...
- 深度学习基础系列(四)| 理解softmax函数
深度学习最终目的表现为解决分类或回归问题.在现实应用中,输出层我们大多采用softmax或sigmoid函数来输出分类概率值,其中二元分类可以应用sigmoid函数. 而在多元分类的问题中,我们默认采 ...
- 深度学习基础系列(十)| Global Average Pooling是否可以替代全连接层?
Global Average Pooling(简称GAP,全局池化层)技术最早提出是在这篇论文(第3.2节)中,被认为是可以替代全连接层的一种新技术.在keras发布的经典模型中,可以看到不少模型甚至 ...
- 深度学习基础系列(一)| 一文看懂用kersa构建模型的各层含义(掌握输出尺寸和可训练参数数量的计算方法)
我们在学习成熟网络模型时,如VGG.Inception.Resnet等,往往面临的第一个问题便是这些模型的各层参数是如何设置的呢?另外,我们如果要设计自己的网路模型时,又该如何设置各层参数呢?如果模型 ...
- 深度学习基础系列(三)| sigmoid、tanh和relu激活函数的直观解释
常见的激活函数有sigmoid.tanh和relu三种非线性函数,其数学表达式分别为: sigmoid: y = 1/(1 + e-x) tanh: y = (ex - e-x)/(ex + e-x) ...
- 深度学习基础系列(二)| 常见的Top-1和Top-5有什么区别?
在深度学习过程中,会经常看见各成熟网络模型在ImageNet上的Top-1准确率和Top-5准确率的介绍,如下图所示: 那Top-1 Accuracy和Top-5 Accuracy是指什么呢?区别在哪 ...
- 吴恩达深度学习笔记(十二)—— Batch Normalization
主要内容: 一.Normalizing activations in a network 二.Fitting Batch Norm in a neural network 三.Why does ...
随机推荐
- JS开启浏览器全屏模式,需要手动触发
<html > <meta charset="UTF-8"> <body> <button onclick="launchFul ...
- [转载]jsonp详解
http://www.cnblogs.com/lemontea/archive/2012/12/11/2812268.html json相信大家都用的多,jsonp我就一直没有机会用到,但也经常看到, ...
- [整理]C语言中的static静态对象
1.说明外部对象(静态外部变量和静态函数) (1)static 用于说明外部变量或函数,使该对象的作用域限定为被编译原文件的剩余部分,即从对象说明开始到所在源文件的结束部分: (2)被st ...
- MySQL实现强制查询走索引和强制查询不缓存
0.表结构如下:(包含两个索引) Create Table: CREATE TABLE `user` ( `userID` ) NOT NULL, `userCode` ) DEFAULT NULL, ...
- oggMonitor是什么
goldengate monitor是一套监控goldengate的软件,如果安装的ogg比较多,使用goldengate monitor可以清楚的看见全部OGG的每个进程运行状态,以及整个OGG的架 ...
- flask基础之app初始化(四)
前言 flask的核心对象是Flask,它定义了flask框架对于http请求的整个处理逻辑.随着服务器被启动,app被创建并初始化,那么具体的过程是这样的呢? 系列文章 flask基础之安装和使用入 ...
- 那些代表性的HTTP状态码,你还只知道404吗?快来看看吧【转】
前言 在网络上发送请求后,经常会根据请求的状态码去判断请求的成功失败与否,常见的状态码有200,404,500. 不过你以为HTTP请求的状态码就只有这么几个么?其实是远远比这个多的. 今天这篇文章我 ...
- ntp/系统时钟/硬件时钟/双系统下计算机时间读取的问题
http://blog.chinaunix.net/uid-182041-id-3464524.html //linux系统时间和硬件时钟问题(date和hwclock) http://j ...
- Linux sudo 配置
1.配置sudo权限 sudo配置信息保存在 /etc/sudoers 文件中,可以使用vi修改.但使用vi修改需要给root用户设置权限,可以使用 visudo 直接修改. visudo 复制这行将 ...
- GitHub如何使用
先马克一下,有空看看:http://blog.csdn.net/xiahouzuoxin/article/details/9393119