Machine Learning Mastery 深度学习表现教程(七)
如何利用学习曲线诊断机器学习模型表现
最后更新于 2019 年 8 月 6 日
学习曲线是模型学习表现随经验或时间的变化图。
学习曲线是机器学习中广泛使用的诊断工具,用于从训练数据集增量学习的算法。在训练期间每次更新之后,可以在训练数据集和等待验证数据集上评估模型,并且可以创建测量表现的图来显示学习曲线。
在训练期间检查模型的学习曲线可以用于诊断学习问题,例如欠训练或过训练模型,以及训练和验证数据集是否具有适当的代表性。
在这篇文章中,你将发现学习曲线,以及它们如何用于诊断机器学习模型的学习和泛化行为,示例图显示了常见的学习问题。
看完这篇文章,你会知道:
- 学习曲线是根据经验显示学习表现随时间变化的图表。
- 训练和验证数据集中模型表现的学习曲线可用于诊断欠拟合、过拟合或拟合良好的模型。
- 模型表现的学习曲线可用于诊断训练或验证数据集是否相对不代表问题领域。
用我的新书更好的深度学习启动你的项目,包括分步教程和所有示例的 Python 源代码文件。
我们开始吧。
用于诊断深度学习模型表现的学习曲线的温和介绍 图片由迈克·萨瑟兰提供,保留部分权利。
概观
本教程分为三个部分;它们是:
- 学习曲线
- 诊断模型行为
- 诊断不具代表性的数据集
机器学习中的学习曲线
一般来说,学习曲线是在 x 轴上显示时间或经验,在 y 轴上显示学习或进步的曲线。
学习曲线(LCs)被认为是监测接触新任务的工人表现的有效工具。LCs 提供了随着任务重复发生而发生的学习过程的数学表示。
——学习曲线模型及应用:文献综述及研究方向,2011。
例如,如果你正在学习一种乐器,你在乐器上的技能可以被评估,并在一年内每周分配一个数字分数。这 52 周的分数图是一条学习曲线,显示了你对乐器的学习是如何随着时间的推移而变化的。
- 学习曲线:学习(y 轴)对经验(x 轴)的线图。
学习曲线在机器学习中被广泛用于随时间递增学习(优化其内部参数)的算法,例如深度学习神经网络。
用于评估学习的指标可能是最大化,这意味着更好的分数(更大的数字)表明更多的学习。分类准确性就是一个例子。
更常见的是使用最小化的分数,例如损失或错误,由此更好的分数(更小的数字)表示更多的学习,值 0.0 表示训练数据集被完美地学习并且没有出错。
在机器学习模型的训练期间,可以评估在训练算法的每个步骤中模型的当前状态。可以在训练数据集上对其进行评估,以给出模型学习有多好的想法也可以在不属于训练数据集的保留验证数据集上对其进行评估。对验证数据集的评估给出了该模型概括的“T2”程度的概念
** 训练学习曲线:从训练数据集中计算出的学习曲线,给出模型学习效果的概念。
- 验证学习曲线:学习曲线是从一个搁置的验证数据集计算出来的,该数据集给出了模型推广程度的概念。
在训练和验证数据集的训练过程中,为机器学习模型创建双重学习曲线是很常见的。
在某些情况下,为多个度量创建学习曲线也是常见的,例如在分类预测建模问题的情况下,其中模型可以根据交叉熵损失进行优化,并且使用分类准确率来评估模型表现。在这种情况下,会创建两个图,一个用于每个指标的学习曲线,每个图可以显示两条学习曲线,一条用于每个训练和验证数据集。
- 优化学习曲线:根据模型参数优化的度量计算的学习曲线,例如损耗。
- 表现学习曲线:根据评估和选择模型的指标计算的学习曲线,例如准确率。
现在我们已经熟悉了学习曲线在机器学习中的使用,让我们看看在学习曲线图中观察到的一些常见形状。
诊断模型行为
学习曲线的形状和动态可以用于诊断机器学习模型的行为,并且反过来可能建议可以进行的配置改变的类型,以改进学习和/或表现。
在学习曲线中,你可能会观察到三种常见的动态;它们是:
- 在它下面。
- 吃多了。
- 很合身。
我们将通过示例仔细研究每一个。这些例子将假设我们看到的是一个最小化指标,这意味着 y 轴上较小的相对分数表示更多或更好的学习。
信息技术下学习曲线
欠拟合指的是无法学习训练数据集的模型。
当模型不能在训练集上获得足够低的误差值时,就会发生拟合不足。
—第 111 页,深度学习,2016。
只能从训练损失的学习曲线中识别欠信息技术模型。
它可能显示一条平坦的线或相对较高损失的噪声值,表明模型根本无法学习训练数据集。
下面提供了一个这样的例子,当模型不具备适合数据集复杂性的能力时,这种例子很常见。
显示容量不足模型的训练学习曲线示例
信息技术不足模型也可以通过训练损失来识别,训练损失正在减少,并在图的末尾继续减少。
这表明该模型能够进一步学习和可能的进一步改进,并且培训过程过早停止。
显示需要进一步培训的 it 不足模型的培训学习曲线示例
在以下情况下,学习曲线会显示拟合不足:
- 无论是否训练,训练损失都保持不变。
- 训练损失持续减少,直到训练结束。
过度学习曲线
过拟合是指模型对训练数据集的学习太好,包括训练数据集中的统计噪声或随机波动。
…拟合更灵活的模型需要估计更多的参数。这些更复杂的模型可能会导致一种被称为过拟合数据的现象,这本质上意味着它们过于紧密地跟随误差或噪声。
—第 22 页,统计学习导论:在 R 中的应用,2013。
过拟合的问题是,模型对训练数据越专门化,它对新数据的泛化能力就越差,导致泛化误差增加。泛化误差的增加可以通过模型在验证数据集上的表现来衡量。
这是一个过拟合数据的例子,[……]。这是一种不理想的情况,因为所获得的拟合不会产生对不属于原始训练数据集的新观测值的响应的准确估计。
—第 24 页,统计学习导论:在 R 中的应用,2013。
如果模型的容量超过了问题所需的容量,这种情况经常发生,反过来,这种情况又会带来太多的灵活性。如果模型训练时间过长,也会出现这种情况。
在以下情况下,学习曲线会显示过拟合:
- 随着经验的积累,训练损失的情节会继续减少。
- 验证损失的曲线下降到一个点,然后又开始增加。
验证损失的拐点可能是训练可能停止的点,因为经验表明,在这个点之后会出现过拟合的动态。
下面的示例图演示了过拟合的情况。
显示过拟合模型的训练和验证学习曲线示例
良好拟合学习曲线
良好的拟合是学习算法的目标,存在于过拟合和欠拟合模型之间。
通过训练和验证损失确定良好的拟合,该损失降低到稳定点,两个最终损失值之间的差距最小。
在训练数据集上,模型的损失几乎总是低于验证数据集。这意味着我们应该预期训练和验证损失学习曲线之间会有一些差距。这种差距被称为“泛化差距”
在以下情况下,学习曲线图显示了良好的拟合:
- 训练损失的曲线下降到稳定点。
- 验证损失图下降到一个稳定点,与训练损失有一个小的差距。
持续的良好体能训练可能会导致过度训练。
下面的示例图展示了一个很好的例子。
显示良好拟合的训练和验证学习曲线示例
诊断不具代表性的数据集
学习曲线也可以用于诊断数据集的属性以及它是否相对有代表性。
不具有代表性的数据集是指可能无法捕获与来自同一领域的另一个数据集相关的统计特征的数据集,例如在训练数据集和验证数据集之间。相对于另一个数据集,如果一个数据集中的样本数量太少,通常会出现这种情况。
有两种常见情况可以观察到;它们是:
- 训练数据集相对不具有代表性。
- 验证数据集相对不具有代表性。
非代表性训练数据集
不具代表性的训练数据集意味着,相对于用于评估问题的验证数据集,训练数据集没有提供足够的信息来了解问题。
如果与验证数据集相比,训练数据集的示例太少,可能会出现这种情况。
这种情况可以通过显示改进的训练损失的学习曲线和显示改进的验证损失的学习曲线来识别,但是两条曲线之间仍然存在很大的差距。
训练和验证学习曲线示例显示了相对于验证数据集可能太小的训练数据集
非代表性验证数据集
不具代表性的验证数据集意味着验证数据集不能提供足够的信息来评估模型的泛化能力。
如果与训练数据集相比,验证数据集的示例太少,可能会出现这种情况。
这种情况可以通过看起来很适合(或其他适合)的训练损失的学习曲线和显示训练损失周围的噪声运动的验证损失的学习曲线来识别。
训练和验证学习曲线示例,显示了相对于训练数据集可能太小的验证数据集
也可以通过低于培训损失的验证损失来识别。在这种情况下,它表明验证数据集可能比训练数据集更易于模型预测。
显示比训练数据集更容易预测的验证数据集的训练和验证学习曲线示例
进一步阅读
如果您想更深入地了解这个主题,本节将提供更多资源。
书
- 深度学习,2016 年。
- 统计学习导论:在 R 中的应用,2013。
报纸
- 学习曲线模型及应用:文献综述及研究方向,2011。
邮件
文章
摘要
在这篇文章中,你发现了学习曲线,以及它们如何用于诊断机器学习模型的学习和泛化行为。
具体来说,您了解到:
- 学习曲线是根据经验显示学习表现随时间变化的图表。
- 训练和验证数据集中模型表现的学习曲线可用于诊断欠拟合、过拟合或拟合良好的模型。
- 模型表现的学习曲线可用于诊断训练或验证数据集是否相对不代表问题领域。
你有什么问题吗? 在下面的评论中提问,我会尽力回答。*
训练深度学习神经网络时如何配置学习率
最后更新于 2019 年 8 月 6 日
神经网络的权重不能用解析法计算。相反,权重必须通过称为随机梯度下降的经验优化过程来发现。
神经网络的随机梯度下降解决的优化问题是具有挑战性的,并且解的空间(权重集)可以由许多好的解(称为全局最优解)组成,并且容易找到,但是技能低的解(称为局部最优解)。
在这个搜索过程的每一步中,模型的变化量,或者说步长,被称为“学习率”,它可能提供了最重要的超参数来调整你的神经网络,以便在你的问题上取得良好的表现。
在本教程中,您将发现在训练深度学习神经网络时使用的学习率超参数。
完成本教程后,您将知道:
- 学习率控制神经网络模型学习问题的速度。
- 如何用合理的默认值配置学习率,诊断行为,并进行敏感性分析。
- 如何通过学习进度计划、动力和自适应学习速度进一步提高绩效。
用我的新书更好的深度学习启动你的项目,包括分步教程和所有示例的 Python 源代码文件。
我们开始吧。
训练深度学习神经网络时如何配置学习率超参数 图片由贝恩德·泰勒提供,保留部分权利。
教程概述
本教程分为六个部分;它们是:
- 学习率是多少?
- 学习率的影响
- 如何配置学习率
- 为学习过程增添动力
- 使用学习进度计划
- 适应性学习率
学习率是多少?
使用随机梯度下降算法训练深度学习神经网络。
随机梯度下降是一种优化算法,它使用来自训练数据集的示例来估计模型当前状态的误差梯度,然后使用误差反向传播算法来更新模型的权重,简称为反向传播。
训练期间更新的权重量被称为步长或“学习率”
具体而言,学习率是用于神经网络训练的可配置超参数,其具有小的正值,通常在 0.0 和 1.0 之间的范围内。
…学习率,决定步长大小的正标量。
—第 86 页,深度学习,2016。
学习率通常用小写希腊字母 eta ( n )来表示。
在训练期间,误差的反向传播估计网络中节点权重所负责的误差量。它不是用全量更新权重,而是根据学习率进行缩放。
这意味着 0.1 的学习率(传统上常见的默认值)意味着每次更新权重时,网络中的权重都会更新 0.1 *(估计权重误差)或估计权重误差的 10%。
学习率的影响
神经网络学习或逼近一个函数,以最佳地将输入映射到训练数据集中示例的输出。
学习率超参数控制模型学习的速率或速度。具体来说,它控制模型权重每次更新时(例如在每批训练示例结束时)更新的分摊误差量。
给定一个完美配置的学习率,该模型将学习在给定数量的训练时期(通过训练数据)中给定可用资源(层数和每层节点数)的最佳近似函数。
通常,较大的学习率允许模型学习得更快,但代价是达到次优的最终权重集。较小的学习率可以允许模型学习更优的或者甚至全局最优的权重集,但是可能花费更长的时间来训练。
在极端情况下,太大的学习率将导致太大的权重更新,并且模型的表现(例如它在训练数据集上的损失)将随着训练时期而振荡。据说振荡表现是由发散的重量引起的。太小的学习率可能永远不会收敛,或者可能陷入次优解。
当学习率过大时,梯度下降会无意中增加而不是减少训练误差。[……]当学习率太小时,训练不仅会更慢,而且可能会永久停留在较高的训练误差上。
—第 429 页,深度学习,2016。
在最坏的情况下,权重更新太大可能会导致权重爆炸(即导致数字溢出)。
当使用高学习率时,可能会遇到正反馈回路,其中大的权重导致大的梯度,然后导致权重的大的更新。如果这些更新持续增加权重的大小,那么[权重]会迅速远离原点,直到出现数值溢出。
—第 238 页,深度学习,2016。
因此,我们不应该使用过大或过小的学习率。然而,我们必须以这样一种方式配置模型,即平均来说,找到一组足够好的权重来近似由训练数据集表示的映射问题。
如何配置学习率
在训练数据集中为模型找到一个好的学习率值是很重要的。
事实上,学习率可能是为您的模型配置的最重要的超参数。
初始学习率【…】这通常是最重要的单个超参数,人们应该始终确保它已经被调整【…】如果只有时间来优化一个超参数,并且使用随机梯度下降,那么这是值得调整的超参数
——深度架构基于梯度训练的实用建议,2012。
事实上,如果有资源来调整超参数,那么大部分时间应该用于调整学习率。
学习率可能是最重要的超参数。如果你有时间只调整一个超参数,调整学习率。
—第 429 页,深度学习,2016。
不幸的是,我们无法分析计算给定数据集上给定模型的最佳学习率。相反,一个好的(或足够好的)学习率必须通过反复试验来发现。
……一般来说,不可能先验地计算出最佳学习率。
—第 72 页,神经锻造:前馈人工神经网络中的监督学习,1999。
学习率要考虑的值范围小于 1.0 且大于 10^-6.
具有标准化输入(或映射到(0,1)区间的输入)的神经网络的典型值小于 1 且大于 10^−6
——深度架构基于梯度训练的实用建议,2012。
学习率将与优化过程的许多其他方面相互作用,并且这些相互作用可能是非线性的。然而,总的来说,较小的学习率需要更多的训练时期。相反,更高的学习率将需要更少的训练时期。此外,给定误差梯度的噪声估计,较小的批次尺寸更适合较小的学习率。
学习率的传统默认值是 0.1 或 0.01,这可能是解决您的问题的一个很好的起点。
默认值 0.01 通常适用于标准的多层神经网络,但是仅仅依赖这个默认值是愚蠢的
——深度架构基于梯度训练的实用建议,2012。
诊断图可用于调查学习率如何影响模型的学习率和学习动态。一个例子是在训练期间创建一个训练时期的损失线图。线图可以显示许多属性,例如:
- 学习速度超过训练时期,如快或慢。
- 模型是学习得太快(急剧上升和平稳)还是学习得太慢(变化很小或没有变化)。
- 通过损失的波动来判断学习率是否过大。
配置学习率既有挑战性又耗时。
选择[学习率]的值可能相当关键,因为如果它太小,误差的减少将非常缓慢,而如果它太大,会导致发散振荡。
—第 95 页,用于模式识别的神经网络,1995。
另一种方法是对所选模型的学习率进行敏感性分析,也称为网格搜索。这有助于突出好的学习率所在的数量级,以及描述学习率和表现之间的关系。
从 0.1 到 10^-5 或 10^-6.的对数标度上的网格搜索学习率是很常见的
通常情况下,网格搜索包括选取近似对数标度的值,例如,在集合{.1,. 01,103,104,105 }内的学习率
—第 434 页,深度学习,2016。
当绘制时,这种灵敏度分析的结果通常显示为“U”形,其中随着学习率随着固定数量的训练时期而降低,损失降低(表现提高),直到由于模型未能收敛而损失再次急剧增加的点。
如果你需要帮助来试验你的模型的学习率,请看帖子:
为学习过程增添动力
通过将历史添加到权重更新中,可以更容易地训练神经网络。
具体而言,当权重被更新时,可以包括权重的先前更新的指数加权平均值。这种随机梯度下降的变化被称为“动量”,并增加了更新过程的惯性,导致一个方向上的许多过去的更新在未来继续朝着那个方向发展。
动量算法累积过去梯度的指数衰减移动平均值,并继续向它们的方向移动。
—第 296 页,深度学习,2016。
动量可以加速那些问题的学习,在这些问题中,优化过程正在导航的高维“权重空间”具有误导梯度下降算法的结构,例如平坦区域或陡峭曲率。
动量法旨在加速学习,尤其是在面对高曲率、小但一致的梯度或噪声梯度时。
—第 296 页,深度学习,2016。
过去更新的惯性量是通过添加一个新的超参数来控制的,该超参数通常被称为“动量或“速度,并使用希腊小写字母 alpha ( a )的符号。
……动量算法引入了一个变量 v,它起着速度的作用——它是参数在参数空间中移动的方向和速度。速度设置为负梯度的指数衰减平均值。
—第 296 页,深度学习,2016。
它具有平滑优化过程的效果,减缓更新以继续先前的方向,而不是卡住或振荡。
一个非常简单的处理差异很大的特征值问题的方法是在梯度下降公式中加入动量项。这有效地通过重量空间增加了运动的惯性,并消除了振荡
—第 267 页,用于模式识别的神经网络,1995。
动量被设置为大于 0.0 且小于 1 的值,其中在实践中使用诸如 0.9 和 0.99 的公共值。
实践中常用的[动量]值包括. 5、. 9 和. 99。
—第 298 页,深度学习,2016。
动量并不能使配置学习率变得更容易,因为步长与动量无关。相反,动量可以与步长一起提高优化过程的速度,从而提高在更少的训练时期发现更好的权重集的可能性。
使用学习进度计划
使用固定学习率的替代方法是在整个训练过程中改变学习率。
学习率随时间变化的方式(训练时期)称为学习率时间表或学习率衰减。
也许最简单的学习率计划是将学习率从大初始值线性降低到小值。这允许在学习过程的开始有大的权重变化,而在学习过程的结束有小的变化或微调。
在实践中,有必要随着时间的推移逐渐降低学习率,因此我们现在表示迭代时的学习率[……]这是因为 SGD 梯度估计器引入了噪声源(m 个训练示例的随机采样),即使我们达到最小值,该噪声源也不会消失。
—第 294 页,深度学习,2016。
事实上,在训练神经网络时,使用学习率计划可能是最佳实践。配置挑战涉及选择初始学习率和学习率时间表,而不是选择固定的学习率超参数。考虑到学习率计划可能允许的更好的表现,初始学习率的选择可能不如选择固定学习率敏感。
学习率可以衰减到接近零的小值。或者,学习率可以在固定数量的训练时期衰减,然后在剩余的训练时期保持恒定在一个小值,以便于更多的时间微调。
在实践中,通常线性衰减学习率,直到迭代[τ]。迭代[τ]后,通常保持[学习率]不变。
—第 295 页,深度学习,2016。
适应性学习率
学习算法可以监控模型在训练数据集上的表现,并且可以相应地调整学习率。
这被称为适应性学习率。
也许最简单的实现是一旦模型的表现稳定下来,就使学习率变小,例如通过将学习率降低两倍或一个数量级。
优化算法的合理选择是具有衰减学习率的动量 SGD(在不同问题上表现更好或更差的流行衰减方案包括线性衰减,直到达到固定的最小学习率,指数衰减,或者每次验证误差平稳时将学习率降低 2-10 倍)。
—第 425 页,深度学习,2016。
或者,如果在固定数量的训练时期内表现没有提高,学习率可以再次提高。
自适应学习率方法通常优于学习率配置不当的模型。
先验选择好的学习率的困难是自适应学习率方法如此有用和流行的原因之一。一个好的自适应算法通常会比简单的反向传播收敛得更快,而反向传播的固定学习率选择不当。
—第 72 页,神经锻造:前馈人工神经网络中的监督学习,1999。
尽管没有一种方法能最好地解决所有问题,但有三种自适应学习率方法已被证明对许多类型的神经网络体系结构和问题类型都是稳健的。
它们是 AdaGrad、RMSProp 和 Adam,并且都为模型中的每个权重保持和调整学习率。
也许最受欢迎的是亚当,因为它建立在 RMSProp 的基础上,增加了动力。
此时,一个自然的问题是:应该选择哪种算法?不幸的是,目前在这一点上没有共识。目前,最流行的优化算法包括 SGD、带动量的 SGD、RMSProp、带动量的 RMSProp、AdaDelta 和 Adam。
—第 309 页,深度学习,2016。
一个稳健的策略可能是首先评估具有自适应学习率的现代随机梯度下降模型(如 Adam)的表现,并将结果用作基线。然后,如果时间允许,探索是否可以通过精心选择的学习率或更简单的学习率计划来实现改进。
进一步阅读
如果您想更深入地了解这个主题,本节将提供更多资源。
邮政
报纸
- 深度架构基于梯度训练的实用建议,2012。
书
- 第八章:深度模型训练优化,深度学习,2016。
- 第六章:学习率和动量,神经锻造:前馈人工神经网络中的监督学习,1999。
- 第 5.7 节:梯度下降,模式识别的神经网络,1995。
文章
- 随机梯度下降,维基百科。
- 反向钻取应该使用什么学习率?,神经网络常见问题。
摘要
在本教程中,您发现了在训练深度学习神经网络时使用的学习率超参数。
具体来说,您了解到:
- 学习率控制神经网络模型学习问题的速度。
- 如何用合理的默认值配置学习率,诊断行为,并进行敏感性分析。
- 如何通过学习进度计划、动力和自适应学习速度进一步提高绩效。
你有什么问题吗? 在下面的评论中提问,我会尽力回答。
用于训练深度学习神经网络的损失和损失函数
最后更新于 2019 年 10 月 23 日
神经网络使用随机梯度下降进行训练,并要求您在设计和配置模型时选择损失函数。
有许多损失函数可供选择,要知道选择什么,甚至什么是损失函数以及它在训练神经网络时所扮演的角色可能是一项挑战。
在这篇文章中,你将发现损失和损失函数在训练深度学习神经网络中的作用,以及如何为你的预测建模问题选择合适的损失函数。
看完这篇文章,你会知道:
- 使用需要损失函数来计算模型误差的优化过程来训练神经网络。
- 最大似然提供了一个框架,用于在训练神经网络和机器学习模型时选择损失函数。
- 交叉熵和均方误差是训练神经网络模型时使用的两种主要损失函数。
用我的新书更好的深度学习启动你的项目,包括分步教程和所有示例的 Python 源代码文件。
我们开始吧。
训练深度学习神经网络的损失和损失函数 图片由瑞安·阿尔布雷提供,保留部分权利。
概观
本教程分为七个部分;它们是:
- 作为优化的神经网络学习
- 什么是损失函数和损失?
- 最大概似法
- 最大似然和交叉熵
- 使用什么损失函数?
- 如何实现损失函数
- 损失函数和报告的模型表现
我们将集中讨论损失函数背后的理论。
有关选择和实现不同损失函数的帮助,请参见文章:
作为优化的神经网络学习
深度学习神经网络从训练数据中学习将一组输入映射到一组输出。
我们无法计算神经网络的完美权重;有太多的未知。相反,学习问题被视为搜索或优化问题,并且使用算法来导航模型可能使用的权重集的空间,以便做出好的或足够好的预测。
典型地,使用随机梯度下降优化算法训练神经网络模型,并且使用误差反向传播算法更新权重。
梯度下降中的“梯度”是指误差梯度。使用具有给定权重集的模型进行预测,并计算这些预测的误差。
梯度下降算法试图改变权重,以便下一次评估减少误差,这意味着优化算法正在沿着误差的梯度(或斜率)向下导航。
既然我们知道训练神经网络解决了一个优化问题,我们可以看看如何计算给定权重集的误差。
什么是损失函数和损失?
在优化算法的上下文中,用于评估候选解(即一组权重)的函数被称为目标函数。
我们可能会寻求最大化或最小化目标函数,这意味着我们正在搜索分别具有最高或最低分数的候选解。
典型地,对于神经网络,我们寻求最小化误差。因此,目标函数通常被称为成本函数或损失函数,由损失函数计算的值被简称为“损失”
我们希望最小化或最大化的函数称为目标函数或准则。当我们最小化它时,我们也可以称它为成本函数、损失函数或误差函数。
—第 82 页,深度学习,2016。
成本或损失函数有一项重要的工作,它必须忠实地将模型的所有方面浓缩成一个单一的数字,使得该数字的改进是一个更好模型的标志。
成本函数将一个可能复杂的系统的所有好的和坏的方面减少到一个单一的数字,一个标量值,这允许对候选解决方案进行排序和比较。
—第 155 页,神经锻造:前馈人工神经网络中的监督学习,1999。
在优化过程中计算模型误差时,必须选择损失函数。
这可能是一个具有挑战性的问题,因为该功能必须捕获问题的属性,并由对项目和利益相关者很重要的关注点来驱动。
因此,重要的是该功能忠实地代表我们的设计目标。如果我们选择了一个较差的误差函数,并获得了不令人满意的结果,那么错误在于我们没有很好地指定搜索的目标。
—第 155 页,神经锻造:前馈人工神经网络中的监督学习,1999。
现在我们已经熟悉了损失函数和损失,我们需要知道使用什么函数。
最大概似法
有许多函数可以用来估计神经网络中一组权重的误差。
我们更喜欢这样一种函数,其中候选解的空间映射到平滑(但高维)的景观上,优化算法可以通过迭代更新模型权重来合理地导航。
最大似然估计,或 MLE,是一个推理框架,用于从历史训练数据中找到参数的最佳统计估计:这正是我们试图用神经网络做的。
最大似然通过最大化从训练数据中得到的似然函数来寻找参数的最佳值。
—第 39 页,用于模式识别的神经网络,1995。
我们有一个包含一个或多个输入变量的训练数据集,并且我们需要一个模型来估计模型权重参数,该参数最好地将输入示例映射到输出或目标变量。
给定输入,模型试图做出与目标变量的数据分布相匹配的预测。在最大似然下,损失函数估计模型预测的分布与训练数据中目标变量的分布有多接近。
解释最大似然估计的一种方法是将其视为最小化由训练集定义的经验分布[…]和模型分布之间的相异度,两者之间的相异度由 KL 散度来衡量。[……]最小化这种 KL 散度正好对应于最小化分布之间的交叉熵。
—第 132 页,深度学习,2016。
使用最大似然作为框架来估计神经网络和一般机器学习的模型参数(权重)的好处是,随着训练数据集中的示例数量增加,模型参数的估计得到改善。这就是所谓的一致性的属性。”
在适当的条件下,极大似然估计量具有一致性的性质,即随着训练样本数趋近于无穷大,一个参数的极大似然估计收敛于该参数的真值。
—第 134 页,深度学习,2016。
现在我们已经熟悉了最大似然的一般方法,我们可以看看误差函数。
最大似然和交叉熵
在最大似然框架下,使用交叉熵测量两个概率分布之间的误差。
当我们对将输入变量映射到类标签感兴趣的分类问题建模时,我们可以将问题建模为预测一个例子属于每个类的概率。在二分类问题中,会有两个类,所以我们可以预测这个例子属于第一类的概率。在多类分类的情况下,我们可以预测属于每个类的例子的概率。
在训练数据集中,一个示例属于给定类的概率将是 1 或 0,因为训练数据集中的每个样本都是该域的已知示例。我们知道答案。
因此,在最大似然估计下,我们将寻求一组模型权重,最小化给定数据集的模型预测概率分布和训练数据集中概率分布之间的差异。这叫做交叉熵。
在大多数情况下,我们的参数模型定义了一个分布……,我们简单地使用最大似然原理。这意味着我们使用训练数据和模型预测之间的交叉熵作为成本函数。
—第 178 页,深度学习,2016。
从技术上讲,交叉熵来自信息论领域,以“比特”为单位。它用于估计估计概率分布和预测概率分布之间的差异。
在预测数量的回归问题中,通常使用均方误差损失函数。
一些基本功能非常常用。均方误差在函数逼近(回归)问题中很流行[……]当输出被解释为指示类中的成员概率时,交叉熵误差函数通常用于分类问题。
—第 155-156 页,神经锻造:前馈人工神经网络中的监督学习,1999。
然而,在最大似然估计的框架下,假设目标变量为高斯分布,均方误差可以被认为是模型预测分布和目标变量分布之间的交叉熵。
许多作者使用“交叉熵”这个术语来明确表示伯努利分布或软最大值分布的负对数似然性,但这是一个用词不当的说法。由负对数似然构成的任何损失都是训练集定义的经验分布和模型定义的概率分布之间的交叉熵。例如,均方误差是经验分布和高斯模型之间的交叉熵。
—第 132 页,深度学习,2016。
因此,当使用最大似然估计的框架时,我们将实现交叉熵损失函数,这在实践中通常意味着分类问题的交叉熵损失函数和回归问题的均方误差损失函数。
几乎普遍地,深度学习神经网络是在最大似然框架下使用交叉熵作为损失函数进行训练的。
大多数现代神经网络是使用最大似然法训练的。这意味着成本函数【……】被描述为训练数据和模型分布之间的交叉熵。
—第 178-179 页,深度学习,2016。
事实上,采用这一框架可能被认为是深度学习的一个里程碑,因为在完全形式化之前,神经网络分类有时通常使用均方误差损失函数。
这些算法的变化之一是用损失函数的交叉熵族代替了均方误差。均方误差在 20 世纪 80 年代和 90 年代很流行,但随着统计界和机器学习界之间思想的传播,它逐渐被交叉熵损失和最大似然原理所取代。
—第 226 页,深度学习,2016。
最大似然法被广泛采用,不仅仅是因为它的理论框架,更主要的是因为它产生的结果。具体而言,在输出层使用 sigmoid 或 softmax 激活函数的分类神经网络使用交叉熵损失函数学习得更快、更鲁棒。
交叉熵损失的使用极大地改善了具有 sigmoid 和 softmax 输出的模型的表现,这些模型以前在使用均方误差损失时会遭受饱和和学习缓慢的问题。
—第 226 页,深度学习,2016。
使用什么损失函数?
我们可以总结前面的部分,并直接建议您应该在最大似然框架下使用的损失函数。
重要的是,损失函数的选择与神经网络输出层使用的激活函数直接相关。这两个设计元素是相互联系的。
将输出层的配置视为关于预测问题框架的选择,将损失函数的选择视为计算问题给定框架的误差的方式。
成本函数的选择与输出单位的选择紧密相连。大多数情况下,我们简单地使用数据分布和模型分布之间的交叉熵。如何表示输出的选择决定了交叉熵函数的形式。
—第 181 页,深度学习,2016。
我们将回顾关于输出层和损失函数的每个问题类型的最佳实践或默认值。
回归问题
预测实际值的问题。
- 输出层配置:一个带有线性激活单元的节点。
- 损失函数:均方误差。
二分类问题
将一个例子归类为属于两个类别之一的问题。
这个问题被框定为预测一个例子属于第一类的可能性,例如你分配给整数值 1 的类,而另一个类被分配给数值 0。
- 输出层配置:一个带乙状线激活单元的节点。
- 损失函数:交叉熵,也称对数损失。
多类分类问题
将一个例子归类为属于两个以上类别之一的问题。
这个问题的框架是预测一个例子属于每一类的可能性。
- 输出层配置:使用 softmax 激活功能,每个类一个节点。
- 损失函数:交叉熵,也称对数损失。
如何实现损失函数
为了使损失函数具体化,本节解释了损失函数的每种主要类型是如何工作的,以及如何在 Python 中计算分数。
均方误差损失
均方误差损失,简称均方误差,是预测值和实际值之间的平方差的平均值。
无论预测值和实际值的符号如何,结果总是正的,理想值为 0.0。损失值被最小化,尽管它可以通过使得分为负而用于最大化优化过程。
下面的 Python 函数提供了一个类似伪代码的函数工作实现,用于计算一系列实际值和一系列预测实值量的均方误差。
# calculate mean squared error
def mean_squared_error(actual, predicted):
sum_square_error = 0.0
for i in range(len(actual)):
sum_square_error += (actual[i] - predicted[i])**2.0
mean_square_error = 1.0 / len(actual) * sum_square_error
return mean_square_error
为了高效实现,我鼓励您使用 Sklearn 均方误差()函数。
交叉熵损失(或对数损失)
交叉熵损失通常简称为“交叉熵”、“对数损失、“逻辑损失”或简称为“对数损失”。
将每个预测的概率与实际的类输出值(0 或 1)进行比较,并根据与期望值的距离计算惩罚概率的分数。惩罚是对数的,为小的差异(0.1 或 0.2)提供小的分数,为大的差异(0.9 或 1.0)提供大的分数。
交叉熵损失被最小化,其中较小的值比较大的值代表更好的模型。预测完美概率的模型的交叉熵或对数损失为 0.0。
二元或两类预测问题的交叉熵实际上是作为所有例子的平均交叉熵来计算的。
下面的 Python 函数提供了一个类似伪代码的函数工作实现,用于计算实际 0 和 1 值列表与类 1 预测概率的交叉熵。
from math import log
# calculate binary cross entropy
def binary_cross_entropy(actual, predicted):
sum_score = 0.0
for i in range(len(actual)):
sum_score += actual[i] * log(1e-15 + predicted[i])
mean_sum_score = 1.0 / len(actual) * sum_score
return -mean_sum_score
注意,我们将一个非常小的值(在本例中为 1E-15)添加到预测概率中,以避免计算 0.0 的对数。这意味着在实践中,最好的可能损失将是非常接近于零的值,但不完全为零。
可以为多类分类计算交叉熵。这些类已经被热编码,这意味着每个类值都有一个二进制特征,并且预测必须具有每个类的预测概率。交叉熵随后在每个二进制特征之间求和,并在数据集中的所有示例之间取平均值。
下面的 Python 函数提供了一个类似伪代码的函数工作实现,用于计算一系列实际的热编码值与每个类的预测概率的交叉熵。
from math import log
# calculate categorical cross entropy
def categorical_cross_entropy(actual, predicted):
sum_score = 0.0
for i in range(len(actual)):
for j in range(len(actual[i])):
sum_score += actual[i][j] * log(1e-15 + predicted[i][j])
mean_sum_score = 1.0 / len(actual) * sum_score
return -mean_sum_score
为了高效实现,我鼓励您使用 Sklearn log_loss()函数。
损失函数和报告的模型表现
给定最大似然的框架,我们知道我们想要在随机梯度下降下使用交叉熵或均方误差损失函数。
然而,我们可能希望也可能不希望使用损失函数来报告模型的表现。
例如,对数损失很难解释,尤其是非机器学习从业者利益相关者。均方差也是如此。相反,报告分别用于分类和回归的模型的准确率和均方根误差可能更重要。
可能还需要根据这些指标而不是损失来选择模型。这是一个重要的考虑因素,因为具有最小损失的模型可能不是具有对项目涉众很重要的最佳度量的模型。
需要考虑的一个好的划分是使用损失来评估和诊断模型的学习情况。这包括优化过程的所有考虑,如过拟合、欠拟合和收敛。然后可以选择一个对项目涉众来说有意义的替代度量来评估模型表现和执行模型选择。
- 损失:仅用于评估诊断模型优化。
- 度量:用于在项目上下文中评估和选择模型。
相同的度量可以用于两个关注点,但是优化过程的关注点更有可能与项目的目标不同,并且需要不同的分数。然而,通常情况下,改善损失会改善,或者在最坏的情况下,对利率指标没有影响。
进一步阅读
如果您想更深入地了解这个主题,本节将提供更多资源。
书
- 深度学习,2016 年。
- 神经锻造:前馈人工神经网络中的监督学习,1999。
- 用于模式识别的神经网络,1995。
文章
摘要
在这篇文章中,你发现了损失和损失函数在训练深度学习神经网络中的作用,以及如何为你的预测建模问题选择合适的损失函数。
具体来说,您了解到:
- 使用需要损失函数来计算模型误差的优化过程来训练神经网络。
- 最大似然提供了一个框架,用于在训练神经网络和机器学习模型时选择损失函数。
- 交叉熵和均方误差是训练神经网络模型时使用的两种主要损失函数。
你有什么问题吗? 在下面的评论中提问,我会尽力回答。
如何在 Keras 开发深度学习模型集成
最后更新于 2020 年 8 月 28 日
深度学习神经网络模型是高度灵活的非线性算法,能够学习几乎无限数量的映射函数。
这种灵活性的一个令人沮丧的地方是最终模型的高度差异。在同一数据集上训练的同一神经网络模型可能会在每次运行时找到许多不同的可能“足够好”的解决方案之一。
模型平均是一种集成学习技术,它减少了最终神经网络模型中的方差,牺牲了模型表现的分布,以获得对模型预期表现的信心。
在本教程中,您将发现如何在 Keras 中开发模型平均集成,以减少最终模型中的方差。
完成本教程后,您将知道:
- 模型平均是一种集成学习技术,可用于降低深度学习神经网络模型的期望方差。
- 分类和回归预测建模问题如何在 Keras 中实现模型平均。
- 如何解决一个多类分类问题,并使用模型平均来减少最终模型的方差。
用我的新书更好的深度学习启动你的项目,包括分步教程和所有示例的 Python 源代码文件。
我们开始吧。
- 2019 年 10 月更新:针对 Keras 2.3 和 TensorFlow 2.0 更新。
- 2020 年 1 月更新:针对 Sklearn v0.22 API 的变化进行了更新。
如何用模型平均集成减少 Keras 中深度学习模型的差异 图片由约翰·梅森提供,保留部分权利。
教程概述
本教程分为六个部分;它们是:
- 模型平均
- 如何在 Keras 中平均模型
- 多类分类问题
- 多类分类的 MLP 模型
- MLP 模型的高方差
- 模型平均集成
模型平均
深度学习神经网络模型是通过随机训练算法进行学习的非线性方法。
这意味着它们高度灵活,能够学习变量之间的复杂关系,并在给定足够资源的情况下逼近任何映射函数。这种灵活性的缺点是模型的方差很大。
这意味着模型高度依赖于用于训练模型的特定训练数据,以及训练过程中的初始条件(随机初始权重)和意外发现。结果是,每次在同一数据集上训练相同的模型配置时,最终模型都会做出不同的预测。
当训练最终模型用于对新数据进行预测时,例如在操作上或机器学习竞赛中,这可能会令人沮丧。
该方法的高方差可以通过为问题训练多个模型并组合它们的预测来解决。这种方法被称为模型平均,属于称为集成学习的技术家族。
如何在 Keras 中平均模型
在 Keras 中开发模型平均集成的最简单方法是在同一数据集上训练多个模型,然后组合来自每个训练模型的预测。
训练多个模型
根据模型的大小和训练数据的大小,训练多个模型可能是资源密集型的。
您可能需要在相同的硬件上顺序训练模型。对于非常大的模型,使用云基础设施并行训练模型可能是值得的,例如亚马逊网络服务。
集合所需的模型数量可能因问题和模型的复杂性而异。这种方法的一个好处是,您可以继续创建模型,将它们添加到集合中,并通过对保持测试集进行预测来评估它们对表现的影响。
对于小模型,您可以按顺序训练模型,并将它们保存在内存中,以便在实验中使用。例如:
...
# train models and keep them in memory
n_members = 10
models = list()
for _ in range(n_members):
# define and fit model
model = ...
# store model in memory as ensemble member
models.add(models)
...
对于大型模型,也许是在不同的硬件上训练的,你可以将每个模型保存到文件中。
...
# train models and keep them to file
n_members = 10
for i in range(n_members):
# define and fit model
model = ...
# save model to file
filename = 'model_' + str(i + 1) + '.h5'
model.save(filename)
print('Saved: %s' % filename)
...
随后可以加载模型。
小模型可以同时加载到内存中,而非常大的模型可能需要一次加载一个来进行预测,然后再进行组合。
from keras.models import load_model
...
# load pre-trained ensemble members
n_members = 10
models = list()
for i in range(n_members):
# load model
filename = 'model_' + str(i + 1) + '.h5'
model = load_model(filename)
# store in memory
models.append(model)
...
组合预测
一旦模型准备好了,每个模型都可以用来进行预测,并且可以组合预测。
在回归问题的情况下,每个模型都在预测实值输出,可以收集值并计算平均值。
...
# make predictions
yhats = [model.predict(testX) for model in models]
yhats = array(yhats)
# calculate average
outcomes = mean(yhats)
在分类问题的情况下,有两种选择。
首先是计算预测整数类值的模式。
...
# make predictions
yhats = [model.predict_classes(testX) for model in models]
yhats = array(yhats)
# calculate mode
outcomes, _ = mode(yhats)
这种方法的缺点是,对于小的集合或有大量类的问题,预测样本可能不够大,模式没有意义。
在二分类问题的情况下,在输出层使用 sigmoid 激活函数,预测概率的平均值可以像回归问题一样计算。
在具有两个以上类别的多类别分类问题的情况下,在输出层上使用 softmax 激活函数,并且在取 argmax 以获得类别值之前,可以计算每个预测类别的概率之和。
...
# make predictions
yhats = [model.predict(testX) for model in models]
yhats = array(yhats)
# sum across ensembles
summed = numpy.sum(yhats, axis=0)
# argmax across classes
outcomes = argmax(summed, axis=1)
这些组合 Keras 模型预测的方法同样适用于多层感知器、卷积和递归神经网络。
现在我们知道了如何在 Keras 中平均来自多个神经网络模型的预测,让我们通过一个案例研究来工作。
多类分类问题
我们将使用一个小的多类分类问题作为基础来演示模型平均集成。
Sklearn 类提供了 make_blobs()函数,该函数可用于创建具有规定数量的样本、输入变量、类和类内样本方差的多类分类问题。
我们用 500 个例子来说明这个问题,输入变量(代表点的 x 和 y 坐标)和每个组内点的标准偏差为 2.0。我们将使用相同的随机状态(伪随机数发生器的种子)来确保我们总是获得相同的 500 分。
# generate 2d classification dataset
X, y = make_blobs(n_samples=500, 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=500, 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)
接下来,我们必须将数据集分成训练集和测试集。我们将使用测试集来评估模型的表现,并使用学习曲线绘制训练期间的表现。我们将使用 30%的数据用于训练,70%用于测试集。
这是一个具有挑战性的问题的例子,在这个问题中,我们没有标记的例子比有标记的例子多。
# split into train and test
n_train = int(0.3 * X.shape[0])
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
接下来,我们可以定义和编译模型。
该模型将预期具有两个输入变量的样本。然后,该模型有一个具有 15 种模式和一个校正线性激活函数的隐藏层,然后是一个具有 3 个节点的输出层,用于预测 3 个类别中每一个的概率和一个 softmax 激活函数。
由于问题是多类的,我们将使用分类交叉熵损失函数来优化模型和随机梯度下降的有效亚当味。
# define model
model = Sequential()
model.add(Dense(15, input_dim=2, activation='relu'))
model.add(Dense(3, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
该模型适合 200 个训练时期,我们将在测试集上评估每个时期的模型,使用测试集作为验证集。
# fit model
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=200, 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()
下面列出了完整的示例。
# fit high variance mlp on blobs classification problem
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=500, centers=3, n_features=2, cluster_std=2, random_state=2)
y = to_categorical(y)
# split into train and test
n_train = int(0.3 * 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(15, 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=200, 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()
运行该示例首先打印最终模型在列车和测试数据集上的表现。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,我们可以看到该模型在训练数据集上实现了约 84%的准确率,在测试数据集上实现了约 76%的准确率;不可怕。
Train: 0.847, Test: 0.766
还创建了一个线图,显示了在每个训练周期内,训练和测试集上模型准确率的学习曲线。
我们可以看到,该模型并没有真正过量,但可能有点不足,可能会受益于容量的增加、更多的培训以及一些正则化。所有这些改进,我们都有意抑制,以迫使我们的案例研究出现高方差。
每个训练时期训练和测试数据集上模型准确率的线图学习曲线
MLP 模型的高方差
重要的是要证明模型的预测确实存在差异。
我们可以通过在同一数据集上重复相同模型配置的拟合和评估,并总结模型的最终表现来演示这一点。
为此,我们首先将模型的拟合和评估拆分为一个可以重复调用的函数。下面的 evaluate_model()函数获取训练和测试数据集,拟合一个模型,然后对其进行评估,在测试数据集上重新调整模型的准确率。
# fit and evaluate a neural net model on the dataset
def evaluate_model(trainX, trainy, testX, testy):
# define model
model = Sequential()
model.add(Dense(15, 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, epochs=200, verbose=0)
# evaluate the model
_, test_acc = model.evaluate(testX, testy, verbose=0)
return test_acc
我们可以调用这个函数 30 次,节省了测试的准确率分数。
# repeated evaluation
n_repeats = 30
scores = list()
for _ in range(n_repeats):
score = evaluate_model(trainX, trainy, testX, testy)
print('> %.3f' % score)
scores.append(score)
一旦收集到,我们可以总结分布分数,首先根据平均值和标准差,假设分布是高斯的,这是非常合理的。
# summarize the distribution of scores
print('Scores Mean: %.3f, Standard Deviation: %.3f' % (mean(scores), std(scores)))
然后,我们可以将分布总结为直方图以显示分布的形状,以及方框和须状图以显示分布的范围和主体。
# histogram of distribution
pyplot.hist(scores, bins=10)
pyplot.show()
# boxplot of distribution
pyplot.boxplot(scores)
pyplot.show()
下面列出了在所选斑点数据集上总结 MLP 模型方差的完整示例。
# demonstrate high variance of mlp model on blobs classification problem
from sklearn.datasets import make_blobs
from keras.utils import to_categorical
from keras.models import Sequential
from keras.layers import Dense
from numpy import mean
from numpy import std
from matplotlib import pyplot
# fit and evaluate a neural net model on the dataset
def evaluate_model(trainX, trainy, testX, testy):
# define model
model = Sequential()
model.add(Dense(15, 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, epochs=200, verbose=0)
# evaluate the model
_, test_acc = model.evaluate(testX, testy, verbose=0)
return test_acc
# generate 2d classification dataset
X, y = make_blobs(n_samples=500, centers=3, n_features=2, cluster_std=2, random_state=2)
y = to_categorical(y)
# split into train and test
n_train = int(0.3 * X.shape[0])
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# repeated evaluation
n_repeats = 30
scores = list()
for _ in range(n_repeats):
score = evaluate_model(trainX, trainy, testX, testy)
print('> %.3f' % score)
scores.append(score)
# summarize the distribution of scores
print('Scores Mean: %.3f, Standard Deviation: %.3f' % (mean(scores), std(scores)))
# histogram of distribution
pyplot.hist(scores, bins=10)
pyplot.show()
# boxplot of distribution
pyplot.boxplot(scores)
pyplot.show()
运行该示例首先打印测试集上每个模型的准确性,最后打印准确性分数样本的平均值和标准偏差。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,我们可以看到样本的平均值为 77%,标准偏差约为 1.4%。假设为高斯分布,我们预计 99%的准确率会在 73%到 81%之间(即高于和低于平均值的 3 个标准差)。
我们可以将模型在测试集上的准确度的标准偏差作为模型所做预测的方差的估计。
> 0.749
> 0.771
> 0.763
> 0.760
> 0.783
> 0.780
> 0.769
> 0.754
> 0.766
> 0.786
> 0.766
> 0.774
> 0.757
> 0.754
> 0.771
> 0.749
> 0.763
> 0.800
> 0.774
> 0.777
> 0.766
> 0.794
> 0.797
> 0.757
> 0.763
> 0.751
> 0.789
> 0.791
> 0.766
> 0.766
Scores Mean: 0.770, Standard Deviation: 0.014
还创建了准确度分数的直方图,显示了非常粗糙的高斯形状,可能带有较长的右尾巴。
图中的大样本和不同数量的箱可能会更好地揭示分布的真实基本形状。
超过 30 次重复的模型测试准确率直方图
还创建了一个方框和触须图,显示了测试集上准确度约为 76.5%的中位数处的一条线,以及约 78%至 76%之间的样本的四分位数范围或中间 50%。
模型试验准确率超过 30 次重复的盒须图
对测试分数样本的分析清楚地表明了在相同数据集上训练的相同模型的表现差异。
测试集上大约 8 个百分点(81%–73%)的可能分数的分布可以被合理地认为是大的,例如高方差结果。
模型平均集成
我们可以使用模型平均来降低模型的方差,并可能降低模型的泛化误差。
具体来说,这将导致保持测试集的标准偏差更小,而训练集的表现更好。我们可以检查这两个假设。
首先,我们必须开发一个函数来准备和返回训练数据集上的拟合模型。
# fit model on dataset
def fit_model(trainX, trainy):
# define model
model = Sequential()
model.add(Dense(15, 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, epochs=200, 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]
print(len(subset))
# make prediction
yhat = ensemble_predictions(subset, testX)
# calculate accuracy
return accuracy_score(testy, yhat)
最后,我们可以创建集成成员数量(x 轴)与测试数据集(y 轴)上许多成员的平均预测准确率的线图。
# plot score vs number of ensemble members
x_axis = [i for i in range(1, n_members+1)]
pyplot.plot(x_axis, scores)
pyplot.show()
下面列出了完整的示例。
# model averaging ensemble and a study of ensemble size on test accuracy
from sklearn.datasets import make_blobs
from keras.utils import to_categorical
from keras.models import Sequential
from keras.layers import Dense
import numpy
from numpy import array
from numpy import argmax
from sklearn.metrics import accuracy_score
from matplotlib import pyplot
# fit model on dataset
def fit_model(trainX, trainy):
# define model
model = Sequential()
model.add(Dense(15, 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, epochs=200, 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]
print(len(subset))
# make prediction
yhat = ensemble_predictions(subset, testX)
# calculate accuracy
return accuracy_score(testy, yhat)
# generate 2d classification dataset
X, y = make_blobs(n_samples=500, centers=3, n_features=2, cluster_std=2, random_state=2)
# split into train and test
n_train = int(0.3 * X.shape[0])
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
trainy = to_categorical(trainy)
# fit all models
n_members = 20
members = [fit_model(trainX, trainy) for _ in range(n_members)]
# evaluate different numbers of ensembles
scores = list()
for i in range(1, n_members+1):
score = evaluate_n_members(members, i, testX, testy)
print('> %.3f' % score)
scores.append(score)
# plot score vs number of ensemble members
x_axis = [i for i in range(1, n_members+1)]
pyplot.plot(x_axis, scores)
pyplot.show()
运行该示例首先将 20 个模型放在同一个训练数据集上,这在现代硬件上可能不到一分钟。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
然后,从 1 个成员到所有 20 个成员测试不同大小的集成,并打印每个集成大小的测试准确率结果。
1
> 0.740
2
> 0.754
3
> 0.754
4
> 0.760
5
> 0.763
6
> 0.763
7
> 0.763
8
> 0.763
9
> 0.760
10
> 0.760
11
> 0.763
12
> 0.763
13
> 0.766
14
> 0.763
15
> 0.760
16
> 0.760
17
> 0.763
18
> 0.766
19
> 0.763
20
> 0.763
最后,创建一个折线图,显示测试集上集合大小和表现之间的关系。
我们可以看到,表现提高到大约五个成员,之后表现稳定在 76%左右。这接近于在分析模型的重复评估期间观察到的平均测试集表现。
集合大小对模型检验准确率的线图
最后,我们可以更新重复评估实验,使用五个模型的集合来代替单个模型,并比较分数的分布。
下面列出了一个完整的重复评估的五个成员的斑点数据集集合的例子。
# repeated evaluation of model averaging ensemble on blobs dataset
from sklearn.datasets import make_blobs
from keras.utils import to_categorical
from keras.models import Sequential
from keras.layers import Dense
import numpy
from numpy import array
from numpy import argmax
from numpy import mean
from numpy import std
from sklearn.metrics import accuracy_score
# fit model on dataset
def fit_model(trainX, trainy):
# define model
model = Sequential()
model.add(Dense(15, 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, epochs=200, 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 ensemble model
def evaluate_members(members, testX, testy):
# make prediction
yhat = ensemble_predictions(members, testX)
# calculate accuracy
return accuracy_score(testy, yhat)
# generate 2d classification dataset
X, y = make_blobs(n_samples=500, centers=3, n_features=2, cluster_std=2, random_state=2)
# split into train and test
n_train = int(0.3 * X.shape[0])
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
trainy = to_categorical(trainy)
# repeated evaluation
n_repeats = 30
n_members = 5
scores = list()
for _ in range(n_repeats):
# fit all models
members = [fit_model(trainX, trainy) for _ in range(n_members)]
# evaluate ensemble
score = evaluate_members(members, testX, testy)
print('> %.3f' % score)
scores.append(score)
# summarize the distribution of scores
print('Scores Mean: %.3f, Standard Deviation: %.3f' % (mean(scores), std(scores)))
运行该示例可能需要几分钟的时间,因为五个模型被拟合和评估,并且该过程被重复 30 次。
打印测试集上每个模型的表现,以提供进度指示。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
模型表现的平均值和标准偏差在运行结束时打印。
> 0.769
> 0.757
> 0.754
> 0.780
> 0.771
> 0.774
> 0.766
> 0.769
> 0.774
> 0.771
> 0.760
> 0.766
> 0.766
> 0.769
> 0.766
> 0.771
> 0.763
> 0.760
> 0.771
> 0.780
> 0.769
> 0.757
> 0.769
> 0.771
> 0.771
> 0.766
> 0.763
> 0.766
> 0.771
> 0.769
Scores Mean: 0.768, Standard Deviation: 0.006
在这种情况下,我们可以看到数据集上五人集成的平均表现为 76%。这非常接近单个模型 77%的平均值。
重要的区别是标准偏差从单个模型的 1.4%缩小到五个模型的 0.6%。我们可能预计,在这个问题上,给定的五个模型的集合的表现下降在大约 74%到大约 78%之间,可能性为 99%。
对在相同数据集上训练的相同模型求平均值,可以提高可靠性,这是最终模型在操作中非常需要的特性。
在给定大数定律的情况下,集合中的更多模型将进一步降低集合在测试数据集上的准确度的标准偏差,至少降低到收益递减的程度。
这表明对于这个特定的模型和预测问题,具有五个成员的模型平均集成足以减少模型的方差。这种方差的减少,反过来也意味着在准备最终模型时有更好的平均表现。
扩展ˌ扩张
本节列出了一些您可能希望探索的扩展教程的想法。
- 平均等级预测。更新示例以平均类整数预测,而不是类概率预测,并比较结果。
- 保存和加载模型。更新示例,将集合成员保存到文件中,然后从单独的脚本中加载它们进行评估。
- 方差敏感度。创建一个新的示例,根据给定重复次数的测试集上模型表现的标准偏差,对集合成员的数量进行敏感性分析,并报告收益递减点。
如果你探索这些扩展,我很想知道。
进一步阅读
如果您想更深入地了解这个主题,本节将提供更多资源。
- 开始使用 Keras 顺序模型
- 硬核层 API
- scipy . stat . mode API
- num py . argmax API
- sklearn . dataset . make _ blobs API
摘要
在本教程中,您发现了如何在 Keras 中开发模型平均集成来减少最终模型中的方差。
具体来说,您了解到:
- 模型平均是一种集成学习技术,可用于降低深度学习神经网络模型的期望方差。
- 分类和回归预测建模问题如何在 Keras 中实现模型平均。
- 如何解决一个多类分类问题,并使用模型平均来减少最终模型的方差。
你有什么问题吗? 在下面的评论中提问,我会尽力回答。
神经网络诀窍(书评)
最后更新于 2019 年 8 月 6 日
深度学习神经网络的配置和训练具有挑战性。
在数百篇研究论文、源代码以及学术界和从业者的头脑中,流传着几十年的技巧和诀窍。
《神经网络:交易技巧》一书最初于 1998 年出版,2012 年在深度学习复兴的风口浪尖上更新,将不同的技巧和诀窍结合成一册。它包括所有深度学习神经网络从业者必读的建议。
在这篇文章中,你会发现《神经网络:交易技巧》一书,该书为神经网络学者和从业者提供了如何最大限度地利用模型的建议。
看完这篇文章,你会知道:
- 为什么写这本书的动机。
- 第一版和第二版章节和主题的分类。
- 每个神经网络从业者必读章节的列表和摘要。
用我的新书更好的深度学习启动你的项目,包括分步教程和所有示例的 Python 源代码文件。
我们开始吧。
神经网络——交易技巧
概观
神经网络:交易技巧是关于从神经网络模型中获得更好表现的技术的论文集合。
第一版于 1998 年出版,由五部分 17 章组成。第二版是在 2012 年新的深度学习复兴的风口浪尖上出版的,包括另外三个部分和 13 个新章节。
如果你是一个深度学习的实践者,那么这是一本必读的书。
我拥有并参考了这两个版本。
动机
这本书的动机是整理经验和理论基础的提示、技巧和最佳实践,用于在实践中从神经网络模型中获得最佳表现。
作者担心的是,许多有用的提示和技巧是该领域的隐性知识,被困在人们的头脑、代码库或会议论文的末尾,该领域的初学者应该意识到它们。
我们相信,研究人员和从业者通过经验和口口相传获得了帮助他们成功地将神经网络应用于困难的现实世界问题的技术和启发。[……]它们通常隐藏在人们的脑海中,或者在篇幅有限的会议文件的封底。
这本书是在 1996 年同名 NIPS 会议的一个研讨会取得成功后,试图将这些技巧组合在一起的一种努力。
这本书是 1996 年 NIPS 研讨会的产物,该研讨会名为贸易技巧,其目标是开始收集和记录这些技巧的过程。研讨会引起的兴趣促使我们扩大我们的收藏,并将其汇编成这本书。
—第 1 页,神经网络:交易的诀窍,第二版,2012 年。
第一版细目
该书的第一版由吉纳维芙·奥尔和克劳斯·罗伯特·穆勒共同编辑,由五个部分和 17 章组成,于 20 年前的 1998 年出版。
每个部分都包括一个有用的序言,总结了在接下来的章节中会发生什么,并且每个章节都是由该领域的一个或多个学者撰写的。
第一版的细目如下:
第一部分:加速学习
- 第一章:高效的反向路由
第二部分:提高泛化能力的正则化技术
- 第二章:提前停止——但是什么时候?
- 第三章:估计重量衰减参数的简单技巧
- 第四章:基于麦凯贝叶斯神经网络框架的超参数搜索控制
- 第五章:神经网络建模中的自适应正则化
- 第六章:大集合平均
第三部分:改进网络模型和算法技巧
- 第七章:方形单位增强的、彻底扩展的多层感知器
- 第八章:多任务学习的十几个技巧
- 第九章:解决神经网络学习的病态
- 第十章:集中神经网络梯度因子
- 第十一章:避免导数反向传播中的舍入误差
第四部分:神经网络训练中先验知识的表示和合并
- 第十二章:模式识别中的变换不变性——切线距离和切线传播
- 第十三章:结合神经网络和上下文驱动搜索的牛顿在线印刷体手写识别
- 第十四章:神经网络分类和先验概率
- 第十五章:将分治法应用于大规模模式识别任务
第五部分:时间序列的技巧
- 第十六章:用神经网络预测经济:挑战和解决方案综述
- 第十七章:如何训练神经网络
这是一本昂贵的书,如果你能买到这第一版的廉价二手书,那么我强烈推荐它。
第二版的增补
这本书的第二版发布于 2012 年,似乎就在成为“深度学习”的大推送开始时因此,这本书抓住了当时的新技术,如分层预处理和受限玻尔兹曼机器。
现在关注 ReLU、带有 CNNs 的 ImageNet 和大型 LSTMs 的使用还为时过早。
尽管如此,第二版包括三个新部分和 13 个新章节。
第二版增加的内容细分如下:
第六部分:深度神经网络中的大学习
- 第十八章:随机梯度下降技巧
- 第十九章:基于梯度的深度架构培训的实用建议
- 第二十章:用无黑森优化训练深度和递归网络
- 第二十一章:高效实现神经网络
第七部分:更好的表示:不变、不纠缠和可重用
- 第二十二章:用 K-均值学习特征表示
- 第二十三章:用于数字识别的深度大多层感知器
- 第二十四章:训练受限玻尔兹曼机器的实用指南
- 第二十五章:深玻尔兹曼机器和定心技巧
- 第二十六章:通过半监督嵌入的深度学习
第八部分:识别用于预测和控制的动力系统
- 第二十七章:应用回声状态网络的实用指南
- 第二十八章:用递归神经网络预测:12 个技巧
- 第二十九章:用递归神经网络解决部分可观察强化学习问题
- 第三十章:设置神经强化控制器的 10 个步骤和一些技巧
必读章节
整本书都是很好的读物,尽管如果你正在寻找可以立即使用的快速有用的技巧,我不建议把它全部读完。
这是因为许多章节集中在作家的宠物项目,或高度专业化的方法。相反,我建议阅读四个具体章节,第一版两个,第二版两个。
仅这四章书第二版就值得购买,我强烈建议为你自己、你的团队或你的办公室拿一本。
幸运的是,这些章节的预印本可以在网上免费获得。
推荐的章节有:
- 第一章 : 高效背板,Yann LeCun 等著。
- 第二章 : 提前停止——但是什么时候?,作者:Lutz Prechelt。
- 第十八章 : 随机梯度下降招数,里昂·博图。
- 第十九章 : 基于梯度的深层建筑训练实用建议,由 Yoshua Bengio 撰写。
让我们依次仔细看看这些章节。
高效背板
本章重点提供非常具体的提示,以充分利用随机梯度下降优化算法和反向传播权重更新算法。
许多不良的反钻行为可以通过技巧来避免,这些技巧在严肃的技术出版物中很少被揭露。本文给出了其中的一些技巧,并解释了它们的工作原理。
—第 9 页,神经网络:交易的诀窍,第一版,1998 年。
本章继续提供一个密集的、理论上支持的提示列表,用于配置算法、准备输入数据等。
这一章太密集了,很难总结,尽管结尾的“讨论和结论”部分提供了一个很好的建议列表,引用自下面的书:
–打乱示例 –通过减去平均值 使输入变量居中–将输入变量标准化为 1 的标准偏差–如果可能,将输入变量去相关。 –选择具有图 1.4 所示 sigmoid 功能的网络 –在 sigmoid 范围内设置目标值,通常为+1 和-1。 –按照 1.16 的规定,将权重初始化为随机值。
训练网络的首选方法应选择如下: –如果训练集很大(超过几百个样本)并且冗余,并且如果任务是分类,请使用经过仔细调整的随机梯度,或者使用随机对角 Levenberg Marquardt 方法。 –如果训练集不太大,或者任务是回归,使用共轭梯度。
—第 47-48 页,神经网络:交易技巧,第一版,1998 年。
自发表以来的 20 年里,应用神经网络领域已经取得了长足的进步(例如,关于 sigmoid 激活函数的评论不再相关),但基础并没有改变。
本章是所有深度学习实践者的必读。
提前停止-但是什么时候?
本章描述了一种简单而强大的正则化方法,称为提前停止,当模型在等待验证数据集上的表现开始下降时,该方法将停止神经网络的训练。
验证可用于检测在神经网络的监督训练期间何时开始过拟合;然后在收敛之前停止训练,以避免过拟合(“提前停止”)
—第 55 页,神经网络:交易的诀窍,第一版,1998 年。
提前停止的挑战在于用于停止训练过程的触发器的选择和配置,提前停止的系统配置是本章的重点。
一般提前停止标准描述如下:
- GL :泛化损失一超过规定阈值就停止。
- PQ :泛化损失和进度的商一超过阈值就停止。
- UP :当条块中的泛化误差增加时停止。
提供了三个建议,例如“T0”技巧:
1.使用快速停止标准,除非网络表现的微小改善(例如 4%)值得大量增加训练时间(例如因素 4)。 2。为了最大化找到“好”解决方案的概率(相对于最大化解决方案的平均质量),使用 GL 标准。 3。为了最大限度地提高解决方案的平均质量,如果网络仅超负荷很少,则使用 PQ 标准,否则使用 UP 标准。
—第 60 页,神经网络:交易的诀窍,第一版,1998 年。
这些规则通过大量的训练和测试问题进行经验分析。这一发现的关键在于,对早期停止标准更有耐心会以额外的计算复杂性为代价,导致更好的保持表现。
我的结论是,较慢的停止标准允许泛化能力有小幅提高(这里:平均约 4%),但需要更多的训练时间(这里:平均约长 4 倍)。
—第 55 页,神经网络:交易的诀窍,第一版,1998 年。
随机梯度下降技巧
本章重点详细回顾了随机梯度下降优化算法和技巧,以帮助获得最大的收益。
本章提供了背景材料,解释了为什么在训练集很大的情况下,SGD 是一个好的学习算法,并提供了有用的建议。
—第 421 页,神经网络:交易的诀窍,第二版,2012 年。
与第一章:高效背板有很多重叠,虽然这一章用方框调出了一路上的提示,但有用的提示列表并没有在这一章的结尾进行总结。
然而,它是所有神经网络从业者的必修读物。
以下是我自己对本章中各种提示的总结,大部分直接引用了第二版:
- 当训练时间是瓶颈时,使用随机梯度下降(batch=1)。
- 随机打乱训练示例。
- 使用预记录技术。
- 监控培训成本和验证错误。
- 使用有限差分检查梯度。
- 用训练集的一个小样本来试验学习率。
- 利用训练示例的稀疏性。
- 使用衰减的学习率。
- 尝试平均随机梯度(即算法的特定变体)。
其中一些提示简洁,没有上下文;我建议读这一章。
基于梯度的深层建筑训练实用建议
本章重点介绍神经网络和早期深度学习模型的有效训练。
它将第一章和第二十九章的经典建议联系在一起,但增加了对(当时)最近深度学习发展的评论,如贪婪逐层预训练、像图形处理器这样的现代硬件、像 BLAS 这样的现代高效代码库,以及来自调整模型训练的真实项目的建议,如训练超参数的顺序。
本章旨在作为一个实用指南,为一些最常用的超参数提供建议,特别是在基于反向传播梯度和基于梯度的优化的学习算法的背景下。
—第 437 页,神经网络:交易的诀窍,第二版,2012 年。
它也很长,分为六个主要部分:
- 深度学习创新。包括贪婪逐层预处理、去噪自动编码器和在线学习。
- 梯度。包括小批量梯度下降和自动微分。
- 超参数。包括学习率、小批量、时期、动量、节点、权重正则化、活动正则化、超参数搜索和推荐。
- 调试和分析。包括监控过拟合的损失、可视化和统计。
- 其他建议。包括 GPU 硬件和使用高效的线性代数库,如 BLAS。
- 开放式问题。包括训练深度模型的难度和自适应学习率。
我总结的太多了;这一章充满了配置和调整神经网络模型的有用建议。
毫无疑问,这是必读,并为后来在 2016 年的书深度学习中描述的推荐提供了种子,其中 Yoshua Bengio 是三位作者之一。
这一章以强烈、乐观的基调结束。
这里总结的实践,加上可用计算能力的提高,现在允许研究人员在远远超出本书第一版时可能的规模上训练神经网络,帮助我们更接近人工智能。
—第 473 页,神经网络:交易的诀窍,第二版,2012 年。
进一步阅读
在亚马逊上得到这本书
- 神经网络:交易技巧,第一版,1998 年。
- 神经网络:交易的诀窍,第二版,2012 年。
其他书籍页面
- 神经网络:交易的诀窍,第二版,2012 年。斯普林格主页。
- 神经网络:交易的诀窍,第二版,2012 年。谷歌图书
推荐章节的预印本
- 高效背板,1998。
- 提前停止–但是什么时候?,1998 年。
- 随机梯度下降技巧,2012。
- 深度架构基于梯度的训练实用建议,2012。
摘要
在这篇文章中,你发现了《神经网络:交易技巧》一书,该书提供了神经网络学者和从业者关于如何最大限度地利用你的模型的建议。
你读过这本书的一部分或全部吗?你看呢? 在下面的评论里告诉我。
在 Keras 中集成神经网络模型权重(Polyak 平均)
最后更新于 2020 年 8 月 28 日
神经网络的训练过程是一个具有挑战性的优化过程,常常无法收敛。
这可能意味着训练结束时的模型可能不是用作最终模型的稳定或表现最佳的权重集。
解决这个问题的一种方法是在训练结束时使用多个模型的平均权重。这被称为 Polyak-Ruppert 平均,并且可以通过使用模型权重的线性或指数递减加权平均值来进一步改进。除了产生更稳定的模型之外,平均模型权重的表现也可以产生更好的表现。
在本教程中,您将发现如何将多个不同模型的权重组合到一个模型中进行预测。
完成本教程后,您将知道:
- 训练神经网络的随机性和挑战性意味着优化过程不会收敛。
- 使用训练结束时观察到的模型的平均权重来创建模型,可以产生更稳定、有时表现更好的解决方案。
- 如何从多个已保存的模型中开发用模型参数的相等、线性和指数加权平均值创建的最终模型。
用我的新书更好的深度学习启动你的项目,包括分步教程和所有示例的 Python 源代码文件。
我们开始吧。
- 2019 年 10 月更新:针对 Keras 2.3 和 TensorFlow 2.0 更新。
- 2020 年 1 月更新:针对 Sklearn v0.22 API 的变化进行了更新。
如何在 Keras 中创建神经网络模型权重的相等、线性和指数加权平均值。
教程概述
本教程分为七个部分;它们是:
- 平均模型权重集合
- 多类分类问题
- 多层感知器模型
- 将多个模型保存到文件
- 具有平均模型权重的新模型
- 用平均模型权重集合进行预测
- 线性和指数递减加权平均
平均模型权重集合
学习深度神经网络模型的权重需要解决高维非凸优化问题。
解决这个优化问题的一个挑战是有很多“好的”解,学习算法可能会反弹,无法在一个解上稳定下来。在随机优化领域,这被称为优化算法在解上收敛的问题,其中解由一组特定的权重值定义。
如果您的模型收敛有问题,您可能会看到的一个症状是训练和/或测试损失值显示出高于预期的方差,例如,它在训练时期上下跳动。
解决这个问题的一种方法是在训练过程结束时合并收集的权重。一般来说,这可能被称为时间平均,被称为 Polyak 平均或 Polyak-Ruppert 平均,以该方法的最初开发者命名。
Polyak 平均包括通过优化算法访问的参数空间对轨迹中的几个点进行平均。
—第 322 页,深度学习,2016。
在学习过程中平均多个有噪声的权重集听起来可能不如优化过程本身那么理想,但可能被证明是一个理想的解决方案,尤其是对于可能需要几天、几周甚至几个月来训练的非常大的神经网络。
本质上的进步是基于一个自相矛盾的想法:必须对一个收敛速度低于最优的缓慢算法进行平均。
——通过平均进行随机近似的加速,1992。
对来自单次训练运行的多个模型的权重进行平均具有平息噪声优化过程的效果,该噪声优化过程可能由于学习超参数(例如,学习率)的选择或正在学习的映射函数的形状而变得有噪声。结果是最终的模型或一组权重,可能会提供更稳定,也许更准确的结果。
基本思想是,优化算法可能会在山谷中来回跳跃几次,而不会访问靠近山谷底部的点。然而,两边所有位置的平均值应该接近谷底。
—第 322 页,深度学习,2016。
Polyak-Ruppert 平均的最简单实现包括计算最近几个训练时期模型权重的平均值。
这可以通过计算加权平均值来改善,在加权平均值中,更多的权重被应用于更近的模型,该权重通过之前的时期线性降低。另一种更广泛使用的方法是在加权平均中使用指数衰减。
Polyak-Ruppert 平均法已被证明可以提高标准 SGD 的收敛性【…】。或者,可以使用参数的指数移动平均值,对最近的参数值赋予更高的权重。
——亚当:一种随机优化的方法,2014。
在最终模型中使用模型权重的平均值或加权平均值是实践中确保从训练运行中获得最佳结果的常用技术。该方法是用于照片分类的谷歌初始 V2 和 V3 深度卷积神经网络模型中的许多“T0”技巧之一,是深度学习领域的里程碑。
使用随时间计算的参数的运行平均值进行模型评估。
——重新思考计算机视觉的初始架构,2015 年。
多类分类问题
我们将使用一个小的多类分类问题作为基础来演示模型权重集成。
Sklearn 类提供了 make_blobs()函数,该函数可用于创建具有规定数量的样本、输入变量、类和类内样本方差的多类分类问题。
该问题有两个输入变量(表示点的 x 和 y 坐标)和每组内点的标准偏差 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 的标准偏差意味着类不是线性可分的(用一条线可分的),导致很多不明确的点。
这是可取的,因为这意味着问题不是微不足道的,并将允许神经网络模型找到许多不同的“足够好”的候选解决方案,从而导致高方差。
具有三个类和按类值着色的点的斑点数据集的散点图
多层感知器模型
在我们定义一个模型之前,我们需要设计一个适合整体的问题。
在我们的问题中,训练数据集相对较小。具体来说,训练数据集中的示例与保持数据集中的示例的比例为 10:1。这模拟了一种情况,即我们可能有大量未标记的示例和少量已标记的示例来训练模型。
我们将从斑点问题中创建 1100 个数据点。模型将在前 100 个点上进行训练,剩余的 1000 个点将保留在测试数据集中,模型无法使用。
该问题是一个多类分类问题,我们将在输出层使用 softmax 激活函数对其进行建模。这意味着模型将以样本属于三类中每一类的概率来预测具有三个元素的向量。因此,在将行分割成训练和测试数据集之前,我们必须对类值进行热编码。我们可以使用 Keras *到 _ 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:]
接下来,我们可以定义和编译模型。
该模型将预期具有两个输入变量的样本。然后,该模型有一个具有 25 个节点的单个隐藏层和一个校正的线性激活函数,然后有一个具有三个节点的输出层来预测三个类中每一个的概率,还有一个 softmax 激活函数。
由于问题是多类的,我们将使用分类交叉熵损失函数来优化模型和具有小学习率和动量的随机梯度下降。
# define model
model = Sequential()
model.add(Dense(25, input_dim=2, activation='relu'))
model.add(Dense(3, activation='softmax'))
opt = SGD(lr=0.01, momentum=0.9)
model.compile(loss='categorical_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))
最后,我们将在训练和验证数据集上绘制每个训练时期的模型准确率的学习曲线。
# 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 keras.optimizers import SGD
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:]
# define model
model = Sequential()
model.add(Dense(25, input_dim=2, activation='relu'))
model.add(Dense(3, activation='softmax'))
opt = SGD(lr=0.01, momentum=0.9)
model.compile(loss='categorical_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))
# learning curves of model accuracy
pyplot.plot(history.history['accuracy'], label='train')
pyplot.plot(history.history['val_accuracy'], label='test')
pyplot.legend()
pyplot.show()
运行该示例将打印最终模型在列车和测试数据集上的表现。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,我们可以看到该模型在训练数据集上获得了大约 86%的准确率,我们知道这是乐观的,在测试数据集上获得了大约 81%的准确率,我们预计这将更加真实。
Train: 0.860, Test: 0.812
还创建了一个线图,显示了在每个训练周期内,训练和测试集上模型准确率的学习曲线。
我们可以看到,在大部分跑步过程中,训练的准确性更加乐观,我们也注意到了最终得分。重要的是,我们确实在训练和测试数据集上的训练期间看到了合理数量的准确性差异,这可能为使用模型权重平均提供了良好的基础。
每个训练时期训练和测试数据集上模型准确率的线图学习曲线
将多个模型保存到文件
模型权重集成的一种方法是在内存中保存模型权重的运行平均值。
这种方法有三个缺点:
- 它要求您事先知道模型权重的组合方式;也许你想尝试不同的方法。
- 它要求你知道用于训练的纪元数量;也许你想使用提前停止。
- 它要求您在内存中至少保留一份整个网络的副本;对于大型模型来说,这可能非常昂贵,如果训练过程崩溃或死亡,这可能会很脆弱。
另一种方法是,作为第一步,在训练期间将模型权重保存到文件中,然后将保存的模型中的权重进行组合,以制作最终模型。
也许实现这一点最简单的方法是手动驱动训练过程,一次一个纪元,然后在纪元结束时保存模型,如果我们已经超过了纪元数量的上限。
例如,对于我们的测试问题,我们将为 500 个时期训练模型,并且可能保存从时期 490 开始的模型(例如,在时期 490 和 499 之间并且包括时期 490 和 499)。
# fit model
n_epochs, n_save_after = 500, 490
for i in range(n_epochs):
# fit model for a single epoch
model.fit(trainX, trainy, epochs=1, verbose=0)
# check if we should save the model
if i >= n_save_after:
model.save('model_' + str(i) + '.h5')
使用模型上的 save() 功能并指定包含纪元号的文件名,可以将模型保存到文件中。
注意,在 Keras 中保存和加载神经网络模型需要安装 h5py 库。您可以使用 pip 安装此库,如下所示:
pip install h5py
将所有这些结合在一起,下面列出了在训练数据集上拟合模型并保存最近 10 个时代的所有模型的完整示例。
# save models to file toward the end of a training run
from sklearn.datasets import make_blobs
from keras.utils import to_categorical
from keras.models import Sequential
from keras.layers import Dense
# 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:]
# 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
n_epochs, n_save_after = 500, 490
for i in range(n_epochs):
# fit model for a single epoch
model.fit(trainX, trainy, epochs=1, verbose=0)
# check if we should save the model
if i >= n_save_after:
model.save('model_' + str(i) + '.h5')
运行该示例会将 10 个模型保存到当前工作目录中。
具有平均模型权重的新模型
我们可以从多个具有相同架构的现有模型中创建一个新模型。
首先,我们需要将模型加载到内存中。这是合理的,因为模型很小。如果您正在处理非常大的模型,那么一次加载一个模型并在内存中平均权重可能会更容易。
load_model() Keras 函数可用于从文件加载已保存的模型。下面的功能 load_all_models() 将从当前工作目录加载模型。它以开始和结束时代作为参数,这样您就可以试验在连续时代中保存的不同组的模型。
# load models from file
def load_all_models(n_start, n_end):
all_models = list()
for epoch in range(n_start, n_end):
# define filename for this ensemble
filename = 'model_' + str(epoch) + '.h5'
# load model from file
model = load_model(filename)
# add to list of members
all_models.append(model)
print('>loaded %s' % filename)
return all_models
我们可以调用函数来加载所有的模型。
# load models in order
members = load_all_models(490, 500)
print('Loaded %d models' % len(members))
加载后,我们可以用模型权重的加权平均值创建一个新模型。
每个模型都有一个 get_weights() 函数,该函数返回一个数组列表,模型中的每个层都有一个数组。我们可以枚举模型中的每一层,从每个模型中检索相同的层,并计算加权平均值。这会给我们一套砝码。
然后,我们可以使用 clone_model() Keras 函数来创建架构的克隆,并调用 set_weights() 函数来使用我们准备的平均权重。下面的*模型 _ 权重 _ 集合()*函数实现了这一点。
# create a model from the weights of multiple models
def model_weight_ensemble(members, weights):
# determine how many layers need to be averaged
n_layers = len(members[0].get_weights())
# create an set of average model weights
avg_model_weights = list()
for layer in range(n_layers):
# collect this layer from each model
layer_weights = array([model.get_weights()[layer] for model in members])
# weighted average of weights for this layer
avg_layer_weights = average(layer_weights, axis=0, weights=weights)
# store average layer weights
avg_model_weights.append(avg_layer_weights)
# create a new model with the same structure
model = clone_model(members[0])
# set the weights in the new
model.set_weights(avg_model_weights)
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
将这些元素结合在一起,我们可以加载 10 个模型,并计算模型权重的平均加权平均值(算术平均值)。完整列表如下。
# average the weights of multiple loaded models
from keras.models import load_model
from keras.models import clone_model
from numpy import average
from numpy import array
# load models from file
def load_all_models(n_start, n_end):
all_models = list()
for epoch in range(n_start, n_end):
# define filename for this ensemble
filename = 'model_' + str(epoch) + '.h5'
# load model from file
model = load_model(filename)
# add to list of members
all_models.append(model)
print('>loaded %s' % filename)
return all_models
# create a model from the weights of multiple models
def model_weight_ensemble(members, weights):
# determine how many layers need to be averaged
n_layers = len(members[0].get_weights())
# create an set of average model weights
avg_model_weights = list()
for layer in range(n_layers):
# collect this layer from each model
layer_weights = array([model.get_weights()[layer] for model in members])
# weighted average of weights for this layer
avg_layer_weights = average(layer_weights, axis=0, weights=weights)
# store average layer weights
avg_model_weights.append(avg_layer_weights)
# create a new model with the same structure
model = clone_model(members[0])
# set the weights in the new
model.set_weights(avg_model_weights)
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
# load all models into memory
members = load_all_models(490, 500)
print('Loaded %d models' % len(members))
# prepare an array of equal weights
n_models = len(members)
weights = [1/n_models for i in range(1, n_models+1)]
# create a new model with the weighted average of all model weights
model = model_weight_ensemble(members, weights)
# summarize the created model
model.summary()
运行该示例首先从文件中加载 10 个模型。
>loaded model_490.h5
>loaded model_491.h5
>loaded model_492.h5
>loaded model_493.h5
>loaded model_494.h5
>loaded model_495.h5
>loaded model_496.h5
>loaded model_497.h5
>loaded model_498.h5
>loaded model_499.h5
Loaded 10 models
从这 10 个模型中创建一个模型权重集合,给予每个模型相等的权重,并报告模型结构的概要。
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_1 (Dense) (None, 25) 75
_________________________________________________________________
dense_2 (Dense) (None, 3) 78
=================================================================
Total params: 153
Trainable params: 153
Non-trainable params: 0
_________________________________________________________________
用平均模型权重集合进行预测
既然我们知道了如何计算模型权重的加权平均值,我们就可以用得到的模型来评估预测。
一个问题是,我们不知道有多少模型适合组合以获得良好的表现。我们可以通过用最后的 n 个模型评估模型加权平均集成来解决这个问题,并改变 n 个模型来查看有多少模型产生了良好的表现。
下面的 evaluate_n_members() 函数将根据给定数量的加载模型创建一个新模型。每个模型在对最终模型的贡献中被赋予相同的权重,然后调用*模型 _ 权重 _ 集成()*函数来创建最终模型,然后在测试数据集上对其进行评估。
# evaluate a specific number of members in an ensemble
def evaluate_n_members(members, n_members, testX, testy):
# reverse loaded models so we build the ensemble with the last models first
members = list(reversed(members))
# select a subset of members
subset = members[:n_members]
# prepare an array of equal weights
weights = [1.0/n_members for i in range(1, n_members+1)]
# create a new model with the weighted average of all model weights
model = model_weight_ensemble(subset, weights)
# make predictions and evaluate accuracy
_, test_acc = model.evaluate(testX, testy, verbose=0)
return test_acc
重要的是,首先颠倒加载模型的列表,以确保使用训练运行中最后的 n 个模型,我们假设这些模型的平均表现可能更好。
# reverse loaded models so we build the ensemble with the last models first
members = list(reversed(members))
然后,我们可以评估从最后 1 个模型到最后 10 个模型的训练运行中保存的最后 n 个模型的不同数量所创建的模型。除了评估组合的最终模型,我们还可以评估测试数据集中每个保存的独立模型,以比较表现。
# 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
_, single_score = members[i-1].evaluate(testX, testy, 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)
可以绘制收集的分数,蓝色圆点代表单个保存模型的准确性,橙色线条代表组合了最后 n 个模型的权重的模型的测试准确性。
# 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()
将所有这些结合在一起,下面列出了完整的示例。
# average of model weights 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 load_model
from keras.models import clone_model
from keras.models import Sequential
from keras.layers import Dense
from matplotlib import pyplot
from numpy import average
from numpy import array
# load models from file
def load_all_models(n_start, n_end):
all_models = list()
for epoch in range(n_start, n_end):
# define filename for this ensemble
filename = 'model_' + str(epoch) + '.h5'
# load model from file
model = load_model(filename)
# add to list of members
all_models.append(model)
print('>loaded %s' % filename)
return all_models
# # create a model from the weights of multiple models
def model_weight_ensemble(members, weights):
# determine how many layers need to be averaged
n_layers = len(members[0].get_weights())
# create an set of average model weights
avg_model_weights = list()
for layer in range(n_layers):
# collect this layer from each model
layer_weights = array([model.get_weights()[layer] for model in members])
# weighted average of weights for this layer
avg_layer_weights = average(layer_weights, axis=0, weights=weights)
# store average layer weights
avg_model_weights.append(avg_layer_weights)
# create a new model with the same structure
model = clone_model(members[0])
# set the weights in the new
model.set_weights(avg_model_weights)
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
# 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]
# prepare an array of equal weights
weights = [1.0/n_members for i in range(1, n_members+1)]
# create a new model with the weighted average of all model weights
model = model_weight_ensemble(subset, weights)
# make predictions and evaluate accuracy
_, test_acc = model.evaluate(testX, testy, verbose=0)
return test_acc
# 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:]
# load models in order
members = load_all_models(490, 500)
print('Loaded %d models' % len(members))
# reverse loaded models so we build the ensemble with the last models first
members = list(reversed(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
_, single_score = members[i-1].evaluate(testX, testy, 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)
# 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()
运行该示例首先加载 10 个保存的模型。
>loaded model_490.h5
>loaded model_491.h5
>loaded model_492.h5
>loaded model_493.h5
>loaded model_494.h5
>loaded model_495.h5
>loaded model_496.h5
>loaded model_497.h5
>loaded model_498.h5
>loaded model_499.h5
Loaded 10 models
报告每个单独保存的模型的表现,以及一个集合模型,该集合模型具有从所有模型到每个模型(包括每个模型)的平均权重,从训练运行结束后向后工作。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
结果表明,后两种模型的测试准确率最高可达 81.4%。我们可以看到,模型权重集成的测试准确率平衡了表现,并且表现良好。
> 1: single=0.814, ensemble=0.814
> 2: single=0.814, ensemble=0.814
> 3: single=0.811, ensemble=0.813
> 4: single=0.805, ensemble=0.813
> 5: single=0.807, ensemble=0.811
> 6: single=0.805, ensemble=0.807
> 7: single=0.802, ensemble=0.809
> 8: single=0.805, ensemble=0.808
> 9: single=0.805, ensemble=0.808
> 10: single=0.810, ensemble=0.807
还会创建一个线图,显示每个单个模型的测试准确率(蓝点)和模型权重集合的表现(橙色线)。
我们可以看到,对模型权重进行平均确实会使最终模型的表现变得均衡,并且表现至少与运行的最终模型一样好。
单一模型测试表现(蓝点)和模型权重集合测试表现(橙色线)的线图
线性和指数递减加权平均
我们可以更新示例并评估集成中模型权重的线性递减权重。
权重可以按如下方式计算:
# prepare an array of linearly decreasing weights
weights = [i/n_members for i in range(n_members, 0, -1)]
这可以用来代替 evaluate_n_members() 函数中的等权重。
下面列出了完整的示例。
# linearly decreasing weighted average of models 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 load_model
from keras.models import clone_model
from keras.models import Sequential
from keras.layers import Dense
from matplotlib import pyplot
from numpy import average
from numpy import array
# load models from file
def load_all_models(n_start, n_end):
all_models = list()
for epoch in range(n_start, n_end):
# define filename for this ensemble
filename = 'model_' + str(epoch) + '.h5'
# load model from file
model = load_model(filename)
# add to list of members
all_models.append(model)
print('>loaded %s' % filename)
return all_models
# create a model from the weights of multiple models
def model_weight_ensemble(members, weights):
# determine how many layers need to be averaged
n_layers = len(members[0].get_weights())
# create an set of average model weights
avg_model_weights = list()
for layer in range(n_layers):
# collect this layer from each model
layer_weights = array([model.get_weights()[layer] for model in members])
# weighted average of weights for this layer
avg_layer_weights = average(layer_weights, axis=0, weights=weights)
# store average layer weights
avg_model_weights.append(avg_layer_weights)
# create a new model with the same structure
model = clone_model(members[0])
# set the weights in the new
model.set_weights(avg_model_weights)
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
# 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]
# prepare an array of linearly decreasing weights
weights = [i/n_members for i in range(n_members, 0, -1)]
# create a new model with the weighted average of all model weights
model = model_weight_ensemble(subset, weights)
# make predictions and evaluate accuracy
_, test_acc = model.evaluate(testX, testy, verbose=0)
return test_acc
# 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:]
# load models in order
members = load_all_models(490, 500)
print('Loaded %d models' % len(members))
# reverse loaded models so we build the ensemble with the last models first
members = list(reversed(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
_, single_score = members[i-1].evaluate(testX, testy, 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)
# 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()
运行该示例再次报告每个单个模型的表现,这一次测试每个平均模型权重集合的准确性,其中模型的贡献呈线性下降。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
我们可以看到,至少在这种情况下,集成实现了比任何独立模型都小的表现提升,达到了大约 81.5%的准确率。
...
> 1: single=0.814, ensemble=0.814
> 2: single=0.814, ensemble=0.815
> 3: single=0.811, ensemble=0.814
> 4: single=0.805, ensemble=0.813
> 5: single=0.807, ensemble=0.813
> 6: single=0.805, ensemble=0.813
> 7: single=0.802, ensemble=0.811
> 8: single=0.805, ensemble=0.810
> 9: single=0.805, ensemble=0.809
> 10: single=0.810, ensemble=0.809
折线图显示了表现的提升,并显示了与使用平均加权集成相比,在所创建的不同大小的集成上,测试准确率方面更稳定的表现。
线性衰减的单一模型测试表现(蓝点)和模型权重集合测试表现(橙色线)的线图
我们也可以试验模型贡献的指数衰减。这要求指定衰减率(α)。下面的示例为递减率为 2 的指数衰减创建权重。
# prepare an array of exponentially decreasing weights
alpha = 2.0
weights = [exp(-i/alpha) for i in range(1, n_members+1)]
下面列出了模型对集合模型中平均权重的贡献呈指数衰减的完整示例。
# exponentially decreasing weighted average of models 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 load_model
from keras.models import clone_model
from keras.models import Sequential
from keras.layers import Dense
from matplotlib import pyplot
from numpy import average
from numpy import array
from math import exp
# load models from file
def load_all_models(n_start, n_end):
all_models = list()
for epoch in range(n_start, n_end):
# define filename for this ensemble
filename = 'model_' + str(epoch) + '.h5'
# load model from file
model = load_model(filename)
# add to list of members
all_models.append(model)
print('>loaded %s' % filename)
return all_models
# create a model from the weights of multiple models
def model_weight_ensemble(members, weights):
# determine how many layers need to be averaged
n_layers = len(members[0].get_weights())
# create an set of average model weights
avg_model_weights = list()
for layer in range(n_layers):
# collect this layer from each model
layer_weights = array([model.get_weights()[layer] for model in members])
# weighted average of weights for this layer
avg_layer_weights = average(layer_weights, axis=0, weights=weights)
# store average layer weights
avg_model_weights.append(avg_layer_weights)
# create a new model with the same structure
model = clone_model(members[0])
# set the weights in the new
model.set_weights(avg_model_weights)
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
# 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]
# prepare an array of exponentially decreasing weights
alpha = 2.0
weights = [exp(-i/alpha) for i in range(1, n_members+1)]
# create a new model with the weighted average of all model weights
model = model_weight_ensemble(subset, weights)
# make predictions and evaluate accuracy
_, test_acc = model.evaluate(testX, testy, verbose=0)
return test_acc
# 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:]
# load models in order
members = load_all_models(490, 500)
print('Loaded %d models' % len(members))
# reverse loaded models so we build the ensemble with the last models first
members = list(reversed(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
_, single_score = members[i-1].evaluate(testX, testy, 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)
# 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: single=0.814, ensemble=0.814
> 2: single=0.814, ensemble=0.815
> 3: single=0.811, ensemble=0.814
> 4: single=0.805, ensemble=0.814
> 5: single=0.807, ensemble=0.813
> 6: single=0.805, ensemble=0.813
> 7: single=0.802, ensemble=0.813
> 8: single=0.805, ensemble=0.813
> 9: single=0.805, ensemble=0.813
> 10: single=0.810, ensemble=0.813
测试准确度分数的线图显示了使用指数衰减代替模型的线性或等权重的更强的稳定效果。
具有指数衰减的单模型测试表现(蓝点)和模型权重集合测试表现(橙色线)的线图
扩展ˌ扩张
本节列出了一些您可能希望探索的扩展教程的想法。
- 车型数量。评估更多模型对最终模型贡献权重的效果。
- 衰变率。评估指数加权平均值使用不同衰减率对测试表现的影响。
如果你探索这些扩展,我很想知道。
进一步阅读
如果您想更深入地了解这个主题,本节将提供更多资源。
书
- 第 8.7.3 节 Polyak 平均,深度学习,2016。
报纸
- 平均化随机近似的加速度,1992。
- 缓慢收敛的罗宾斯-蒙罗过程的有效估计,1988。
应用程序接口
文章
摘要
在本教程中,您发现了如何将多个不同模型的权重组合到一个模型中进行预测。
具体来说,您了解到:
- 训练神经网络的随机性和挑战性意味着优化过程不会收敛。
- 使用训练结束时观察到的模型的平均权重来创建模型,可以产生更稳定、有时表现更好的解决方案。
- 如何从多个已保存的模型中开发用模型参数的相等、线性和指数加权平均值创建的最终模型。
你有什么问题吗? 在下面的评论中提问,我会尽力回答。