Python深度学习(四)机器学习基础

本章涵盖了

  • 分类和回归以外的机器学习形式
  • 机器学习模型的正式评估过程
  • 为深度学习准备数据
  • 特征工程
  • 解决过拟合
  • 解决机器学习问题的通用工作流程

在第三章的三个实际例子之后,您应该开始熟悉如何使用神经网络来处理分类和回归问题,并且您已经看到了机器学习的核心问题:过拟合。
这一章将把你的一些新直觉形式化到一个坚固的概念框架中,用来解决深度学习的问题。我们将把所有这些概念——模型评估、数据预处理和特性工程,以及处理过拟合——整合到一个详细的七步工作流程中,以处理任何机器学习任务。

4.1机器学习的四个分支

在前面的示例中,您已经熟悉了三种特定类型的机器学习问题:二分类、多分类和标量回归。这三个都是监督学习(supervised learning)的实例,目标是学习训练输入和训练目标之间的关系。
监督学习只是冰山一角——机器学习是一个有着复杂子领域类别的广阔领域。机器学习算法通常分为四大类,在下面的部分中进行描述。

4.1.1 有监督学习

这是目前为止最常见的情况。它包括,在给定一组示例(通常由人来注释)的情景下,学习将输入数据映射到已知目标(也称为注解(annotations))。到目前为止,你在这本书中遇到的四个例子都是有监督学习的典型例子。一般来说,目前备受关注的深度学习的几乎所有应用都属于这一类,例如光学字符识别(optical character recognition, OCR)、语音识别、图像分类和语言翻译。
虽然有监督学习主要由分类和回归组成,但也有更多的外来变体,包括以下(有例子):

  • 序列生成(Sequence generation)——给定一张图片,预测描述它的标题。序列生成有时可以重新表述为一系列分类问题(例如在序列中反复预测单词或词组(Token))。
  • 语法树预测(Syntax tree prediction)——给定一个句子,预测它分解成的语法树。
  • 对象检测(Object detection)—— 给定一张图片,在图片内的特定对象周围画一个边框。这也可以表示为一个分类问题(给定许多候选边界框,对每个边界框的内容进行分类)或一个联合了分类和回归的问题,其中边界框坐标是通过向量回归预测的。
  • 图像分割(Image segmentation)——给定一幅图片,在特定对象上绘制像素级的蒙版。

4.1.2无监督学习

机器学习的这个分支包括在不借助任何目标的情况下对输入数据进行有趣的转换,以实现数据可视化、数据压缩或数据去噪,或者更好地理解当前数据中的相关性。无监督学习是数据分析的主要内容,在试图解决一个有监督学习的问题之前,它通常是更好地理解数据集的必要步骤。降维(Dimensionality reduction)和聚类(clustering)是无监督学习的著名类别。

4.1.3 自助(Self-supervised)学习

这是监督学习的一个特例,但它的不同之处足以让它应该有自己的类别。自主监督学习是一种无需人为标记标签的监督学习——可以把它看作是没有任何人参与的监督学习。这里仍然涉及到标签(因为学习必须受到某些东西的监督),但它们是由输入数据生成的,通常使用启发式算法。
例如,autoencoders是自监督学习的一个众所周知的实例,其中生成的目标是输入,没有修改。同样地,给定过去的帧,试图预测视频中的下一帧,或给定之前的单词,预测文本中的下一个单词,都是自我监督学习的实例(短暂的监督学习(temporally supervised learning),在这种情况下:监督来自未来的输入数据)。请注意,监督学习、自我监督学习和非监督学习之间的区别有时可能是模糊的——这些类别更像是没有固定边界的连续统一体。自我监督学习可以被重新解释为监督学习或非监督学习,这取决于你关注的是学习机制还是它的应用环境。

请注意,在本书中,我们将特别关注监督学习,因为它是目前深度学习的主要形式,具有广泛的行业应用。在后面的章节中,我们还将简要介绍自我监督学习。

4.1.4强化学习

这个机器学习的分支被长期忽视,直到最近在谷歌DeepMind成功地将其应用于学习玩Atari游戏(后来,学习玩围棋到最顶尖水平)之后,它开始受到很多关注。在强化学习中,agent会接收环境的信息,并学会选择能获得最大回报的行为。例如,可以通过强化学习来训练一个神经网络,它“看”电子游戏屏幕并输出游戏动作以最大化它的分数。
目前,强化学习主要是一个研究领域,在游戏之外还没有取得显著的实际成功。然而,随着时间的推移,我们预计强化学习将接管越来越广泛的现实应用领域:自动驾驶汽车、机器人、资源管理、教育等等。这个想法的时机已经到来,或者即将到来。

分类及回归术语表
分类和回归涉及到许多专业术语。你在前面的例子中遇到过一些,在以后的章节中你会看到更多。它们有精确的、特定于机器学习的定义,您应该熟悉它们:

  • Sample or input:输入到模型中的一个数据点
  • Prediction or output:模型的输出结果
  • Target:答案。理想情况下,根据一个外部来源的数据,模型应该预测的结果
  • Prediction error or loss value: 模型的预期与目标间的差距的度量
  • Classes:分类问题中可供选择的一组可能的标签。例如,当分类猫和狗的图片时,“狗”和“猫”是两个类。
  • Label:分类问题中类注释的特定实例。例如,如果图片#1234被注释为包含类“dog”,那么“dog”就是图片#1234的标签。
  • Ground-truth or annotations:数据集的所有目标,通常由人类收集。
  • Binary classification:一个分类任务,其中每个输入样本应该被分为两个互斥的类别
  • Multiclass classification:一个分类任务,其中每个输入样本都应该被分为两类以上:例如,对手写数字进行分类。
  • Multilabel classification— 一个分类任务,其中每个输入样本可以分配多个标签。例如,一个给定的图像可能同时包含一只猫和一只狗,并且应该用“猫”标签和“狗”的标签进行标注。每个图像的标签数量通常是可变的。
  • Scalar regression:目标是连续标量值的任务。预测房价就是一个很好的例子:不同的目标价格形成一个连续的空间。
  • Vector regression:目标是一组连续值的任务:例如,一个连续向量。如果你对多个值进行回归(例如图像中边界框的坐标),然后进行向量回归。
  • Mini-batch or batch:由模型同时处理的一小部分样品(通常在8到128之间)。为了便于在GPU上进行内存分配,样本的数量通常是2的幂。在训练时,使用一个迷你批处理(mini-batch)来计算应用于模型权重的单一梯度下降更新。

4.2 评估机器学习模型

评估一个模型总是归结为将可用数据分成三组:训练、验证和测试。您利用训练数据进行训练,并根据验证数据评估您的模型。一旦您的模型准备好了,您就可以在测试数据上最后一次测试它。
你可能会问,为什么不准备两套呢:一套是训练,一套是测试?你会对训练数据进行训练并对测试数据进行评估。更简单!
原因是,开发一个模型总是涉及到调整其配置:例如,选择层的数量或层的大小(称为模型的超参数(hyperparameters),以区别于参数,后者是网络的权重)。通过将模型在验证数据上的性能作为反馈信号来执行此调优。从本质上讲,这种调优是一种学习形式:在某些参数空间中搜索良好的配置。因此,根据模型在验证集上的性能调整模型的配置会很快导致对验证集的过拟合,即使您的模型从未直接针对验证集进行过训练。
这种现象的核心是信息泄露的概念。每次根据模型在验证集上的性能调整模型的超参数时,关于验证数据的一些信息就会泄漏到模型中。如果只对一个参数这样做一次,那么很少有信息会泄漏,您的验证集将保持可靠,以评估模型。但是,如果您重复这么多次——运行一个实验、对验证集进行评估,并将模型修改为结果——那么您就会将越来越多关于验证集的信息泄漏到模型中。
最终,您将得到一个在验证数据上人工执行良好的模型,因为这是您优化它的目的。您关心的是全新数据的性能,而不是验证数据,因此您需要使用完全不同的、从未见过的数据集来评估模型:测试数据集。您的模型不应该访问任何关于测试集的信息,即使是间接的。
如果基于测试集性能对模型进行了任何调整,那么您的泛化度量将是有缺陷的。
将数据分割成训练、验证和测试集看起来很简单,但是有一些高级的方法可以在数据很少的时候派上用场。让我们回顾一下三种经典的评估方法:简单的保留验证、Kfold验证和通过变换迭代K-fold验证。

  • 简单的保留验证(SIMPLE HOLD-OUT VALIDATION)
    将数据分离部分作为测试集。对剩余的数据进行训练,并用测试集进行评估。正如您在前几节中看到的,为了防止信息泄漏,您不应该基于测试集对模型进行调优,因此您还应该保留一个验证集。
    从示意图上看,保持验证如图4.1所示。下面的清单显示了一个简单的实现。

Listing 4.1 Hold-out validation


这是最简单的评估手段,它有一个缺陷:如果可用的数据很少,那么您的验证和测试集可能包含的样本太少,无法从统计学上代表手边的数据。这很容易识别:如果在分割之前对数据进行不同的随机洗牌最终会产生非常不同的模型性能度量,那么您就会遇到这个问题。K-fold验证和迭代K-fold验证是解决这一问题的两种方法,下面将对此进行讨论。

  • K-FOLD验证法(K-FOLD VALIDATION)
    使用这种方法,您可以将数据分割为大小相同的K个分区。对于每个分区i,在其余的K - 1分区上训练一个模型,并在分区i上评估它。你的最终成绩是K分的平均值。当模型的性能显示出基于traintest分割的显著差异时,这种方法是有帮助的。与保持验证一样,这种方法不会使您免于使用不同的验证集进行模型校准。
    从示意图上看,K-fold交叉验证类似于图4.2。清单4.2显示了一个简单的实现:

Listing 4.2 K-fold cross-validation



  • 混排迭代K-FOLD方法(ITERATED K-FOLD VALIDATION WITH SHUFFLING)
    在这种情况下,可用数据相对较少,需要尽可能精确地评估模型。我发现它在Kaggle比赛上非常有用。它包括多次应用K-fold验证,每次对数据进行洗牌,然后再以K种方式对数据进行拆分。最终得分是K-fold验证每次运行时获得的平均得分。注意,您最终培训和评估P×K模型(P是您使用的迭代次数),这可能代价非常昂贵,

4.2.2注意事项

在选择评估方案时要注意以下几点:

  • 数据的代表性(Data representativeness)——您希望您的训练集和测试集都能代表手边的数据。举例来说,如果你要分类数字图片,并且你从一个样例数组开始,而这些样例数组都按照它们的类别进行了排序,这时候你将前80%的数组数据作为训练集,并将后续的20%作为你的测试集,将导致你的训练数据只包含了分类0-7,而你的测试数据集只包含分类8-9。这似乎是一个荒谬的错误,但却出奇地普遍。因此,在将数据分解为训练和测试集之前,您通常应该随机地打乱(randomly shuffle)数据。
  • 时间箭头的方向(The arrow of time)——如果你想根据给定的过去来预测未来(例如,明天的天气,股票运动等等),你不应该在分割数据之前随机的洗牌数据。因为这样做会造成暂时的泄漏:您的模型将有效地接受来自未来数据的训练。在这种情况下,您应该始终确保测试集中的所有数据都在训练集中的数据之后。
  • 数据中的冗余(Redundancy in your data)——如果数据中的某些数据点出现了两次(这在真实数据中相当常见),那么将数据重组并将其分割为训练集和验证集将导致训练集和验证集之间的冗余。实际上,您将在部分训练数据上测试,这是您能做的最糟糕的事情!确保你的训练集和验证集是分开的。

4.3 神经网络的数据预处理

数据预处理的目的是使手头的原始数据更易于被神经网络接受。这包括向量化、规范化、处理缺失值和特征提取(vectorization, normalization, handling missing values, and feature extraction)。

  • 向量化(VECTORIZATION)
    神经网络中的所有输入和目标必须是浮点数据的张量(或者,在特定情况下,整数的张量)。任何需要处理的数据——声音、图像、文本——都必须首先转换为张量,这一步称为数据向量化。例如,在前面的两个文本分类示例中,我们从表示为整数列表(表示单词序列)的文本开始,并使用one-hot编码将它们转换为float32的张量。在对数字进行分类和预测房价的例子中,数据已经以向量形式出现,所以您可以跳过这一步。
  • 数值归一化(VALUE NORMALIZATION)
    在digit-classification示例中,利用从0-255范围内的整数编码的图像数据开始,编码灰度值。在将这些数据输入网络之前,您必须将其转换为float32并除以255,这样您最终会得到0-1范围内的浮点值。类似地,当预测房价时,你从各种范围的特征开始——一些特征有小的浮点值,另一些特征有相当大的整数值。在将这些数据输入网络之前,必须独立地对每个特性进行规范化,使其具有标准差1和平均值0。

一般来说,输入相对较大值的神经网络数据是不安全的(例如,多位整数,这比网络权值所取的初始值大得多)或异构数据(例如,一个特性在0-1范围内,另一个特性在100-200范围内的数据)。这样做将触发大的梯度更新,从而阻碍网络收敛。为了使您的网络更容易学习,您的数据应该具有以下特征:

  • 只取小值(Take small values)— 典型的,大多数值必须在0-1的范围内。
  • 必须同质( Be homogenous)— 也就是说,所有的特征大致都应该相同的范围内取值。
    此外,下面更严格的归一化实践是常见的,并且可以提供帮助,尽管它并不总是必要的(例如,您在digit-classification示例中没有这样做):
  • 分别对每个特性进行规范化,使其平均值为0。
  • 独立地规范化每个特性,使其标准差为1。
    备注:这就是概率论里面学到的,如何将非单元高斯分布转换为单元高斯分布时的变换公式 xx = (x- u) / σ,通过这种变换,就能符合单元高斯分布的要求了。 心塞,十年前学的数学,都还给老师了。
    使用Numpy数组很容易做到这一点:


  • 处理缺失值(HANDLING MISSING VALUES)
    您的数据有时可能缺少值。例如,在房价示例中,第一个特征(数据中索引0的列)是人均犯罪率。如果这个特性不是对所有样本可用呢?因此,在训练或测试数据中就会有缺失值。
    一般来说,使用神经网络,将输入的缺失值设置为0是安全的,条件是0已经不是一个有意义的值。网络将从暴露的数据中了解到值0表示丢失数据,并将开始忽略该值。
    注意,如果您期望测试数据中有缺失值,但是网络是在没有任何缺失值的数据上训练的,那么网络不会忽略缺失值!在这种情况下,您应该人为地生成缺少条目的训练样本:将一些训练样本复制几次,并删除一些您希望在测试数据中丢失的特征。

4.3.2 特征工程(Feature engineering)

特征工程是利用你自己关于数据和机器学习算法(在本例中是神经网络)的知识,在数据进入模型之前对其应用硬编码(非学习)转换,使算法更好地工作。在许多情况下,期望机器学习模型能够从完全任意的数据中学习是不合理的。数据需要以使模型的工作更容易的方式呈现给模型。
让我们来看一个直观的例子。假设您正在开发一个模型,该模型可以将时钟的图像作为输入,并可以输出一天的时间(参见图4.3)。


如果您选择使用图像的原始像素作为输入数据,那么您就会遇到一个机器学习的难题。你需要一个卷积神经网络来解决它,你需要花费相当多的计算资源来训练网络。
但是,如果您已经在较高的层次上理解了这个问题(您已经理解了人类如何在时钟面上读取时间),那么您就可以为机器学习算法提供更好的输入特性:例如,很容易写五行代码
Python脚本来跟踪时钟指针的黑色像素,并输出每只指针尖端的(x, y)坐标。然后,一个简单的机器学习算法可以学习将这些坐标与一天的适当时间相关联。
你甚至可以更进一步:做一个坐标变换,把(x, y)坐标表示成关于图像中心的极坐标。你的输入将成为每个时钟指针的角度。此时,您的特性使问题变得非常简单,不需要机器学习;一个简单的舍入操作和字典查找就足以恢复一天的大致时间。
这就是特征工程的本质:用更简单的方式表达问题,从而使问题变得更容易。这通常需要深入了解问题。
在深度学习之前,特征工程曾经很重要,因为经典的浅层算法没有足够的假设空间来学习有用的特征。将数据呈现给算法的方式对算法的成功至关重要。例如,在卷积神经网络成功之前MNIST数字分类问题,解决方案通常基于硬编码的特征,如数字图像中的线圈数,图像中每个数字的高度,像素值的直方图,等等。
幸运的是,现代深度学习消除了对大多数特征工程的需求,因为神经网络能够从原始数据中自动提取有用的特征。这是否意味着只要你使用深层神经网络,你就不需要担心特征工程?不,有两个原因:

  • 好的特性仍然允许您在使用更少的资源的同时更优雅地解决问题。例如,用卷积神经网络解决时钟面读取的问题是荒谬的。
  • 好的特性使您可以用更少的数据来解决问题。深度学习模型自主学习特征的能力依赖于大量的训练数据;如果您只有几个示例,那么它们特性中的信息值就变得至关重要。

4.4过拟合和欠拟合

在前一章的所有三个例子中——预测电影评论、主题分类和房价回归——模型在被定义的验证数据上的表现总是在几个迭代之后达到顶峰,然后开始下降:模型很快开始过拟合训练数据。过拟合发生在每一个机器学习问题。学习如何处理过拟合是掌握机器学习的关键。
机器学习的根本问题是优化和泛化之间的矛盾。优化是指调整模型以获得对训练数据的最佳性能(机器学习中的学习)的过程,而泛化是指训练后的模型对以前从未见过的数据的表现。游戏的目标是得到好的泛化,当然,你不能控制泛化;只能根据训练数据调整模型。
在训练开始时,优化和泛化是相关的:训练数据的损失越小,测试数据的损失就越小。当这种情况发生时,你的模型被认为是不合适的:仍有一些进展;网络还没有在训练数据中对所有相关模式进行建模。但是,在对训练数据进行一定数量的迭代之后,泛化停止了改进,验证度量停止了,然后开始下降:模型开始过度拟合。也就是说,它开始学习特定于训练数据的模式。但当涉及到新数据时,这些模式会产生误导或不相关。
为了防止模型学习到在训练数据中挖掘到误导或不相关的模式,最好的解决方案是获取更多的训练数据。对更多数据进行训练的模型自然会得到更好的推广。如果这是不可能的,那么下一个最好的解决方案就是调整模型允许存储的信息量,或者对允许存储的信息添加约束。如果一个网络只能记住少量的模式,那么优化过程就会迫使它把注意力集中在最突出的模式上,这样就能更好地推广。
用这种方法进行过拟合的过程称为正则化(regularization)。让我们回顾一些最常见的正则化技术,并将它们应用到实践中,以改进3.4节中的电影分类模型。

4.4.1减小网络规模

防止过拟合的最简单方法是减小模型的大小:模型中可学习参数的数量(由层数和每层单元数决定)。在深度学习中,模型中可学习参数的数量通常被称为模型的容量。直观地说,一个具有更多参数的模型具有更多的记忆能力(memorization capacity),因此可以很容易地学习到一个完美的字典式的训练样本与其目标之间的映射——一个没有泛化能力的映射。例如,可以很容易的建立一个具有500,000个二进制参数的模型来学习MNIST训练集中的每个数字的分类:我们每50,000个数字只需要10个二进制参数。(没看懂表达的是啥,反正意思就是要说复杂参数任意导致过拟合训练数据)但是这样的模型对于新数字样本的分类是无用的。始终牢记这一点:深度学习模型往往很好地适应训练数据,但真正的挑战是泛化,而不是拟合。
另一方面,如果网络的记忆资源有限,它就无法轻易地学习这种映射;因此,为了使损失最小化,它将不得不学习对目标具有预测能力的压缩表示——准确地说是我们感兴趣的表示类型。与此同时,请记住,您应该使用具有足够参数的模型以确保不会欠拟合:您的模型不应该缺乏记忆资源。在容量过大和容量不足之间需要找到一个折衷办法。
不幸的是,没有什么神奇的公式可以确定正确的层数或每个层的大小。您必须评估不同体系结构的数组(当然是在验证集上,而不是在测试集上),以便为数据找到正确的模型大小。找到合适的模型大小的一般工作流程是从相对较少的层和参数开始,增加层的大小或添加新层,直到您看到验证损失的回报递减为止。
让我们在电影评论分类网络上试试,接下来显示原始网络。

Listing 4.3 Original model

from keras import models
from keras import layers
model = models.Sequential()
model.add(layers.Dense(16, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(16, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

现在我们试着用这个更小的网络来代替它。

Listing 4.4 Version of the model with lower capacity

model = models.Sequential()
model.add(layers.Dense(4, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(4, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

图4.4显示了原始网络和较小网络的验证损失的比较。点是较小网络的验证损失值,叉是初始网络(记住,验证损失越低越好)。



如您所见,较小的网络开始过拟合的时间比参考网络晚(在经历了六个迭代而不是四个迭代之后),一旦开始过拟合,其性能下降的速度就会更慢。
现在,为了好玩,让我们在这个基准上添加一个容量大得多的网络——远远超过问题所要求的。

Listing 4.5 Version of the model with higher capacity

model = models.Sequential()
model.add(layers.Dense(512, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

图4.5显示了与参考网络相比,更大的网络发生了什么。点是较大网络的验证损失值,叉号是初始网络。



更大的网络几乎是在一个迭代之后就开始过拟合,而且过拟合程度要严重得多。它的验证损失也更大。
同时,图4.6显示了两个网络的训练损失。正如你所看到的,大网络的训练损失很快就接近于零。网络的容量越大,建模训练数据的速度就越快(导致了低的训练损失),但是它越容易过度拟合(导致了训练和验证损失之间的巨大差异)。


4.4.2添加权重正则化

你可能对奥卡姆剃刀的原理很熟悉:对于某件事给出两种解释,最可能是正确的解释是最简单的一种——假设更少的那个。这一思想也适用于神经网络学习的模型:给定一些训练数据和网络架构,多组权重值(多模型)可以解释数据。更简单的模型比复杂的模型更不容易过拟合。
在这个上下文中,一个简单的模型是一个参数值分布具有较少熵的模型(或者一个参数较少的模型,如您在前一节中所见)。因此,减轻过拟合的一种常见方法是通过强制其权值取很小的值来限制网络的复杂性,从而使权值的分布更有规律。这被称为权值正则化,它是通过增加网络的损失函数来实现的,代价与拥有大权值相关。这种代价有两种:

  • L1 regularization— 增加的代价与权重系数(权重的L1范数)的绝对值成正比。
  • L2 regularization— 增加的代价与权重系数(权重的L2范数)的平方成正比。L2正则化在神经网络中也称为权值衰减(weight decay)。别让不同的名字迷惑了你:权值衰减在数学上和L2正则化是一样的。
    在Keras中,权重正则化是通过将权重正则化实例(weight regularizer instances)作为关键字参数传递到层来添加的。让我们在电影评论分类网络中加入L2权重正则化:

Listing 4.6 Adding L2 weight regularization to the model

from keras import regularizers
model = models.Sequential()
model.add(layers.Dense(16, kernel_regularizer=regularizers.l2(0.001),
activation='relu', input_shape=(10000,)))
model.add(layers.Dense(16, kernel_regularizer=regularizers.l2(0.001),
activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

l2(0.001)表示层的权重系数矩阵中的每一个系数都将给网络的总损耗增加0.001 * weight_coefficient_value。注意,因为这个惩罚只在训练时添加,所以这个网络在训练时的损失要比在测试时高得多。
图4.7显示了L2正则化惩罚的影响。正如您所看到的,具有L2正则化(dots)的模型比参考模型(cross)更抗过拟合,尽管两个模型的参数数量相同。



作为L2正则化的一种替代方法,您可以使用以下Keras中的一个权重调节器。

Listing 4.7 Different weight regularizers available in Keras


4.4.3 添加dropout(丢失)

Dropout是神经网络最有效和最常用的正则化技术之一,由Geoff Hinton和他在多伦多大学的学生开发。Dropout应用到一个layer上,包括随机丢失(dropping out) (设置为0)训练期间层的许多输出特征。假设一个给定的层通常会在训练过程中返回一个向量[0.2,0.5,1.3,0.8,1.1]。应用dropout之后,这个向量将会有几个零项随机分布:例如[0,0.5,1.3,0,1.1]。丢失率(dropout rate)是被归零的特征的分数;它的值通常为0.2到0.5之间。在测试时,没有单元会被丢失(dropped out);相反,该层的输出值被缩减为与丢失率相等的因子,以平衡比训练时更多的单元处于活动状态这一事实。
考虑一个Numpy矩阵,它包含一个层的输出layer_output,形状为(batch_size, feature)。在训练时,我们随机将矩阵中值的一部分归零:

# At training time, drops out 50% of the units in the output
layer_output *= np.random.randint(0, high=2, size=layer_output.shape)

在测试时,我们通过丢失率降低输出。这里是0.5 (因为我们之前减少了一半的单元):

# At test time
layer_output *= 0.5

注意,这个过程可以通过在训练时执行操作和在测试时保持输出不变来实现,这通常是它在实践中实现的方式(参见图4.8):




这种方法似乎很奇怪,也很随意。为什么这有助于减少过拟合?
Hinton说,他的灵感来自于银行使用的一种防止欺诈的机制。用他自己的话来说,“我去了我的银行。出纳员们不停地变换,我问其中一个为什么。他说他不知道,但他们经常移动。我想一定是因为要想成功诈骗银行需要员工之间的合作。这让我意识到,在每个例子中随机移除不同的神经元子集,可以防止阴谋,从而减少过拟合。核心思想是在一个层的输出值中引入噪声可以打破那些不重要的偶发事件模式(Hinton称之为阴谋),如果没有噪声存在,网络就会开始记忆这些模式。
在Keras中,您可以通过dropout层在网络中引入dropout,它适用于它前面的层的输出:

model.add(layers.Dropout(0.5))

让我们在IMDB网络中添加两个Dropout层,看看它们在减少过拟合方面做得如何。

Listing 4.8 Adding dropout to the IMDB network

model = models.Sequential()
model.add(layers.Dense(16, activation='relu', input_shape=(10000,)))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(16, activation='relu'))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(1, activation='sigmoid'))

图4.9显示了结果的图表。同样,这是对参考网络的明显改进。



综上所述,以下是防止神经网络过拟合最常见的方法:

  • 得到更多的训练数据。
  • 减少网络的能力。
  • 添加权值正规化。
  • 添加Dropout。

4.5机器学习的通用工作流

在本节中,我们将展示一个通用蓝图,您可以使用它来攻破和解决任何机器学习问题。蓝图把你在这一章学到的概念联系在一起:问题定义,评估,特性工程,和解决过拟合。

4.5.1定义问题并组装数据集

首先,您必须定义手边的问题:

  • 您的输入数据是什么?你想预测什么?只有当你有可用的训练数据时,你才能学会预测:例如,只有当你有可用的电影评论和情感注释时,你才能学会对电影评论的情绪进行分类。因此,数据可用性通常是这个阶段的限制因素(除非你有办法付钱让别人帮你收集数据)。
  • 你面临什么样的问题?是二分类吗?多分类?标量回归?向量回归?多分类多标签分类问题?
    其他的东西,比如聚类、生成或强化学习?识别问题类型将指导您选择模型体系结构、损失函数等。
    除非您知道您的输入和输出是什么,以及您将使用什么数据,否则您无法进入下一个阶段。注意你在这个阶段所做的假设:
  • 你假设你的输出可以通过你的输入来预测。
  • 你假设您的可用数据具有足够的信息来了解输入和输出之间的关系。

在你有一个可行的模型之前,这些仅仅是假设,等待着被证实或失效。不是所有的问题都能解决;仅仅因为你装配了输入X和目标Y的样本并不意味着X包含足够的信息来预测。例如,如果你试图预测某只股票在股票市场上的走势,考虑到它最近的历史价格,你不太可能成功,因为历史价格不包含太多的预测信息。

你应该知道的一类不可解问题是非平稳问题(nonstationary problems)。假设您正在尝试构建一个服装推荐引擎,您正在对它为期一个月的数据进行训练(8月份),并且希望在冬季开始生成推荐。一个大问题是,人们购买的服装种类随季节而变化:购买服装在几个月的时间里是一种非固定的现象。你试图建立的模型会随着时间而改变。在这种情况下,正确的做法是不断地根据最近的数据重新训练您的模型,或者在问题稳定的时间段收集数据。对于像买衣服这样的周期性问题,几年的数据就足以捕捉到季节的变化——但是记住把一年中的时间作为你的模型的输入!(备注:机器学习没法解决非平稳序列,其实也好理解,非平稳序列中,你的训练数据与用来预测的数据间的关联信息应该很少。当然例子中如果在同一个季节内部的预测,那就属于一个平稳序列了)
记住,机器学习只能用于记忆训练数据中的模式。你只能认出你以前看到的东西。通过对过去数据的机器学习来预测未来,是在假设未来会像过去一样。通常情况并非如此。

4.5.2选择成功的标准

要控制某事,你需要能够观察它。为了获得成功,你必须定义你所说的成功是什么——accuracy?Precision和recall?客户保留利率?您的成功度量标准将指导损失函数的选择:您的模型将如何优化。它应该直接与你的更高层次的目标相一致,比如你事业的成功。

对于均衡分类问题,每个类都是等概率的,receiver operating特性曲线(ROC AUC)下的accuracy和面积是常用的度量标准。对于分类不平衡的问题,可以使用precision和recall。对于排序问题或多标签分类,可以使用平均精度(mean average precision)。而且,需要定义自己的标准来衡量成功的情况并不少见。要了解机器学习成功指标的多样性以及它们如何与不同的问题领域相关联,浏览data science com网站是很有帮助的。

4.5.3 决策评估方法

一旦你知道你的目标是什么,你必须确定如何衡量你目前的进展。我们之前回顾了三个常见的评估方案:

  • 保持一个保留验证集的方法:当你有足够的数据
  • 执行K-fold cross-validation:当你只有少量的样本用于保留验证时,该选择是明知可靠的选择
  • 执行迭代K-fold验证:在可用数据很少的情况下执行高度精确的模型评估
    选一个吧。在大多数情况下,第一种方法都能很好地工作。

4.5.4 准备数据

一旦你知道你在训练什么,你在优化什么,以及如何评估你的方法,你就几乎准备好开始训练模型了。但首先,你应该以一种可以被输入机器学习模型的方式来格式化你的数据——在这里,我们假设一个深层神经网络:

  • 如前所述,数据应该格式化为张量。
  • 张量中的值通常应该按比例缩小到较小的值:例如在[-1,1]或[0,1]之间。
  • 如果不同的特征采用不同范围(异构数据)的值,则应该对数据进行归一化(normalized)
  • 可能需要一些特性工程,特别是对于小数据问题

一旦输入数据和目标数据的张量就绪,就可以开始训练模型。

4.5.5开发一个比基线更好的模型

您在此阶段的目标是实现统计能力(statistical power):即开发一个能够超过静默基线的小型模型。在MNIST digit-classification示例中,任何accuracy达到0.1以上的都可以称为具有统计能力;在IMDB示例中,它的accuracy要求大于0.5。

注意,实现统计能力并不总是可能的。如果在尝试了多个合理的体系结构之后,您不能打破一个随机的基线,那么您所问问题的答案可能不会出现在输入数据中。记住你做了两个假设:

  • 你假设你的输出可以预测给定输入。
  • 您假设可用的数据足以提供足够的信息来了解输入和输出之间的关系。

很可能这些假设是错误的,在这种情况下,你必须回到绘图板(就是说要重新开始设计)。
假设一切顺利,您需要做出三个关键选择来构建您的第一个工作模型:

  • 最后一层的激活函数(Last-layer activation):它建立了网络输出的有用约束。例如,IMDB分类示例在最后一层使用了sigmoid;回归示例没有在最后一层使用任何激活函数等等。
  • 损失函数——这应该与您试图解决的问题类型相匹配。例如,IMDB示例使用binary_crossentropy,回归示例使用mse等等。
  • 优化配置——您将使用什么优化器?它的学习率是多少?在大多数情况下,使用rmsprop,并配合它的默认学习率是安全的。
    关于损失函数的选择,请注意,并不是总是能够直接优化用于度量问题成功与否的度量标准。有时,没有简单的方法可以将度量转换为损失函数;毕竟,损失函数只需要给定一小批(mini-batch)数据就可以计算(理想情况下,损失函数只需要计算单个数据点),并且必须是可微的(否则,您不能使用反向传播来训练网络)。例如,广泛使用的分类度量ROC AUC不能直接进行优化。因此,在分类任务中,通常会优化ROC AUC的代理度量,比如交叉熵。一般来说,你可以希望交叉熵越低,ROC AUC就越高。
    表4.1为模型选择正确的最后一层激活和损失函数


4.5.6 扩展:开发一个过拟合的模型

一旦你得到了一个具有统计能力的模型,问题就变成了,你的模型是否足够强大?它是否有足够的层和参数来正确地建模手边的问题?例如,一个只有一个隐藏层和两个单元的网络在MNIST上具有统计能力,但不足以很好地解决这个问题。记住,机器学习中的广义张量是在优化和泛化之间;理想的模型是在不完全拟合和过度拟合之间的边界;在undercapacity和overcapacity之间。想弄清楚这条边界在哪里,你必须先穿过它。
为了弄清楚您需要多大的模型,您必须开发一个过拟合的模型。
这很简单:

  1. 添加层
  2. 使得层更大
  3. 在更多的epochs中训练
    始终监控训练损失和验证损失,以及您关心的任何指标的训练和验证值。当您看到模型在验证数据上的性能开始下降时,您已经实现了过拟合。
    下一个阶段是开始对模型进行正则化(regularizing)和调优,以尽可能接近既欠拟合也不过拟合的理想模型。

4.5.7模型正则化,调整超参数

这个步骤将花费最多的时间:您将反复修改模型,训练它,评估您的验证数据(此时不是测试数据),再次修改它,然后重复,直到模型达到最佳状态。以下是一些你应该尝试的事情:

  • 添加dropout
  • 尝试不同的架构:添加或移除层
  • 添加L1和L2正则化(regularization)
  • 尝试不同的超参数(例如每层单元的数量或优化器的学习率)来找到最佳配置。
  • 可选地,迭代特性工程:添加新特征,或删除那些看起来不提供信息的特征。

注意以下内容:每次使用验证过程的反馈来优化模型时,您都会将验证过程的信息泄漏到模型中。重复几次,这是无害的;但是在多次迭代中系统地完成,最终会导致您的模型过拟合验证过程(即使模型没有直接针对任何验证数据进行训练)。这使得评估过程不那么可靠。

一旦你建立了一个令人满意的模型配置,您可以在所有可用数据(训练和验证)上训练您的最终产品模型,并在测试集中最后一次评估它。如果测试集上的性能明显低于验证数据上的性能,这可能意味着您的验证过程根本不可靠,或者您在调优模型参数时开始度拟合验证数据。在这种情况下,您可能希望切换到更可靠的评估协议(例如迭代K-fold验证)。

章节总结

  • 定义手头的问题和要训练的数据。收集这些数据,或者在需要时用标签对其进行注释。
  • 选择在你的问题上如何衡量成功。您将在验证数据上监视哪些指标?
  • 确定评估方法:保留验证?K-fold验证?应该使用哪一部分数据进行验证?
  • 开发第一个比基本基线更好的模型:一个具有统计能力的模型。
  • 开发一个过拟合的模型。
  • 根据验证数据的性能,调整模型并调优其超参数。很多机器学习研究倾向于只关注这一步,但要把大局记在心里。

备注:

1,用sigmoid作为激活函数,为什么往往损失函数选用binary_crossentropy
参考地址:https://blog.csdn.net/wtq1993/article/details/51741471

2,softmax与categorical_crossentropy的关系,以及sigmoid与bianry_crossentropy的关系。
参考地址:https://www.zhihu.com/question/36307214

3,各大损失函数的定义:MSE,MAE,MAPE,hinge,squad_hinge,binary_crossentropy等
参考地址:https://www.cnblogs.com/laurdawn/p/5841192.html

推荐阅读更多精彩内容