Machine Learning Mastery 深度学习表现教程(四)
如何在 Keras 中创建深度学习模型的装袋集成
最后更新于 2020 年 8 月 25 日
集成学习是组合来自多个模型的预测的方法。
在集成学习中,重要的是组成集成的模型是好的,从而产生不同的预测误差。在不同方面都很好的预测可以产生比任何单个成员模型的预测更稳定且通常更好的预测。
实现模型之间差异的一种方法是在可用训练数据的不同子集上训练每个模型。通过使用重采样方法,例如交叉验证和自举,在训练数据的不同子集上自然地训练模型,该重采样方法被设计来估计模型在通常看不见的数据上的平均表现。在该估计过程中使用的模型可以被组合在所谓的基于重采样的集成中,例如交叉验证集成或自举聚合(或装袋)集成。
在本教程中,您将发现如何为深度学习神经网络模型开发一套不同的基于重采样的集成。
完成本教程后,您将知道:
- 如何使用随机拆分来评估模型表现,并根据模型开发一个集成。
- 如何使用 10 倍交叉验证来评估表现,并开发交叉验证集成。
- 如何使用 bootstrap 估计表现,并使用 bagging 集成组合模型。
用我的新书更好的深度学习启动你的项目,包括分步教程和所有示例的 Python 源代码文件。
我们开始吧。
- 2019 年 10 月更新:针对 Keras 2.3 和 TensorFlow 2.0 更新。
- 2020 年 1 月更新:针对 Sklearn v0.22 API 的变化进行了更新。
如何在 Keras 创建用于深度学习的随机拆分、交叉验证和装袋集成 吉安·卢卡·庞蒂摄,版权所有。
教程概述
本教程分为六个部分;它们是:
- 数据重采样集合
- 多类分类问题
- 单层多层感知器模型
- 随机拆分集合
- 交叉验证一起
- 装袋集成团
数据重采样集合
组合来自多个模型的预测可以产生更稳定的预测,并且在某些情况下,预测具有比任何贡献模型更好的表现。
有效的集成需要意见不同的成员。每个成员都必须有技能(例如,表现比随机机会更好),但理想情况下,在不同的方面表现良好。从技术上讲,我们可以说,我们更喜欢集成成员的预测具有低相关性,或者预测误差。
鼓励集成之间差异的一种方法是在不同的训练数据集上使用相同的学习算法。这可以通过重复对训练数据集进行重采样来实现,该数据集又用于训练新模型。使用训练数据上稍微不同的视角来拟合多个模型,并且反过来,当组合时,产生不同的误差并且通常更稳定和更好的预测。
我们可以将这些方法统称为数据重采样集合。
这种方法的一个好处是,可以使用不利用训练数据集中所有示例的重采样方法。任何未用于拟合模型的示例都可以用作测试数据集,以估计所选模型配置的泛化误差。
我们可以使用三种流行的重采样方法来创建重采样集合;它们是:
- 随机拆分。数据集被重复采样,数据被随机分成训练集和测试集。
- k 倍交叉验证。数据集被分割成 k 个大小相等的折叠,k 个模型被训练,每个折叠都有机会被用作保持集,其中模型在所有剩余的折叠上被训练。
- 引导聚合。随机样本通过替换收集,给定样本中未包含的样本用作测试集。
也许最广泛使用的重采样集成方法是自举聚合,更通常被称为装袋。带有替换的重采样允许训练数据集中有更多的差异,从而偏置模型,进而导致所得模型的预测之间有更多的差异。
重新采样集成模型会对您的项目做出一些特定的假设:
- 需要对未知数据的模型表现进行稳健估计;如果没有,则可以使用单个训练/测试分割。
- 使用模型集合有可能提升表现;如果没有,则可以使用适合所有可用数据的单一模型。
- 在训练数据集的样本上拟合一个以上的神经网络模型的计算成本并不令人望而却步;如果没有,所有的资源都应该放在一个模型上。
神经网络模型非常灵活,因此由重采样集成提供的表现提升并不总是可能的,因为基于所有可用数据训练的单个模型可以表现得很好。
因此,使用重采样集合的最佳点是这样的情况,其中需要表现的稳健估计,并且可以拟合多个模型来计算该估计,但是还需要在表现估计期间创建的一个(或多个)模型用作最终模型(例如,新的最终模型不能拟合所有可用的训练数据)。
现在我们已经熟悉了重采样集成方法,我们可以通过一个依次应用每种方法的示例来进行工作。
多类分类问题
我们将使用一个小的多类分类问题作为基础来演示模型重采样集成。
Sklearn 类提供了 make_blobs()函数,该函数可用于创建具有规定数量的样本、输入变量、类和类内样本方差的多类分类问题。
我们用 1000 个例子来说明这个问题,输入变量(代表点的 x 和 y 坐标)和每个组内点的标准偏差为 2.0。我们将使用相同的随机状态(用于伪随机数发生器的种子)来确保我们总是获得相同的 1000 分。
# generate 2d classification dataset
X, y = make_blobs(n_samples=1000, centers=3, n_features=2, cluster_std=2, random_state=2)
结果是我们可以建模的数据集的输入和输出元素。
为了了解问题的复杂性,我们可以在二维散点图上绘制每个点,并按类值给每个点着色。
下面列出了完整的示例。
# scatter plot of blobs dataset
from sklearn.datasets import make_blobs
from matplotlib import pyplot
from pandas import DataFrame
# generate 2d classification dataset
X, y = make_blobs(n_samples=1000, centers=3, n_features=2, cluster_std=2, random_state=2)
# scatter plot, dots colored by class value
df = DataFrame(dict(x=X[:,0], y=X[:,1], label=y))
colors = {0:'red', 1:'blue', 2:'green'}
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()
运行该示例会创建整个数据集的散点图。我们可以看到,2.0 的标准偏差意味着类不是线性可分的(用一条线可分的),导致了很多不明确的点。
这是可取的,因为这意味着问题不是微不足道的,并且将允许神经网络模型找到许多不同的“T0”足够好的“T1”候选解,从而导致高方差。
具有三个类和按类值着色的点的斑点数据集的散点图
单层多层感知器模型
我们将定义一个多层感知器神经网络,或 MLP,它学习问题相当好。
该问题是一个多类分类问题,我们将在输出层使用 softmax 激活函数对其进行建模。这意味着该模型将以样本属于 3 类中每一类的概率来预测具有 3 个元素的向量。因此,第一步是对类值进行一次热编码。
y = to_categorical(y)
接下来,我们必须将数据集分成训练集和测试集。我们将使用测试集来评估模型的表现,并使用学习曲线绘制训练期间的表现。我们将使用 90%的数据进行训练,10%用于测试集。
我们选择大分割是因为这是一个有噪声的问题,表现良好的模型需要尽可能多的数据来学习复杂的分类函数。
# split into train and test
n_train = int(0.9 * X.shape[0])
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
接下来,我们可以定义并组合模型。
该模型将预期具有两个输入变量的样本。然后,该模型有一个具有 50 个节点的单个隐藏层和一个校正的线性激活函数,然后有一个具有 3 个节点的输出层来预测 3 个类中每个类的概率,以及一个 softmax 激活函数。
由于问题是多类的,我们将使用分类交叉熵损失函数来优化模型和随机梯度下降的有效亚当味。
# define model
model = Sequential()
model.add(Dense(50, input_dim=2, activation='relu'))
model.add(Dense(3, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
该模型适合 50 个训练时期,我们将在测试集上评估每个时期的模型,使用测试集作为验证集。
# fit model
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=50, 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['accuracy'], label='train')
pyplot.plot(history.history['val_accuracy'], label='test')
pyplot.legend()
pyplot.show()
下面列出了完整的示例。
# develop an mlp for blobs dataset
from sklearn.datasets import make_blobs
from keras.utils import to_categorical
from keras.models import Sequential
from keras.layers import Dense
from matplotlib import pyplot
# generate 2d classification dataset
X, y = make_blobs(n_samples=1000, centers=3, n_features=2, cluster_std=2, random_state=2)
# one hot encode output variable
y = to_categorical(y)
# split into train and test
n_train = int(0.9 * X.shape[0])
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# define model
model = Sequential()
model.add(Dense(50, input_dim=2, activation='relu'))
model.add(Dense(3, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
# fit model
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=50, 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))
# learning curves of model accuracy
pyplot.plot(history.history['accuracy'], label='train')
pyplot.plot(history.history['val_accuracy'], label='test')
pyplot.legend()
pyplot.show()
运行该示例首先打印最终模型在列车和测试数据集上的表现。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,我们可以看到模型在训练数据集上达到了大约 83%的准确率,在测试数据集上达到了大约 86%的准确率。
选择将数据集分成训练集和测试集意味着测试集很小,不能代表更广泛的问题。反过来,测试集上的表现不代表模型;在这种情况下,它是乐观的偏见。
Train: 0.830, Test: 0.860
还创建了一个线图,显示了在每个训练周期内,训练和测试集上模型准确率的学习曲线。
我们可以看到模型有一个相当稳定的拟合。
每个训练时期训练和测试数据集上模型准确率的线图学习曲线
随机拆分集合
模型的不稳定性和小的测试数据集意味着我们不知道这个模型在新数据上的表现。
我们可以尝试一种简单的重采样方法,在训练集和测试集中重复生成新的数据集随机分割,并拟合新的模型。计算模型在每个分割中的平均表现将给出模型泛化误差的更好估计。
然后,我们可以组合在随机分裂上训练的多个模型,期望集成的表现可能比平均单个模型更稳定和更好。
我们将从问题域生成 10 倍多的样本点,并将它们作为一个看不见的数据集保留下来。在这个大得多的数据集上对模型的评估将被用作这个问题的模型的泛化误差的代理或更精确的估计。
这个额外的数据集不是测试数据集。从技术上讲,这是为了演示的目的,但我们假装在模型训练时无法获得这些数据。
# generate 2d classification dataset
dataX, datay = make_blobs(n_samples=55000, centers=3, n_features=2, cluster_std=2, random_state=2)
X, newX = dataX[:5000, :], dataX[5000:, :]
y, newy = datay[:5000], datay[5000:]
所以现在我们有 5000 个例子来训练我们的模型并估计它的一般表现。我们还有 50,000 个例子,可以用来更好地逼近单个模型或集合的真实总体表现。
接下来,我们需要一个函数来拟合和评估训练数据集上的单个模型,并返回拟合模型在测试数据集上的表现。我们还需要合适的模型,这样我们就可以把它作为一个整体的一部分。下面的 evaluate_model()函数实现了这种行为。
# evaluate a single mlp model
def evaluate_model(trainX, trainy, testX, testy):
# encode targets
trainy_enc = to_categorical(trainy)
testy_enc = to_categorical(testy)
# define model
model = Sequential()
model.add(Dense(50, input_dim=2, activation='relu'))
model.add(Dense(3, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
# fit model
model.fit(trainX, trainy_enc, epochs=50, verbose=0)
# evaluate the model
_, test_acc = model.evaluate(testX, testy_enc, verbose=0)
return model, test_acc
接下来,我们可以创建训练数据集的随机分割,并在每个分割上拟合和评估模型。
我们可以使用 Sklearn 库中的 train_test_split()函数将数据集随机分割成训练集和测试集。它以 X 和 y 数组作为参数,并且“ test_size ”以百分比的形式指定测试数据集的大小。我们将使用 5000 个例子中的 10%作为测试。
然后,我们可以调用 evaluate_model()来拟合和评估模型。然后可以将返回的准确率和模型添加到列表中供以后使用。
在本例中,我们将限制拆分的数量,并依次将拟合模型的数量限制为 10 个。
# multiple train-test splits
n_splits = 10
scores, members = list(), list()
for _ in range(n_splits):
# split data
trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.10)
# evaluate model
model, test_acc = evaluate_model(trainX, trainy, testX, testy)
print('>%.3f' % test_acc)
scores.append(test_acc)
members.append(model)
在拟合和评估模型之后,我们可以使用为域选择的配置来估计给定模型的预期表现。
# summarize expected performance
print('Estimated Accuracy %.3f (%.3f)' % (mean(scores), std(scores)))
我们不知道有多少模型会在集合中有用。很可能会有一个收益递减点,在此之后,更多成员的加入不再改变集成的表现。
然而,我们可以评估从 1 到 10 的不同集合大小,并在看不见的保持数据集上绘制它们的表现。
我们还可以评估保持数据集上的每个模型,并计算这些分数的平均值,以更好地近似所选模型在预测问题上的真实表现。
# evaluate different numbers of ensembles on hold out set
single_scores, ensemble_scores = list(), list()
for i in range(1, n_splits+1):
ensemble_score = evaluate_n_members(members, i, newX, newy)
newy_enc = to_categorical(newy)
_, single_score = members[i-1].evaluate(newX, newy_enc, verbose=0)
print('> %d: single=%.3f, ensemble=%.3f' % (i, single_score, ensemble_score))
ensemble_scores.append(ensemble_score)
single_scores.append(single_score)
最后,我们可以比较和计算平均模型在预测问题上的总体表现的更稳健的估计,然后在保持数据集上绘制集成大小对准确率的表现。
# plot score vs number of ensemble members
print('Accuracy %.3f (%.3f)' % (mean(single_scores), std(single_scores)))
x_axis = [i for i in range(1, n_splits+1)]
pyplot.plot(x_axis, single_scores, marker='o', linestyle='None')
pyplot.plot(x_axis, ensemble_scores, marker='o')
pyplot.show()
将所有这些结合在一起,下面列出了完整的示例。
# random-splits mlp ensemble on blobs dataset
from sklearn.datasets import make_blobs
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from keras.utils import to_categorical
from keras.models import Sequential
from keras.layers import Dense
from matplotlib import pyplot
from numpy import mean
from numpy import std
import numpy
from numpy import array
from numpy import argmax
# evaluate a single mlp model
def evaluate_model(trainX, trainy, testX, testy):
# encode targets
trainy_enc = to_categorical(trainy)
testy_enc = to_categorical(testy)
# define model
model = Sequential()
model.add(Dense(50, input_dim=2, activation='relu'))
model.add(Dense(3, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
# fit model
model.fit(trainX, trainy_enc, epochs=50, verbose=0)
# evaluate the model
_, test_acc = model.evaluate(testX, testy_enc, verbose=0)
return model, test_acc
# make an ensemble prediction for multi-class classification
def ensemble_predictions(members, testX):
# make predictions
yhats = [model.predict(testX) for model in members]
yhats = array(yhats)
# sum across ensemble members
summed = numpy.sum(yhats, axis=0)
# argmax across classes
result = argmax(summed, axis=1)
return result
# evaluate a specific number of members in an ensemble
def evaluate_n_members(members, n_members, testX, testy):
# select a subset of members
subset = members[:n_members]
# make prediction
yhat = ensemble_predictions(subset, testX)
# calculate accuracy
return accuracy_score(testy, yhat)
# generate 2d classification dataset
dataX, datay = make_blobs(n_samples=55000, centers=3, n_features=2, cluster_std=2, random_state=2)
X, newX = dataX[:5000, :], dataX[5000:, :]
y, newy = datay[:5000], datay[5000:]
# multiple train-test splits
n_splits = 10
scores, members = list(), list()
for _ in range(n_splits):
# split data
trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.10)
# evaluate model
model, test_acc = evaluate_model(trainX, trainy, testX, testy)
print('>%.3f' % test_acc)
scores.append(test_acc)
members.append(model)
# summarize expected performance
print('Estimated Accuracy %.3f (%.3f)' % (mean(scores), std(scores)))
# evaluate different numbers of ensembles on hold out set
single_scores, ensemble_scores = list(), list()
for i in range(1, n_splits+1):
ensemble_score = evaluate_n_members(members, i, newX, newy)
newy_enc = to_categorical(newy)
_, single_score = members[i-1].evaluate(newX, newy_enc, verbose=0)
print('> %d: single=%.3f, ensemble=%.3f' % (i, single_score, ensemble_score))
ensemble_scores.append(ensemble_score)
single_scores.append(single_score)
# plot score vs number of ensemble members
print('Accuracy %.3f (%.3f)' % (mean(single_scores), std(single_scores)))
x_axis = [i for i in range(1, n_splits+1)]
pyplot.plot(x_axis, single_scores, marker='o', linestyle='None')
pyplot.plot(x_axis, ensemble_scores, marker='o')
pyplot.show()
运行该示例首先在 10 个不同的数据集随机分割成训练集和测试集上拟合和评估 10 个模型。
根据这些分数,我们估计数据集上的平均模型拟合将达到约 83%的准确率,标准偏差约为 1.9%。
>0.816
>0.836
>0.818
>0.806
>0.814
>0.824
>0.830
>0.848
>0.868
>0.858
Estimated Accuracy 0.832 (0.019)
然后,我们在未看到的数据集上评估每个模型的表现,以及从 1 到 10 个模型的模型集合的表现。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
从这些分数中,我们可以看出,平均模型在这个问题上的表现的更准确估计约为 82%,并且估计的表现是乐观的。
> 1: single=0.821, ensemble=0.821
> 2: single=0.821, ensemble=0.820
> 3: single=0.820, ensemble=0.820
> 4: single=0.820, ensemble=0.821
> 5: single=0.821, ensemble=0.821
> 6: single=0.820, ensemble=0.821
> 7: single=0.820, ensemble=0.821
> 8: single=0.820, ensemble=0.821
> 9: single=0.820, ensemble=0.820
> 10: single=0.820, ensemble=0.821
Accuracy 0.820 (0.000)
准确度分数之间的很大差异是以百分比的分数出现的。
创建了一个图表,将未看到的保持数据集上每个单独模型的准确率显示为蓝点,将给定数量的成员(从 1 到 10)的集合的表现显示为橙色线和点。
我们可以看到,至少在这种情况下,使用 4 到 8 个成员的集合会产生比大多数单独运行更好的准确率(橙色线在许多蓝点上方)。
显示随机分割重采样的单一模型准确率(蓝点)与不同大小集合准确率的线图
该图确实显示了一些单个模型可以比一组模型表现得更好(橙色线上方的蓝点),但是我们无法选择这些模型。这里,我们证明了在没有额外数据(例如,样本外数据集)的情况下,4 到 8 个成员的集合将比随机选择的训练测试模型给出更好的平均表现。
更多的重复(例如 30 或 100)可以导致更稳定的集成表现。
交叉验证一起
作为估计模型平均表现的重采样方法,重复随机分割的一个问题是它是乐观的。
一种被设计得不太乐观并因此被广泛使用的方法是 k 倍交叉验证法。
该方法偏差较小,因为数据集中的每个示例在测试数据集中仅使用一次来估计模型表现,这与随机训练测试分割不同,在随机训练测试分割中,给定的示例可能被多次用于评估模型。
该过程有一个名为 k 的参数,它表示给定数据样本要分成的组数。每个模型得分的平均值提供了对模型表现的偏差较小的估计。k 的典型值是 10。
因为神经网络模型的训练在计算上非常昂贵,所以通常在交叉验证期间使用表现最好的模型作为最终模型。
或者,来自交叉验证过程的结果模型可以被组合以提供交叉验证集合,该集合可能比给定的单个模型平均具有更好的表现。
我们可以使用 scikit 中的 KFold 类-学会将数据集拆分成 k 个折叠。它将拆分的次数、是否对样本进行混洗以及混洗前使用的伪随机数发生器的种子作为参数。
# prepare the k-fold cross-validation configuration
n_folds = 10
kfold = KFold(n_folds, True, 1)
一旦类被实例化,它就可以被枚举,以将索引的每个分割部分放入训练集和测试集的数据集中。
# cross validation estimation of performance
scores, members = list(), list()
for train_ix, test_ix in kfold.split(X):
# select samples
trainX, trainy = X[train_ix], y[train_ix]
testX, testy = X[test_ix], y[test_ix]
# evaluate model
model, test_acc = evaluate_model(trainX, trainy, testX, testy)
print('>%.3f' % test_acc)
scores.append(test_acc)
members.append(model)
一旦计算出每个折叠的得分,得分的平均值就可以用来报告该方法的预期表现。
# summarize expected performance
print('Estimated Accuracy %.3f (%.3f)' % (mean(scores), std(scores)))
现在我们已经收集了在 10 个折叠上评估的 10 个模型,我们可以使用它们来创建一个交叉验证集合。在集成中使用所有 10 个模型似乎是直观的,然而,我们可以像上一节一样,从 1 到 10 个成员来评估集成的每个子集的准确性。
下面列出了分析交叉验证集成的完整示例。
# cross-validation mlp ensemble on blobs dataset
from sklearn.datasets import make_blobs
from sklearn.model_selection import KFold
from sklearn.metrics import accuracy_score
from keras.utils import to_categorical
from keras.models import Sequential
from keras.layers import Dense
from matplotlib import pyplot
from numpy import mean
from numpy import std
import numpy
from numpy import array
from numpy import argmax
# evaluate a single mlp model
def evaluate_model(trainX, trainy, testX, testy):
# encode targets
trainy_enc = to_categorical(trainy)
testy_enc = to_categorical(testy)
# define model
model = Sequential()
model.add(Dense(50, input_dim=2, activation='relu'))
model.add(Dense(3, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
# fit model
model.fit(trainX, trainy_enc, epochs=50, verbose=0)
# evaluate the model
_, test_acc = model.evaluate(testX, testy_enc, verbose=0)
return model, test_acc
# make an ensemble prediction for multi-class classification
def ensemble_predictions(members, testX):
# make predictions
yhats = [model.predict(testX) for model in members]
yhats = array(yhats)
# sum across ensemble members
summed = numpy.sum(yhats, axis=0)
# argmax across classes
result = argmax(summed, axis=1)
return result
# evaluate a specific number of members in an ensemble
def evaluate_n_members(members, n_members, testX, testy):
# select a subset of members
subset = members[:n_members]
# make prediction
yhat = ensemble_predictions(subset, testX)
# calculate accuracy
return accuracy_score(testy, yhat)
# generate 2d classification dataset
dataX, datay = make_blobs(n_samples=55000, centers=3, n_features=2, cluster_std=2, random_state=2)
X, newX = dataX[:5000, :], dataX[5000:, :]
y, newy = datay[:5000], datay[5000:]
# prepare the k-fold cross-validation configuration
n_folds = 10
kfold = KFold(n_folds, True, 1)
# cross validation estimation of performance
scores, members = list(), list()
for train_ix, test_ix in kfold.split(X):
# select samples
trainX, trainy = X[train_ix], y[train_ix]
testX, testy = X[test_ix], y[test_ix]
# evaluate model
model, test_acc = evaluate_model(trainX, trainy, testX, testy)
print('>%.3f' % test_acc)
scores.append(test_acc)
members.append(model)
# summarize expected performance
print('Estimated Accuracy %.3f (%.3f)' % (mean(scores), std(scores)))
# evaluate different numbers of ensembles on hold out set
single_scores, ensemble_scores = list(), list()
for i in range(1, n_folds+1):
ensemble_score = evaluate_n_members(members, i, newX, newy)
newy_enc = to_categorical(newy)
_, single_score = members[i-1].evaluate(newX, newy_enc, verbose=0)
print('> %d: single=%.3f, ensemble=%.3f' % (i, single_score, ensemble_score))
ensemble_scores.append(ensemble_score)
single_scores.append(single_score)
# plot score vs number of ensemble members
print('Accuracy %.3f (%.3f)' % (mean(single_scores), std(single_scores)))
x_axis = [i for i in range(1, n_folds+1)]
pyplot.plot(x_axis, single_scores, marker='o', linestyle='None')
pyplot.plot(x_axis, ensemble_scores, marker='o')
pyplot.show()
运行该示例首先打印 10 个模型在交叉验证的每个折叠上的表现。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
据报道,这些模型的平均表现约为 82%,这似乎不如前一节中使用的随机拆分方法乐观。
>0.834
>0.870
>0.818
>0.806
>0.836
>0.804
>0.820
>0.830
>0.828
>0.822
Estimated Accuracy 0.827 (0.018)
接下来,在看不见的保持集上评估每个保存的模型。
这些分数的平均值也约为 82%,这突出表明,至少在这种情况下,对模型总体表现的交叉验证估计是合理的。
> 1: single=0.819, ensemble=0.819
> 2: single=0.820, ensemble=0.820
> 3: single=0.820, ensemble=0.820
> 4: single=0.821, ensemble=0.821
> 5: single=0.820, ensemble=0.821
> 6: single=0.821, ensemble=0.821
> 7: single=0.820, ensemble=0.820
> 8: single=0.819, ensemble=0.821
> 9: single=0.820, ensemble=0.821
> 10: single=0.820, ensemble=0.821
Accuracy 0.820 (0.001)
创建单个模型准确率(蓝点)和集合大小与准确率(橙色线)的关系图。
与前面的示例一样,模型表现之间的真正差异在于模型准确率的几分之一。
橙色线表明,随着成员数量的增加,集合的准确性会增加到收益递减的程度。
我们可以看到,至少在这种情况下,在一个集成中使用四个或更多的模型进行交叉验证比几乎所有的单个模型都具有更好的表现。
我们还可以看到,使用集合中所有模型的默认策略将是有效的。
显示交叉验证重采样的单一模型准确率(蓝点)与不同大小集合准确率的线图
装袋集成
从集成学习的角度来看,随机分裂和 k-fold 交叉验证的一个限制是模型非常相似。
自举法是一种统计技术,用于通过对多个小数据样本的估计进行平均来估计一个群体的数量。
重要的是,样本是通过一次从一个大的数据样本中提取观察值,并在选择后将其返回给数据样本来构建的。这使得一个给定的观察可以不止一次地包含在一个给定的小样本中。这种采样方法称为替换采样。
该方法可用于估计神经网络模型的表现。给定样本中未选择的示例可以用作测试集来估计模型的表现。
自举是一种用于估计模型表现的稳健方法。它确实受到乐观偏见的影响,但在实践中通常几乎和 k 倍交叉验证一样准确。
集成学习的好处是,每个模型的每个数据样本都有偏差,允许给定的示例在样本中出现多次。反过来,这意味着在这些样本上训练的模型会有偏差,重要的是方式不同。结果可能是更准确的集合预测。
通常,在集成学习中使用自举方法被称为自举聚合或装袋。
我们可以使用 scikit 的重采样()功能-学习选择一个有替换的子样本。该函数采用一个数组进行二次采样,再采样的大小作为参数。我们将在行索引中执行选择,我们可以依次使用行索引来选择 X 和 y 数组中的行。
样本的大小将为 4,500,即 90%的数据,尽管在使用重采样的情况下,测试集可能大于 10%,但可能有 500 多个示例未被选择。
# multiple train-test splits
n_splits = 10
scores, members = list(), list()
for _ in range(n_splits):
# select indexes
ix = [i for i in range(len(X))]
train_ix = resample(ix, replace=True, n_samples=4500)
test_ix = [x for x in ix if x not in train_ix]
# select data
trainX, trainy = X[train_ix], y[train_ix]
testX, testy = X[test_ix], y[test_ix]
# evaluate model
model, test_acc = evaluate_model(trainX, trainy, testX, testy)
print('>%.3f' % test_acc)
scores.append(test_acc)
members.append(model)
当使用 bagging 集成学习策略时,通常使用简单的 overfit 模型,如未运行的决策树。
过度约束和过度训练的神经网络可以获得更好的表现。然而,在这个例子中,我们将使用前面章节中相同的 MLP。
此外,通常在装袋中继续添加集成成员,直到集成表现稳定下来,因为装袋不会过度填充数据集。我们将再次像前面的例子一样将成员人数限制在 10 人。
下面列出了使用多层感知器评估模型表现和集成学习的自举聚合的完整示例。
# bagging mlp ensemble on blobs dataset
from sklearn.datasets import make_blobs
from sklearn.utils import resample
from sklearn.metrics import accuracy_score
from keras.utils import to_categorical
from keras.models import Sequential
from keras.layers import Dense
from matplotlib import pyplot
from numpy import mean
from numpy import std
import numpy
from numpy import array
from numpy import argmax
# evaluate a single mlp model
def evaluate_model(trainX, trainy, testX, testy):
# encode targets
trainy_enc = to_categorical(trainy)
testy_enc = to_categorical(testy)
# define model
model = Sequential()
model.add(Dense(50, input_dim=2, activation='relu'))
model.add(Dense(3, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
# fit model
model.fit(trainX, trainy_enc, epochs=50, verbose=0)
# evaluate the model
_, test_acc = model.evaluate(testX, testy_enc, verbose=0)
return model, test_acc
# make an ensemble prediction for multi-class classification
def ensemble_predictions(members, testX):
# make predictions
yhats = [model.predict(testX) for model in members]
yhats = array(yhats)
# sum across ensemble members
summed = numpy.sum(yhats, axis=0)
# argmax across classes
result = argmax(summed, axis=1)
return result
# evaluate a specific number of members in an ensemble
def evaluate_n_members(members, n_members, testX, testy):
# select a subset of members
subset = members[:n_members]
# make prediction
yhat = ensemble_predictions(subset, testX)
# calculate accuracy
return accuracy_score(testy, yhat)
# generate 2d classification dataset
dataX, datay = make_blobs(n_samples=55000, centers=3, n_features=2, cluster_std=2, random_state=2)
X, newX = dataX[:5000, :], dataX[5000:, :]
y, newy = datay[:5000], datay[5000:]
# multiple train-test splits
n_splits = 10
scores, members = list(), list()
for _ in range(n_splits):
# select indexes
ix = [i for i in range(len(X))]
train_ix = resample(ix, replace=True, n_samples=4500)
test_ix = [x for x in ix if x not in train_ix]
# select data
trainX, trainy = X[train_ix], y[train_ix]
testX, testy = X[test_ix], y[test_ix]
# evaluate model
model, test_acc = evaluate_model(trainX, trainy, testX, testy)
print('>%.3f' % test_acc)
scores.append(test_acc)
members.append(model)
# summarize expected performance
print('Estimated Accuracy %.3f (%.3f)' % (mean(scores), std(scores)))
# evaluate different numbers of ensembles on hold out set
single_scores, ensemble_scores = list(), list()
for i in range(1, n_splits+1):
ensemble_score = evaluate_n_members(members, i, newX, newy)
newy_enc = to_categorical(newy)
_, single_score = members[i-1].evaluate(newX, newy_enc, verbose=0)
print('> %d: single=%.3f, ensemble=%.3f' % (i, single_score, ensemble_score))
ensemble_scores.append(ensemble_score)
single_scores.append(single_score)
# plot score vs number of ensemble members
print('Accuracy %.3f (%.3f)' % (mean(single_scores), std(single_scores)))
x_axis = [i for i in range(1, n_splits+1)]
pyplot.plot(x_axis, single_scores, marker='o', linestyle='None')
pyplot.plot(x_axis, ensemble_scores, marker='o')
pyplot.show()
运行该示例将打印每个引导示例的未使用示例的模型表现。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
我们可以看到,在这种情况下,模型的预期表现不如随机训练测试分割乐观,可能与 k 倍交叉验证的发现非常相似。
>0.829
>0.820
>0.830
>0.821
>0.831
>0.820
>0.834
>0.815
>0.829
>0.827
Estimated Accuracy 0.825 (0.006)
也许是由于引导采样过程,我们看到每个模型的实际表现在更大的看不见的保持数据集上稍差一些。
考虑到用自举代替采样引入的偏差,这是可以预期的。
> 1: single=0.819, ensemble=0.819
> 2: single=0.818, ensemble=0.820
> 3: single=0.820, ensemble=0.820
> 4: single=0.818, ensemble=0.821
> 5: single=0.819, ensemble=0.820
> 6: single=0.820, ensemble=0.820
> 7: single=0.820, ensemble=0.820
> 8: single=0.819, ensemble=0.820
> 9: single=0.820, ensemble=0.820
> 10: single=0.819, ensemble=0.820
Accuracy 0.819 (0.001)
创造的情节令人鼓舞。
我们看到,在大约四个成员之后,袋装集成在保持数据集上取得了比任何单个模型更好的表现。毫无疑问,鉴于个别车型的平均表现略低。
显示单个模型准确率(蓝点)与不同装袋尺寸的集成准确率的线图
扩展ˌ扩张
本节列出了一些您可能希望探索的扩展教程的想法。
- 单车型。将每个集合的表现与基于所有可用数据训练的一个模型进行比较。
- CV 集合大小。为交叉验证集成试验更大和更小的集成大小,并比较它们的表现。
- 装袋集合限额。增加装袋团队的成员数量,找到收益递减点。
如果你探索这些扩展,我很想知道。
进一步阅读
如果您想更深入地了解这个主题,本节将提供更多资源。
邮件
报纸
- 神经网络集成、交叉验证和主动学习,1995。
应用程序接口
- 开始使用 Keras 顺序模型
- 硬核层 API
- scipy . stat . mode API
- num py . argmax API
- sklearn . dataset . make _ blobs API
- sklearn . model _ selection . train _ test _ split API
- sklearn.model_selection。KFold 原料药
- 硬化实用程序。重新压缩 API
摘要
在本教程中,您发现了如何为深度学习神经网络模型开发一套不同的基于重采样的集成。
具体来说,您了解到:
- 如何使用随机拆分来评估模型表现,并根据模型开发一个集成。
- 如何使用 10 倍交叉验证来评估表现,并开发交叉验证集成。
- 如何使用 bootstrap 估计表现,并使用 bagging 集成组合模型。
你有什么问题吗? 在下面的评论中提问,我会尽力回答。
如何通过深度学习展示自己的基本功
最后更新于 2019 年 8 月 6 日
深度学习的技能非常受欢迎,尽管这些技能很难识别和展示。
解释你熟悉一种技术或一类问题与能够在真实数据集上使用开源 API 有效地使用它是非常不同的。
作为一名深度学习实践者,展示技能的最有效方式也许是开发模型。从业者可以在标准的公开可用的机器学习数据集上练习,并建立一个已完成项目的组合,以利用未来的项目并展示能力。
在这篇文章中,您将发现如何使用小项目来展示使用深度学习进行预测建模的基本能力。
看完这篇文章,你会知道:
- 解释深入学习数学、理论和方法不足以证明能力。
- 开发一个完整的小项目组合可以让你展示你开发和交付熟练模型的能力。
- 使用一个系统的五步项目模板来执行项目,使用一个九步模板来展示结果,可以让你有条不紊地完成项目,并清晰地传达发现。
用我的新书更好的深度学习启动你的项目,包括分步教程和所有示例的 Python 源代码文件。
我们开始吧。
可用于展示基本深度学习能力的简单框架 图片由安吉拉和安德鲁提供,版权所有。
概观
本教程分为五个部分;它们是:
- 你将如何展示基本的深度学习能力?
- 用投资组合展示技能
- 如何选择投资组合项目
- 系统项目模板
- 呈现结果的模板
你将如何展示基本的深度学习能力?
你如何知道你有预测建模问题的深度学习方法的基本能力?
- 也许你读过一本书?
- 也许你已经完成了一些教程?
- 也许你对原料药很熟悉?
如果必须的话,你会如何向别人展示这种能力?
- 也许你能解释一下常见的问题以及如何解决它们?
- 或许你可以总结一下流行的技巧?
- 也许你可以参考著名的论文?
这样够吗?
如果你正在为一个角色雇佣一个深度学习的实践者,这会让你满意吗?
***不够,也不会让我满意。*T3】
用投资组合展示技能
解决方案是使用现代企业雇佣开发人员的相同技术。
开发人员可以整天被问到数学和算法是如何工作的,但是企业需要的是能够提供工作和可维护代码的人。
这同样适用于深度学习实践者。
从业者可以整天被问到梯度下降和反向传播的数学问题,但企业需要的是能够提供稳定模型和熟练预测的人。
这可以通过使用开源深度学习库和标准机器学习数据集开发已完成项目的组合来实现。
该产品组合有三个主要用途:
- 发展技能。从业者可以使用项目组合来逐步开发和展示技能,利用以前的项目在更大和更具挑战性的未来项目中的工作。
- 展示技能。雇主可以使用投资组合来确认从业者能够提供稳定的结果和熟练的预测。
- 讨论技能。作品集可以作为面试中讨论的起点,在面试中,技术、结果和设计决策被描述、理解和辩护。
有许多问题类型和许多专门类型的数据加载和神经网络模型来解决它们,例如计算机视觉、时间序列和自然语言处理中的问题。
在专业化之前,你必须能够展示基本技能。具体来说,你必须能够证明你能够使用深度学习的技术系统地完成应用机器学习项目的步骤。
这就提出了一个问题:
- ***你应该用什么项目来展示基本技能,这些项目应该如何组织才能最好地展示这些技能?*T3】
如何选择投资组合项目
使用标准和公开可用的机器学习数据集。
理想情况下,有些数据集可以通过许可使用,如公共领域、GPL 或知识共享,这样您就可以自由复制它们,甚至可以将它们与您完成的项目一起重新分发。
选择数据集的方式有很多,比如对领域的兴趣、之前的经验、难度等。
相反,我建议在选择投资组合中包含的数据集时要有策略。我推荐的三种数据集选择方法是:
- 受欢迎程度:一个很好的起点可能是选择受欢迎的数据集,比如在被浏览次数最多或引用次数最多的数据集当中。流行的数据集很常见,可以提供比较点,并有助于简化它们的表示。
- 问题类型。另一种方法可能是根据问题的一般类别选择数据集,例如回归、二进制分类和多类分类。展示基本问题类型的技能很重要。
- 问题属性。最后一种方法可能是基于数据集的特定属性来选择数据集,您希望展示其熟练程度,例如类不平衡、输入变量类型混合等。这一领域经常被忽视,真实数据很少像标准机器学习数据集那样干净和简单;寻找更具挑战性的例子提供了出色的演示。
定位和下载标准机器学习数据集的两个绝佳位置是:
小数据。我建议从适合内存的小数据集开始,比如 UCI 机器学习存储库中的许多数据集。这是因为它允许您专注于数据准备和建模,至少最初是这样,并快速完成许多不同的配置。更大的数据集导致训练模型的速度更慢,并且可能需要云基础设施。
足够好的表现。我还建议不要以数据集上的最佳模型表现为目标。数据集实际上是预测建模问题的一种表现形式,在现实中它可以成为一个没有尽头的研究项目。相反,重点是为定义一个熟练的模型建立一个门槛,然后证明你可以为这个问题开发和运用一个熟练的模型。
小范围。最后,我建议保持项目规模小,最好在正常工作日完成,尽管你可能需要在晚上和周末分散工作。每个项目都有一个目标:系统地处理数据集并交付一个熟练的模型。请注意,如果没有小心的时间拳击,这个项目很容易从你身边溜走。
总之:
- 使用标准的公开可用的机器学习数据集。
- 战略性地选择数据集。
- 更喜欢适合内存的较小数据集。
- 目标是开发有技巧的模型,而不是最优模型。
- 保持每个项目小而集中。
这种性质的已完成项目提供了许多好处,包括:
- 演示解决预测问题的方法。
- 演示适用于数据处理和模型评估的 API 知识。
- 用特定的深度学习模型和技术展示能力。
- 演示时间和范围管理,假设您交付了一个熟练的模型。
- 在结果和发现的展示中展示良好的沟通。
系统项目模板
以系统的方式处理给定的数据集至关重要。
在预测建模问题中有标准的步骤,并且系统化表明您知道这些步骤,并且已经在项目中考虑过它们。
在投资组合项目上系统化突出了你在新项目上同样系统化。
投资组合中项目的步骤可能包括以下内容。
- 问题描述。描述预测建模问题,包括领域和相关背景。
- 汇总数据。描述可用的数据,包括统计摘要和数据可视化。
- 评估模型。抽查一套模型类型、配置、数据准备方案等,以缩小问题的范围。
- 提升表现。提高一个或多个模型的表现,这些模型可以很好地配合超参数调整和集成方法。
- 显示结果。介绍项目的发现。
在这个过程之前的第一步,第零步,可能是选择您希望用于演示的开源深度学习和机器学习库。
我鼓励你尽可能缩小范围。一些额外的提示包括:
- 使用重复的 k-fold 交叉验证来评估模型,尤其是使用适合内存的较小数据集。
- 使用保持测试集,该测试集可用于展示做出预测和评估最终最佳表现模型的能力。
- 建立一个基线表现,以便提供一个阈值来定义熟练或非熟练模型。
- 公开展示你的结果,包括所有的代码和数据,最好是你拥有和控制的公共位置,比如 GitHub 或者博客。
善于以这种方式完成项目是非常宝贵的。你总是能够很快得到好的结果。
具体来说,在几个小时到几天内,高于平均水平,甚至可能比最佳质量差几个百分点。即使在标准问题上,也很少有从业者如此自律和高效。
呈现结果的模板
这个项目可能只有你展示它的能力好,包括结果和发现。
我强烈建议您使用以下一种(或全部)方法来展示您的项目:
- 博文。把你的结果写在你自己的博客上。
- GitHub 资源库。将所有代码和数据存储在 GitHub 存储库中,并使用允许丰富文本和图像的托管 Markdown 文件或笔记本来呈现结果。
- YouTube 视频。用视频形式展示你的结果和发现,也许用幻灯片。
我也强烈建议您在开始项目之前定义演示的结构,并在开始时填写细节。
我在展示项目结果时推荐的模板如下:
- 1.问题描述。描述正在解决的问题、数据来源、输入和输出。
- 2.数据汇总。描述数据中的分布和关系,可能还有数据准备和建模的想法。
- 3.测试线束。描述如何执行模型选择,包括重采样方法和模型评估指标。
- 4.基线表现。描述定义模型是否熟练的基线模型表现(使用测试工具)。
- 5.实验结果。展示实验结果,也许测试一套模型、模型配置、数据准备方案等等。每个小节都应该有某种形式的:
- 5.1 意图:为什么要运行实验?
- 5.2 期望:实验的预期结果是什么?
- 5.3 方法:实验中使用了哪些数据、模型、配置?
- 5.4 结果:实验的实际结果是什么?
- 5.5 发现:结果是什么意思,和预期有什么关系,还启发了哪些实验?
- 6.改进 ( 可选)。描述尝试提高表现更好的模型的表现的实验结果,例如超参数调整和集成方法。
- 7.最终模型。描述最终型号的选择,包括配置和表现。演示保存和加载模型以及演示在保持数据集上进行预测的能力是一个好主意。
- 8.延伸。描述项目中已考虑但未解决的、将来可以探索的领域。
- 9.资源。描述对数据、代码、应用编程接口、论文等的相关引用。
这些可以是帖子或报告中的部分,也可以是幻灯片演示的部分。
进一步阅读
如果您想更深入地了解这个主题,本节将提供更多资源。
邮件
资料组
摘要
在这篇文章中,您发现了如何展示使用深度学习进行预测建模的基本能力。
具体来说,您了解到:
- 解释深入学习数学、理论和方法不足以证明能力。
- 开发一个完整的小项目组合可以让你展示你开发和交付熟练模型的能力。
- 使用一个系统的五步项目模板来执行项目,使用一个九步模板来展示结果,可以让您既有条不紊地完成项目,又能清楚地传达发现的问题。
你有什么问题吗? 在下面的评论中提问,我会尽力回答。
如何使用 ReLU 修复梯度消失问题
最后更新于 2020 年 8 月 25 日
梯度消失问题是训练深度神经网络时可能遇到的不稳定行为的一个例子。
它描述了深层多层前馈网络或递归神经网络无法将有用的梯度信息从模型的输出端传播回模型输入端附近的层的情况。
结果是具有许多层的模型通常无法在给定的数据集上学习,或者具有许多层的模型过早地收敛到一个糟糕的解决方案。
已经提出并研究了许多修复和解决方法,例如替代权重初始化方案、无监督预训练、分层训练以及梯度下降的变化。或许最常见的变化是使用了修正线性激活函数,该函数已成为新的默认函数,而不是上世纪 90 年代末和 2000 年代默认的双曲正切激活函数。
在本教程中,您将发现如何在训练神经网络模型时诊断梯度消失问题,以及如何使用替代激活函数和权重初始化方案来修复它。
完成本教程后,您将知道:
- 梯度消失问题限制了具有经典流行的激活函数(如双曲正切)的深度神经网络的发展。
- 如何使用 ReLU 和 He 权重初始化来固定用于分类的深度神经网络多层感知器。
- 如何使用 TensorBoard 诊断一个消失的梯度问题,并确认 ReLU 的影响,以改善通过模型的梯度流。
用我的新书更好的深度学习启动你的项目,包括分步教程和所有示例的 Python 源代码文件。
我们开始吧。
- 2019 年 10 月更新:针对 Keras 2.3 和 TensorFlow 2.0 更新。
如何使用校正的线性激活功能来修复消失的梯度 图片由利亚姆·莫洛尼提供,版权所有。
教程概述
本教程分为五个部分;它们是:
- 梯度消失问题
- 两个圆的二分类问题
- 两个圆问题的多层感知器模型
- 两个圆问题的 ReLU 深 MLP 模型
- 查看培训期间的平均梯度大小
梯度消失问题
使用随机梯度下降来训练神经网络。
这包括首先计算模型产生的预测误差,并使用该误差来估计用于更新网络中每个权重的梯度,以便下次产生较小的误差。这种误差梯度通过网络从输出层向后传播到输入层。
期望训练具有许多层的神经网络,因为更多层的增加增加了网络的容量,使其能够学习大的训练数据集,并有效地表示从输入到输出的更复杂的映射函数。
具有许多层的训练网络(例如深度神经网络)的一个问题是,当梯度通过网络向后传播时,梯度急剧减小。当误差到达接近模型输入的层时,误差可能非常小,以至于影响很小。因此,这个问题被称为“消失梯度”问题。
梯度消失使得很难知道参数应该向哪个方向移动来改善成本函数…
—第 290 页,深度学习,2016。
事实上,误差梯度在深度神经网络中可能是不稳定的,不仅会消失,还会爆炸,其中梯度随着它在网络中向后传播而呈指数级增加。这被称为“爆炸梯度”问题。
术语梯度消失指的是这样一个事实,即在前馈网络(FFN)中,反向传播的误差信号通常作为距最终层的距离的函数而指数地减小(或增大)。
——训练超深前馈网络的随机游走初始化,2014。
梯度消失是递归神经网络的一个特殊问题,因为网络的更新包括为每个输入时间步长展开网络,实际上创建了一个需要权重更新的非常深的网络。一个适度的递归神经网络可能有 200 到 400 个输入时间步长,在概念上形成一个非常深的网络。
在多层感知器中,梯度消失问题可以通过训练期间模型的缓慢改进速率以及可能的过早收敛来表现,例如,持续训练不会导致任何进一步的改进。检查训练期间权重的变化,我们会看到更靠近输出层的层中发生更多的变化(即更多的学习),而靠近输入层的层中发生更少的变化。
有许多技术可以用来降低前馈神经网络的梯度消失问题的影响,最显著的是交替权重初始化方案和交替激活函数的使用。
已经研究并应用了训练深度网络(前馈和递归)的不同方法[以努力解决梯度消失],例如预训练、更好的随机初始缩放、更好的优化方法、特定架构、正交初始化等。
——训练超深前馈网络的随机游走初始化,2014。
在本教程中,我们将仔细研究替代权重初始化方案和激活函数的使用,以允许更深层次的神经网络模型的训练。
两个圆的二分类问题
作为我们探索的基础,我们将使用一个非常简单的两类或二分类问题。
Sklearn 类提供了 make_circles()函数,该函数可用于创建具有规定样本数和统计噪声的二进制分类问题。
每个示例都有两个输入变量,用于定义点在二维平面上的 x 和 y 坐标。这两个类的点排列成两个同心圆(它们有相同的中心)。
数据集中的点数由参数指定,其中一半将从每个圆中绘制。通过定义噪声标准偏差的“噪声”参数对点进行采样时,可以添加高斯噪声,其中 0.0 表示没有噪声或从圆圈中精确绘制的点。伪随机数发生器的种子可以通过“ random_state ”参数指定,该参数允许每次调用函数时对完全相同的点进行采样。
下面的示例从两个带有噪声且值为 1 的圆生成 1,000 个示例来播种伪随机数发生器。
# generate circles
X, y = make_circles(n_samples=1000, noise=0.1, random_state=1)
我们可以创建数据集的图形,绘制输入变量( X )的 x 和 y 坐标,并用类值(0 或 1)给每个点着色。
下面列出了完整的示例。
# scatter plot of the circles dataset with points colored by class
from sklearn.datasets import make_circles
from numpy import where
from matplotlib import pyplot
# generate circles
X, y = make_circles(n_samples=1000, noise=0.1, random_state=1)
# select indices of points with each class label
for i in range(2):
samples_ix = where(y == i)
pyplot.scatter(X[samples_ix, 0], X[samples_ix, 1], label=str(i))
pyplot.legend()
pyplot.show()
运行该示例会创建一个图,显示 1,000 个生成的数据点以及用于给每个点着色的每个点的类值。
我们可以看到 0 类的点是蓝色的,代表外圆,1 类的点是橙色的,代表内圆。
生成样本的统计噪声意味着两个圆之间有一些点的重叠,给问题增加了一些模糊性,使其变得不平凡。这是所希望的,因为神经网络可以从许多可能的解决方案中选择一个来分类两个圆之间的点,并且总是产生一些误差。
点按类值着色的圆形数据集散点图
既然我们已经定义了一个问题作为我们探索的基础,我们可以考虑开发一个模型来解决它。
两个圆问题的多层感知器模型
我们可以开发一个多层感知器模型来解决两个圆的问题。
这将是一个简单的前馈神经网络模型,是按照我们在 20 世纪 90 年代末和 21 世纪初被教导的那样设计的。
首先,我们将从两个圆的问题中生成 1000 个数据点,并将输入重新缩放到范围[-1,1]。数据几乎已经在这个范围内,但我们会确保。
通常,我们会使用训练数据集准备数据缩放,并将其应用于测试数据集。为了在本教程中保持简单,我们将在将数据分割成训练集和测试集之前,将所有数据一起缩放。
# generate 2d classification dataset
X, y = make_circles(n_samples=1000, noise=0.1, random_state=1)
# scale input data to [-1,1]
scaler = MinMaxScaler(feature_range=(-1, 1))
X = scaler.fit_transform(X)
接下来,我们将把数据分成训练集和测试集。
一半的数据将用于训练,剩下的 500 个例子将用作测试集。在本教程中,测试集也将作为验证数据集,因此我们可以了解模型在训练期间如何在保持集上执行。
# split into train and test
n_train = 500
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
接下来,我们将定义模型。
对于数据集中的两个变量,该模型将有一个带有两个输入的输入层,一个带有五个节点的隐藏层,以及一个带有一个节点的输出层,用于预测类概率。隐藏层将使用双曲正切激活函数(tanh),输出层将使用逻辑激活函数(sigmoid)来预测 0 类或 1 类或介于两者之间的东西。
在隐藏层中使用双曲正切激活函数是 20 世纪 90 年代和 2000 年代的最佳实践,当在隐藏层中使用时,其表现通常优于逻辑函数。将网络权重从均匀分布初始化为小的随机值也是一种好的做法。这里,我们将从范围[0.0,1.0]中随机初始化权重。
# define model
model = Sequential()
init = RandomUniform(minval=0, maxval=1)
model.add(Dense(5, input_dim=2, activation='tanh', kernel_initializer=init))
model.add(Dense(1, activation='sigmoid', kernel_initializer=init))
该模型使用二元交叉熵损失函数,并使用随机梯度下降进行优化,学习率为 0.01,大动量为 0.9。
# compile model
opt = SGD(lr=0.01, momentum=0.9)
model.compile(loss='binary_crossentropy', optimizer=opt, metrics=['accuracy'])
该模型针对 500 个训练时期进行训练,并且在每个时期结束时与训练数据集一起评估测试数据集。
# fit model
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=500, 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 training history
pyplot.plot(history.history['accuracy'], label='train')
pyplot.plot(history.history['val_accuracy'], label='test')
pyplot.legend()
pyplot.show()
将所有这些结合在一起,下面列出了完整的示例。
# mlp for the two circles classification problem
from sklearn.datasets import make_circles
from sklearn.preprocessing import MinMaxScaler
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import SGD
from keras.initializers import RandomUniform
from matplotlib import pyplot
# generate 2d classification dataset
X, y = make_circles(n_samples=1000, noise=0.1, random_state=1)
# scale input data to [-1,1]
scaler = MinMaxScaler(feature_range=(-1, 1))
X = scaler.fit_transform(X)
# split into train and test
n_train = 500
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# define model
model = Sequential()
init = RandomUniform(minval=0, maxval=1)
model.add(Dense(5, input_dim=2, activation='tanh', kernel_initializer=init))
model.add(Dense(1, activation='sigmoid', kernel_initializer=init))
# compile model
opt = SGD(lr=0.01, momentum=0.9)
model.compile(loss='binary_crossentropy', optimizer=opt, metrics=['accuracy'])
# fit model
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=500, 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 training history
pyplot.plot(history.history['accuracy'], label='train')
pyplot.plot(history.history['val_accuracy'], label='test')
pyplot.legend()
pyplot.show()
运行该示例只需几秒钟就能适应模型。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
我们可以看到,在这种情况下,模型很好地学习了问题,在训练数据集和测试数据集上都达到了大约 81.6%的准确率。
Train: 0.816, Test: 0.816
创建了列车和测试集上模型准确率的线图,显示了所有 500 个训练时期的表现变化。
该图表明,对于本次运行,列车和测试集的表现在 epoch 300 附近以大约 80%的准确率开始下降。
MLP 在两个圆问题中训练和测试集准确率在训练时期的线图
既然我们已经看到了如何使用 tanh 激活函数来开发一个经典的 MLP 来解决两个圆的问题,我们可以考虑修改模型,使其具有更多的隐藏层。
两个圆问题的更深 MLP 模型
传统上,开发深度多层感知器模型具有挑战性。
使用双曲正切激活函数的深度模型不容易训练,这种糟糕的表现大部分归咎于梯度消失问题。
我们可以尝试使用上一节中开发的 MLP 模型对此进行研究。
隐藏层数可以从 1 层增加到 5 层;例如:
# define model
init = RandomUniform(minval=0, maxval=1)
model = Sequential()
model.add(Dense(5, input_dim=2, activation='tanh', kernel_initializer=init))
model.add(Dense(5, activation='tanh', kernel_initializer=init))
model.add(Dense(5, activation='tanh', kernel_initializer=init))
model.add(Dense(5, activation='tanh', kernel_initializer=init))
model.add(Dense(5, activation='tanh', kernel_initializer=init))
model.add(Dense(1, activation='sigmoid', kernel_initializer=init))
然后,我们可以重新运行该示例并查看结果。
下面列出了更深的 MLP 的完整例子。
# deeper mlp for the two circles classification problem
from sklearn.datasets import make_circles
from sklearn.preprocessing import MinMaxScaler
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import SGD
from keras.initializers import RandomUniform
from matplotlib import pyplot
# generate 2d classification dataset
X, y = make_circles(n_samples=1000, noise=0.1, random_state=1)
scaler = MinMaxScaler(feature_range=(-1, 1))
X = scaler.fit_transform(X)
# split into train and test
n_train = 500
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# define model
init = RandomUniform(minval=0, maxval=1)
model = Sequential()
model.add(Dense(5, input_dim=2, activation='tanh', kernel_initializer=init))
model.add(Dense(5, activation='tanh', kernel_initializer=init))
model.add(Dense(5, activation='tanh', kernel_initializer=init))
model.add(Dense(5, activation='tanh', kernel_initializer=init))
model.add(Dense(5, activation='tanh', kernel_initializer=init))
model.add(Dense(1, activation='sigmoid', kernel_initializer=init))
# compile model
opt = SGD(lr=0.01, momentum=0.9)
model.compile(loss='binary_crossentropy', optimizer=opt, metrics=['accuracy'])
# fit model
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=500, 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 training history
pyplot.plot(history.history['accuracy'], label='train')
pyplot.plot(history.history['val_accuracy'], label='test')
pyplot.legend()
pyplot.show()
运行该示例首先打印拟合模型在训练和测试数据集上的表现。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,我们可以看到,在达到大约 50%准确率的训练集和测试集上,表现都很差。这表明所配置的模型不能了解问题,也不能概括解决方案。
Train: 0.530, Test: 0.468
列车上模型准确率的线图和训练期间的测试集讲述了类似的故事。我们可以看到表现很糟糕,实际上随着训练的进行会变得更糟。
两个圆问题中深 MLP 超训练时段的训练和测试集准确率的线图
两个圆问题的 ReLU 深 MLP 模型
在开发多层感知器网络以及其他网络类型(如 CNN)时,修正后的线性激活函数取代了双曲正切激活函数,成为新的首选默认值。
这是因为激活函数看起来和行为都像一个线性函数,使得它更容易训练,更不容易饱和,但实际上是一个非线性函数,迫使负输入值为 0。当训练更深的模型时,它被认为是解决梯度消失问题的一种可能方法。
使用整流线性激活函数(或简称 ReLU)时,最好使用 he 权重初始化方案。我们可以使用 ReLU 和何初始化定义的五个隐藏层,如下所示。
# define model
model = Sequential()
model.add(Dense(5, input_dim=2, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(5, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(5, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(5, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(5, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(1, activation='sigmoid'))
将这些结合在一起,下面列出了完整的代码示例。
# deeper mlp with relu for the two circles classification problem
from sklearn.datasets import make_circles
from sklearn.preprocessing import MinMaxScaler
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import SGD
from keras.initializers import RandomUniform
from matplotlib import pyplot
# generate 2d classification dataset
X, y = make_circles(n_samples=1000, noise=0.1, random_state=1)
scaler = MinMaxScaler(feature_range=(-1, 1))
X = scaler.fit_transform(X)
# split into train and test
n_train = 500
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# define model
model = Sequential()
model.add(Dense(5, input_dim=2, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(5, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(5, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(5, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(5, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(1, activation='sigmoid'))
# compile model
opt = SGD(lr=0.01, momentum=0.9)
model.compile(loss='binary_crossentropy', optimizer=opt, metrics=['accuracy'])
# fit model
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=500, 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 training history
pyplot.plot(history.history['accuracy'], label='train')
pyplot.plot(history.history['val_accuracy'], label='test')
pyplot.legend()
pyplot.show()
运行该示例将打印模型在列车和测试数据集上的表现。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,我们可以看到,这一小小的改变已经允许模型学习问题,在两个数据集上实现了大约 84%的准确率,优于使用 tanh 激活函数的单层模型。
Train: 0.836, Test: 0.840
还创建了列车上的模型准确率和训练时期的测试集的线图。这个情节显示了与我们目前所看到的截然不同的动态。
这个模型似乎很快就学会了这个问题,并在大约 100 个时代内达成了一个解决方案。
用两个圆问题中的 ReLU 对深 MLP 进行超训练时的训练和测试集准确率的线图
ReLU 激活函数的使用使我们能够为这个简单的问题拟合一个更深层次的模型,但是这种能力不会无限扩展。例如,增加层数会导致学习速度变慢,达到大约 20 层时,模型不再能够学习问题,至少在所选的配置下是如此。
例如,下面是具有 15 个隐藏层的同一模型的训练和测试准确率的线图,表明它仍然能够学习问题。
带 15 个隐层的 ReLU 深 MLP 训练和超训练时段测试集准确率线图
下面是具有 20 层的相同模型的各个时期的列车和测试准确率的线图,显示配置不再能够学习问题。
具有 20 个隐藏层的 ReLU 的深 MLP 超训练时期的训练和测试集准确率的线图
虽然 ReLU 的使用是有效的,但我们不能确信 tanh 函数的使用会因为梯度消失而失败,ReLU 会成功,因为它克服了这个问题。
查看培训期间的平均梯度大小
本节假设您使用的是带有 Keras 的 TensorFlow 后端。如果不是这样,您可以跳过这一部分。
在使用 tanh 激活函数的情况下,我们知道网络有足够的容量来学习问题,但是层数的增加阻止了它这样做。
很难将梯度消失诊断为表现不佳的原因。一个可能的信号是查看每个训练时期每层梯度的平均大小。
我们期望靠近输出的层比靠近输入的层具有更大的平均梯度。
Keras 提供了 TensorBoard 回调,可用于记录训练期间模型的属性,例如每层的平均梯度。然后,可以使用张量流提供的张量板界面查看这些统计数据。
我们可以配置这个回调来记录每层每训练时期的平均梯度,然后确保回调被用作训练模型的一部分。
# prepare callback
tb = TensorBoard(histogram_freq=1, write_grads=True)
# fit model
model.fit(trainX, trainy, validation_data=(testX, testy), epochs=500, verbose=0, callbacks=[tb])
我们可以使用这个回调,首先使用双曲正切激活函数研究深度模型拟合中梯度的动态,然后使用修正的线性激活函数将动态与相同的模型拟合进行比较。
首先,下面列出了使用 tanh 和 TensorBoard 回调的深度 MLP 模型的完整示例。
# deeper mlp for the two circles classification problem with callback
from sklearn.datasets import make_circles
from sklearn.preprocessing import MinMaxScaler
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import SGD
from keras.initializers import RandomUniform
from keras.callbacks import TensorBoard
# generate 2d classification dataset
X, y = make_circles(n_samples=1000, noise=0.1, random_state=1)
scaler = MinMaxScaler(feature_range=(-1, 1))
X = scaler.fit_transform(X)
# split into train and test
n_train = 500
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# define model
init = RandomUniform(minval=0, maxval=1)
model = Sequential()
model.add(Dense(5, input_dim=2, activation='tanh', kernel_initializer=init))
model.add(Dense(5, activation='tanh', kernel_initializer=init))
model.add(Dense(5, activation='tanh', kernel_initializer=init))
model.add(Dense(5, activation='tanh', kernel_initializer=init))
model.add(Dense(5, activation='tanh', kernel_initializer=init))
model.add(Dense(1, activation='sigmoid', kernel_initializer=init))
# compile model
opt = SGD(lr=0.01, momentum=0.9)
model.compile(loss='binary_crossentropy', optimizer=opt, metrics=['accuracy'])
# prepare callback
tb = TensorBoard(histogram_freq=1, write_grads=True)
# fit model
model.fit(trainX, trainy, validation_data=(testX, testy), epochs=500, verbose=0, callbacks=[tb])
运行该示例会创建一个新的“ logs/ ”子目录,其中包含一个文件,该文件包含回调在训练期间记录的统计信息。
我们可以在 TensorBoard 网络界面查看统计数据。该接口可以从命令行启动,要求您指定日志目录的完整路径。
例如,如果您在“ /code ”目录中运行代码,那么日志目录的完整路径将是“ /code/logs/ ”。
下面是启动要在命令行(命令提示符)上执行的 TensorBoard 界面的命令。请务必更改日志目录的路径。
python -m tensorboard.main --logdir=/code/logs/
接下来,打开您的网络浏览器并输入以下网址:
如果一切顺利,你会看到 TensorBoard 的网页界面。
可以在界面的“分布”和“直方图选项卡下查看每个训练时期每层平均梯度的图。使用搜索过滤器“ kernel_0_grad ”,可以过滤图以仅显示密集层的梯度,不包括偏差。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
首先,为 6 个层中的每一个层创建线图(5 个隐藏,1 个输出)。地块名称表示层,其中“ dense_1 ”表示输入层后的隐藏层,“ dense_6 ”表示输出层。
我们可以看到输出层在整个运行过程中有很多活动,每个时期的平均梯度在 0.05 到 0.1 左右。我们还可以在第一个隐藏层看到一些类似范围的活动。因此,梯度会穿透到第一个隐藏层,但最后一个层和最后一个隐藏层会看到大部分活动。
深 MLP 平均每层梯度的张量板线图
深 MLP 平均每层梯度的张量板密度图
我们可以通过 ReLU 激活功能从深 MLP 收集同样的信息。
下面列出了完整的示例。
# deeper mlp with relu for the two circles classification problem with callback
from sklearn.datasets import make_circles
from sklearn.preprocessing import MinMaxScaler
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import SGD
from keras.callbacks import TensorBoard
# generate 2d classification dataset
X, y = make_circles(n_samples=1000, noise=0.1, random_state=1)
scaler = MinMaxScaler(feature_range=(-1, 1))
X = scaler.fit_transform(X)
# split into train and test
n_train = 500
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# define model
model = Sequential()
model.add(Dense(5, input_dim=2, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(5, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(5, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(5, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(5, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(1, activation='sigmoid'))
# compile model
opt = SGD(lr=0.01, momentum=0.9)
model.compile(loss='binary_crossentropy', optimizer=opt, metrics=['accuracy'])
# prepare callback
tb = TensorBoard(histogram_freq=1, write_grads=True)
# fit model
model.fit(trainX, trainy, validation_data=(testX, testy), epochs=500, verbose=0, callbacks=[tb])
如果您是新手,TensorBoard 界面可能会令人困惑。
为了简单起见,在运行第二个示例之前,删除“日志””子目录。
运行后,您可以以同样的方式启动 TensorBoard 界面,并通过网络浏览器访问它。
与使用 tanh 的深度模型的梯度相比,每个训练时期的每层平均梯度图显示了不同的情况。
我们可以看到,第一个隐藏层看到更多的梯度,更一致的更大的传播,可能是 0.2 到 0.4,而不是 0.05 和 0.1 看到的 tanh。我们还可以看到中间的隐藏层会看到很大的梯度。
用 ReLU 绘制深 MLP 每层平均梯度的张量板线图
用 ReLU 绘制深 MLP 每层平均梯度的张量板密度图
ReLU 激活功能允许更多的梯度在训练过程中通过模型反向流动,这可能是表现提高的原因。
扩展ˌ扩张
本节列出了一些您可能希望探索的扩展教程的想法。
- 重量初始化。使用 tanh 激活更新深度 MLP,以使用 Xavier 均匀权重初始化并报告结果。
- 学习算法。使用 tanh 激活更新深度 MLP,以使用自适应学习算法(如 Adam)并报告结果。
- 权重变化。更新 tanh 和 relu 示例,以记录和绘制每个时期模型权重的 L1 向量范数,作为训练期间每个层改变多少的代理,并比较结果。
- 研究模型深度。使用 tanh 激活的 MLP 创建一个实验,并报告模型的表现,因为隐藏层数从 1 增加到 10。
- 增加宽度。将 tanh 激活的 MLP 隐藏层中的节点数量从 5 增加到 25,并在层数从 1 增加到 10 时报告表现。
如果你探索这些扩展,我很想知道。
进一步阅读
如果您想更深入地了解这个主题,本节将提供更多资源。
邮件
报纸
- 训练超深前馈网络的随机游走初始化,2014。
- 递归网络中的梯度流:学习长期依赖的难度,2001。
书
应用程序接口
文章
摘要
在本教程中,您发现了如何在训练神经网络模型时诊断梯度消失问题,以及如何使用替代激活函数和权重初始化方案来修复它。
具体来说,您了解到:
- 梯度消失问题限制了具有经典流行的激活函数(如双曲正切)的深度神经网络的发展。
- 如何使用 ReLU 和 He 权重初始化来固定用于分类的深度神经网络多层感知器。
- 如何使用 TensorBoard 诊断一个消失的梯度问题,并确认 ReLU 的影响,以改善通过模型的梯度流。
你有什么问题吗? 在下面的评论中提问,我会尽力回答。
如何通过添加噪声来提高深度学习模型的鲁棒性
最后更新于 2020 年 8 月 28 日
用小的训练数据集向约束不足的神经网络模型添加噪声可以产生正则化效果并减少过拟合。
Keras 支持通过称为高斯噪声层的单独层添加高斯噪声。此层可用于向现有模型添加噪波。
在本教程中,您将发现如何在 Keras 中向深度学习模型添加噪声,以便减少过拟合并提高模型泛化。
完成本教程后,您将知道:
- 噪声可以通过高斯噪声层添加到神经网络模型中。
- 高斯噪声可用于向输入值或隐藏层之间添加噪声。
- 如何在多层感知器分类模型中添加高斯分布层以减少过拟合。
用我的新书更好的深度学习启动你的项目,包括分步教程和所有示例的 Python 源代码文件。
我们开始吧。
- 2019 年 10 月更新:针对 Keras 2.3 和 TensorFlow 2.0 更新。
如何通过添加噪声来提高深度学习模型的鲁棒性迈克尔·穆勒摄,版权所有。
教程概述
本教程分为三个部分;它们是:
- Keras 噪声正则化
- 模型中的噪声正则化
- 噪声正则化案例研究
Keras 噪声正则化
Keras 支持通过 GaussianNoise 层向模型添加噪声。
这是一个会给给定形状的输入添加噪声的层。噪声的平均值为零,要求将噪声的标准偏差指定为一个参数。例如:
# import noise layer
from keras.layers import GaussianNoise
# define noise layer
layer = GaussianNoise(0.1)
层的输出将具有与输入相同的形状,唯一的修改是将噪声添加到值中。
模型中的噪声正则化
神经网络模型可以以几种不同的方式使用高斯神经网络。
首先,它可以用作输入层,直接向输入变量添加噪声。这是神经网络中传统的使用噪声作为正则化方法。
下面是一个将高斯层定义为采用两个输入变量的模型的输入层的示例。
...
model.add(GaussianNoise(0.01, input_shape=(2,)))
...
噪声也可以添加到模型中的隐藏层之间。考虑到 Keras 的灵活性,可以在使用激活功能之前或之后添加噪声。在激活之前添加可能更有意义;然而,两种选择都是可能的。
下面是一个 GaussianNoise 层的示例,它在校正线性激活函数(ReLU) 之前将噪声添加到密集层的线性输出中,这可能是隐藏层之间更合适的噪声使用方式。
...
model.add(Dense(32))
model.add(GaussianNoise(0.1))
model.add(Activation('relu'))
model.add(Dense(32))
...
噪声也可以在激活功能后添加,很像使用噪声激活功能。这种用法的一个缺点是,结果值可能超出激活函数正常提供的范围。例如,具有附加噪声的值可能小于零,而 relu 激活函数将只输出 0 或更大的值。
...
model.add(Dense(32, activation='reu'))
model.add(GaussianNoise(0.1))
model.add(Dense(32))
...
让我们来看看噪声正则化如何与一些常见的网络类型一起使用。
MLP 噪声正则化
下面的示例在两个密集的完全连接的层之间添加了噪声。
# example of noise between fully connected layers
from keras.layers import Dense
from keras.layers import GaussianNoise
from keras.layers import Activation
...
model.add(Dense(32))
model.add(GaussianNoise(0.1))
model.add(Activation('relu'))
model.add(Dense(1))
...
噪声正则化
下面的例子在卷积网络的汇聚层之后增加了噪声。
# example of noise for a CNN
from keras.layers import Dense
from keras.layers import Conv2D
from keras.layers import MaxPooling2D
from keras.layers import GaussianNoise
...
model.add(Conv2D(32, (3,3)))
model.add(Conv2D(32, (3,3)))
model.add(MaxPooling2D())
model.add(GaussianNoise(0.1))
model.add(Dense(1))
...
RNN 丢弃正规化
以下示例在 LSTM 循环层和密集全连接层之间添加了噪声。
# example of noise between LSTM and fully connected layers
from keras.layers import Dense
from keras.layers import Activation
from keras.layers import LSTM
from keras.layers import GaussianNoise
...
model.add(LSTM(32))
model.add(GaussianNoise(0.5))
model.add(Activation('relu'))
model.add(Dense(1))
...
既然我们已经看到了如何向神经网络模型添加噪声,让我们来看一个向 overfit 模型添加噪声以减少泛化误差的案例研究。
噪声正则化案例研究
在本节中,我们将演示如何使用噪声正则化来减少简单二进制分类问题上 MLP 的过拟合。
此示例提供了一个模板,用于将噪声正则化应用于您自己的神经网络,以解决分类和回归问题。
二分类问题
我们将使用一个标准的二分类问题,它定义了两个观察值的二维同心圆,每个类一个半圆。
每个观察都有两个相同规模的输入变量和一个 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 个节点和校正的线性激活函数。输出层使用 sigmoid 激活函数来预测类值 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['accuracy'], label='train')
pyplot.plot(history.history['val_accuracy'], 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['accuracy'], label='train')
pyplot.plot(history.history['val_accuracy'], label='test')
pyplot.legend()
pyplot.show()
运行该示例会报告列车和测试数据集上的模型表现。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
我们可以看到,该模型在训练数据集上的表现优于测试数据集,这可能是过拟合的一个迹象。
Train: 1.000, Test: 0.757
创建一个图形,显示列车和测试集上模型准确率的线图。
我们可以看到过拟合模型的预期形状,其中测试准确率增加到一个点,然后开始再次降低。
训练时训练和测试数据集上的准确率线图显示出过拟合
具有输入层噪声的 MLP
数据集由具有受控统计噪声量的点定义。
然而,由于数据集很小,我们可以给输入值添加更多的噪声。这将产生创建更多样本或对域进行重采样的效果,使输入空间的结构人为地更加平滑。这可能会使问题更容易学习,并提高泛化表现。
我们可以添加一个高斯层作为输入层。噪音必须很小。假设输入值在范围[0,1]内,我们将添加平均值为 0.0、标准偏差为 0.01 的高斯噪声,这是任意选择的。
# define model
model = Sequential()
model.add(GaussianNoise(0.01, input_shape=(2,)))
model.add(Dense(500, activation='relu'))
model.add(Dense(1, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
下面列出了此更改的完整示例。
# mlp overfit on the two circles dataset with input noise
from sklearn.datasets import make_circles
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import GaussianNoise
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(GaussianNoise(0.01, input_shape=(2,)))
model.add(Dense(500, 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['accuracy'], label='train')
pyplot.plot(history.history['val_accuracy'], label='test')
pyplot.legend()
pyplot.show()
运行该示例会报告列车和测试数据集上的模型表现。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,我们可能会在测试数据集上看到模型表现的小幅提升,而不会对训练数据集产生负面影响。
Train: 1.000, Test: 0.771
我们清楚地看到在训练过程中添加的噪声对模型评估的影响,如线图所示。噪声会影响模型在训练期间跳跃的准确性,这可能是由于噪声引入了与训练数据集中的真实点相冲突的点。
也许较低的输入噪声标准偏差会更合适。
该模型仍然显示出一种过度训练的模式,随着训练时间的推移,测试准确率先上升后下降。
列车线形图和带有输入层噪声的测试准确率
具有隐藏层噪声的 MLP
向输入值添加噪声的另一种方法是在隐藏层之间添加噪声。
这可以通过在应用激活函数之前向层的线性输出(加权和)添加噪声来实现,在这种情况下是整流的线性激活函数。我们还可以对噪声使用较大的标准偏差,因为在这个水平上,模型对噪声不太敏感,因为过拟合可能会导致较大的权重。我们将使用 0.1 的标准偏差,同样是任意选择的。
# define model
model = Sequential()
model.add(Dense(500, input_dim=2))
model.add(GaussianNoise(0.1))
model.add(Activation('relu'))
model.add(Dense(1, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
下面列出了隐藏层之间有高斯噪声的完整示例。
# mlp overfit on the two circles dataset with hidden layer noise
from sklearn.datasets import make_circles
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Activation
from keras.layers import GaussianNoise
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))
model.add(GaussianNoise(0.1))
model.add(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['accuracy'], label='train')
pyplot.plot(history.history['val_accuracy'], label='test')
pyplot.legend()
pyplot.show()
运行该示例会报告列车和测试数据集上的模型表现。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,我们可以看到模型在等待测试集上的表现显著提高。
# Train: 0.967, Test: 0.814
我们还可以从训练时期的准确率线图中看到,模型似乎不再显示过拟合的特性。
列车线形图及隐层噪声测试准确率
我们还可以在第一个隐藏层的输出通过激活函数后进行实验并添加噪声。
# define model
model = Sequential()
model.add(Dense(500, input_dim=2, activation='relu'))
model.add(GaussianNoise(0.1))
model.add(Dense(1, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
下面列出了完整的示例。
# mlp overfit on the two circles dataset with hidden layer noise (alternate)
from sklearn.datasets import make_circles
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import GaussianNoise
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(GaussianNoise(0.1))
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['accuracy'], label='train')
pyplot.plot(history.history['val_accuracy'], label='test')
pyplot.legend()
pyplot.show()
运行该示例会报告列车和测试数据集上的模型表现。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
令人惊讶的是,我们在模型的表现上几乎看不到差异。
Train: 0.967, Test: 0.814
同样,我们可以从训练时期的准确率线图中看到,模型不再显示过拟合的迹象。
列车线形图和带隐层噪声的测试准确率(备选)
扩展ˌ扩张
本节列出了一些您可能希望探索的扩展教程的想法。
- 重复评估。更新示例,使用有无噪声模型的重复评估,并将表现报告为重复的平均值和标准偏差。
- 网格搜索标准差。开发网格搜索,以便发现可靠地产生最佳表现模型的噪声量。
- 输入和隐藏噪声。更新示例,在模型的输入层和隐藏层引入噪声。
如果你探索这些扩展,我很想知道。
进一步阅读
如果您想更深入地了解这个主题,本节将提供更多资源。
摘要
在本教程中,您发现了如何在 Keras 中向深度学习模型添加噪声,以减少过拟合并提高模型泛化能力。
具体来说,您了解到:
- 噪声可以通过高斯噪声层添加到神经网络模型中。
- 高斯噪声可用于向输入值或隐藏层之间添加噪声。
- 如何在多层感知器分类模型中添加高斯分布层以减少过拟合。
你有什么问题吗? 在下面的评论中提问,我会尽力回答。
如何使用数据缩放提高深度学习模型的稳定性和表现
最后更新于 2020 年 8 月 25 日
深度学习神经网络学习如何从训练数据集中的示例将输入映射到输出。
模型的权重被初始化为小的随机值,并且响应于训练数据集上的误差估计,通过优化算法被更新。
考虑到模型中使用的权重较小,以及预测值和期望值之间的误差,用于训练模型的输入和输出规模是一个重要因素。未缩放的输入变量会导致学习过程缓慢或不稳定,而回归问题中未缩放的目标变量会导致梯度爆炸,导致学习过程失败。
数据准备包括在训练神经网络模型之前,使用标准化和规范化等技术来重新调整输入和输出变量。
在本教程中,您将发现如何通过缩放数据来提高神经网络的稳定性和建模表现。
完成本教程后,您将知道:
- 当使用深度学习神经网络时,数据缩放是推荐的预处理步骤。
- 数据缩放可以通过规范化或标准化实值输入和输出变量来实现。
- 如何应用标准化和规范化来提高多层感知器模型在回归预测建模问题上的表现。
用我的新书更好的深度学习启动你的项目,包括分步教程和所有示例的 Python 源代码文件。
我们开始吧。
如何通过数据缩放提高神经网络的稳定性和建模表现 图片由哈维尔·桑切斯·波特罗提供,版权所有。
教程概述
本教程分为六个部分;它们是:
- 您的数据规模很重要
- 数据缩放方法
- 回归预测建模问题
- 具有未缩放数据的多层感知器
- 具有缩放输出变量的多层感知器
- 具有缩放输入变量的多层感知器
您的数据规模很重要
深度学习神经网络模型学习从输入变量到输出变量的映射。
因此,从域中提取的数据的规模和分布对于每个变量可能是不同的。
输入变量可能具有不同的单位(例如英尺、公里和小时),这反过来意味着变量具有不同的标度。
输入变量之间的比例差异可能会增加建模问题的难度。这方面的一个例子是,大的输入值(例如数百或数千个单位的分布)会导致模型学习大的权重值。具有大权重值的模型通常是不稳定的,这意味着它可能在学习过程中表现不佳,并且对输入值敏感,从而导致较高的泛化误差。
预处理最常见的形式之一是对输入变量进行简单的线性重新缩放。
—第 298 页,用于模式识别的神经网络,1995。
具有大范围值的目标变量反过来可能导致大的误差梯度值,导致权重值急剧变化,使得学习过程不稳定。
缩放输入和输出变量是使用神经网络模型的关键步骤。
在实践中,在将输入数据呈现给网络之前,对其应用预处理变换几乎总是有利的。类似地,网络的输出经常被后处理以给出所需的输出值。
—第 296 页,用于模式识别的神经网络,1995。
缩放输入变量
输入变量是网络在输入层或可见层进行预测的变量。
一个好的经验法则是,输入变量应该是小值,可能在 0-1 的范围内,或者用零平均值和一个标准偏差标准化。
输入变量是否需要缩放取决于您的问题和每个变量的具体情况。
您可能有一系列数量作为输入,例如价格或温度。
如果数量的分布是正态的,那么就应该标准化,否则数据就应该标准化。如果数量值的范围很大(10s、100s 等),则适用。)或小(0.01,0.0001)。
如果数量值很小(接近 0-1)并且分布有限(例如,标准偏差接近 1),那么也许您可以不按比例缩放数据。
问题可能很复杂,并且可能不清楚如何最好地缩放输入数据。
如果有疑问,标准化输入序列。如果您有资源,探索使用原始数据、标准化数据和规范化数据建模,并查看结果模型的表现是否有有益的差异。
如果输入变量是线性组合的,如在 MLP[多层感知器]中,那么很少严格需要标准化输入,至少在理论上是这样。[……]然而,有各种各样的实际原因,为什么标准化输入可以使训练更快,并减少陷入局部最优的机会。
— 我应该对数据进行规范化/标准化/重新缩放吗?神经网络常见问题
缩放输出变量
输出变量是网络预测的变量。
您必须确保输出变量的比例与网络输出层上激活函数(传递函数)的比例相匹配。
如果您的输出激活函数的范围为[0,1],那么显然您必须确保目标值在该范围内。但是选择一个适合目标分布的输出激活函数通常比强迫你的数据符合输出激活函数要好。
— 我应该对数据进行规范化/标准化/重新缩放吗?神经网络常见问题
如果你的问题是一个回归问题,那么输出将是一个实值。
这最好用线性激活函数来建模。如果值的分布是正态的,那么可以标准化输出变量。否则,输出变量可以规范化。
数据缩放方法
您可能需要考虑两种类型的数据缩放:规范化和标准化。
这些都可以通过 Sklearn 库来实现。
数据标准化
规范化是从原始范围对数据进行重新缩放,以便所有值都在 0 和 1 的范围内。
规范化要求您知道或能够准确估计最小和最大可观察值。您可能能够从您的可用数据中估计这些值。
值标准化如下:
y = (x - min) / (max - min)
其中最小值和最大值属于被归一化的值 x 。
例如,对于数据集,我们可以将最小和最大可观察值估计为 30 和-10。然后,我们可以对任何值进行归一化,如 18.8,如下所示:
y = (x - min) / (max - min)
y = (18.8 - (-10)) / (30 - (-10))
y = 28.8 / 40
y = 0.72
您可以看到,如果提供的 x 值超出了最小值和最大值的界限,则结果值将不会在 0 和 1 的范围内。您可以在进行预测之前检查这些观察值,并从数据集中删除它们,或者将它们限制在预定义的最大值或最小值。
您可以使用 Sklearn 对象最小最大缩放器来规范化数据集。
最小最大缩放器和其他缩放技术的良好实践用法如下:
- 使用可用的训练数据安装定标器。对于归一化,这意味着训练数据将用于估计最小和最大可观察值。这是通过调用 fit() 函数来实现的。
- 将量表应用于训练数据。这意味着您可以使用规范化的数据来训练您的模型。这是通过调用*变换()*函数来完成的。
- 将刻度应用于向前的数据。这意味着你可以在未来准备新的数据来做预测。
最小最大缩放器的默认比例是将变量重新缩放到范围[0,1],尽管可以通过“特征 _ 范围参数指定首选比例,并为所有变量指定包含最小值和最大值的元组。
# create scaler
scaler = MinMaxScaler(feature_range=(-1,1))
如果需要,可以反转变换。这对于将预测转换回其原始比例以进行报告或绘图非常有用。这可以通过调用 inverse_transform() 函数来实现。
以下示例提供了使用最小最大缩放器来规范化数据的一般演示。
# demonstrate data normalization with sklearn
from sklearn.preprocessing import MinMaxScaler
# load data
data = ...
# create scaler
scaler = MinMaxScaler()
# fit scaler on data
scaler.fit(data)
# apply transform
normalized = scaler.transform(data)
# inverse transform
inverse = scaler.inverse_transform(normalized)
也可以使用 fit_transform() 功能,一步完成拟合和变换;例如:
# demonstrate data normalization with sklearn
from sklearn.preprocessing import MinMaxScaler
# load data
data = ...
# create scaler
scaler = MinMaxScaler()
# fit and transform in one step
normalized = scaler.fit_transform(data)
# inverse transform
inverse = scaler.inverse_transform(normalized)
数据标准化
数据集标准化涉及重新调整值的分布,以便观察值的平均值为 0,标准偏差为 1。有时被称为“T0”美白
这可以被认为是减去平均值或使数据居中。
像标准化一样,标准化可能是有用的,甚至在某些机器学习算法中,当您的数据具有不同比例的输入值时,标准化是必需的。
标准化假设您的观测值符合高斯分布(钟形曲线),具有良好的平均值和标准偏差。如果没有达到这个期望,你仍然可以标准化你的数据,但是你可能得不到可靠的结果。
标准化要求你知道或能够准确估计可观察值的平均值和标准偏差。您可能能够从您的培训数据中估计这些值。
值标准化如下:
y = (x - mean) / standard_deviation
其中表示计算如下:
mean = sum(x) / count(x)
标准偏差计算如下:
standard_deviation = sqrt( sum( (x - mean)² ) / count(x))
我们可以推测平均值为 10,标准偏差约为 5。使用这些值,我们可以将第一个值 20.7 标准化如下:
y = (x - mean) / standard_deviation
y = (20.7 - 10) / 5
y = (10.7) / 5
y = 2.14
数据集的均值和标准差估计值比最小值和最大值对新数据更稳健。
您可以使用 Sklearn 对象标准缩放器来标准化数据集。
# demonstrate data standardization with sklearn
from sklearn.preprocessing import StandardScaler
# load data
data = ...
# create scaler
scaler = StandardScaler()
# fit scaler on data
scaler.fit(data)
# apply transform
standardized = scaler.transform(data)
# inverse transform
inverse = scaler.inverse_transform(standardized)
也可以使用 fit_transform() 功能,一步完成拟合和变换;例如:
# demonstrate data standardization with sklearn
from sklearn.preprocessing import StandardScaler
# load data
data = ...
# create scaler
scaler = StandardScaler()
# fit and transform in one step
standardized = scaler.fit_transform(data)
# inverse transform
inverse = scaler.inverse_transform(standardized)
回归预测建模问题
回归预测建模问题涉及预测实值量。
我们可以在make _ revolution()函数中使用 Sklearn 库提供的标准回归问题生成器。该函数将从具有给定数量的输入变量、统计噪声和其他属性的简单回归问题中生成示例。
我们将使用这个函数来定义一个有 20 个输入特征的问题;其中 10 个功能将是有意义的,10 个将不相关。总共将随机生成 1000 个示例。伪随机数发生器将被固定,以确保我们每次运行代码时都能得到相同的 1000 个例子。
# generate regression dataset
X, y = make_regression(n_samples=1000, n_features=20, noise=0.1, random_state=1)
每个输入变量都具有高斯分布,目标变量也是如此。
我们可以通过创建一些输入变量和输出变量的直方图来演示这一点。
# regression predictive modeling problem
from sklearn.datasets import make_regression
from matplotlib import pyplot
# generate regression dataset
X, y = make_regression(n_samples=1000, n_features=20, noise=0.1, random_state=1)
# histograms of input variables
pyplot.subplot(211)
pyplot.hist(X[:, 0])
pyplot.subplot(212)
pyplot.hist(X[:, 1])
pyplot.show()
# histogram of target variable
pyplot.hist(y)
pyplot.show()
运行该示例会创建两个图形。
第一个显示了二十个输入变量中前两个的直方图,显示每个变量都有一个高斯数据分布。
回归问题二十个输入变量中两个的直方图
第二个图显示了目标变量的直方图,与输入变量相比,显示了更大的变量范围,并且还是高斯数据分布。
回归问题目标变量的直方图
现在我们有了一个可以作为调查基础的回归问题,我们可以开发一个模型来解决它。
具有未缩放数据的多层感知器
我们可以为回归问题开发一个多层感知器(MLP)模型。
将在原始数据上演示一个模型,不需要对输入或输出变量进行任何缩放。我们预计模型表现将普遍较差。
第一步是将数据分成训练集和测试集,这样我们就可以拟合和评估模型。我们将从域中生成 1,000 个示例,并将数据集分成两半,为训练和测试数据集使用 500 个示例。
# split into train and test
n_train = 500
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
接下来,我们可以定义一个 MLP 模型。该模型将在问题的 20 个输入变量中有 20 个输入。
单个隐藏层将使用 25 个节点和一个校正的线性激活函数。输出层有一个单目标变量节点和一个线性激活函数来直接预测真实值。
# define model
model = Sequential()
model.add(Dense(25, input_dim=20, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(1, activation='linear'))
均方误差损失函数将用于优化模型,随机梯度下降优化算法将用于学习率为 0.01、动量为 0.9 的合理默认配置。
# compile model
model.compile(loss='mean_squared_error', optimizer=SGD(lr=0.01, momentum=0.9))
该模型将适用于 100 个训练时期,测试集将用作验证集,在每个训练时期结束时进行评估。
训练结束时,在训练和测试数据集上计算均方误差,以了解模型学习问题的程度。
# evaluate the model
train_mse = model.evaluate(trainX, trainy, verbose=0)
test_mse = model.evaluate(testX, testy, verbose=0)
最后,在每个训练周期结束时,训练集和测试集的均方误差的学习曲线用线图表示,在学习问题的同时,提供学习曲线以获得模型的动力学概念。
# plot loss during training
pyplot.title('Mean Squared Error')
pyplot.plot(history.history['loss'], label='train')
pyplot.plot(history.history['val_loss'], label='test')
pyplot.legend()
pyplot.show()
将这些元素结合在一起,下面列出了完整的示例。
# mlp with unscaled data for the regression problem
from sklearn.datasets import make_regression
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import SGD
from matplotlib import pyplot
# generate regression dataset
X, y = make_regression(n_samples=1000, n_features=20, noise=0.1, random_state=1)
# split into train and test
n_train = 500
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# define model
model = Sequential()
model.add(Dense(25, input_dim=20, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(1, activation='linear'))
# compile model
model.compile(loss='mean_squared_error', optimizer=SGD(lr=0.01, momentum=0.9))
# fit model
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=100, verbose=0)
# evaluate the model
train_mse = model.evaluate(trainX, trainy, verbose=0)
test_mse = model.evaluate(testX, testy, verbose=0)
print('Train: %.3f, Test: %.3f' % (train_mse, test_mse))
# plot loss during training
pyplot.title('Mean Squared Error')
pyplot.plot(history.history['loss'], label='train')
pyplot.plot(history.history['val_loss'], label='test')
pyplot.legend()
pyplot.show()
运行该示例符合模型,并计算列车和测试集的均方误差。
在这种情况下,模型无法学习问题,导致对 NaN 值的预测。在训练期间,模型权重爆炸给出了非常大的误差,反过来,为权重更新计算误差梯度。
Train: nan, Test: nan
这表明,至少目标变量需要一些数据缩放。
创建训练历史的线图,但不显示任何内容,因为模型几乎立即导致 NaN 均方误差。
具有缩放输出变量的多层感知器
可以更新 MLP 模型来缩放目标变量。
减小目标变量的规模将反过来减小用于更新权重的梯度的大小,并导致更稳定的模型和训练过程。
给定目标变量的高斯分布,重新缩放变量的自然方法是标准化变量。这需要估计变量的平均值和标准偏差,并使用这些估计值来执行重新缩放。
最佳实践是估计训练数据集的均值和标准差,并使用这些变量来缩放训练和测试数据集。这是为了避免在模型评估过程中出现任何数据泄露。
Sklearn 转换器期望输入数据是行和列的矩阵,因此在转换之前,目标变量的 1D 数组必须被重新整形为 2D 数组。
# reshape 1d arrays to 2d arrays
trainy = trainy.reshape(len(trainy), 1)
testy = testy.reshape(len(trainy), 1)
然后我们可以创建并应用标准缩放器来重新缩放目标变量。
# created scaler
scaler = StandardScaler()
# fit scaler on training dataset
scaler.fit(trainy)
# transform training dataset
trainy = scaler.transform(trainy)
# transform test dataset
testy = scaler.transform(testy)
重新缩放目标变量意味着估计模型的表现和绘制学习曲线将以缩放变量的平方单位而不是原始比例的平方单位来计算均方误差。这使得在该领域的上下文中解释错误具有挑战性。
在实践中,通过首先反转测试数据集目标变量和模型预测的变换,并使用未缩放数据的均方根误差来估计模型表现,来估计模型的表现可能是有帮助的。这是留给读者的练习。
下面列出了回归问题中标准化 MLP 目标变量的完整示例。
# mlp with scaled outputs on the regression problem
from sklearn.datasets import make_regression
from sklearn.preprocessing import StandardScaler
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import SGD
from matplotlib import pyplot
# generate regression dataset
X, y = make_regression(n_samples=1000, n_features=20, noise=0.1, random_state=1)
# split into train and test
n_train = 500
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# reshape 1d arrays to 2d arrays
trainy = trainy.reshape(len(trainy), 1)
testy = testy.reshape(len(trainy), 1)
# created scaler
scaler = StandardScaler()
# fit scaler on training dataset
scaler.fit(trainy)
# transform training dataset
trainy = scaler.transform(trainy)
# transform test dataset
testy = scaler.transform(testy)
# define model
model = Sequential()
model.add(Dense(25, input_dim=20, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(1, activation='linear'))
# compile model
model.compile(loss='mean_squared_error', optimizer=SGD(lr=0.01, momentum=0.9))
# fit model
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=100, verbose=0)
# evaluate the model
train_mse = model.evaluate(trainX, trainy, verbose=0)
test_mse = model.evaluate(testX, testy, verbose=0)
print('Train: %.3f, Test: %.3f' % (train_mse, test_mse))
# plot loss during training
pyplot.title('Loss / Mean Squared Error')
pyplot.plot(history.history['loss'], label='train')
pyplot.plot(history.history['val_loss'], label='test')
pyplot.legend()
pyplot.show()
运行该示例符合模型,并计算列车和测试集的均方误差。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,模型似乎确实学会了这个问题,并实现了接近零的均方误差,至少达到了小数点后三位。
Train: 0.003, Test: 0.007
创建每个训练时期的训练(蓝色)和测试(橙色)数据集的均方误差线图。
在这种情况下,我们可以看到模型快速学会有效地将输入映射到回归问题的输出,并在运行过程中在两个数据集上都取得了良好的表现,既没有过拟合也没有欠拟合训练数据集。
每个训练时期训练 a 测试数据集的均方误差线图
重复这个实验并标准化目标变量并比较结果可能会很有趣。
具有缩放输入变量的多层感知器
我们已经看到,当拟合具有广泛分布的目标变量的回归模型时,数据缩放可以稳定训练过程。
还可以通过缩放输入变量来提高模型的稳定性和表现。
在本节中,我们将设计一个实验来比较不同缩放方法对输入变量的表现。
输入变量也有高斯数据分布,就像目标变量一样,因此我们认为数据标准化是最好的方法。情况并非总是如此。
我们可以将未缩放输入变量的表现与符合标准化和规范化输入变量的模型进行比较。
第一步是定义一个函数来创建相同的 1000 个数据样本,将它们分成训练集和测试集,并应用通过输入参数指定的数据缩放方法。下面的 get_dataset() 函数实现了这一点,要求为输入和目标变量提供定标器,并返回分割成输入和输出组件的训练和测试数据集,准备训练和评估模型。
# prepare dataset with input and output scalers, can be none
def get_dataset(input_scaler, output_scaler):
# generate dataset
X, y = make_regression(n_samples=1000, n_features=20, noise=0.1, random_state=1)
# split into train and test
n_train = 500
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# scale inputs
if input_scaler is not None:
# fit scaler
input_scaler.fit(trainX)
# transform training dataset
trainX = input_scaler.transform(trainX)
# transform test dataset
testX = input_scaler.transform(testX)
if output_scaler is not None:
# reshape 1d arrays to 2d arrays
trainy = trainy.reshape(len(trainy), 1)
testy = testy.reshape(len(trainy), 1)
# fit scaler on training dataset
output_scaler.fit(trainy)
# transform training dataset
trainy = output_scaler.transform(trainy)
# transform test dataset
testy = output_scaler.transform(testy)
return trainX, trainy, testX, testy
接下来,我们可以定义一个函数来拟合给定数据集上的 MLP 模型,并返回测试数据集上拟合模型的均方误差。
下面的 evaluate_model() 函数实现了这个行为。
# fit and evaluate mse of model on test set
def evaluate_model(trainX, trainy, testX, testy):
# define model
model = Sequential()
model.add(Dense(25, input_dim=20, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(1, activation='linear'))
# compile model
model.compile(loss='mean_squared_error', optimizer=SGD(lr=0.01, momentum=0.9))
# fit model
model.fit(trainX, trainy, epochs=100, verbose=0)
# evaluate the model
test_mse = model.evaluate(testX, testy, verbose=0)
return test_mse
使用随机学习算法训练神经网络。这意味着相同的模型适合相同的数据可能会导致不同的表现。
在我们的实验中,我们可以通过重复评估每个模型配置来解决这个问题,在这种情况下,可以选择数据缩放、多次并报告表现作为所有运行的错误分数的平均值。我们将重复每次运行 30 次,以确保平均值在统计上是稳健的。
下面的*repeat _ evaluation()*函数实现了这一点,将输入和输出变量的定标器作为参数,用这些定标器对一个模型进行 30 次评估,一路上打印错误分数,并返回每次运行计算出的错误分数列表。
# evaluate model multiple times with given input and output scalers
def repeated_evaluation(input_scaler, output_scaler, n_repeats=30):
# get dataset
trainX, trainy, testX, testy = get_dataset(input_scaler, output_scaler)
# repeated evaluation of model
results = list()
for _ in range(n_repeats):
test_mse = evaluate_model(trainX, trainy, testX, testy)
print('>%.3f' % test_mse)
results.append(test_mse)
return results
最后,我们可以运行实验,并以三种不同的方式评估同一数据集上的同一模型:
- 没有输入的缩放,标准化的输出。
- 标准化的输入,标准化的输出。
- 标准化的输入,标准化的输出。
报告每种配置的误差平均值和标准偏差,然后创建方框图和须图来总结每种配置的误差分数。
# unscaled inputs
results_unscaled_inputs = repeated_evaluation(None, StandardScaler())
# normalized inputs
results_normalized_inputs = repeated_evaluation(MinMaxScaler(), StandardScaler())
# standardized inputs
results_standardized_inputs = repeated_evaluation(StandardScaler(), StandardScaler())
# summarize results
print('Unscaled: %.3f (%.3f)' % (mean(results_unscaled_inputs), std(results_unscaled_inputs)))
print('Normalized: %.3f (%.3f)' % (mean(results_normalized_inputs), std(results_normalized_inputs)))
print('Standardized: %.3f (%.3f)' % (mean(results_standardized_inputs), std(results_standardized_inputs)))
# plot results
results = [results_unscaled_inputs, results_normalized_inputs, results_standardized_inputs]
labels = ['unscaled', 'normalized', 'standardized']
pyplot.boxplot(results, labels=labels)
pyplot.show()
将这些元素结合在一起,下面列出了完整的示例。
# compare scaling methods for mlp inputs on regression problem
from sklearn.datasets import make_regression
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import MinMaxScaler
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import SGD
from matplotlib import pyplot
from numpy import mean
from numpy import std
# prepare dataset with input and output scalers, can be none
def get_dataset(input_scaler, output_scaler):
# generate dataset
X, y = make_regression(n_samples=1000, n_features=20, noise=0.1, random_state=1)
# split into train and test
n_train = 500
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# scale inputs
if input_scaler is not None:
# fit scaler
input_scaler.fit(trainX)
# transform training dataset
trainX = input_scaler.transform(trainX)
# transform test dataset
testX = input_scaler.transform(testX)
if output_scaler is not None:
# reshape 1d arrays to 2d arrays
trainy = trainy.reshape(len(trainy), 1)
testy = testy.reshape(len(trainy), 1)
# fit scaler on training dataset
output_scaler.fit(trainy)
# transform training dataset
trainy = output_scaler.transform(trainy)
# transform test dataset
testy = output_scaler.transform(testy)
return trainX, trainy, testX, testy
# fit and evaluate mse of model on test set
def evaluate_model(trainX, trainy, testX, testy):
# define model
model = Sequential()
model.add(Dense(25, input_dim=20, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(1, activation='linear'))
# compile model
model.compile(loss='mean_squared_error', optimizer=SGD(lr=0.01, momentum=0.9))
# fit model
model.fit(trainX, trainy, epochs=100, verbose=0)
# evaluate the model
test_mse = model.evaluate(testX, testy, verbose=0)
return test_mse
# evaluate model multiple times with given input and output scalers
def repeated_evaluation(input_scaler, output_scaler, n_repeats=30):
# get dataset
trainX, trainy, testX, testy = get_dataset(input_scaler, output_scaler)
# repeated evaluation of model
results = list()
for _ in range(n_repeats):
test_mse = evaluate_model(trainX, trainy, testX, testy)
print('>%.3f' % test_mse)
results.append(test_mse)
return results
# unscaled inputs
results_unscaled_inputs = repeated_evaluation(None, StandardScaler())
# normalized inputs
results_normalized_inputs = repeated_evaluation(MinMaxScaler(), StandardScaler())
# standardized inputs
results_standardized_inputs = repeated_evaluation(StandardScaler(), StandardScaler())
# summarize results
print('Unscaled: %.3f (%.3f)' % (mean(results_unscaled_inputs), std(results_unscaled_inputs)))
print('Normalized: %.3f (%.3f)' % (mean(results_normalized_inputs), std(results_normalized_inputs)))
print('Standardized: %.3f (%.3f)' % (mean(results_standardized_inputs), std(results_standardized_inputs)))
# plot results
results = [results_unscaled_inputs, results_normalized_inputs, results_standardized_inputs]
labels = ['unscaled', 'normalized', 'standardized']
pyplot.boxplot(results, labels=labels)
pyplot.show()
运行该示例会打印沿途每个模型运行的均方误差。
在三种配置中的每一种都被评估了 30 次之后,报告每种配置的平均误差。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,我们可以看到,正如我们预期的那样,缩放输入变量确实会导致模型具有更好的表现。出乎意料的是,使用标准化输入代替标准化输入可以获得更好的表现。这可能与第一隐藏层中整流线性激活函数的选择有关。
...
>0.010
>0.012
>0.005
>0.008
>0.008
Unscaled: 0.007 (0.004)
Normalized: 0.001 (0.000)
Standardized: 0.008 (0.004)
创建了一个带有三个方框图和触须图的图形,总结了每种配置的误差分数分布。
曲线图显示,未缩放和标准化输入变量的误差分数分布之间几乎没有差异,标准化输入变量导致更好的表现和更稳定或更紧密的误差分数分布。
这些结果突出表明,重要的是实际实验和确认数据缩放方法的结果,而不是假设给定的数据准备方案将根据观察到的数据分布发挥最佳作用。
回归问题的具有未标度、标准化和标准化输入变量的均方误差的盒形和须形图
扩展ˌ扩张
本节列出了一些您可能希望探索的扩展教程的想法。
- 标准化目标变量。更新示例并标准化,而不是标准化目标变量并比较结果。
- 目标变量的比较缩放。更新示例,使用重复实验比较标准化和规范化目标变量,并比较结果。
- 其他刻度。更新示例以评估标准化和比较表现时的其他最小/最大比例,例如[-1,1]和[0.0,0.5]。
如果你探索这些扩展,我很想知道。
进一步阅读
如果您想更深入地了解这个主题,本节将提供更多资源。
邮件
- 如何在 Python 中扩展长短期记忆网络的数据
- 如何用 Python 从零开始扩展机器学习数据
- 如何在 Python 中对时间序列数据进行规范化和标准化
- 如何用 Sklearn 为 Python 机器学习准备数据
书
- 第 8.2 节输入标准化和编码,模式识别的神经网络,1995。
应用程序接口
文章
- 我是否应该对数据进行规范化/标准化/重新缩放?神经网络常见问题
摘要
在本教程中,您发现了如何通过缩放数据来提高神经网络的稳定性和建模表现。
具体来说,您了解到:
- 当使用深度学习神经网络时,数据缩放是推荐的预处理步骤。
- 数据缩放可以通过规范化或标准化实值输入和输出变量来实现。
- 如何应用标准化和规范化来提高多层感知器模型在回归预测建模问题上的表现。
你有什么问题吗? 在下面的评论中提问,我会尽力回答。