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了的更多相关文章

  1. 深度学习基础系列(七)| Batch Normalization

    Batch Normalization(批量标准化,简称BN)是近些年来深度学习优化中一个重要的手段.BN能带来如下优点: 加速训练过程: 可以使用较大的学习率: 允许在深层网络中使用sigmoid这 ...

  2. 深度学习基础系列(五)| 深入理解交叉熵函数及其在tensorflow和keras中的实现

    在统计学中,损失函数是一种衡量损失和错误(这种损失与“错误地”估计有关,如费用或者设备的损失)程度的函数.假设某样本的实际输出为a,而预计的输出为y,则y与a之间存在偏差,深度学习的目的即是通过不断地 ...

  3. 深度学习基础系列(十一)| Keras中图像增强技术详解

    在深度学习中,数据短缺是我们经常面临的一个问题,虽然现在有不少公开数据集,但跟大公司掌握的海量数据集相比,数量上仍然偏少,而某些特定领域的数据采集更是非常困难.根据之前的学习可知,数据量少带来的最直接 ...

  4. 深度学习基础系列(四)| 理解softmax函数

    深度学习最终目的表现为解决分类或回归问题.在现实应用中,输出层我们大多采用softmax或sigmoid函数来输出分类概率值,其中二元分类可以应用sigmoid函数. 而在多元分类的问题中,我们默认采 ...

  5. 深度学习基础系列(十)| Global Average Pooling是否可以替代全连接层?

    Global Average Pooling(简称GAP,全局池化层)技术最早提出是在这篇论文(第3.2节)中,被认为是可以替代全连接层的一种新技术.在keras发布的经典模型中,可以看到不少模型甚至 ...

  6. 深度学习基础系列(一)| 一文看懂用kersa构建模型的各层含义(掌握输出尺寸和可训练参数数量的计算方法)

    我们在学习成熟网络模型时,如VGG.Inception.Resnet等,往往面临的第一个问题便是这些模型的各层参数是如何设置的呢?另外,我们如果要设计自己的网路模型时,又该如何设置各层参数呢?如果模型 ...

  7. 深度学习基础系列(三)| sigmoid、tanh和relu激活函数的直观解释

    常见的激活函数有sigmoid.tanh和relu三种非线性函数,其数学表达式分别为: sigmoid: y = 1/(1 + e-x) tanh: y = (ex - e-x)/(ex + e-x) ...

  8. 深度学习基础系列(二)| 常见的Top-1和Top-5有什么区别?

    在深度学习过程中,会经常看见各成熟网络模型在ImageNet上的Top-1准确率和Top-5准确率的介绍,如下图所示: 那Top-1 Accuracy和Top-5 Accuracy是指什么呢?区别在哪 ...

  9. 吴恩达深度学习笔记(十二)—— Batch Normalization

        主要内容: 一.Normalizing activations in a network 二.Fitting Batch Norm in a neural network 三.Why does ...

随机推荐

  1. 【实操笔记】MySQL主从同步功能实现

    写在前边: 这两天来了个需求,配置部署两台服务器的MySQL数据同步,折腾了两天查了很多相关资料,一直连不上,后来发现其实是数据库授权的ip有问题,我们用的服务器是机房中的虚拟机加上反向代理出来的,坑 ...

  2. GitHub更新已经fork的项目

    clone 自己的 fork 分支到本地 可以直接使用 GitHub 客户端,clone 到本地,如果使用命令行,命令为: $ git clone git@github.com:morethink/g ...

  3. Spring Data JPA原生SQL查询

    package com.test.cms.dao.repository;import org.springframework.stereotype.Repository;import javax.pe ...

  4. Android 动态添加Spinner(.java文件内实现) 实现 改变spinner 内文字属性

    动态添加spinner 控件 Spinner s = new Spinner(this); String []items={"自己定义的要显示的数组"}; my_SpinnerAd ...

  5. Python练习-从小就背不下来的99乘法表

    心血来潮,灵机一动,反正就是无聊的做了一个很简单的小玩意: for i in range(1,10):#让i 1-9 循环9次 print("\n")#每循环一次进行一次换行 fo ...

  6. 双击CAD对象,显示自定义对话框实现方法

    class TlsApplication : IExtensionApplication { void IExtensionApplication.Initialize() { TTest.Start ...

  7. win10系统下我的电脑右键没有属性

    1.右键点击系统左下角的开始,菜单中点击运行 2.在输入框中输入regeidt,点击确定打开系统的注册表编辑器 3.然后依次打开HKEY_CURRENT_USER\Software\Microsoft ...

  8. Linux SSH Backdoor分析排查

    1.SSH后门分类 SSH后门方式有以下几种 软链接 SSH Server wrapper SSH Keylogger 2.软链接 利用方法 [root@helen]# ln -sf /usr/sbi ...

  9. Python标准库笔记(8) — pprint模块

    struct模块提供了用于在字节字符串和Python原生数据类型之间转换函数,比如数字和字符串. Python版本: 2.x & 3.x 该模块作用是完成Python数值和C语言结构体的Pyt ...

  10. IIC串行总线的组成及其工作原理

    ------------------最近项目上用到了一款美信的DS1308RTC芯片,由于是挂在了Zynq的PS MIO上,需要软件人员协助才能测试:觉得太麻烦了,想通过飞线,然后在Vivado中调用 ...