【Keras】减少过拟合的秘诀——Dropout正则化

34次阅读

共计 8652 个字符,预计需要花费 22 分钟才能阅读完成。

摘要:Dropout 正则化是最简单的神经网络正则化方法。阅读完本文,你就学会了在 Keras 框架中,如何将深度学习神经网络 Dropout 正则化添加到深度学习神经网络模型里。
 Dropout 正则化是最简单的神经网络正则化方法。其原理非常简单粗暴:任意丢弃神经网络层中的输入,该层可以是数据样本中的输入变量或来自先前层的激活。它能够模拟具有大量不同网络结构的神经网络,并且反过来使网络中的节点更具有鲁棒性。
阅读完本文,你就学会了在 Keras 框架中,如何将深度学习神经网络 Dropout 正则化添加到深度学习神经网络模型里,具体内容如下:如何使用 Keras API 创建 Dropout 层;如何使用 Keras API 将 Dropout 正则化添加到 MLP、CNN 和 RNN 层;在现有模型中,如何使用 Dropout 正则化减少过拟合。

Keras 中的 Dopout 正则化
在 Keras 深度学习框架中,我们可以使用 Dopout 正则化,其最简单的 Dopout 形式是 Dropout 核心层。
在创建 Dopout 正则化时,可以将 dropout rate 的设为某一固定值,当 dropout rate=0.8 时,实际上,保留概率为 0.2。下面的例子中,dropout rate=0.5。
layer = Dropout(0.5)
Dropout 层
将 Dropout 层添加到模型的现有层和之前的输出层之间,神经网络将这些输出反馈到后续层中。用 dense()方法指定两个全连接网络层:

model.append(Dense(32))
model.append(Dense(32))

在这两层中间插入一个 dropout 层,这样一来,第一层的输出将对第二层实现 Dropout 正则化,后续层与此类似。现在,我们对第二层实现了 Dropout 正则化。

model.append(Dense(32))
model.append(Dropout(0.5))
model.append(Dense(32))

Dropout 也可用于可见层,如神经网络的输入。在这种情况下,就要把 Dropout 层作为网络的第一层,并将 input_shape 参数添加到层中,来制定预期输入。

model.add(Dropout(0.5, input_shape=(2,)))

下面,我们来看看 Dropout 正则化如何与常见的网络类型一起使用。
MLP Dropout 正则化
在两个全连接层之间添加 Dropout 正则化,代码如下所示:
# example of dropout between fully connected layers
from keras.layers import Dense
from keras.layers import Dropout

model.add(Dense(32))
model.add(Dropout(0.5))
model.add(Dense(1))

CNN Dropout 正则化
我们可以在卷积层和池化层后使用 Dropout 正则化。一般来说,Dropout 仅在池化层后使用。
# example of dropout for a CNN
from keras.layers import Dense
from keras.layers import Conv2D
from keras.layers import MaxPooling2D
from keras.layers import Dropout

model.add(Conv2D(32, (3,3)))
model.add(Conv2D(32, (3,3)))
model.add(MaxPooling2D())
model.add(Dropout(0.5))
model.add(Dense(1))

在这种情况下,我们要将 Dropout 应用于特征图的每个单元中。
在卷积神经网络中使用 Dropout 正则化的另一个方法是,将卷积层中的整个特征图都丢弃,然后在池化期间也不再使用。这种方法称为空间丢弃,即 Spatial Dropout。
“我们创建了一个新的 Dropout 正则化方法,我们将其称为 Spatial Dropout。在这个方法中,我们将 Dropout 值扩展到整个特征映射中。”
——《使用卷积神经网络有效的进行对象本地化,2015》
在 Keras 中,通过 SpatialDropout2D 层提供 Spatial Dropout 正则化。
# example of spatial dropout for a CNN
from keras.layers import Dense
from keras.layers import Conv2D
from keras.layers import MaxPooling2D
from keras.layers import SpatialDropout2D

model.add(Conv2D(32, (3,3)))
model.add(Conv2D(32, (3,3)))
model.add(SpatialDropout2D(0.5))
model.add(MaxPooling2D())
model.add(Dense(1))

RNN Dropout 正则化
我们在 LSTM 循环层和全连接层之间使用 Dropout 正则化,代码如下所示:
# example of dropout between LSTM and fully connected layers
from keras.layers import Dense
from keras.layers import LSTM
from keras.layers import Dropout

model.add(LSTM(32))
model.add(Dropout(0.5))
model.add(Dense(1))

在这里,将 Dropout 应用于 LSTM 层的 32 个输出中,这样,LSTM 层就作为全连接层的输入。
还有一种方法可以将 Dropout 与 LSTM 之类的循环层一起使用。LSTM 可以将相同的 Dropout 掩码用于所有的输入中。这个方法也可用于跨样本时间步长的循环输入连接。这种使用递归模型进行 Dropout 正则化则称为变分循环神经网络(Variational RNN)。
“变分循环神经网络在每个时间步长使用相同的 Dropout 掩码,包括循环层。这与在 RNN 中实现 Dropout 正则化一样,在每个时间步长丢弃相同的神经网络单元,并且随意的丢弃输入、输出和循环连接。这和现有的技术形成对比,在现有的技术中,不同的神经网络单元将在不同的时间步长被丢弃,并且不会对全连接层进行丢弃。”
——《循环神经网络中 Dropout 的基础应用,2016》
Keras 通过循环层上的两个参数来支持变分神经网络(输入和循环输入样本时间步长的一致性丢弃),这称为 输入“Dropout”和循环输入的“recurrent_dropout”。
# example of dropout between LSTM and fully connected layers
from keras.layers import Dense
from keras.layers import LSTM
from keras.layers import Dropout

model.add(LSTM(32))
model.add(Dropout(0.5))
model.add(Dense(1))

Dropout 正则化案例
在本节中,我们将演示如何使用 Dropout 正则化来减少 MLP 在简单二元分类问题上的过拟合。在这里,我们提供了一个在神经网络上应用 Dropout 正则化的模板,你也可以将其用于分类和回归问题。
二元分类问题
在这里,我们使用一个标准的二元分类问题,即定义两个二维同心圆,每个类为一个圆。
每个观测值都有两个输入变量,它们具有相同的比例,类输出值为 0 或 1。这个数据集就是“圆”数据集。
我们可以使用 make_circles()方法生成观测结果。我们为数据添加噪声和随机数生成器,以防每次运行代码时使用相同的样本。
# generate 2d classification dataset
X, y = make_circles(n_samples=100, noise=0.1, random_state=1)
我们可以用 x 和 y 坐标绘制一个数据集,并将观察到的颜色定义为类值。生成和绘制数据集的代码如下:
# generate two circles dataset
from sklearn.datasets import make_circles
from matplotlib import pyplot
from pandas import DataFrame
# generate 2d classification dataset
X, y = make_circles(n_samples=100, noise=0.1, random_state=1)
# scatter plot, dots colored by class value
df = DataFrame(dict(x=X[:,0], y=X[:,1], label=y))
colors = {0:’red’, 1:’blue’}
fig, ax = pyplot.subplots()
grouped = df.groupby(‘label’)
for key, group in grouped:
group.plot(ax=ax, kind=’scatter’, x=’x’, y=’y’, label=key, color=colors[key])
pyplot.show()
运行以上代码,会创建一个散点图,散点图展示每个类中观察到的同心圆形状。我们可以看到,因为噪声,圆圈并不明显。

这是一个特别好的测试问题,因为类不可能用一条直线表示,比如它不是线性可微分的,在这种情况下,就需要使用非线性方法来解决,比如神经网络。
在这里,我们只生成了 100 个样本,这对于神经网络来说,样本是相当少了。但是它提供了训练数据集的过拟合现象,并且在测试数据及上的误差更大:这是使用正则化的一个特别好的例子。除此之外,这个样本集中有噪声,这就使神经网络模型有机会学习不一致样本的各个方面。
多层感知器的过拟合
我们可以创建一个 MLP 模型来解决这个二元分类问题。
该模型将具有一个隐藏层,它的节点比解决该问题所需节点要多得多,从而产生过拟合。另外,我们训练模型的时间也大大超过正常训练模型所需要的时间。
在定义模型之前,我们将数据集拆分为训练集和测试集:30 个训练数据来训练模型和 70 个测试数据来评估拟合模型性能。
# generate 2d classification dataset
X, y = make_circles(n_samples=100, noise=0.1, random_state=1)
# split into train and test
n_train = 30
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
接下来,我们可以定义模型。
在隐藏层中使用 500 个节点和矫正过得线性激活函数;在输出层中使用 S 型激活函数预测类的值(0 或 1)。
该模型使用二元交叉熵损失函数进行优化,这个函数适用于二元分类问题和梯度下降到有效 Adam 问题。
# define model
model = Sequential()
model.add(Dense(500, input_dim=2, activation=’relu’))
model.add(Dense(1, activation=’sigmoid’))
model.compile(loss=’binary_crossentropy’, optimizer=’adam’, metrics=[‘accuracy’])
将训练数据训练 4000 次,默认每次训练次数为 32。然后用测试数据集验证该模型性能,代码如下。
# fit model
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=4000, verbose=0)
测试的方法如下。
# evaluate the model
_, train_acc = model.evaluate(trainX, trainy, verbose=0)
_, test_acc = model.evaluate(testX, testy, verbose=0)
print(‘Train: %.3f, Test: %.3f’ % (train_acc, test_acc))
最后,在每次训练的时候绘制模型的性能。
如果模型在训练数据集时的确是过拟合,那么我们训练集上的准确度线图更加准确,并且准确度随着模型学习训练数据集中的统计噪声而再次下降。
# plot history
pyplot.plot(history.history[‘acc’], label=’train’)
pyplot.plot(history.history[‘val_acc’], label=’test’)
pyplot.legend()
pyplot.show()
将以上所有代码组合起来,如下所示。
# mlp overfit on the two circles dataset
from sklearn.datasets import make_circles
from keras.layers import Dense
from keras.models import Sequential
from matplotlib import pyplot
# generate 2d classification dataset
X, y = make_circles(n_samples=100, noise=0.1, random_state=1)
# split into train and test
n_train = 30
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# define model
model = Sequential()
model.add(Dense(500, input_dim=2, activation=’relu’))
model.add(Dense(1, activation=’sigmoid’))
model.compile(loss=’binary_crossentropy’, optimizer=’adam’, metrics=[‘accuracy’])
# fit model
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=4000, verbose=0)
# evaluate the model
_, train_acc = model.evaluate(trainX, trainy, verbose=0)
_, test_acc = model.evaluate(testX, testy, verbose=0)
print(‘Train: %.3f, Test: %.3f’ % (train_acc, test_acc))
# plot history
pyplot.plot(history.history[‘acc’], label=’train’)
pyplot.plot(history.history[‘val_acc’], label=’test’)
pyplot.legend()
pyplot.show()
运行以上代码,我们可以看到模型在训练和测试数据集上的性能:模型在训练数据集上的性能优于测试数据集,这是过度拟合的一个可能标志。
鉴于神经网络和训练算法的随机性,模型的测试结果可能会有所不同。由于该模型严重过拟合,该模型在同一数据集上运行的结果差异并不会很大。
Train: 1.000, Test: 0.757
下图为模型在训练和测试集上的精度图,我们可以看到过拟合模型的预期性能,其中测试精度增加到一定值以后,再次开始减小。

使用 Dropout 正则化减少 MLP 过拟合
我们使用 Dropout 正则化更新这个示例,即在隐藏层和输出层之间插入一个新的 Dropout 层来实现。在这里,指定 Dropout rate=0.4。
# define model
model = Sequential()
model.add(Dense(500, input_dim=2, activation=’relu’))
model.add(Dropout(0.4))
model.add(Dense(1, activation=’sigmoid’))
model.compile(loss=’binary_crossentropy’, optimizer=’adam’, metrics=[‘accuracy’])
下面列出了隐藏层后添加了 dropout 层的完整更新示例。
# mlp with dropout on the two circles dataset
from sklearn.datasets import make_circles
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from matplotlib import pyplot
# generate 2d classification dataset
X, y = make_circles(n_samples=100, noise=0.1, random_state=1)
# split into train and test
n_train = 30
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# define model
model = Sequential()
model.add(Dense(500, input_dim=2, activation=’relu’))
model.add(Dropout(0.4))
model.add(Dense(1, activation=’sigmoid’))
model.compile(loss=’binary_crossentropy’, optimizer=’adam’, metrics=[‘accuracy’])
# fit model
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=4000, verbose=0)
# evaluate the model
_, train_acc = model.evaluate(trainX, trainy, verbose=0)
_, test_acc = model.evaluate(testX, testy, verbose=0)
print(‘Train: %.3f, Test: %.3f’ % (train_acc, test_acc))
# plot history
pyplot.plot(history.history[‘acc’], label=’train’)
pyplot.plot(history.history[‘val_acc’], label=’test’)
pyplot.legend()
pyplot.show()
运行以上代码,查看模型在训练和测试集上的性能。你所得到的结果可能会有所不同,在这种情况下,该模型具有较高的方差。在这里,我们可以看到,Dropout 导致训练集的准确度有所下降,从 100% 降至 96%,而测试集的准确度从 75% 提高到 81%。
Train: 0.967, Test: 0.814
从这里我们可以看出,该模型已经不再适合训练数据集了。
尽管使用 Dropout 正则化时会产生很多噪音,训练数据集和测试数据集的模型精度持续增加。
在后续学习中,你可以进一步探索以下这些问题:
1. 输入 Dropout。在输入变量上使用 Dropout 正则化,更新示例,并比较结果。
2. 权重约束。在隐藏层添加 max-norm 权重约束,更新示例,并比较结果。
3. 反复评估。更新示例,重复评估过拟合和 Dropout 模型,总结并比较平均结果。
4. 网格搜索率。创建 Dropout 概率的网格搜索,并报告 Dropout rate 和测试数据集准确度二者之间的关系。
拓展阅读
论文
1.《使用卷积神经网络进行高效的对象本地化,2015》
2.《递归神经网络中的理论 Dropout 应用,2016》
博文
1. 基于 Keras 深度学习模型中的 Dropout 正则化
2. 如何使用 LSTM 网络的 Dropout 进行时间序列预测
API
1.Keras Regularizers API
2.Keras Core Layers API
3.Keras Convolutional Layers API
4.Keras Recurrent Layers API
5.sklearn.datasets.make_circles API
总结
阅读完本文,你已经了解了如何将深度学习正则化添加到深度学习神经网络模型的 API 中。具体来说,有以下几个内容:
1. 如何使用 Keras API 创建 Dropout 层。
2. 如何使用 Keras API 将 Dropout 正则化添加到 MLP、CNN 和 RNN 层。
3. 如何向现有模型中添加 Dropout 正则化,以此减少过拟合。

本文作者:【方向】阅读原文
本文为云栖社区原创内容,未经允许不得转载。

正文完
 0