Machine-Learning-Mastery-深度学习表现教程-九-

83 阅读1小时+

Machine Learning Mastery 深度学习表现教程(九)

原文:Machine Learning Mastery

协议:CC BY-NC-SA 4.0

使用噪声训练神经网络来减少过拟合

原文:machinelearningmastery.com/train-neura…

最后更新于 2019 年 8 月 6 日

用小数据集训练神经网络会导致网络记住所有的训练示例,进而导致过拟合,在保持数据集上表现不佳。

给定高维输入空间中点的不完整或稀疏采样,小数据集也可能代表神经网络更难学习的映射问题。

使输入空间更平滑、更容易学习的一种方法是在训练过程中给输入添加噪声。

在这篇文章中,你会发现在训练过程中给神经网络添加噪声可以提高网络的鲁棒性,从而获得更好的泛化能力和更快的学习速度。

看完这篇文章,你会知道:

  • 小数据集会给神经网络的学习带来挑战,并且示例可以记忆。
  • 在训练过程中加入噪声可以使训练过程更加稳健,减少泛化误差。
  • 传统上,噪声会添加到输入中,但也可以添加到权重、梯度甚至激活函数中。

用我的新书更好的深度学习启动你的项目,包括分步教程和所有示例的 Python 源代码文件。

我们开始吧。

Train Neural Networks With Noise to Reduce Overfitting

用噪声训练神经网络以减少过拟合。

概观

本教程分为五个部分;它们是:

  1. 小训练数据集的挑战
  2. 训练期间添加随机噪声
  3. 如何以及在哪里添加噪音
  4. 训练期间添加噪音的示例
  5. 训练时增加噪音的技巧

小训练数据集的挑战

训练大型神经网络时,小数据集会带来问题。

第一个问题是网络可能会有效地记忆训练数据集。该模型可以学习特定的输入示例及其相关输出,而不是学习从输入到输出的一般映射。这将导致模型在训练数据集上表现良好,而在新数据(如保持数据集)上表现不佳。

第二个问题是,小数据集提供较少的机会来描述输入空间的结构及其与输出的关系。更多的训练数据提供了更丰富的问题描述,模型可以从中学习。更少的数据点意味着,这些点可能代表不和谐和不连贯的结构,而不是平滑的输入空间,这可能导致一个困难的(如果不是不可理解的)映射函数。

获取更多的数据并不总是可能的。此外,获得更多数据可能无法解决这些问题。

训练期间添加随机噪声

一种改进泛化误差和改进映射问题结构的方法是添加随机噪声。

许多研究[…]已经注意到,向训练数据中添加少量输入噪声(抖动)通常有助于泛化和容错。

—第 273 页,神经锻造:前馈人工神经网络中的监督学习,1999。

起初,这听起来像是让学习更具挑战性的方法。这是一个提高表现的反直觉建议,因为在训练过程中,人们会期望噪声会降低模型的表现。

试探性地,我们可能预期噪声将“涂抹”每个数据点,并使网络难以精确拟合单个数据点,因此将减少过拟合。在实践中,已经证明用噪声进行训练确实可以提高网络的泛化能力。

—第 347 页,用于模式识别的神经网络,1995。

在神经网络模型的训练期间添加噪声具有正则化效果,并且反过来提高了模型的鲁棒性。已经表明,与权重正则化方法的情况一样,增加惩罚项对损失函数有类似的影响。

众所周知,在训练期间向神经网络的输入数据添加噪声在某些情况下会导致泛化表现的显著提高。先前的工作表明,这种带噪声的训练相当于一种正则化形式,其中在误差函数中增加了一个额外的项。

——带噪训练相当于 Tikhonov 正则化,2008。

实际上,添加噪声会扩大训练数据集的大小。每次训练样本暴露在模型中时,随机噪声都会被添加到输入变量中,使它们每次暴露在模型中时都不同。这样,给输入样本添加噪声就是数据扩充的一种简单形式。

在神经网络的输入中注入噪声也可以被视为一种数据扩充形式。

—第 241 页,深度学习,2016。

添加噪声意味着网络记忆训练样本的能力降低,因为它们一直在变化,导致网络权重更小,网络更健壮,泛化误差更低。

噪声意味着好像从已知样本附近的域中抽取新样本,平滑输入空间的结构。这种平滑可能意味着映射函数对于网络来说更容易学习,从而导致更好更快的学习。

……输入噪声和权重噪声分别促使神经网络输出成为输入或其权重的平滑函数。

——反向传播训练中添加噪声对泛化表现的影响,1996。

如何以及在哪里添加噪音

训练期间使用的最常见的噪声类型是向输入变量添加高斯噪声。

高斯噪声或白噪声的平均值为零,标准偏差为 1,可以根据需要使用伪随机数发生器生成。在信号处理中使用术语“电路中不相关的随机噪声”之后,将高斯噪声添加到神经网络的输入中传统上被称为“T0”抖动或“T2”随机抖动。

添加的噪声量(例如扩展或标准偏差)是一个可配置的超参数。太少的噪声没有影响,而太多的噪声使得映射函数太具挑战性而难以学习。

这通常是通过在每个输入模式呈现给网络之前向其添加一个随机向量来完成的,因此,如果模式正在被回收,则每次添加一个不同的随机向量。

——带噪训练相当于 Tikhonov 正则化,2008。

随机噪声的标准偏差控制传播量,并且可以根据每个输入变量的比例进行调整。如果首先对输入变量的规模进行标准化,配置起来会更容易。

噪音只在训练时增加。在模型评估期间或当模型用于对新数据进行预测时,不会添加噪声。

噪声的添加也是自动特征学习的重要部分,例如在自动编码器的情况下,所谓的去噪自动编码器明确要求模型在存在添加到输入的噪声的情况下学习鲁棒的特征。

我们已经看到,重建标准本身不能保证有用特征的提取,因为它可能导致显而易见的解决方案“简单地复制输入”或类似的不感兴趣的解决方案,这些解决方案通常会最大化相互信息。[……]我们改变了重建标准,以实现一个更具挑战性和更有趣的目标:清理部分损坏的输入,或者简而言之,去噪。

——堆叠去噪自动编码器:利用局部去噪标准在深度网络中学习有用的表示,2010。

尽管输入端的额外噪声是最常见且研究最广泛的方法,但在训练过程中,随机噪声可能会添加到网络的其他部分。一些例子包括:

  • 给激活添加噪声,即每层的输出。
  • 给权重添加噪声,即输入的替代。
  • 给梯度添加噪点,即更新权重的方向。
  • 给输出添加噪声,即标签或目标变量。

将噪声添加到层激活允许在网络中的任何点使用噪声。这对非常深的网络可能是有益的。噪声可以添加到层输出本身,但这更有可能通过使用噪声激活功能来实现。

将噪声添加到权重允许该方法以一致的方式在整个网络中使用,而不是将噪声添加到输入和层激活中。这在递归神经网络中特别有用。

另一种将噪声用于模型调整的方法是将其加入权重。这项技术主要用于递归神经网络。[……]应用于权重的噪声也可以被解释为等同于(在某些假设下)更传统形式的正则化,鼓励要学习的函数的稳定性。

—第 242 页,深度学习,2016。

向梯度添加噪声更侧重于提高优化过程本身的鲁棒性,而不是输入域的结构。噪音的量可以在训练开始时很高,并随着时间的推移而减少,很像一个衰减的学习率。这种方法已被证明是非常深的网络和各种不同网络类型的有效方法。

当优化各种各样的模型时,包括非常深的全连接网络,以及用于问题回答和算法学习的专用架构,我们不断看到注入的梯度噪声带来的改进。[……]我们的实验表明,通过衰减方差添加退火高斯噪声比使用固定高斯噪声效果更好

——添加梯度噪声改善超深度网络的学习,2015。

向激活、权重或梯度添加噪声都提供了一种更通用的方法来添加噪声,该方法对于提供给模型的输入变量类型是不变的。

如果问题域被认为或预期有错误标记的例子,那么向类标签添加噪声可以提高模型对此类错误的鲁棒性。虽然,这可能很容易破坏学习过程。

在回归或时间序列预测的情况下,向连续目标变量添加噪声很像向输入变量添加噪声,可能是更好的使用情形。

训练期间添加噪音的示例

本节总结了一些在训练过程中加入噪声的例子。

Lasse Holmstrom 在 1992 年发表的题为“在反向传播训练中使用加性噪声”的论文中,用 MLPs 分析和实验研究了随机噪声的加入他们建议首先标准化输入变量,然后使用交叉验证来选择训练期间使用的噪声量。

如果应该建议单一的通用噪声设计方法,我们会选择最大化交叉验证的似然函数。该方法易于实现,完全由数据驱动,并具有理论一致性结果支持的有效性

Klaus Gref 等人在他们 2016 年发表的题为《 LSTM:搜索空间奥德赛》》的论文中,使用超参数搜索一组序列预测任务的输入变量上高斯噪声的标准偏差,发现它几乎普遍会导致更差的表现。

输入上的加性高斯噪声,神经网络的传统正则化器,也被用于 LSTM。然而,我们发现,它不仅几乎总是损害表现,还会略微增加训练次数。

亚历克斯·格雷夫斯(Alex Graves)等人在他们 2013 年的开创性论文《使用深度递归神经网络的语音识别》》中称,语音识别取得了当时最先进的结果,但在训练过程中,噪声增加了 LSTMs 的权重。

……使用了权重噪声(在训练期间向网络权重添加高斯噪声)。每个训练序列添加一次权重噪声,而不是在每个时间步长添加。权重噪声倾向于“简化”神经网络,即减少传输参数所需的信息量,从而提高泛化能力。

在 2011 年之前的一篇研究不同类型的静态和自适应权重噪声的论文中,标题为“神经网络的实用变分推理”,“格雷夫斯建议使用提前停止,并结合权重噪声的加入来使用低信噪比模型。

……在实践中,当用重量噪音训练时,需要提前停止以防止过度训练。

训练时增加噪音的技巧

本节提供了一些在神经网络训练过程中添加噪声的技巧。

添加噪音的问题类型

无论正在解决的问题是什么类型,噪声都可能会加入到训练中。

尝试向分类和回归类型问题添加噪声是合适的。

噪声类型可以专用于用作模型输入的数据类型,例如,图像情况下的二维噪声和音频数据情况下的信号噪声。

给不同的网络类型增加噪音

在训练过程中添加噪声是一种通用方法,无论使用哪种类型的神经网络都可以使用。

这是一种主要用于多层感知器的方法,给出了它们的优先优势,但是可以并且正在用于卷积和递归神经网络。

先重新缩放数据

重要的是,噪声的加入对模型有一致的影响。

这需要重新调整输入数据的比例,以便所有变量都具有相同的比例,这样,当噪声以固定的方差添加到输入时,它具有相同的效果。也适用于向权重和梯度添加噪声,因为它们也受到输入比例的影响。

这可以通过输入变量的标准化或规范化来实现。

如果在数据缩放后添加了随机噪声,那么可能需要重新缩放变量,也许是每一个小批量。

测试噪音量

您无法知道在您的训练数据集中,噪声对您的特定模型有多大好处。

尝试不同的噪音量,甚至不同类型的噪音,以便发现什么效果最好。

系统化并使用受控实验,也许是在一系列数值的较小数据集上。

仅噪音训练

噪声仅在模型训练期间添加。

请确保在评估模型的过程中,或者在使用模型对新数据进行预测时,没有添加任何噪声源。

进一步阅读

如果您想更深入地了解这个主题,本节将提供更多资源。

报纸

文章

摘要

在这篇文章中,你发现在训练过程中向神经网络添加噪声可以提高网络的鲁棒性,从而获得更好的泛化能力和更快的学习速度。

具体来说,您了解到:

  • 小数据集会给神经网络的学习带来挑战,并且示例可以记忆。
  • 在训练过程中加入噪声可以使训练过程更加稳健,减少泛化误差。
  • 传统上,噪声会添加到输入中,但也可以添加到权重、梯度甚至激活函数中。

你有什么问题吗? 在下面的评论中提问,我会尽力回答。

了解学习率对神经网络表现的影响

原文:machinelearningmastery.com/understand-…

最后更新于 2020 年 9 月 12 日

使用随机梯度下降优化算法训练深度学习神经网络。

学习率是一个超参数,它控制每次更新模型权重时,响应估计误差而改变模型的程度。选择学习率具有挑战性,因为值太小可能会导致长时间的训练过程,从而陷入停滞,而值太大可能会导致学习次优权重集的速度过快或训练过程不稳定。

在配置神经网络时,学习率可能是最重要的超参数。因此,了解如何研究学习率对模型表现的影响以及建立学习率对模型行为动态的直觉是至关重要的。

在本教程中,您将发现学习率、学习率计划和自适应学习率对模型表现的影响。

完成本教程后,您将知道:

  • 多大的学习率导致训练不稳定,小的学习率导致训练失败。
  • 动量可以加速训练,学习率计划可以帮助收敛优化过程。
  • 自适应学习率可以加速训练,减轻选择学习率和学习率时间表的压力。

用我的新书更好的深度学习启动你的项目,包括分步教程和所有示例的 Python 源代码文件。

我们开始吧。

  • 2019 年 2 月更新:修复了在编译()而不是 fit()函数上错误定义回调的问题。
  • 2019 年 10 月更新:针对 Keras 2.3 和 TensorFlow 2.0 更新。
  • 2020 年 1 月更新:针对 Sklearn v0.22 API 的变化进行了更新。

Understand the Dynamics of Learning Rate on Model Performance With Deep Learning Neural Networks

通过深度学习神经网络了解学习率对模型表现的动态影响

教程概述

本教程分为六个部分;它们是:

  1. 学习率和梯度下降
  2. 在 Keras 中配置学习率
  3. 多类分类问题
  4. 学习速度和动量的影响
  5. 学习进度计划的影响
  6. 适应性学习率的影响

学习率和梯度下降

使用随机梯度下降算法训练深度学习神经网络。

随机梯度下降是一种优化算法,它使用来自训练数据集的示例来估计模型当前状态的误差梯度,然后使用误差反向传播算法更新模型的权重,简称反向传播。

训练期间更新的权重量被称为步长或“学习率

具体而言,学习率是用于神经网络训练的可配置超参数,其具有小的正值,通常在 0.0 和 1.0 之间的范围内。

学习率控制模型适应问题的速度。较小的学习率需要更多的训练时期,因为每次更新对权重所做的改变较小,而较大的学习率会导致快速变化,并且需要较少的训练时期。

太大的学习率会导致模型过快地收敛到次优解,而太小的学习率会导致过程停滞。

训练深度学习神经网络的挑战包括仔细选择学习率。它可能是模型最重要的超参数。

学习率可能是最重要的超参数。如果你有时间只调整一个超参数,调整学习率。

—第 429 页,深度学习,2016。

既然我们已经熟悉了什么是学习率,那么让我们来看看如何为神经网络配置学习率。

有关学习率及其工作原理的更多信息,请参见帖子:

在 Keras 中配置学习率

Keras 深度学习库允许您轻松配置随机梯度下降优化算法的多种不同变体的学习率。

随机梯度下降

Keras 提供了实现随机梯度下降优化器的 SGD 类,具有学习率和动量。

首先,必须创建和配置类的实例,然后在模型上调用 fit() 函数时,将其指定给“优化器”参数。

默认学习率为 0.01,默认不使用动量。

from keras.optimizers import SGD
...
opt = SGD()
model.compile(..., optimizer=opt)

学习率可以通过“ lr 参数指定,动量可以通过“动量参数指定。

from keras.optimizers import SGD
...
opt = SGD(lr=0.01, momentum=0.9)
model.compile(..., optimizer=opt)

该类还通过“衰减”参数支持学习率衰减。

随着学习率的衰减,每次更新(例如,每个小批量的结束)都会计算学习率,如下所示:

lrate = initial_lrate * (1 / (1 + decay * iteration))

其中 lrate 是当前纪元的学习率, initial_lrate 是指定为 SGD 参数的学习率,衰减是大于零的衰减速率,迭代是当前更新号。

from keras.optimizers import SGD
...
opt = SGD(lr=0.01, momentum=0.9, decay=0.01)
model.compile(..., optimizer=opt)

学习进度计划

Keras 通过回调支持学习率计划。

回调与优化算法分开操作,尽管它们调整优化算法使用的学习率。建议在使用学习进度回拨时使用 SGD。

回调被实例化和配置,然后在训练模型时在 fit()函数的“回调”参数的列表中指定。

Keras 提供了reduce lronplateaau,当检测到模型表现平稳时,例如给定数量的训练时期没有变化时,它将调整学习率。该回调旨在降低模型停止改进后的学习率,希望微调模型权重。

reduce lronplateaau要求您通过“ monitor ”参数指定训练期间要监控的指标,学习率将通过“因子”参数乘以的值,以及指定在触发学习率变化之前要等待的训练时期数的“耐心”参数。

例如,如果验证损失在 100 个时期内没有改善,我们可以监控验证损失,并将学习率降低一个数量级:

# snippet of using the ReduceLROnPlateau callback
from keras.callbacks import ReduceLROnPlateau
...
rlrop = ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=100)
model.fit(..., callbacks=[rlrop])

Keras 还提供了学习率调度器回调,允许您指定在每个时期调用的函数,以调整学习率。

您可以定义您的 Python 函数,该函数接受两个参数(纪元和当前学习率)并返回新的学习率。

# snippet of using the LearningRateScheduler callback
from keras.callbacks import LearningRateScheduler
...

def my_learning_rate(epoch, lrate):
	return lrate

lrs = LearningRateScheduler(my_learning_rate)
model.fit(..., callbacks=[lrs])

自适应学习率梯度下降

Keras 还提供了一套简单随机梯度下降的扩展,支持自适应学习率。

因为每种方法都适应学习率,通常每个模型权重一个学习率,所以通常只需要很少的配置。

三种常用的自适应学习率方法包括:

优化器
from keras.optimizers import RMSprop
...
opt = RMSprop()
model.compile(..., optimizer=opt)
Adagrad 优化器
from keras.optimizers import Adagrad
...
opt = Adagrad()
model.compile(..., optimizer=opt)
亚当优化器
from keras.optimizers import Adam
...
opt = Adam()
model.compile(..., optimizer=opt)

多类分类问题

我们将使用一个小的多类分类问题作为基础来演示学习率对模型表现的影响。

Sklearn 类提供了 make_blobs()函数,该函数可用于创建具有规定数量的样本、输入变量、类和类内样本方差的多类分类问题。

该问题有两个输入变量(表示点的 xy 坐标)和每组内点的标准偏差 2.0。我们将使用相同的随机状态(伪随机数发生器的种子)来确保我们总是获得相同的数据点。

# 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 numpy import where
# generate 2d classification dataset
X, y = make_blobs(n_samples=1000, centers=3, n_features=2, cluster_std=2, random_state=2)
# scatter plot for each class value
for class_value in range(3):
	# select indices of points with the class label
	row_ix = where(y == class_value)
	# scatter plot for points with a different color
	pyplot.scatter(X[row_ix, 0], X[row_ix, 1])
# show plot
pyplot.show()

运行该示例会创建整个数据集的散点图。我们可以看到,2.0 的标准差意味着类不是线性可分的(用一条线可分),造成了很多模棱两可的点。

这是可取的,因为这意味着问题不是微不足道的,并将允许神经网络模型找到许多不同的“足够好”的候选解决方案。

Scatter Plot of Blobs Dataset With Three Classes and Points Colored by Class Value

具有三个类和按类值着色的点的斑点数据集的散点图

学习速度和动量的影响

在本节中,我们将开发一个多层感知器(MLP)模型来解决斑点分类问题,并研究不同学习率和动量的影响。

学习速度动态

第一步是开发一个函数,该函数将从问题中创建样本,并将它们分成训练和测试数据集。

此外,我们还必须对目标变量进行热编码,这样我们就可以开发一个模型来预测一个例子属于每个类的概率。

下面的 prepare_data() 函数实现了这种行为,返回分割成输入和输出元素的训练和测试集。

# prepare train and test dataset
def prepare_data():
	# 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 = 500
	trainX, testX = X[:n_train, :], X[n_train:, :]
	trainy, testy = y[:n_train], y[n_train:]
	return trainX, trainy, testX, testy

接下来,我们可以开发一个函数来拟合和评估 MLP 模型。

首先,我们将定义一个简单的 MLP 模型,该模型期望来自 blobs 问题的两个输入变量,具有一个包含 50 个节点的隐藏层,以及一个包含三个节点的输出层,以预测三个类中每个类的概率。隐藏层的节点将使用整流线性激活函数(ReLU) ,而输出层的节点将使用 softmax 激活函数。

# define model
model = Sequential()
model.add(Dense(50, input_dim=2, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(3, activation='softmax'))

我们将使用随机梯度下降优化器,并要求指定学习率,以便我们可以评估不同的速率。该模型将被训练为最小化交叉熵。

# compile model
opt = SGD(lr=lrate)
model.compile(loss='categorical_crossentropy', optimizer=opt, metrics=['accuracy'])

该模型将适合 200 个训练时期,通过少量的试错发现,测试集将用作验证数据集,因此我们可以在训练过程中了解模型的泛化误差。

# fit model
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=200, verbose=0)

一旦合适,我们将在训练和测试集上绘制模型的准确性。

# plot learning curves
pyplot.plot(history.history['accuracy'], label='train')
pyplot.plot(history.history['val_accuracy'], label='test')
pyplot.title('lrate='+str(lrate), pad=-50)

下面的 fit_model() 函数将这些元素联系在一起,并在给定要评估的训练和测试数据集以及特定学习率的情况下,拟合模型并绘制其表现。

# fit a model and plot learning curve
def fit_model(trainX, trainy, testX, testy, lrate):
	# define model
	model = Sequential()
	model.add(Dense(50, input_dim=2, activation='relu', kernel_initializer='he_uniform'))
	model.add(Dense(3, activation='softmax'))
	# compile model
	opt = SGD(lr=lrate)
	model.compile(loss='categorical_crossentropy', optimizer=opt, metrics=['accuracy'])
	# fit model
	history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=200, verbose=0)
	# plot learning curves
	pyplot.plot(history.history['accuracy'], label='train')
	pyplot.plot(history.history['val_accuracy'], label='test')
	pyplot.title('lrate='+str(lrate), pad=-50)

我们现在可以研究火车上不同学习率的动态,并测试模型的准确性。

在本例中,我们将在 1E-0 (1.0)到 1E-7 的对数标度上评估学习率,并通过调用 fit_model() 函数为每个学习率创建线图。

# create learning curves for different learning rates
learning_rates = [1E-0, 1E-1, 1E-2, 1E-3, 1E-4, 1E-5, 1E-6, 1E-7]
for i in range(len(learning_rates)):
	# determine the plot number
	plot_no = 420 + (i+1)
	pyplot.subplot(plot_no)
	# fit model and plot learning curves for a learning rate
	fit_model(trainX, trainy, testX, testy, learning_rates[i])
# show learning curves
pyplot.show()

将所有这些结合在一起,下面列出了完整的示例。

# study of learning rate on accuracy for blobs problem
from sklearn.datasets import make_blobs
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import SGD
from keras.utils import to_categorical
from matplotlib import pyplot

# prepare train and test dataset
def prepare_data():
	# 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 = 500
	trainX, testX = X[:n_train, :], X[n_train:, :]
	trainy, testy = y[:n_train], y[n_train:]
	return trainX, trainy, testX, testy

# fit a model and plot learning curve
def fit_model(trainX, trainy, testX, testy, lrate):
	# define model
	model = Sequential()
	model.add(Dense(50, input_dim=2, activation='relu', kernel_initializer='he_uniform'))
	model.add(Dense(3, activation='softmax'))
	# compile model
	opt = SGD(lr=lrate)
	model.compile(loss='categorical_crossentropy', optimizer=opt, metrics=['accuracy'])
	# fit model
	history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=200, verbose=0)
	# plot learning curves
	pyplot.plot(history.history['accuracy'], label='train')
	pyplot.plot(history.history['val_accuracy'], label='test')
	pyplot.title('lrate='+str(lrate), pad=-50)

# prepare dataset
trainX, trainy, testX, testy = prepare_data()
# create learning curves for different learning rates
learning_rates = [1E-0, 1E-1, 1E-2, 1E-3, 1E-4, 1E-5, 1E-6, 1E-7]
for i in range(len(learning_rates)):
	# determine the plot number
	plot_no = 420 + (i+1)
	pyplot.subplot(plot_no)
	# fit model and plot learning curves for a learning rate
	fit_model(trainX, trainy, testX, testy, learning_rates[i])
# show learning curves
pyplot.show()

运行该示例会创建一个包含八个不同评估学习率的八条线图的图形。训练数据集上的分类准确率用蓝色标记,而测试数据集上的准确率用橙色标记。

:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。

这些图显示了在过大的学习率为 1.0 时的行为振荡,以及模型在过小的学习率为 1E-6 和 1E-7 时无法学习任何东西。

我们可以看到,该模型能够以 1E-1、1E-2 和 1E-3 的学习率很好地学习问题,尽管随着学习率的降低,学习速度逐梯度慢。对于所选择的模型配置,结果表明 0.1 的中等学习率在训练和测试集上产生良好的模型表现。

Line Plots of Train and Test Accuracy for a Suite of Learning Rates on the Blobs Classification Problem

斑点分类问题上一组学习率的训练和测试准确率的线图

动量动力学

动量可以平滑学习算法的进程,进而可以加速训练过程。

我们可以改编上一节的例子,用固定的学习率来评估动量的效果。在这种情况下,我们将选择 0.01 的学习率,在前一节中,该学习率收敛到一个合理的解决方案,但是需要比 0.1 的学习率更多的时期

fit_model() 函数可以更新为取一个“动量参数,而不是学习率参数,该参数可以在 SGD 类的配置中使用,并在生成的图中报告。

下面列出了该功能的更新版本。

# fit a model and plot learning curve
def fit_model(trainX, trainy, testX, testy, momentum):
	# define model
	model = Sequential()
	model.add(Dense(50, input_dim=2, activation='relu', kernel_initializer='he_uniform'))
	model.add(Dense(3, activation='softmax'))
	# compile model
	opt = SGD(lr=0.01, momentum=momentum)
	model.compile(loss='categorical_crossentropy', optimizer=opt, metrics=['accuracy'])
	# fit model
	history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=200, verbose=0)
	# plot learning curves
	pyplot.plot(history.history['accuracy'], label='train')
	pyplot.plot(history.history['val_accuracy'], label='test')
	pyplot.title('momentum='+str(momentum), pad=-80)

通常使用接近 1.0 的动量值,例如 0.9 和 0.99。

在本例中,我们将演示与动量值为 0.5 和更高动量值的模型相比,没有动量的模型的动力学。

# create learning curves for different momentums
momentums = [0.0, 0.5, 0.9, 0.99]
for i in range(len(momentums)):
	# determine the plot number
	plot_no = 220 + (i+1)
	pyplot.subplot(plot_no)
	# fit model and plot learning curves for a momentum
	fit_model(trainX, trainy, testX, testy, momentums[i])
# show learning curves
pyplot.show()

将所有这些结合在一起,下面列出了完整的示例。

# study of momentum on accuracy for blobs problem
from sklearn.datasets import make_blobs
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import SGD
from keras.utils import to_categorical
from matplotlib import pyplot

# prepare train and test dataset
def prepare_data():
	# 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 = 500
	trainX, testX = X[:n_train, :], X[n_train:, :]
	trainy, testy = y[:n_train], y[n_train:]
	return trainX, trainy, testX, testy

# fit a model and plot learning curve
def fit_model(trainX, trainy, testX, testy, momentum):
	# define model
	model = Sequential()
	model.add(Dense(50, input_dim=2, activation='relu', kernel_initializer='he_uniform'))
	model.add(Dense(3, activation='softmax'))
	# compile model
	opt = SGD(lr=0.01, momentum=momentum)
	model.compile(loss='categorical_crossentropy', optimizer=opt, metrics=['accuracy'])
	# fit model
	history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=200, verbose=0)
	# plot learning curves
	pyplot.plot(history.history['accuracy'], label='train')
	pyplot.plot(history.history['val_accuracy'], label='test')
	pyplot.title('momentum='+str(momentum), pad=-80)

# prepare dataset
trainX, trainy, testX, testy = prepare_data()
# create learning curves for different momentums
momentums = [0.0, 0.5, 0.9, 0.99]
for i in range(len(momentums)):
	# determine the plot number
	plot_no = 220 + (i+1)
	pyplot.subplot(plot_no)
	# fit model and plot learning curves for a momentum
	fit_model(trainX, trainy, testX, testy, momentums[i])
# show learning curves
pyplot.show()

运行该示例会创建一个图形,其中包含四个不同动量值的线图。训练数据集上的分类准确率用蓝色标记,而测试数据集上的准确率用橙色标记。

:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。

我们可以看到动量的加入确实加速了模型的训练。具体而言,0.9 和 0.99 的动量值在大约 50 个训练时期内实现了合理的训练和测试准确率,而当不使用动量时为 200 个训练时期。

在所有使用动量的情况下,模型在保持测试数据集上的准确率看起来更稳定,在训练时期表现出较少的波动性。

Line Plots of Train and Test Accuracy for a Suite of Momentums on the Blobs Classification Problem

关于斑点分类问题的列车线图和一组动量的检验准确率

学习进度计划的影响

我们将在这一部分看两个学习进度。

第一个是内置在 SGD 类中的衰减,第二个是reduce lronplateaau回调。

学习率衰减

SGD 类提供了指定学习率衰减的“衰减”参数。

从等式或代码中可能不清楚这种衰减对更新后的学习率有什么影响。我们可以用一个工作实例来说明这一点。

下面的函数实现了在 SGD 类中实现的学习率衰减。

# learning rate decay
def decay_lrate(initial_lrate, decay, iteration):
	return initial_lrate * (1.0 / (1.0 + decay * iteration))

我们可以使用这个函数来计算具有不同衰减值的多次更新的学习率。

我们将比较一系列衰减值[1E-1,1E-2,1E-3,1E-4],初始学习率为 0.01,权重更新为 200。

decays = [1E-1, 1E-2, 1E-3, 1E-4]
lrate = 0.01
n_updates = 200
for decay in decays:
	# calculate learning rates for updates
	lrates = [decay_lrate(lrate, decay, i) for i in range(n_updates)]
	# plot result
	pyplot.plot(lrates, label=str(decay))

下面列出了完整的示例。

# demonstrate the effect of decay on the learning rate
from matplotlib import pyplot

# learning rate decay
def	decay_lrate(initial_lrate, decay, iteration):
	return initial_lrate * (1.0 / (1.0 + decay * iteration))

decays = [1E-1, 1E-2, 1E-3, 1E-4]
lrate = 0.01
n_updates = 200
for decay in decays:
	# calculate learning rates for updates
	lrates = [decay_lrate(lrate, decay, i) for i in range(n_updates)]
	# plot result
	pyplot.plot(lrates, label=str(decay))
pyplot.legend()
pyplot.show()

运行该示例会创建一个折线图,显示不同衰减值的学习率随更新的变化。

我们可以看到,在所有情况下,学习率都是从初始值 0.01 开始的。我们可以看到,1E-4 的小衰减值(红色)几乎没有影响,而 1E-1 的大衰减值(蓝色)有着戏剧性的效果,在 50 个纪元内将学习率降低到 0.002 以下(比初始值小一个数量级左右),并达到最终值 0.0004 左右(比初始值小两个数量级左右)。

我们可以看到学习率的变化不是线性的。我们还可以看到,学习率的变化取决于批次大小,之后会执行更新。在上一节的示例中,500 个示例中 32 的默认批处理大小导致每个时期 16 次更新和 200 个时期的 3,200 次更新。

使用 0.1 的衰减和 0.01 的初始学习率,我们可以计算出最终学习率是大约 3.1E-05 的微小值。

Line Plot of the Effect of Decay on Learning Rate Over Multiple Weight Updates

多次权重更新中衰减对学习率影响的线图

我们可以更新上一节中的示例,以评估不同学习率衰减值的动态性。

将学习率固定在 0.01 并且不使用动量,我们将期望非常小的学习率衰减将是优选的,因为大的学习率衰减将迅速导致学习率太小,模型无法有效学习。

fit_model() 函数可以更新为采用“衰减参数,该参数可用于配置 SGD 类的衰减。

下面列出了该功能的更新版本。

# fit a model and plot learning curve
def fit_model(trainX, trainy, testX, testy, decay):
	# define model
	model = Sequential()
	model.add(Dense(50, input_dim=2, activation='relu', kernel_initializer='he_uniform'))
	model.add(Dense(3, activation='softmax'))
	# compile model
	opt = SGD(lr=0.01, decay=decay)
	model.compile(loss='categorical_crossentropy', optimizer=opt, metrics=['accuracy'])
	# fit model
	history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=200, verbose=0)
	# plot learning curves
	pyplot.plot(history.history['accuracy'], label='train')
	pyplot.plot(history.history['val_accuracy'], label='test')
	pyplot.title('decay='+str(decay), pad=-80)

我们可以评估[1E-1,1E-2,1E-3,1E-4]的相同四个衰减值及其对模型准确率的影响。

下面列出了完整的示例。

# study of decay rate on accuracy for blobs problem
from sklearn.datasets import make_blobs
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import SGD
from keras.utils import to_categorical
from matplotlib import pyplot

# prepare train and test dataset
def prepare_data():
	# 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 = 500
	trainX, testX = X[:n_train, :], X[n_train:, :]
	trainy, testy = y[:n_train], y[n_train:]
	return trainX, trainy, testX, testy

# fit a model and plot learning curve
def fit_model(trainX, trainy, testX, testy, decay):
	# define model
	model = Sequential()
	model.add(Dense(50, input_dim=2, activation='relu', kernel_initializer='he_uniform'))
	model.add(Dense(3, activation='softmax'))
	# compile model
	opt = SGD(lr=0.01, decay=decay)
	model.compile(loss='categorical_crossentropy', optimizer=opt, metrics=['accuracy'])
	# fit model
	history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=200, verbose=0)
	# plot learning curves
	pyplot.plot(history.history['accuracy'], label='train')
	pyplot.plot(history.history['val_accuracy'], label='test')
	pyplot.title('decay='+str(decay), pad=-80)

# prepare dataset
trainX, trainy, testX, testy = prepare_data()
# create learning curves for different decay rates
decay_rates = [1E-1, 1E-2, 1E-3, 1E-4]
for i in range(len(decay_rates)):
	# determine the plot number
	plot_no = 220 + (i+1)
	pyplot.subplot(plot_no)
	# fit model and plot learning curves for a decay rate
	fit_model(trainX, trainy, testX, testy, decay_rates[i])
# show learning curves
pyplot.show()

运行该示例会创建一个图形,其中包含不同评估学习率衰减值的四条线图。训练数据集上的分类准确率用蓝色标记,而测试数据集上的准确率用橙色标记。

:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。

我们可以看到,1E-1 和 1E-2 的大衰减值确实会使该模型在这个问题上的学习率衰减过快,从而导致表现不佳。衰减值越小,表现越好,1E-4 的值可能会导致完全不使用衰减的类似结果。事实上,我们可以计算出 1E-4 衰减的最终学习率约为 0.0075,只比初始值 0.01 小一点点。

Line Plots of Train and Test Accuracy for a Suite of Decay Rates on the Blobs Classification Problem

斑点分类问题上一组衰减率的训练和检验准确率的线图

高原学习率下降

在给定数量的时期内,在监控指标没有变化的情况下,减少学习率会将学习率降低一个因子。

我们可以探究不同“耐心”值的效果,即在降低学习率之前等待改变的纪元数。我们将使用默认的学习率 0.01,并将“因子”参数设置为 0.1,从而将学习率降低一个数量级。

rlrp = ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=patience, min_delta=1E-7)

回顾培训期间对学习率的影响将会很有趣。我们可以通过创建一个新的 Keras Callback 来做到这一点,它负责记录每个训练时期结束时的学习率。然后,我们可以检索记录的学习率,并创建一个折线图来查看学习率如何受到下降的影响。

我们可以创建一个名为学习率监视器的自定义回调。在训练开始时调用 on_train_begin() 函数,在该函数中我们可以定义一个学习率的空列表。在每个训练时期结束时调用 on_epoch_end() 函数,在该函数中,我们可以从优化器中检索优化器和当前学习率,并将其存储在列表中。下面列出了完整的学习率监视器回调。

# monitor the learning rate
class LearningRateMonitor(Callback):
	# start of training
	def on_train_begin(self, logs={}):
		self.lrates = list()

	# end of each training epoch
	def on_epoch_end(self, epoch, logs={}):
		# get and store the learning rate
		optimizer = self.model.optimizer
		lrate = float(backend.get_value(self.model.optimizer.lr))
		self.lrates.append(lrate)

在前几节中开发的 fit_model() 函数可以更新,以创建和配置 ReduceLROnPlateau 回调和我们新的 LearningRateMonitor 回调,并在 fit 调用中向模型注册它们。

该函数还会以“耐心”作为参数,这样我们就可以评估不同的值。

# fit model
rlrp = ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=patience, min_delta=1E-7)
lrm = LearningRateMonitor()
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=200, verbose=0, callbacks=[rlrp, lrm])

在本例中,我们将希望创建几个图,因此 fit_model() 函数将返回每个训练时期的学习率以及训练数据集上的损失和准确性的列表,而不是直接创建子图。

下面列出了这些更新的功能。

# fit a model and plot learning curve
def fit_model(trainX, trainy, testX, testy, patience):
	# define model
	model = Sequential()
	model.add(Dense(50, input_dim=2, activation='relu', kernel_initializer='he_uniform'))
	model.add(Dense(3, activation='softmax'))
	# compile model
	opt = SGD(lr=0.01)
	model.compile(loss='categorical_crossentropy', optimizer=opt, metrics=['accuracy'])
	# fit model
	rlrp = ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=patience, min_delta=1E-7)
	lrm = LearningRateMonitor()
	history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=200, verbose=0, callbacks=[rlrp, lrm])
	return lrm.lrates, history.history['loss'], history.history['accuracy']

ReduceLROnPlateau 中的耐心控制学习率下降的频率。

我们将在 blobs 问题上测试适合该模型的几个不同的耐心值,并跟踪每次运行的学习率、损失和准确性系列。

# create learning curves for different patiences
patiences = [2, 5, 10, 15]
lr_list, loss_list, acc_list, = list(), list(), list()
for i in range(len(patiences)):
	# fit model and plot learning curves for a patience
	lr, loss, acc = fit_model(trainX, trainy, testX, testy, patiences[i])
	lr_list.append(lr)
	loss_list.append(loss)
	acc_list.append(acc)

在运行结束时,我们将为每个耐心值的学习率、训练损失和每个耐心值的训练准确率创建带有线图的数字。

我们可以创建一个助手函数来轻松地为我们记录的每个系列创建一个带有子情节的图形。

# create line plots for a series
def line_plots(patiences, series):
	for i in range(len(patiences)):
		pyplot.subplot(220 + (i+1))
		pyplot.plot(series[i])
		pyplot.title('patience='+str(patiences[i]), pad=-80)
	pyplot.show()

将这些元素结合在一起,下面列出了完整的示例。

# study of patience for the learning rate drop schedule on the blobs problem
from sklearn.datasets import make_blobs
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import SGD
from keras.utils import to_categorical
from keras.callbacks import Callback
from keras.callbacks import ReduceLROnPlateau
from keras import backend
from matplotlib import pyplot

# monitor the learning rate
class LearningRateMonitor(Callback):
	# start of training
	def on_train_begin(self, logs={}):
		self.lrates = list()

	# end of each training epoch
	def on_epoch_end(self, epoch, logs={}):
		# get and store the learning rate
		optimizer = self.model.optimizer
		lrate = float(backend.get_value(self.model.optimizer.lr))
		self.lrates.append(lrate)

# prepare train and test dataset
def prepare_data():
	# 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 = 500
	trainX, testX = X[:n_train, :], X[n_train:, :]
	trainy, testy = y[:n_train], y[n_train:]
	return trainX, trainy, testX, testy

# fit a model and plot learning curve
def fit_model(trainX, trainy, testX, testy, patience):
	# define model
	model = Sequential()
	model.add(Dense(50, input_dim=2, activation='relu', kernel_initializer='he_uniform'))
	model.add(Dense(3, activation='softmax'))
	# compile model
	opt = SGD(lr=0.01)
	model.compile(loss='categorical_crossentropy', optimizer=opt, metrics=['accuracy'])
	# fit model
	rlrp = ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=patience, min_delta=1E-7)
	lrm = LearningRateMonitor()
	history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=200, verbose=0, callbacks=[rlrp, lrm])
	return lrm.lrates, history.history['loss'], history.history['accuracy']

# create line plots for a series
def line_plots(patiences, series):
	for i in range(len(patiences)):
		pyplot.subplot(220 + (i+1))
		pyplot.plot(series[i])
		pyplot.title('patience='+str(patiences[i]), pad=-80)
	pyplot.show()

# prepare dataset
trainX, trainy, testX, testy = prepare_data()
# create learning curves for different patiences
patiences = [2, 5, 10, 15]
lr_list, loss_list, acc_list, = list(), list(), list()
for i in range(len(patiences)):
	# fit model and plot learning curves for a patience
	lr, loss, acc = fit_model(trainX, trainy, testX, testy, patiences[i])
	lr_list.append(lr)
	loss_list.append(loss)
	acc_list.append(acc)
# plot learning rates
line_plots(patiences, lr_list)
# plot loss
line_plots(patiences, loss_list)
# plot accuracy
line_plots(patiences, acc_list)

运行该示例会创建三个图形,每个图形包含不同耐心值的线图。

:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。

第一个图显示了每个评估的耐心值在训练时期的学习率的线图。我们可以看到,最小的两个耐心值在 25 个纪元内迅速将学习率降至最小值,最大的 15 个耐心值仅遭受一次学习率下降。

从这些图中,我们可以预期这个模型在这个问题上的 5 和 10 的耐心值会导致更好的表现,因为它们允许在降低学习率以细化权重之前使用更大的学习率一段时间。

Line Plots of Learning Rate Over Epochs for Different Patience Values Used in the ReduceLROnPlateau Schedule

减少延迟计划中使用的不同耐心值在不同时期的学习率线图

下图显示了每个耐心值在训练数据集中的损失。

该图显示,耐心值 2 和 5 导致模型快速收敛,可能达到次优损失值。在耐心水平为 10 和 15 的情况下,损失会合理下降,直到学习率下降到可以看到损失的巨大变化的水平以下。这发生在耐心 10 的中途,接近耐心 15 的运行结束。

Line Plots of Training Loss Over Epochs for Different Patience Values Used in the ReduceLROnPlateau Schedule

减少延迟计划中使用的不同耐心值在不同时期的训练损失线图

最后一个图显示了每个耐心值在不同训练时期的训练集准确度。

我们可以看到,事实上,2 和 5 个时期的小耐心值导致模型过早收敛到低于最优的模型,准确率分别约为 65%和低于 75%。更大的耐心值导致更好的模型表现,耐心 10 在 150 个纪元之前显示出收敛,而耐心 15 在给定几乎完全不变的学习率的情况下继续显示出不稳定的准确率的影响。

这些图显示了学习率的降低是解决问题的一种明智方式,所选择的模型配置可以产生一组熟练且收敛的稳定的最终权重,这是训练结束时最终模型的一个理想特性。

Line Plots of Training Accuracy Over Epochs for Different Patience Values Used in the ReduceLROnPlateau Schedule

减少延迟计划中使用的不同耐心值在不同时期的训练准确率线图

适应性学习率的影响

学习率和学习率计划对于深度学习神经网络模型的配置和表现都是具有挑战性的。

Keras 提供了许多具有自适应学习率的随机梯度下降的流行变体,例如:

  • 自适应梯度算法。
  • 均方根传播。
  • 自适应矩估计。

每种方法都为网络中的每个权重提供了不同的学习率调整方法。

没有单一的最佳算法,赛车优化算法在一个问题上的结果不太可能转移到新的问题上。

我们可以研究不同的自适应学习率方法对 blobs 问题的动态性。可以更新 fit_model() 函数,取一个优化算法的名字进行求值,在编译 MLP 模型时可以指定给“优化器”参数。然后将使用每个方法的默认参数。下面列出了该功能的更新版本。

# fit a model and plot learning curve
def fit_model(trainX, trainy, testX, testy, optimizer):
	# define model
	model = Sequential()
	model.add(Dense(50, input_dim=2, activation='relu', kernel_initializer='he_uniform'))
	model.add(Dense(3, activation='softmax'))
	# compile model
	model.compile(loss='categorical_crossentropy', optimizer=optimizer, metrics=['accuracy'])
	# fit model
	history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=200, verbose=0)
	# plot learning curves
	pyplot.plot(history.history['accuracy'], label='train')
	pyplot.plot(history.history['val_accuracy'], label='test')
	pyplot.title('opt='+optimizer, pad=-80)

我们可以探索 RMSprop、AdaGrad 和 Adam 这三种流行的方法,并将它们的行为与具有静态学习率的简单随机梯度下降进行比较。

我们期望算法的自适应学习率版本表现相似或更好,也许在更少的训练时期适应问题,但重要的是,产生更稳定的模型。

# prepare dataset
trainX, trainy, testX, testy = prepare_data()
# create learning curves for different optimizers
momentums = ['sgd', 'rmsprop', 'adagrad', 'adam']
for i in range(len(momentums)):
	# determine the plot number
	plot_no = 220 + (i+1)
	pyplot.subplot(plot_no)
	# fit model and plot learning curves for an optimizer
	fit_model(trainX, trainy, testX, testy, momentums[i])
# show learning curves
pyplot.show()

将这些元素结合在一起,下面列出了完整的示例。

# study of sgd with adaptive learning rates in the blobs problem
from sklearn.datasets import make_blobs
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import SGD
from keras.utils import to_categorical
from keras.callbacks import Callback
from keras import backend
from matplotlib import pyplot

# prepare train and test dataset
def prepare_data():
	# 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 = 500
	trainX, testX = X[:n_train, :], X[n_train:, :]
	trainy, testy = y[:n_train], y[n_train:]
	return trainX, trainy, testX, testy

# fit a model and plot learning curve
def fit_model(trainX, trainy, testX, testy, optimizer):
	# define model
	model = Sequential()
	model.add(Dense(50, input_dim=2, activation='relu', kernel_initializer='he_uniform'))
	model.add(Dense(3, activation='softmax'))
	# compile model
	model.compile(loss='categorical_crossentropy', optimizer=optimizer, metrics=['accuracy'])
	# fit model
	history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=200, verbose=0)
	# plot learning curves
	pyplot.plot(history.history['accuracy'], label='train')
	pyplot.plot(history.history['val_accuracy'], label='test')
	pyplot.title('opt='+optimizer, pad=-80)

# prepare dataset
trainX, trainy, testX, testy = prepare_data()
# create learning curves for different optimizers
momentums = ['sgd', 'rmsprop', 'adagrad', 'adam']
for i in range(len(momentums)):
	# determine the plot number
	plot_no = 220 + (i+1)
	pyplot.subplot(plot_no)
	# fit model and plot learning curves for an optimizer
	fit_model(trainX, trainy, testX, testy, momentums[i])
# show learning curves
pyplot.show()

运行该示例会创建一个图形,其中包含不同优化算法的四条线图。训练数据集上的分类准确率用蓝色标记,而测试数据集上的准确率用橙色标记。

:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。

同样,我们可以看到,默认学习率为 0.01 并且没有动量的 SGD 确实学习了问题,但是需要几乎所有 200 个时期,并且导致训练数据的不稳定准确率,并且在测试数据集上更是如此。这些图显示,所有三种自适应学习率方法都能够更快地学习问题,并且在训练和测试集准确性方面波动性显著更小。

RMSProp 和 Adam 都表现出了相似的表现,在 50 个训练期内有效地学习了问题,并花费剩余的训练时间进行了非常小的权重更新,但没有像我们在上一节中看到的学习率计划那样收敛。

Line Plots of Train and Test Accuracy for a Suite of Adaptive Learning Rate Methods on the Blobs Classification Problem

一套自适应学习率方法在斑点分类问题上的训练和测试准确率的线图

进一步阅读

如果您想更深入地了解这个主题,本节将提供更多资源。

邮件

报纸

应用程序接口

文章

摘要

在本教程中,您发现了学习率、学习率计划和自适应学习率对模型表现的影响。

具体来说,您了解到:

  • 多大的学习率导致训练不稳定,小的学习率导致训练失败。
  • 动量可以加速训练,学习率计划可以帮助收敛优化过程。
  • 自适应学习率可以加速训练,减轻选择学习率和学习率时间表的压力。

你有什么问题吗? 在下面的评论中提问,我会尽力回答。

可视化梯度消失问题

原文:machinelearningmastery.com/visualizing…

最后更新于 2021 年 11 月 26 日

深度学习是最近的发明。部分原因是由于计算能力的提高,允许我们在神经网络中使用更多层的感知器。但与此同时,只有在我们知道如何解决梯度消失问题后,我们才能训练一个深度网络。

在本教程中,我们直观地考察了消失梯度问题存在的原因。

完成本教程后,您将知道

  • 什么是消失的梯度
  • 神经网络的哪种配置容易受到梯度消失的影响
  • 如何在 Keras 中运行手动训练循环
  • 如何从 Keras 模型中提取权重和梯度

我们开始吧

Visualizing the vanishing gradient problem

可视化消失梯度问题 图片由亚里沙安东提供,保留部分权利。

教程概述

本教程分为 5 个部分;它们是:

  1. 多层感知器模型的配置
  2. 梯度消失问题示例
  3. 看每一层的重量
  4. 观察每一层的梯度
  5. 格洛特初始化

多层感知器模型的配置

由于神经网络是通过梯度下降训练的,人们认为神经网络中需要一个可微函数作为激活函数。这导致我们习惯性地使用 sigmoid 函数或双曲正切函数作为激活。

对于二进制分类问题,如果我们想进行逻辑回归,使 0 和 1 是理想的输出,那么 sigmoid 函数是首选的,因为它在这个范围内:  sigma(x)=11+ex\ sigma(x)= \frac{1}{1+e^{-x}} 如果我们需要在输出端激活 sigmoid,那么在神经网络的所有层中使用它是很自然的。此外,神经网络中的每一层都有一个权重参数。最初,权重必须是随机的,自然我们会使用一些简单的方法来实现,比如使用均匀随机或正态分布。

梯度消失问题示例

为了说明梯度消失的问题,我们来举个例子试试。神经网络是一种非线性函数。因此它最适合非线性数据集的分类。我们利用 Sklearn 的make_circle()函数生成一些数据:

from sklearn.datasets import make_circles
import matplotlib.pyplot as plt

# Make data: Two circles on x-y plane as a classification problem
X, y = make_circles(n_samples=1000, factor=0.5, noise=0.1)

plt.figure(figsize=(8,6))
plt.scatter(X[:,0], X[:,1], c=y)
plt.show()

这不难分类。一种天真的方法是构建一个 3 层神经网络,它可以给出一个相当好的结果:

from tensorflow.keras.layers import Dense, Input
from tensorflow.keras import Sequential

model = Sequential([
    Input(shape=(2,)),
    Dense(5, "relu"),
    Dense(1, "sigmoid")
])
model.compile(optimizer="adam", loss="binary_crossentropy", metrics=["acc"])
model.fit(X, y, batch_size=32, epochs=100, verbose=0)
print(model.evaluate(X,y))
32/32 [==============================] - 0s 1ms/step - loss: 0.2404 - acc: 0.9730
[0.24042171239852905, 0.9729999899864197]

请注意,我们在上面的隐藏层中使用了整流线性单位(ReLU)。默认情况下,Keras 中的密集层将使用线性激活(即无激活),这通常是无用的。我们通常在现代神经网络中使用 ReLU。但我们也可以像 20 年前的所有人一样,尝试传统的方式:

model = Sequential([
    Input(shape=(2,)),
    Dense(5, "sigmoid"),
    Dense(1, "sigmoid")
])
model.compile(optimizer="adam", loss="binary_crossentropy", metrics=["acc"])
model.fit(X, y, batch_size=32, epochs=100, verbose=0)
print(model.evaluate(X,y))
32/32 [==============================] - 0s 1ms/step - loss: 0.6927 - acc: 0.6540
[0.6926590800285339, 0.6539999842643738]

准确度差很多。事实证明,增加更多的层会更糟(至少在我的实验中是这样):

model = Sequential([
    Input(shape=(2,)),
    Dense(5, "sigmoid"),
    Dense(5, "sigmoid"),
    Dense(5, "sigmoid"),
    Dense(1, "sigmoid")
])
model.compile(optimizer="adam", loss="binary_crossentropy", metrics=["acc"])
model.fit(X, y, batch_size=32, epochs=100, verbose=0)
print(model.evaluate(X,y))
32/32 [==============================] - 0s 1ms/step - loss: 0.6922 - acc: 0.5330
[0.6921834349632263, 0.5329999923706055]

鉴于训练算法的随机性,您的结果可能会有所不同。你可能会看到 5 层乙状结肠网络的表现比 3 层差很多。但这里的想法是,你不能恢复高准确率,因为我们可以通过整流线性单位激活仅仅增加层。

看每一层的重量

难道我们不应该得到一个更强大、层次更多的神经网络吗?

是的,应该是。但事实证明,随着我们添加更多的层,我们触发了消失的梯度问题。为了说明发生了什么,让我们看看训练网络时权重是什么样子的。

在 Keras 中,我们可以在训练过程中插入一个回调函数。我们将创建自己的回调对象,以截取并记录每个时期结束时多层感知器(MLP)模型的每一层的权重。

from tensorflow.keras.callbacks import Callback

class WeightCapture(Callback):
    "Capture the weights of each layer of the model"
    def __init__(self, model):
        super().__init__()
        self.model = model
        self.weights = []
        self.epochs = []

    def on_epoch_end(self, epoch, logs=None):
        self.epochs.append(epoch) # remember the epoch axis
        weight = {}
        for layer in model.layers:
            if not layer.weights:
                continue
            name = layer.weights[0].name.split("/")[0]
            weight[name] = layer.weights[0].numpy()
        self.weights.append(weight)

我们导出Callback类并定义on_epoch_end()函数。这个类需要创建的模型来初始化。在每个纪元结束时,它将读取每一层并将权重保存到 numpy 数组中。

为了方便实验创建 MLP 的不同方法,我们制作了一个辅助函数来建立神经网络模型:

def make_mlp(activation, initializer, name):
    "Create a model with specified activation and initalizer"
    model = Sequential([
        Input(shape=(2,), name=name+"0"),
        Dense(5, activation=activation, kernel_initializer=initializer, name=name+"1"),
        Dense(5, activation=activation, kernel_initializer=initializer, name=name+"2"),
        Dense(5, activation=activation, kernel_initializer=initializer, name=name+"3"),
        Dense(5, activation=activation, kernel_initializer=initializer, name=name+"4"),
        Dense(1, activation="sigmoid", kernel_initializer=initializer, name=name+"5")
    ])
    return model

我们特意创建了一个具有 4 个隐藏层的神经网络,这样我们就可以看到每一层对训练的反应。我们将改变每个隐藏层的激活函数以及权重初始化。为了让事情更容易区分,我们将命名每个层,而不是让 Keras 指定一个名称。输入是 xy 平面上的坐标,因此输入形状是向量 2。输出是二进制分类。因此,我们使用 sigmoid 激活来使输出落在 0 至 1 的范围内。

然后我们可以compile()模型来提供评估指标,并在fit()调用中传递回调来训练模型:

initializer = RandomNormal(mean=0.0, stddev=1.0)
batch_size = 32
n_epochs = 100

model = make_mlp("sigmoid", initializer, "sigmoid")
capture_cb = WeightCapture(model)
capture_cb.on_epoch_end(-1)
model.compile(optimizer="rmsprop", loss="binary_crossentropy", metrics=["acc"])
model.fit(X, y, batch_size=batch_size, epochs=n_epochs, callbacks=[capture_cb], verbose=1)

这里我们先通过调用make_mlp()来创建神经网络。然后我们设置回调对象。由于神经网络中每一层的权重都是在创建时初始化的,所以我们特意调用回调函数来记住它们被初始化为什么。然后我们像往常一样从模型中调用compile()fit(),并提供回调对象。

拟合模型后,我们可以用整个数据集对其进行评估:

...
print(model.evaluate(X,y))
[0.6649572253227234, 0.5879999995231628]

这里,这意味着使用 sigmoid 激活的所有层的模型的对数损失为 0.665,准确率为 0.588。

我们可以进一步研究的是权重如何随着训练的迭代而变化。除了第一层和最后一层之外,所有层的权重都是 5×5 矩阵。我们可以检查权重的平均值和标准偏差,以了解权重的外观:

def plotweight(capture_cb):
    "Plot the weights' mean and s.d. across epochs"
    fig, ax = plt.subplots(2, 1, sharex=True, constrained_layout=True, figsize=(8, 10))
    ax[0].set_title("Mean weight")
    for key in capture_cb.weights[0]:
        ax[0].plot(capture_cb.epochs, [w[key].mean() for w in capture_cb.weights], label=key)
    ax[0].legend()
    ax[1].set_title("S.D.")
    for key in capture_cb.weights[0]:
        ax[1].plot(capture_cb.epochs, [w[key].std() for w in capture_cb.weights], label=key)
    ax[1].legend()
    plt.show()

plotweight(capture_cb)

这导致如下图:

我们看到平均权重仅在前 10 次迭代左右快速移动。只有第一层的权重随着其标准差的上升而变得更加多样化。

我们可以在相同的过程中重新启动双曲正切(tanh)激活:

# tanh activation, large variance gaussian initialization
model = make_mlp("tanh", initializer, "tanh")
capture_cb = WeightCapture(model)
capture_cb.on_epoch_end(-1)
model.compile(optimizer="rmsprop", loss="binary_crossentropy", metrics=["acc"])
model.fit(X, y, batch_size=batch_size, epochs=n_epochs, callbacks=[capture_cb], verbose=0)
print(model.evaluate(X,y))
plotweight(capture_cb)
[0.012918001972138882, 0.9929999709129333]

对数损失和准确率都得到了提高。如果我们看一下图,我们看不到权重的平均值和标准差的突然变化,相反,所有层的平均值和标准差都在慢慢收敛。

在 ReLU 激活中可以看到类似的情况:

# relu activation, large variance gaussian initialization
model = make_mlp("relu", initializer, "relu")
capture_cb = WeightCapture(model)
capture_cb.on_epoch_end(-1)
model.compile(optimizer="rmsprop", loss="binary_crossentropy", metrics=["acc"])
model.fit(X, y, batch_size=batch_size, epochs=n_epochs, callbacks=[capture_cb], verbose=0)
print(model.evaluate(X,y))
plotweight(capture_cb)
[0.016895903274416924, 0.9940000176429749]

观察每一层的梯度

我们在上面看到了不同激活函数的效果。但事实上,重要的是坡度,因为我们在训练中跑得很好。Xavier Glorot 和 Yoshua Bengio 的论文《理解训练深度前馈神经网络的难度》建议在每次训练迭代中查看每一层的梯度以及它的标准差。

布拉德利(2009)发现,在初始化之后,当从输出层向输入层移动时,反向传播的梯度变小。他研究了每一层都有线性激活的网络,发现反向传播梯度的方差随着我们在网络中的后退而减小

——《理解深度前馈神经网络训练的难点》(2010)

为了理解激活函数如何与训练期间感知的梯度相关,我们需要手动运行训练循环。

在 Tensorflow-Keras 中,可以通过打开梯度带来运行训练循环,然后使神经网络模型产生输出,之后我们可以通过从梯度带自动微分来获得梯度。随后,我们可以根据梯度下降更新规则更新参数(权重和偏差)。

因为梯度很容易在这个循环中获得,所以我们可以复制它。以下是我们如何实现训练循环,同时保留梯度的副本:

optimizer = tf.keras.optimizers.RMSprop()
loss_fn = tf.keras.losses.BinaryCrossentropy()

def train_model(X, y, model, n_epochs=n_epochs, batch_size=batch_size):
    "Run training loop manually"
    train_dataset = tf.data.Dataset.from_tensor_slices((X, y))
    train_dataset = train_dataset.shuffle(buffer_size=1024).batch(batch_size)

    gradhistory = []
    losshistory = []
    def recordweight():
        data = {}
        for g,w in zip(grads, model.trainable_weights):
            if '/kernel:' not in w.name:
                continue # skip bias
            name = w.name.split("/")[0]
            data[name] = g.numpy()
        gradhistory.append(data)
        losshistory.append(loss_value.numpy())
    for epoch in range(n_epochs):
        for step, (x_batch_train, y_batch_train) in enumerate(train_dataset):
            with tf.GradientTape() as tape:
                y_pred = model(x_batch_train, training=True)
                loss_value = loss_fn(y_batch_train, y_pred)

            grads = tape.gradient(loss_value, model.trainable_weights)
            optimizer.apply_gradients(zip(grads, model.trainable_weights))

            if step == 0:
                recordweight()
    # After all epochs, record again
    recordweight()
    return gradhistory, losshistory

上面函数中的键是嵌套的 for 循环。其中,我们启动tf.GradientTape()并向模型传递一批数据以获得预测,然后使用损失函数对其进行评估。然后,我们可以通过比较损失和模型中的可训练重量,从磁带中拉出梯度。接下来,我们使用优化器更新权重,优化器将隐式处理梯度下降算法中的学习权重和动量。

作为刷新,这里的梯度表示如下。对于计算的损失值LL和权重为W=[w1w2w3w4w5]W=[w_1,w_2,w_3,w_4,w_5](例如,在输出层上)的层,梯度就是矩阵

 frac partialLpartialW= Big[ frac partialLpartialW1, frac partialL partialw2, frac partialL partialw3, frac partialL partialw4, frac partialLpartialW5 Big]\ frac { \ partial L } { partial W } = \ Big[\ frac { \ partial L } { partial W _ 1 },\ frac { \ partial L } { \ partial w _ 2 },\ frac { \ partial L } { \ partial w _ 3 },\ frac { \ partial L } { \ partial w _ 4 },\ frac { \ partial L } { partial W _ 5 } \ Big]

但是在我们开始下一次迭代训练之前,我们有机会进一步操作梯度:我们将梯度与权重进行匹配,以获得每个权重的名称,然后将梯度的副本保存为 numpy 数组。我们每个时期只对权重和权重减轻进行一次采样,但您可以将其更改为更高频率的采样。

有了这些,我们可以绘制出不同时期的梯度。在下面,我们创建模型(但不调用compile(),因为我们以后不会调用fit())并运行手动训练循环,然后绘制梯度以及梯度的标准差:

from sklearn.metrics import accuracy_score

def plot_gradient(gradhistory, losshistory):
    "Plot gradient mean and sd across epochs"
    fig, ax = plt.subplots(3, 1, sharex=True, constrained_layout=True, figsize=(8, 12))
    ax[0].set_title("Mean gradient")
    for key in gradhistory[0]:
        ax[0].plot(range(len(gradhistory)), [w[key].mean() for w in gradhistory], label=key)
    ax[0].legend()
    ax[1].set_title("S.D.")
    for key in gradhistory[0]:
        ax[1].semilogy(range(len(gradhistory)), [w[key].std() for w in gradhistory], label=key)
    ax[1].legend()
    ax[2].set_title("Loss")
    ax[2].plot(range(len(losshistory)), losshistory)
    plt.show()

model = make_mlp("sigmoid", initializer, "sigmoid")
print("Before training: Accuracy", accuracy_score(y, (model(X) > 0.5)))
gradhistory, losshistory = train_model(X, y, model)
print("After training: Accuracy", accuracy_score(y, (model(X) > 0.5)))
plot_gradient(gradhistory, losshistory)

它报告了一个弱分类结果:

Before training: Accuracy 0.5
After training: Accuracy 0.652

我们得到的图显示梯度消失:

从剧情来看,损失并没有明显减少。梯度的平均值(即梯度矩阵中所有元素的平均值)仅在最后一层具有显著的值,而所有其他层实际上为零。梯度的标准偏差大约在 0.01 和 0.001 之间。

使用 tanh 激活重复此操作,我们会看到不同的结果,这解释了为什么表现更好:

model = make_mlp("tanh", initializer, "tanh")
print("Before training: Accuracy", accuracy_score(y, (model(X) > 0.5)))
gradhistory, losshistory = train_model(X, y, model)
print("After training: Accuracy", accuracy_score(y, (model(X) > 0.5)))
plot_gradient(gradhistory, losshistory)
Before training: Accuracy 0.502
After training: Accuracy 0.994

从梯度平均值的图中,我们看到每一层的梯度都在均匀地摆动。梯度的标准偏差也比乙状结肠激活的情况大一个数量级,在 0.1 到 0.01 左右。

最后,我们还可以在整流线性单元(ReLU)激活中看到类似的情况。在这种情况下,损失迅速下降,因此我们将其视为神经网络中更有效的激活:

model = make_mlp("relu", initializer, "relu")
print("Before training: Accuracy", accuracy_score(y, (model(X) > 0.5)))
gradhistory, losshistory = train_model(X, y, model)
print("After training: Accuracy", accuracy_score(y, (model(X) > 0.5)))
plot_gradient(gradhistory, losshistory)
Before training: Accuracy 0.503
After training: Accuracy 0.995

以下是完整的代码:

import numpy as np
import tensorflow as tf
from tensorflow.keras.callbacks import Callback
from tensorflow.keras.layers import Dense, Input
from tensorflow.keras import Sequential
from tensorflow.keras.initializers import RandomNormal
import matplotlib.pyplot as plt
from sklearn.datasets import make_circles
from sklearn.metrics import accuracy_score

tf.random.set_seed(42)
np.random.seed(42)

# Make data: Two circles on x-y plane as a classification problem
X, y = make_circles(n_samples=1000, factor=0.5, noise=0.1)
plt.figure(figsize=(8,6))
plt.scatter(X[:,0], X[:,1], c=y)
plt.show()

# Test performance with 3-layer binary classification network
model = Sequential([
    Input(shape=(2,)),
    Dense(5, "relu"),
    Dense(1, "sigmoid")
])
model.compile(optimizer="adam", loss="binary_crossentropy", metrics=["acc"])
model.fit(X, y, batch_size=32, epochs=100, verbose=0)
print(model.evaluate(X,y))

# Test performance with 3-layer network with sigmoid activation
model = Sequential([
    Input(shape=(2,)),
    Dense(5, "sigmoid"),
    Dense(1, "sigmoid")
])
model.compile(optimizer="adam", loss="binary_crossentropy", metrics=["acc"])
model.fit(X, y, batch_size=32, epochs=100, verbose=0)
print(model.evaluate(X,y))

# Test performance with 5-layer network with sigmoid activation
model = Sequential([
    Input(shape=(2,)),
    Dense(5, "sigmoid"),
    Dense(5, "sigmoid"),
    Dense(5, "sigmoid"),
    Dense(1, "sigmoid")
])
model.compile(optimizer="adam", loss="binary_crossentropy", metrics=["acc"])
model.fit(X, y, batch_size=32, epochs=100, verbose=0)
print(model.evaluate(X,y))

# Illustrate weights across epochs
class WeightCapture(Callback):
    "Capture the weights of each layer of the model"
    def __init__(self, model):
        super().__init__()
        self.model = model
        self.weights = []
        self.epochs = []

    def on_epoch_end(self, epoch, logs=None):
        self.epochs.append(epoch) # remember the epoch axis
        weight = {}
        for layer in model.layers:
            if not layer.weights:
                continue
            name = layer.weights[0].name.split("/")[0]
            weight[name] = layer.weights[0].numpy()
        self.weights.append(weight)

def make_mlp(activation, initializer, name):
    "Create a model with specified activation and initalizer"
    model = Sequential([
        Input(shape=(2,), name=name+"0"),
        Dense(5, activation=activation, kernel_initializer=initializer, name=name+"1"),
        Dense(5, activation=activation, kernel_initializer=initializer, name=name+"2"),
        Dense(5, activation=activation, kernel_initializer=initializer, name=name+"3"),
        Dense(5, activation=activation, kernel_initializer=initializer, name=name+"4"),
        Dense(1, activation="sigmoid", kernel_initializer=initializer, name=name+"5")
    ])
    return model

def plotweight(capture_cb):
    "Plot the weights' mean and s.d. across epochs"
    fig, ax = plt.subplots(2, 1, sharex=True, constrained_layout=True, figsize=(8, 10))
    ax[0].set_title("Mean weight")
    for key in capture_cb.weights[0]:
        ax[0].plot(capture_cb.epochs, [w[key].mean() for w in capture_cb.weights], label=key)
    ax[0].legend()
    ax[1].set_title("S.D.")
    for key in capture_cb.weights[0]:
        ax[1].plot(capture_cb.epochs, [w[key].std() for w in capture_cb.weights], label=key)
    ax[1].legend()
    plt.show()

initializer = RandomNormal(mean=0, stddev=1)
batch_size = 32
n_epochs = 100

# Sigmoid activation
model = make_mlp("sigmoid", initializer, "sigmoid")
capture_cb = WeightCapture(model)
capture_cb.on_epoch_end(-1)
model.compile(optimizer="rmsprop", loss="binary_crossentropy", metrics=["acc"])
print("Before training: Accuracy", accuracy_score(y, (model(X).numpy() > 0.5).astype(int)))
model.fit(X, y, batch_size=batch_size, epochs=n_epochs, callbacks=[capture_cb], verbose=0)
print("After training: Accuracy", accuracy_score(y, (model(X).numpy() > 0.5).astype(int)))
print(model.evaluate(X,y))
plotweight(capture_cb)

# tanh activation
model = make_mlp("tanh", initializer, "tanh")
capture_cb = WeightCapture(model)
capture_cb.on_epoch_end(-1)
model.compile(optimizer="rmsprop", loss="binary_crossentropy", metrics=["acc"])
print("Before training: Accuracy", accuracy_score(y, (model(X).numpy() > 0.5).astype(int)))
model.fit(X, y, batch_size=batch_size, epochs=n_epochs, callbacks=[capture_cb], verbose=0)
print("After training: Accuracy", accuracy_score(y, (model(X).numpy() > 0.5).astype(int)))
print(model.evaluate(X,y))
plotweight(capture_cb)

# relu activation
model = make_mlp("relu", initializer, "relu")
capture_cb = WeightCapture(model)
capture_cb.on_epoch_end(-1)
model.compile(optimizer="rmsprop", loss="binary_crossentropy", metrics=["acc"])
print("Before training: Accuracy", accuracy_score(y, (model(X).numpy() > 0.5).astype(int)))
model.fit(X, y, batch_size=batch_size, epochs=n_epochs, callbacks=[capture_cb], verbose=0)
print("After training: Accuracy", accuracy_score(y, (model(X).numpy() > 0.5).astype(int)))
print(model.evaluate(X,y))
plotweight(capture_cb)

# Show gradient across epochs
optimizer = tf.keras.optimizers.RMSprop()
loss_fn = tf.keras.losses.BinaryCrossentropy()

def train_model(X, y, model, n_epochs=n_epochs, batch_size=batch_size):
    "Run training loop manually"
    train_dataset = tf.data.Dataset.from_tensor_slices((X, y))
    train_dataset = train_dataset.shuffle(buffer_size=1024).batch(batch_size)

    gradhistory = []
    losshistory = []
    def recordweight():
        data = {}
        for g,w in zip(grads, model.trainable_weights):
            if '/kernel:' not in w.name:
                continue # skip bias
            name = w.name.split("/")[0]
            data[name] = g.numpy()
        gradhistory.append(data)
        losshistory.append(loss_value.numpy())
    for epoch in range(n_epochs):
        for step, (x_batch_train, y_batch_train) in enumerate(train_dataset):
            with tf.GradientTape() as tape:
                y_pred = model(x_batch_train, training=True)
                loss_value = loss_fn(y_batch_train, y_pred)

            grads = tape.gradient(loss_value, model.trainable_weights)
            optimizer.apply_gradients(zip(grads, model.trainable_weights))

            if step == 0:
                recordweight()
    # After all epochs, record again
    recordweight()
    return gradhistory, losshistory

def plot_gradient(gradhistory, losshistory):
    "Plot gradient mean and sd across epochs"
    fig, ax = plt.subplots(3, 1, sharex=True, constrained_layout=True, figsize=(8, 12))
    ax[0].set_title("Mean gradient")
    for key in gradhistory[0]:
        ax[0].plot(range(len(gradhistory)), [w[key].mean() for w in gradhistory], label=key)
    ax[0].legend()
    ax[1].set_title("S.D.")
    for key in gradhistory[0]:
        ax[1].semilogy(range(len(gradhistory)), [w[key].std() for w in gradhistory], label=key)
    ax[1].legend()
    ax[2].set_title("Loss")
    ax[2].plot(range(len(losshistory)), losshistory)
    plt.show()

model = make_mlp("sigmoid", initializer, "sigmoid")
print("Before training: Accuracy", accuracy_score(y, (model(X) > 0.5)))
gradhistory, losshistory = train_model(X, y, model)
print("After training: Accuracy", accuracy_score(y, (model(X) > 0.5)))
plot_gradient(gradhistory, losshistory)

model = make_mlp("tanh", initializer, "tanh")
print("Before training: Accuracy", accuracy_score(y, (model(X) > 0.5)))
gradhistory, losshistory = train_model(X, y, model)
print("After training: Accuracy", accuracy_score(y, (model(X) > 0.5)))
plot_gradient(gradhistory, losshistory)

model = make_mlp("relu", initializer, "relu")
print("Before training: Accuracy", accuracy_score(y, (model(X) > 0.5)))
gradhistory, losshistory = train_model(X, y, model)
print("After training: Accuracy", accuracy_score(y, (model(X) > 0.5)))
plot_gradient(gradhistory, losshistory)

格洛特初始化

我们没有在上面的代码中演示,但是 glrot 和 Bengio 的论文中最著名的结果是 glrot 初始化。其建议初始化具有均匀分布的神经网络的一层的权重:

因此,在初始化深层网络时,标准化因素可能很重要,因为通过各层会产生倍增效应,我们建议采用以下初始化程序来近似满足我们的目标,即在网络中上下移动时保持激活方差和反向传播梯度方差。我们称之为规范化初始化: W \ sim U \ Big[-\ frac { 6 } } { \ sqrt { n _ j+n _ { j+1 } }、\ frac { 6 } } { \ sqrt { n _ j+n _ { j+1 } } \ Big]

——《理解深度前馈神经网络训练的难点》(2010)

这源于线性激活,条件是梯度的标准偏差在各层之间保持一致。在 sigmoid 和 tanh 激活中,线性区域较窄。因此,我们可以理解为什么 ReLU 是解决梯度消失问题的关键。与替换激活函数相比,改变权重初始化在帮助解决梯度消失问题方面不太明显。但这可以是一个让你探索的练习,看看这如何有助于改善结果。

进一步阅读

格洛特和本吉奥的论文可在以下网站获得:

梯度消失问题在机器学习中是众所周知的,许多书都涉及到了它。例如,

之前我们有关于梯度消失和爆炸的帖子:

您可能还会发现以下文档有助于解释我们上面使用的一些语法:

摘要

在本教程中,您直观地看到了经过校正的线性单位(ReLU)如何帮助解决消失的梯度问题。

具体来说,您了解到:

  • 梯度消失问题如何影响神经网络的表现
  • 为什么 ReLU 激活是梯度消失问题的解决方案
  • 如何在 Keras 中使用自定义回调在训练循环中间提取数据
  • 如何编写自定义训练循环
  • 如何从神经网络的一个层读取权重和梯度

使用权重正则化减少深度学习模型的过拟合

原文:machinelearningmastery.com/weight-regu…

最后更新于 2019 年 8 月 6 日

神经网络学习一组最佳地将输入映射到输出的权重。

具有大网络权重的网络可能是不稳定网络的标志,其中输入的小变化可能导致输出的大变化。这可能是网络过度训练训练数据集的一个迹象,并且在对新数据进行预测时可能表现不佳。

解决这个问题的一种方法是更新学习算法,以鼓励网络保持较小的权重。这被称为权重正则化,它可以作为一种通用技术来减少训练数据集的过拟合,并提高模型的泛化能力。

在这篇文章中,您将发现权重正则化是一种减少神经网络过拟合的方法。

看完这篇文章,你会知道:

  • 神经网络中的大权重是一个更复杂的网络过度训练数据的标志。
  • 在训练期间根据网络权重的大小来惩罚网络可以减少过拟合。
  • 可以将 L1 或 L2 向量范数惩罚添加到网络优化中,以鼓励更小的权重。

用我的新书更好的深度学习启动你的项目,包括分步教程和所有示例的 Python 源代码文件。

我们开始吧。

A Gentle Introduction to Weight Regularization to Reduce Overfitting for Deep Learning Models

为深度学习模型减少过拟合的权重调整的温和介绍 jojo nicdao 摄,保留部分权利。

大权重问题

在拟合神经网络模型时,我们必须使用随机梯度下降和训练数据集来学习网络的权重(即模型参数)。

我们训练网络的时间越长,训练数据的权重将变得越专业,过拟合训练数据。权重的大小将会增加,以便处理训练数据中看到的示例的细节。

大权重使网络不稳定。尽管权重将专门用于训练数据集,但预期输入上的微小变化或统计噪声将导致输出的巨大差异。

大的权重往往会导致节点函数的急剧转变,从而导致输入的小变化导致输出的大变化。

—第 269 页神经锻造:前馈人工神经网络中的监督学习,1999。

一般来说,我们称这个模型具有大的方差和小的偏差。也就是说,该模型对训练数据集中的特定示例(统计噪声)很敏感。

权重大的模型比权重小的模型更复杂。这是一个网络的迹象,可能过于专门用于训练数据。实际上,我们更喜欢选择更简单的模型来解决问题(例如奥卡姆剃刀)。我们更喜欢重量较小的型号。

……给定一些训练数据和网络架构,多组权重值(多个模型)可以解释这些数据。简单的模型比复杂的模型更不容易过拟合。在这种情况下,一个简单的模型是参数值的分布具有较小熵的模型

—第 107 页,Python 深度学习,2017。

另一个可能的问题是,可能有许多输入变量,每个变量与输出变量的相关程度不同。有时我们可以使用方法来帮助选择输入变量,但是变量之间的相互关系往往并不明显。

对网络中不太相关或不相关的输入使用小权重甚至零权重将允许模型专注于学习。这也将导致一个更简单的模型。

鼓励小重量

学习算法可以更新,以鼓励网络使用小权重。

一种方法是改变网络优化中使用的损耗计算,同时考虑权重的大小。

记住,当我们训练神经网络时,我们最小化损失函数,例如分类中的对数损失或回归中的均方误差。在批量计算预测值和期望值之间的损失时,我们可以将网络中所有权重的当前大小相加,或者在计算中添加一个层。这被称为惩罚,因为我们惩罚的模型与模型中权重的大小成正比。

许多正则化方法都是基于限制模型的容量,例如神经网络、线性回归或逻辑回归,方法是在目标函数中添加[…]惩罚。

—第 230 页,深度学习,2016。

较大的权重导致较大的惩罚,表现为较大的损失分数。然后,优化算法将推动模型具有更小的权重,即权重不大于在训练数据集上表现良好所需的权重。

较小的权重被认为更规则或不太专业,因此,我们称这种惩罚为权重正则化。

当这种惩罚模型系数的方法用于其他机器学习模型,如线性回归或逻辑回归时,它可以被称为收缩,因为惩罚会促使系数在优化过程中收缩。

收缩。这种方法包括拟合一个包含所有 p 预测因子的模型。然而,估计的系数向零收缩……这种收缩(也称为正则化)具有减少方差的效果

—第 204 页,统计学习导论:在 R 中的应用,2013。

向神经网络添加权重大小惩罚或权重正则化具有减少泛化误差和允许模型较少关注不太相关的输入变量的效果。

1)它通过选择解决学习问题的最小向量来抑制权重向量的任何无关分量。2)如果尺寸选择正确,权重衰减可以抑制静态噪声对目标的一些影响。

——简单的权重衰减可以提高泛化,1992。

如何处罚大重量

根据权重的大小对模型进行处罚有两个部分。

第一个是权重大小的计算,第二个是优化过程应该对惩罚的关注程度。

计算重量大小

神经网络的权重是实值,可以是正的,也可以是负的,因此,简单地增加权重是不够的。有两种主要方法用于计算权重的大小,它们是:

  • 计算权重绝对值之和,称为 L1。
  • 计算权重的平方值之和,称为 L2。

如果可能的话,L1 鼓励权重为 0.0,从而导致权重更稀疏(权重具有更多的 0.0 值)。L2 提供了更多的细微差别,既更严厉地惩罚较大的权重,但导致权重不那么稀疏。在线性和逻辑回归中使用 L2 通常被称为岭回归。当试图发展一种对惩罚的直觉或使用惩罚的例子时,知道这一点是很有用的。

在其他学术界,L2 正则化也被称为岭回归或 Tikhonov 正则化。

—第 231 页,深度学习,2016。

权重可以被认为是一个向量,向量的大小被称为它的范数,来自线性代数。因此,基于权重的大小来惩罚模型也被称为权重或参数范数惩罚。

有可能将 L1 和 L2 计算权重大小的方法都包括在内。这类似于线性和逻辑回归的弹性网络算法中使用的惩罚。

L2 方法可能是最常用的,在神经网络领域传统上被称为“权重衰减”。它在统计学中被称为“收缩,这个名字鼓励你在学习过程中思考惩罚对模型权重的影响。

正则化器的这种特殊选择在机器学习文献中被称为权重衰减,因为在顺序学习算法中,它鼓励权重值衰减到零,除非有数据支持。在统计学中,它提供了一个参数收缩方法的例子,因为它将参数值收缩到零。

—第 144-145 页,模式识别与机器学习,2006。

回想一下,每个节点都有输入权重和偏置权重。因为“输入”是恒定的,所以偏差权重一般不包含在处罚中。

处罚的控制影响

当训练网络时,权重的计算大小被添加到损失目标函数中。

可以使用一个新的超参数(称为α(a)或有时称为λ)对它们进行加权,而不是将每个权重直接加到惩罚上。这控制了学习过程应该对惩罚的关注程度。或者换句话说,根据权重的大小来惩罚模型的数量。

alpha 超参数的值介于 0.0(无惩罚)和 1.0(完全惩罚)之间。该超参数控制模型中的偏差量,从 0.0 或低偏差(高方差)到 1.0 或高偏差(低方差)。

如果惩罚力度太大,模型会低估权重,并低估问题。如果惩罚太弱,模型将被允许过度训练数据。

权重的向量范数通常是每层计算的,而不是在整个网络中计算的。这允许更灵活地选择所使用的正则化类型(例如,L1 用于输入,L2 用于其他地方)以及阿尔法值的灵活性,尽管默认情况下在每个层上使用相同的阿尔法值是很常见的。

在神经网络的环境中,有时希望对网络的每一层使用具有不同 a 系数的单独惩罚。因为搜索多个超参数的正确值可能很昂贵,所以在所有层使用相同的权重衰减只是为了减小搜索空间的大小,这仍然是合理的。

—第 230 页,深度学习,2016。

使用权重正则化的技巧

本节提供了一些在神经网络中使用权重正则化的技巧。

适用于所有网络类型

权重正则化是一种通用方法。

它可以用于大多数,也许是所有类型的神经网络模型,尤其是最常见的多层感知器、卷积神经网络和长短期记忆递归神经网络。

在 LSTMs 的情况下,可能需要对输入和循环连接使用不同的惩罚或惩罚配置。

标准化输入数据

更新输入变量以具有相同的比例通常是一种好的做法。

当输入变量具有不同的标度时,网络权重的标度将相应地变化。这在使用权重正则化时引入了一个问题,因为权重的绝对值或平方值必须相加才能用于惩罚。

这个问题可以通过规范化或标准化输入变量来解决。

使用更大的网络

对于更大的网络(更多的层或更多的节点)来说,更容易过度填充训练数据是很常见的。

当使用权重正则化时,可以使用更大的网络,过拟合的风险更小。一个好的配置策略可能是从更大的网络开始,并使用权重衰减。

网格搜索参数

正则化超参数通常使用较小的值来控制每个权重对惩罚的贡献。

也许可以从测试对数标度的值开始,例如 0.1、0.001 和 0.0001。然后按照最有希望的数量级进行网格搜索。

一起使用 L1 + L2

与其试图在 L1 和 L2 的点球之间做出选择,不如两者都用。

现代有效的线性回归方法,如弹性网,同时使用 L1 和 L2 惩罚,这可能是一个有用的尝试方法。这给了你 L2 的细微差别和 L1 鼓励的稀疏性。

在训练有素的网络上使用

权重正则化的使用可以允许更精细的训练方案。

例如,模型可以在没有任何正则化的情况下首先适合训练数据,然后使用权重惩罚来减少已经表现良好的模型的权重的大小,随后进行更新。

你有什么使用权重正则化的小技巧吗? 在下面的评论里告诉我。

进一步阅读

如果您想更深入地了解这个主题,本节将提供更多资源。

报纸

文章

摘要

在这篇文章中,您发现了权重正则化作为一种减少神经网络过拟合的方法。

具体来说,您了解到:

  • 神经网络中的大权重是一个更复杂的网络过度训练数据的标志。
  • 在训练期间根据网络权重的大小来惩罚网络可以减少过拟合。
  • 可以将 L1 或 L2 向量范数惩罚添加到网络优化中,以鼓励更小的权重。

你有什么问题吗? 在下面的评论中提问,我会尽力回答。

如何为深度学习神经网络开发加权平均集成

原文:machinelearningmastery.com/weighted-av…

最后更新于 2020 年 8 月 25 日

建模平均集成平均地组合来自每个模型的预测,并且通常比给定的单个模型产生更好的平均表现。

有时候,有一些非常好的模型,我们希望对集合预测贡献更多,也可能有一些不太熟练的模型,它们可能有用,但应该对集合预测贡献更少。加权平均集成是一种方法,它允许多个模型按其信任度或估计表现的比例对预测做出贡献。

在本教程中,您将发现如何使用 Keras 在 Python 中开发深度学习神经网络模型的加权平均集成。

完成本教程后,您将知道:

  • 模型平均集成是有限的,因为它们要求每个集成成员对预测的贡献相等。
  • 加权平均集成允许每个集成成员对预测的贡献与成员在保持数据集上的信任或表现成比例加权。
  • 如何在 Keras 中实现加权平均集成,并将结果与模型平均集成和独立模型进行比较。

用我的新书更好的深度学习启动你的项目,包括分步教程和所有示例的 Python 源代码文件。

我们开始吧。

  • 2019 年 10 月更新:针对 Keras 2.3 和 TensorFlow 2.0 更新。
  • 2020 年 1 月更新:针对 Sklearn v0.22 API 的变化进行了更新。

How to Develop a Weighted Average Ensemble for Deep Learning Neural Networks

如何开发深度学习神经网络的加权平均集成 图片由西蒙·马特辛格提供,版权所有。

教程概述

本教程分为六个部分;它们是:

  1. 加权平均集成
  2. 多类分类问题
  3. 多层感知器模型
  4. 模型平均集成
  5. 网格搜索加权平均集成
  6. 优化加权平均集成

加权平均集成

模型平均是一种集成学习方法,其中每个集成成员对最终预测的贡献相等。

在回归的情况下,集合预测被计算为成员预测的平均值。在预测类别标签的情况下,预测被计算为成员预测的模式。在预测类别概率的情况下,预测可以被计算为每个类别标签的总概率的 argmax。

这种方法的一个局限性是,每个模型对集合所做的最终预测有相同的贡献。有一个要求,所有的集成成员都有技能,而不是随机的机会,尽管一些模型被认为表现得比其他模型好得多或差得多。

加权集成是模型平均集成的扩展,其中每个成员对最终预测的贡献由模型的表现加权。

模型权重是小正值,所有权重之和等于 1,允许权重指示每个模型的信任百分比或预期表现。

我们可以把权重 Wk 看作是对预测因子 k 的信念,因此我们将权重限制为正,并求和为一。

——用集成学习:过度适应有多有用,1996。

权重的统一值(例如 1/k,其中 k 是集成成员的数量)意味着加权集成充当简单的平均集成。没有找到权重的解析解(我们无法计算);相反,可以使用训练数据集或保持验证数据集来估计权重值。

使用用于拟合集合成员的相同训练集来找到权重将可能导致过拟合模型。更稳健的方法是使用集合成员在训练期间看不到的保持验证数据集。

最简单,也许是最详尽的方法是网格搜索每个集成成员的 0 到 1 之间的权重值。或者,诸如线性解算器或梯度下降优化的优化过程可用于使用单位范数权重约束来估计权重,以确保权重向量和为 1。

除非保持验证数据集很大且具有代表性,否则与简单的平均集成相比,加权集成有机会过度填充。

在不计算显式权重系数的情况下,给定模型增加更多权重的一个简单替代方法是给定模型多次添加到集合中。尽管不太灵活,但它允许给定的表现良好的模型对集成做出的给定预测做出不止一次的贡献。

多类分类问题

我们将使用一个小的多类分类问题作为基础来演示加权平均集成。

Sklearn 类提供了 make_blobs()函数,该函数可用于创建具有规定数量的样本、输入变量、类和类内样本方差的多类分类问题。

该问题有两个输入变量(表示点的 xy 坐标)和每组内点的标准偏差 2.0。我们将使用相同的随机状态(伪随机数发生器的种子)来确保我们总是获得相同的数据点。

# 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 的标准偏差意味着类不是线性可分的(用一条线可分的),导致了很多不明确的点。

这是可取的,因为这意味着问题不是微不足道的,并将允许神经网络模型找到许多不同的“足够好”的候选解决方案,从而导致高方差。

Scatter Plot of Blobs Dataset With Three Classes and Points Colored by Class Value

具有三个类和按类值着色的点的斑点数据集的散点图

多层感知器模型

在我们定义模型之前,我们需要设计一个适合加权平均集成的问题。

在我们的问题中,训练数据集相对较小。具体来说,训练数据集中的示例与保持数据集中的示例的比例为 10:1。这模拟了一种情况,即我们可能有大量未标记的示例和少量已标记的示例来训练模型。

我们将从斑点问题中创建 1100 个数据点。模型将在前 100 个点上进行训练,剩余的 1000 个点将保留在测试数据集中,模型无法使用。

该问题是一个多类分类问题,我们将在输出层使用 softmax 激活函数对其进行建模。这意味着模型将以样本属于三类中每一类的概率来预测具有三个元素的向量。因此,在将行分割成训练和测试数据集之前,我们必须对类值进行热编码。我们可以使用 Keras to _ classic()函数来实现这一点。

# generate 2d classification dataset
X, y = make_blobs(n_samples=1100, 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 = 100
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
print(trainX.shape, testX.shape)

接下来,我们可以定义和编译模型。

该模型将预期具有两个输入变量的样本。然后,该模型有一个具有 25 个节点的单个隐藏层和一个校正的线性激活函数,然后有一个具有三个节点的输出层来预测三个类中每一个的概率,还有一个 softmax 激活函数。

由于问题是多类的,我们将使用分类交叉熵损失函数来优化模型和随机梯度下降的有效亚当味

# define model
model = Sequential()
model.add(Dense(25, input_dim=2, activation='relu'))
model.add(Dense(3, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', 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))

最后,我们将在训练和验证数据集上绘制每个训练时期的模型准确率的学习曲线。

# learning curves of model accuracy
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=1100, 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 = 100
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
print(trainX.shape, testX.shape)
# define model
model = Sequential()
model.add(Dense(25, 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=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))
# learning curves of model accuracy
pyplot.plot(history.history['accuracy'], label='train')
pyplot.plot(history.history['val_accuracy'], label='test')
pyplot.legend()
pyplot.show()

运行该示例首先打印每个数据集的形状以供确认,然后打印最终模型在训练和测试数据集上的表现。

:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。

在这种情况下,我们可以看到该模型在训练数据集上获得了大约 87%的准确率,我们知道这是乐观的,在测试数据集上获得了大约 81%的准确率,我们预计这将更加真实。

(100, 2) (1000, 2)
Train: 0.870, Test: 0.814

还创建了一个线图,显示了在每个训练周期内,训练和测试集上模型准确率的学习曲线。

我们可以看到,在大部分跑步过程中,训练的准确性更加乐观,我们也注意到了最终得分。

Line Plot Learning Curves of Model Accuracy on Train and Test Dataset over Each Training Epoch

每个训练时期训练和测试数据集上模型准确率的线图学习曲线

既然我们已经确定该模型是开发集成的一个很好的候选对象,我们接下来可以考虑开发一个简单的模型平均集成。

模型平均集成

在考虑开发加权平均集成之前,我们可以开发一个简单的模型平均集成。

模型平均集成的结果可以用作比较点,因为我们期望配置良好的加权平均集成表现更好。

首先,我们需要拟合多个模型来开发一个集合。我们将定义一个名为 fit_model() 的函数,在训练数据集上创建和拟合一个单一的模型,我们可以重复调用这个函数来创建任意多个模型。

# fit model on dataset
def fit_model(trainX, trainy):
	trainy_enc = to_categorical(trainy)
	# define model
	model = Sequential()
	model.add(Dense(25, 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=500, verbose=0)
	return model

我们可以调用这个函数来创建 10 个模型的池。

# fit all models
n_members = 10
members = [fit_model(trainX, trainy) for _ in range(n_members)]

接下来,我们可以开发模型平均集成。

我们不知道有多少成员适合这个问题,所以我们可以创建从 1 到 10 个成员的不同大小的集成,并在测试集上评估每个成员的表现。

我们还可以在测试集的表现中评估每个独立模型的表现。这为模型平均集成提供了一个有用的比较点,因为我们预计该集成将平均胜过随机选择的单个模型。

每个模型预测每个类别标签的概率,例如,有三个输出。通过对预测概率使用 argmax()函数,可以将单个预测转换为类别标签,例如返回具有最大概率值的预测中的索引。我们可以通过对每个类别预测的概率求和并对结果使用 argmax() 来集成来自多个模型的预测。下面的*集成预测()*函数实现了这种行为。

# 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

我们可以通过从所有模型列表中选择所需数量的模型,调用*integration _ predictions()*函数进行预测,然后通过将其与真实值进行比较来计算预测的准确性,从而估计给定大小的集成的表现。下面的 evaluate_n_members() 函数实现了这个行为。

# 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)

每个尺寸的集成的分数可以被存储起来以供以后绘制,并且收集每个单独模型的分数并报告平均表现。

# evaluate different numbers of ensembles on hold out set
single_scores, ensemble_scores = list(), list()
for i in range(1, len(members)+1):
	# evaluate model with i members
	ensemble_score = evaluate_n_members(members, i, testX, testy)
	# evaluate the i'th model standalone
	testy_enc = to_categorical(testy)
	_, single_score = members[i-1].evaluate(testX, testy_enc, verbose=0)
	# summarize this step
	print('> %d: single=%.3f, ensemble=%.3f' % (i, single_score, ensemble_score))
	ensemble_scores.append(ensemble_score)
	single_scores.append(single_score)
# summarize average accuracy of a single final model
print('Accuracy %.3f (%.3f)' % (mean(single_scores), std(single_scores)))

最后,我们创建了一个图表,显示了每个单独模型的准确性(蓝点)和模型平均集成的表现,因为成员数量从 1 个增加到 10 个(橙色线)。

将所有这些结合在一起,下面列出了完整的示例。

# model averaging ensemble for the blobs dataset
from sklearn.datasets import make_blobs
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

# fit model on dataset
def fit_model(trainX, trainy):
	trainy_enc = to_categorical(trainy)
	# define model
	model = Sequential()
	model.add(Dense(25, 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=500, verbose=0)
	return model

# 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
X, y = make_blobs(n_samples=1100, centers=3, n_features=2, cluster_std=2, random_state=2)
# split into train and test
n_train = 100
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
print(trainX.shape, testX.shape)
# fit all models
n_members = 10
members = [fit_model(trainX, trainy) for _ in range(n_members)]
# evaluate different numbers of ensembles on hold out set
single_scores, ensemble_scores = list(), list()
for i in range(1, len(members)+1):
	# evaluate model with i members
	ensemble_score = evaluate_n_members(members, i, testX, testy)
	# evaluate the i'th model standalone
	testy_enc = to_categorical(testy)
	_, single_score = members[i-1].evaluate(testX, testy_enc, verbose=0)
	# summarize this step
	print('> %d: single=%.3f, ensemble=%.3f' % (i, single_score, ensemble_score))
	ensemble_scores.append(ensemble_score)
	single_scores.append(single_score)
# summarize average accuracy of a single final model
print('Accuracy %.3f (%.3f)' % (mean(single_scores), std(single_scores)))
# plot score vs number of ensemble members
x_axis = [i for i in range(1, len(members)+1)]
pyplot.plot(x_axis, single_scores, marker='o', linestyle='None')
pyplot.plot(x_axis, ensemble_scores, marker='o')
pyplot.show()

运行该示例首先报告每个单个模型的表现,以及给定大小(1、2、3 等)的模型平均集合。成员。

:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。

在这次运行中,单个模型的平均表现约为 80.4%,我们可以看到一个由 5 到 9 名成员组成的集合将达到 80.8%到 81%的表现。正如预期的那样,中等规模模型平均集成的表现平均超过随机选择的单个模型的表现。

(100, 2) (1000, 2)
> 1: single=0.803, ensemble=0.803
> 2: single=0.805, ensemble=0.808
> 3: single=0.798, ensemble=0.805
> 4: single=0.809, ensemble=0.809
> 5: single=0.808, ensemble=0.811
> 6: single=0.805, ensemble=0.808
> 7: single=0.805, ensemble=0.808
> 8: single=0.804, ensemble=0.809
> 9: single=0.810, ensemble=0.810
> 10: single=0.794, ensemble=0.808
Accuracy 0.804 (0.005)

接下来,创建一个图表,将单个模型(蓝点)的准确率与不断增大的模型平均集合(橙色线)进行比较。

在这一轮中,橙色系列的集成明显显示出比单一型号更好或相当的表现(如果隐藏圆点的话)。

Line Plot Showing Single Model Accuracy (blue dots) and Accuracy of Ensembles of Increasing Size (orange line)

显示单一模型准确率(蓝点)和增加尺寸的集合准确率(橙色线)的线图

现在我们知道如何开发一个模型平均集成,我们可以通过加权集成成员的贡献来进一步扩展该方法。

网格搜索加权平均集成

模型平均集成允许每个集成成员对集成的预测贡献相等的量。

我们可以更新这个例子,这样每个集成成员的贡献就会被一个系数加权,这个系数表示模型的信任或预期表现。权重值是介于 0 和 1 之间的小值,像百分比一样处理,这样所有集合成员的权重总和为 1。

首先,我们必须更新*集成 _ 预测()*函数,以利用每个集成成员的权重向量。

我们必须计算加权和,而不是简单地对每个集成成员的预测求和。我们可以使用 for 循环手动实现这一点,但是这效率非常低;例如:

# calculated a weighted sum of predictions
def weighted_sum(weights, yhats):
	rows = list()
	for j in range(yhats.shape[1]):
		# enumerate values
		row = list()
		for k in range(yhats.shape[2]):
			# enumerate members
			value = 0.0
			for i in range(yhats.shape[0]):
				value += weights[i] * yhats[i,j,k]
			row.append(value)
		rows.append(row)
	return array(rows)

相反,我们可以使用高效的 NumPy 函数来实现加权和,如 einsum()tensordot()

对这些函数的全面讨论有点超出范围,因此如果您是线性代数和/或 NumPy 的新手,请参考 API 文档了解如何使用这些函数的更多信息,因为它们具有挑战性。我们将使用 tensordot() 函数来应用具有所需求和的张量积;更新后的*集合 _ 预测()*功能如下。

# make an ensemble prediction for multi-class classification
def ensemble_predictions(members, weights, testX):
	# make predictions
	yhats = [model.predict(testX) for model in members]
	yhats = array(yhats)
	# weighted sum across ensemble members
	summed = tensordot(yhats, weights, axes=((0),(0)))
	# argmax across classes
	result = argmax(summed, axis=1)
	return result

接下来,我们必须更新 evaluate_ensemble() ,以便在为集成进行预测时传递权重。

# evaluate a specific number of members in an ensemble
def evaluate_ensemble(members, weights, testX, testy):
	# make prediction
	yhat = ensemble_predictions(members, weights, testX)
	# calculate accuracy
	return accuracy_score(testy, yhat)

我们将使用一个由五名成员组成的中等规模的集合,该集合在模型平均集合中表现良好。

# fit all models
n_members = 5
members = [fit_model(trainX, trainy) for _ in range(n_members)]

然后,我们可以估计测试数据集中每个单独模型的表现作为参考。

# evaluate each single model on the test set
testy_enc = to_categorical(testy)
for i in range(n_members):
	_, test_acc = members[i].evaluate(testX, testy_enc, verbose=0)
	print('Model %d: %.3f' % (i+1, test_acc))

接下来,我们可以对五个集成成员中的每一个使用 1/5 或 0.2 的权重,并使用新的函数来估计模型平均集成的表现,即所谓的等权重集成。

我们希望这个组合能表现得和任何单一的模型一样好或者更好。

# evaluate averaging ensemble (equal weights)
weights = [1.0/n_members for _ in range(n_members)]
score = evaluate_ensemble(members, weights, testX, testy)
print('Equal Weights Score: %.3f' % score)

最后,我们可以开发一个加权平均集成。

为集成成员寻找权重的一个简单但详尽的方法是网格搜索值。我们可以以 0.1 的步长定义一个从 0.0 到 1.0 的权重值的过程网格,然后用这些值生成所有可能的五元素向量。生成所有可能的组合称为笛卡尔乘积,可以使用标准库中的 itertools.product()函数在 Python 中实现。

这种方法的一个限制是,权重向量的总和不会像要求的那样等于 1(称为单位范数)。我们可以通过计算绝对权重值之和(称为 L1 范数)并将每个权重除以该值来强制生成的权重向量具有单位范数。下面的 normalize() 函数实现了这个破解。

# normalize a vector to have unit norm
def normalize(weights):
	# calculate l1 vector norm
	result = norm(weights, 1)
	# check for a vector of all zeros
	if result == 0.0:
		return weights
	# return normalized vector (unit norm)
	return weights / result

现在,我们可以枚举笛卡尔乘积生成的每个权重向量,对其进行归一化,并通过进行预测和保留在最终权重平均集成中使用的最佳值来评估它。

# grid search weights
def grid_search(members, testX, testy):
	# define weights to consider
	w = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
	best_score, best_weights = 0.0, None
	# iterate all possible combinations (cartesian product)
	for weights in product(w, repeat=len(members)):
		# skip if all weights are equal
		if len(set(weights)) == 1:
			continue
		# hack, normalize weight vector
		weights = normalize(weights)
		# evaluate weights
		score = evaluate_ensemble(members, weights, testX, testy)
		if score > best_score:
			best_score, best_weights = score, weights
			print('>%s %.3f' % (best_weights, best_score))
	return list(best_weights)

一旦被发现,我们就可以在测试数据集上报告我们的加权平均集成的表现,我们期望它比最好的单个模型更好,并且理想地比模型平均集成更好。

# grid search weights
weights = grid_search(members, testX, testy)
score = evaluate_ensemble(members, weights, testX, testy)
print('Grid Search Weights: %s, Score: %.3f' % (weights, score))

下面列出了完整的示例。

# grid search for coefficients in a weighted average ensemble for the blobs problem
from sklearn.datasets import make_blobs
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
from numpy import array
from numpy import argmax
from numpy import tensordot
from numpy.linalg import norm
from itertools import product

# fit model on dataset
def fit_model(trainX, trainy):
	trainy_enc = to_categorical(trainy)
	# define model
	model = Sequential()
	model.add(Dense(25, 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=500, verbose=0)
	return model

# make an ensemble prediction for multi-class classification
def ensemble_predictions(members, weights, testX):
	# make predictions
	yhats = [model.predict(testX) for model in members]
	yhats = array(yhats)
	# weighted sum across ensemble members
	summed = tensordot(yhats, weights, axes=((0),(0)))
	# argmax across classes
	result = argmax(summed, axis=1)
	return result

# evaluate a specific number of members in an ensemble
def evaluate_ensemble(members, weights, testX, testy):
	# make prediction
	yhat = ensemble_predictions(members, weights, testX)
	# calculate accuracy
	return accuracy_score(testy, yhat)

# normalize a vector to have unit norm
def normalize(weights):
	# calculate l1 vector norm
	result = norm(weights, 1)
	# check for a vector of all zeros
	if result == 0.0:
		return weights
	# return normalized vector (unit norm)
	return weights / result

# grid search weights
def grid_search(members, testX, testy):
	# define weights to consider
	w = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
	best_score, best_weights = 0.0, None
	# iterate all possible combinations (cartesian product)
	for weights in product(w, repeat=len(members)):
		# skip if all weights are equal
		if len(set(weights)) == 1:
			continue
		# hack, normalize weight vector
		weights = normalize(weights)
		# evaluate weights
		score = evaluate_ensemble(members, weights, testX, testy)
		if score > best_score:
			best_score, best_weights = score, weights
			print('>%s %.3f' % (best_weights, best_score))
	return list(best_weights)

# generate 2d classification dataset
X, y = make_blobs(n_samples=1100, centers=3, n_features=2, cluster_std=2, random_state=2)
# split into train and test
n_train = 100
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
print(trainX.shape, testX.shape)
# fit all models
n_members = 5
members = [fit_model(trainX, trainy) for _ in range(n_members)]
# evaluate each single model on the test set
testy_enc = to_categorical(testy)
for i in range(n_members):
	_, test_acc = members[i].evaluate(testX, testy_enc, verbose=0)
	print('Model %d: %.3f' % (i+1, test_acc))
# evaluate averaging ensemble (equal weights)
weights = [1.0/n_members for _ in range(n_members)]
score = evaluate_ensemble(members, weights, testX, testy)
print('Equal Weights Score: %.3f' % score)
# grid search weights
weights = grid_search(members, testX, testy)
score = evaluate_ensemble(members, weights, testX, testy)
print('Grid Search Weights: %s, Score: %.3f' % (weights, score))

运行该示例首先创建五个单一模型,并在测试数据集上评估它们的表现。

:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。

在这次运行中,我们可以看到 model 2 具有大约 81.7%准确率的最佳独奏表现。

接下来,创建一个表现约为 80.7%的模型平均集成,与大多数模型相比,这是合理的,但不是全部。

(100, 2) (1000, 2)
Model 1: 0.798
Model 2: 0.817
Model 3: 0.798
Model 4: 0.806
Model 5: 0.810
Equal Weights Score: 0.807

接下来,执行网格搜索。它相当慢,在现代硬件上可能需要大约 20 分钟。使用诸如 Joblib 之类的库可以很容易地使该过程并行。

每次发现一组新的表现最高的权重时,都会在测试数据集上报告其表现。我们可以看到,在运行过程中,流程发现单独使用模型 2 会产生良好的表现,直到它被更好的东西取代。

我们可以看到,使用仅关注第一个和第二个模型的权重在这次运行中获得了最佳表现,在测试数据集上的准确率为 81.8%。这在同一个数据集上执行了单个模型和模型平均集成。

>[0\. 0\. 0\. 0\. 1.] 0.810
>[0\.  0\.  0\.  0.5 0.5] 0.814
>[0\.         0\.         0\.         0.33333333 0.66666667] 0.815
>[0\. 1\. 0\. 0\. 0.] 0.817
>[0.23076923 0.76923077 0\.         0\.         0\.        ] 0.818
Grid Search Weights: [0.23076923076923075, 0.7692307692307692, 0.0, 0.0, 0.0], Score: 0.818

另一种寻找权重的方法是随机搜索,这已被证明对模型超参数调整更有效。

加权平均 MLP 集成

搜索权重值的另一种方法是使用定向优化过程。

优化是一个搜索过程,但搜索过程不是随机或穷尽地对可能的解的空间进行采样,而是使用任何可用的信息来进行搜索的下一步,例如朝着一组具有较低误差的权重。

SciPy 库提供了许多优秀的优化算法,包括局部和全局搜索方法。

SciPy 提供了差分进化方法的实现。这是为数不多的随机全局搜索算法之一,对于具有连续输入的函数优化,只是起作用,而且效果很好。

微分进化() SciPy 函数要求指定一个函数来评估一组权重并返回一个要最小化的分数。我们可以将分类误差降至最低(1–准确率)。

与网格搜索一样,我们在评估权重向量之前,通常会对其进行归一化。下面的 loss_function() 函数将在优化过程中用作评估函数。

# loss function for optimization process, designed to be minimized
def loss_function(weights, members, testX, testy):
	# normalize weights
	normalized = normalize(weights)
	# calculate error rate
	return 1.0 - evaluate_ensemble(members, normalized, testX, testy)

我们还必须指定优化过程的界限。我们可以将边界定义为 5 维超立方体(例如,5 个集成成员的 5 个权重),其值在 0.0 和 1.0 之间。

# define bounds on each weight
bound_w = [(0.0, 1.0) for _ in range(n_members)]

除了权重之外,我们的损失函数还需要三个参数,我们将提供一个元组,然后在每次评估一组权重时传递给对*损失函数()*的调用。

# arguments to the loss function
search_arg = (members, testX, testy)

我们现在可以称我们的优化过程为。

我们将算法的总迭代次数限制在 1000 次,并使用小于默认的容差来检测搜索过程是否收敛。

# global optimization of ensemble weights
result = differential_evolution(loss_function, bound_w, search_arg, maxiter=1000, tol=1e-7)

调用 differential_evolution() 的结果是一个包含各种搜索信息的字典。

重要的是,' x '键包含搜索过程中找到的最佳权重集。我们可以检索最佳权重集,然后在测试集中报告它们以及它们在加权集合中使用时的表现。

# get the chosen weights
weights = normalize(result['x'])
print('Optimized Weights: %s' % weights)
# evaluate chosen weights
score = evaluate_ensemble(members, weights, testX, testy)
print('Optimized Weights Score: %.3f' % score)

将所有这些结合在一起,下面列出了完整的示例。

# global optimization to find coefficients for weighted ensemble on blobs problem
from sklearn.datasets import make_blobs
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
from numpy import array
from numpy import argmax
from numpy import tensordot
from numpy.linalg import norm
from scipy.optimize import differential_evolution

# fit model on dataset
def fit_model(trainX, trainy):
	trainy_enc = to_categorical(trainy)
	# define model
	model = Sequential()
	model.add(Dense(25, 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=500, verbose=0)
	return model

# make an ensemble prediction for multi-class classification
def ensemble_predictions(members, weights, testX):
	# make predictions
	yhats = [model.predict(testX) for model in members]
	yhats = array(yhats)
	# weighted sum across ensemble members
	summed = tensordot(yhats, weights, axes=((0),(0)))
	# argmax across classes
	result = argmax(summed, axis=1)
	return result

# # evaluate a specific number of members in an ensemble
def evaluate_ensemble(members, weights, testX, testy):
	# make prediction
	yhat = ensemble_predictions(members, weights, testX)
	# calculate accuracy
	return accuracy_score(testy, yhat)

# normalize a vector to have unit norm
def normalize(weights):
	# calculate l1 vector norm
	result = norm(weights, 1)
	# check for a vector of all zeros
	if result == 0.0:
		return weights
	# return normalized vector (unit norm)
	return weights / result

# loss function for optimization process, designed to be minimized
def loss_function(weights, members, testX, testy):
	# normalize weights
	normalized = normalize(weights)
	# calculate error rate
	return 1.0 - evaluate_ensemble(members, normalized, testX, testy)

# generate 2d classification dataset
X, y = make_blobs(n_samples=1100, centers=3, n_features=2, cluster_std=2, random_state=2)
# split into train and test
n_train = 100
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
print(trainX.shape, testX.shape)
# fit all models
n_members = 5
members = [fit_model(trainX, trainy) for _ in range(n_members)]
# evaluate each single model on the test set
testy_enc = to_categorical(testy)
for i in range(n_members):
	_, test_acc = members[i].evaluate(testX, testy_enc, verbose=0)
	print('Model %d: %.3f' % (i+1, test_acc))
# evaluate averaging ensemble (equal weights)
weights = [1.0/n_members for _ in range(n_members)]
score = evaluate_ensemble(members, weights, testX, testy)
print('Equal Weights Score: %.3f' % score)
# define bounds on each weight
bound_w = [(0.0, 1.0)  for _ in range(n_members)]
# arguments to the loss function
search_arg = (members, testX, testy)
# global optimization of ensemble weights
result = differential_evolution(loss_function, bound_w, search_arg, maxiter=1000, tol=1e-7)
# get the chosen weights
weights = normalize(result['x'])
print('Optimized Weights: %s' % weights)
# evaluate chosen weights
score = evaluate_ensemble(members, weights, testX, testy)
print('Optimized Weights Score: %.3f' % score)

运行该示例首先创建五个单一模型,并在测试数据集上评估每个模型的表现。

:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。

在这次运行中,我们可以看到模型 3 和 4 都表现最好,准确率约为 82.2%。

接下来,在测试集上评估具有所有五个成员的模型平均集成,报告准确率为 81.8%,这优于一些但不是全部的单个模型。

(100, 2) (1000, 2)
Model 1: 0.814
Model 2: 0.811
Model 3: 0.822
Model 4: 0.822
Model 5: 0.809
Equal Weights Score: 0.818

优化过程相对较快。

我们可以看到,该过程找到了一组最关注模型 3 和模型 4 的权重,并将剩余的注意力分散到其他模型中,实现了约 82.4%的准确率,优于模型平均集成和单个模型。

Optimized Weights: [0.1660322  0.09652591 0.33991854 0.34540932 0.05211403]
Optimized Weights Score: 0.824

值得注意的是,在这些示例中,我们将测试数据集视为验证数据集。这样做是为了保持例子的重点和技术上的简单。在实践中,集合权重的选择和调整将由验证数据集选择,单个模型、模型平均集合和加权集合将在单独的测试集中进行比较。

扩展ˌ扩张

本节列出了一些您可能希望探索的扩展教程的想法。

  • 并行化网格搜索。更新网格搜索示例,使用 Joblib 库并行化权重计算。
  • 实现随机搜索。更新网格搜索示例以使用权重系数的随机搜索。
  • 尝试本地搜索。尝试由 SciPy 库提供的本地搜索过程,而不是全局搜索,并比较表现。
  • 重复全局优化。对给定的一组模型重复多次全局优化过程,以查看是否可以在运行中找到不同的权重集。

如果你探索这些扩展,我很想知道。

进一步阅读

如果您想更深入地了解这个主题,本节将提供更多资源。

报纸

应用程序接口

文章

摘要

在本教程中,您发现了如何使用 Keras 在 Python 中开发深度学习神经网络模型的加权平均集成。

具体来说,您了解到:

  • 模型平均集成是有限的,因为它们要求每个集成成员对预测的贡献相等。
  • 加权平均集成允许每个集成成员对预测的贡献与成员在保持数据集上的信任或表现成比例加权。
  • 如何在 Keras 中实现加权平均集成,并将结果与模型平均集成和独立模型进行比较。

你有什么问题吗? 在下面的评论中提问,我会尽力回答。