BOOK 十一月 12, 2023

《机器学习实战》书摘

文章字数 31k 阅读约需 28 mins. 阅读次数

第一部分 机器学习的基础知识

第1章 机器学习概览

1.2 为什么使用机器学习

使用机器学习方法挖掘大量数据来帮助发现不太明显的规律。这称作数据挖掘。

1.4 机器学习系统的类型

根据训练期间接受的监督数量和监督类型,可以将机器学习系统分为以下四个主要类别:有监督学习、无监督学习、半监督学习和强化学习。

在机器学习里,属性是一种数据类型(例如“里程”),而特征取决于上下文,可能有多个含义,但是通常状况下,特征意味着一个属性加上其值(例如,“里程=15 000”)。尽管如此,许多人还是在使用属性和特征这两个名词时不做区分。

降维的目的是在不丢失太多信息的前提下简化数据。方法之一是将多个相关特征合并为一个。例如,汽车里程与其使用年限存在很大的相关性,所以降维算法会将它们合并成一个代表汽车磨损的特征。这个过程叫作特征提取。

1.5 机器学习的主要挑战

对复杂问题而言,数据比算法更重要

不过需要指出的是,中小型数据集依然非常普遍,获得额外的训练数据并不总是一件轻而易举或物美价廉的事情,所以暂时先不要抛弃算法。

如果样本集太小,将会出现采样噪声(即非代表性数据被选中);而即便是非常大的样本数据,如果采样方式欠妥,也同样可能导致非代表性数据集,这就是所谓的采样偏差。

在机器学习中,这称为过拟合,也就是指模型在训练数据上表现良好,但是泛化时却不尽如人意。

通过约束模型使其更简单,并降低过拟合的风险,这个过程称为正则化。

在学习时,应用正则化的程度可以通过一个超参数来控制。超参数是学习算法(不是模型)的参数。因此,它不受算法本身的影响。超参数必须在训练之前设置好,并且在训练期间保持不变。

欠拟合和过拟合正好相反。它的产生通常是因为对于底层的数据结构来说,你的模型太过简单。

机器学习系统有很多类型:有监督和无监督,批量的和在线的,基于实例的和基于模型的,等等。

在一个机器学习项目中,你从训练集中采集数据,然后将数据交给学习算法来计算。如果算法是基于模型的,它会调整一些参数来将模型适配于训练集(即对训练集本身做出很好的预测),然后算法就可以对新的场景做出合理的预测。如果算法是基于实例的,它会记住这些示例,并根据相似度度量将它们与所学的实例进行比较,从而泛化这些新实例。

如果训练集的数据太少或数据代表性不够,包含太多噪声或者被一些无关特征污染(垃圾进,垃圾出),那么系统将无法很好地工作。最后,你的模型既不能太简单(会导致欠拟合),也不能太复杂(会导致过拟合)。

第2章 端到端的机器学习项目

2.3 获取数据

标准差通常表示为σ(希腊字母sigma),它是方差的平方根,方差是均值平方差的平均值。当特征具有常见的钟形正态分布(也称为高斯分布)时,适用“68-95-99.7”规则:大约68%的值落在均值的1σ内,95%落在2σ以内,99.7%落在3σ之内。

2.5 机器学习算法的数据准备

这就是独热编码,因为只有一个属性为1(热),其他均为0(冷)。

如果类别属性具有大量可能的类别(例如,国家代码、专业、物种),那么独热编码会导致大量的输入特征,这可能会减慢训练并降低性能。如果发生这种情况,你可能想要用相关的数字特征代替类别输入。例如,你可以用与海洋的距离来替换ocean_proximity特征(类似地,可以用该国家的人口和人均GDP来代替国家代码)。或者,你可以用可学习的低维向量(称为嵌入)来替换每个类别。每个类别的表征可以在训练期间学习。这是表征学习的示例

最小-最大缩放(又叫作归一化)很简单:将值重新缩放使其最终范围归于0~1之间。实现方法是将值减去最小值并除以最大值和最小值的差。

标准化则完全不一样:首先减去平均值(所以标准化值的均值总是零),然后除以方差,从而使得结果的分布具备单位方差。不同于最小-最大缩放的是,标准化不将值绑定到特定范围,对某些算法而言,这可能是个问题(例如,神经网络期望的输入值范围通常是0~1)。但是标准化的方法受异常值的影响更小。

2.8 启动、监控和维护你的系统

验证码是一种确保用户不是机器人的测试,这些测试经常被用作标记训练数据的廉价方法。

第3章 分类

最常见的有监督学习任务包括回归任务(预测值)和分类任务(预测类)。

3.1 MNIST

这个数据集被广为使用,因此也被称作是机器学习领域的“Hello World”

3.3 性能测量

K-折交叉验证的意思是将训练集分解成K个折叠(在本例中,为3折),然后每次留其中1个折叠进行预测,剩余的折叠用来训练

这说明准确率通常无法成为分类器的首要性能指标,特别是当你处理有偏数据集时(即某些类比其他类更为频繁)。

评估分类器性能的更好方法是混淆矩阵,其总体思路就是统计A类别实例被分成为B类别的次数。例如,要想知道分类器将数字3和数字5混淆多少次,只需要通过混淆矩阵的第5行第3列来查看。

混淆矩阵能提供大量信息,但有时你可能希望指标更简洁一些。正类预测的准确率是一个有意思的指标,它也称为分类器的精度(见公式3-1):

公式3-1:精度

TP是真正类的数量,FP是假正类的数量。

精度通常与另一个指标一起使用,这个指标就是召回率,也称为灵敏度或者真正类率:它是分类器正确检测到的正类实例的比率(见公式3-2):

公式3-2:召回率

FN是假负类的数量。

如果你对混淆矩阵还是感到疑惑,图3-2或许可以帮助你理解。

图3-2:混淆矩阵显示了真负(左上)、假正(右上)、假负(左下)和真正(右下)的示例

因此我们可以很方便地将精度和召回率组合成一个单一的指标,称为F1分数。当你需要一个简单的方法来比较两种分类器时,这是个非常不错的指标。F1分数是精度和召回率的谐波平均值(见公式3-3)。正常的平均值平等对待所有的值,而谐波平均值会给予低值更高的权重。因此,只有当召回率和精度都很高时,分类器才能得到较高的F1分数。

公式3-3:F<sub>1</sub>

你不能同时增加精度又减少召回率,反之亦然。这称为精度/召回率权衡。

图3-3

在这个精度/召回率权衡中,图像按其分类器评分进行排名,而高于所选决策阈值的图像被认为是正的;阈值越高,召回率越低,但是(通常)精度越高

由于ROC曲线与精度/召回率(PR)曲线非常相似,因此你可能会问如何决定使用哪种曲线。有一个经验法则是,当正类非常少见或者你更关注假正类而不是假负类时,应该选择PR曲线,反之则是ROC曲线。

3.4 多类分类器

要创建一个系统将数字图片分为10类(从0到9),一种方法是训练10个二元分类器,每个数字一个(0-检测器、1-检测器、2-检测器,以此类推)。然后,当你需要对一张图片进行检测分类时,获取每个分类器的决策分数,哪个分类器给分最高,就将其分为哪个类。这称为一对剩余(OvR)策略,也称为一对多(one-versus-all)。

另一种方法是为每一对数字训练一个二元分类器:一个用于区分0和1,一个区分0和2,一个区分1和2,以此类推。这称为一对一(OvO)策略。如果存在N个类别,那么这需要训练N×(N-1)/2个分类器。

OvO的主要优点在于,每个分类器只需要用到部分训练集对其必须区分的两个类进行训练。

有些算法(例如支持向量机分类器)在数据规模扩大时表现糟糕。对于这类算法,OvO是一个优先的选择,因为在较小训练集上分别训练多个分类器比在大型数据集上训练少数分类器要快得多。但是对大多数二元分类器来说,OvR策略还是更好的选择。

3.6 多标签分类

假设分类器经过训练,已经可以识别出三张脸——爱丽丝、鲍勃和查理,那么当看到一张爱丽丝和查理的照片时,它应该输出[1,0,1](意思是“是爱丽丝,不是鲍勃,是查理”)这种输出多个二元标签的分类系统称为多标签分类系统。

3.7 多输出分类

分类和回归之间的界限有时很模糊,比如这个示例。可以说,预测像素强度更像是回归任务而不是分类。而多输出系统也不仅仅限于分类任务,可以让一个系统给每个实例输出多个标签,同时包括类标签和值标签。

第4章 训练模型

4.1 线性回归

训练模型就是设置模型参数直到模型最拟合训练集的过程。

回归模型最常见的性能指标是均方根误差(RMSE)

因此,在训练线性回归模型时,你需要找到最小化RMSE的θ值。在实践中,将均方误差(MSE)最小化比最小化RMSE更为简单,二者效果相同(因为使函数最小化的值,同样也使其平方根最小)

伪逆本身是使用被称为奇异值分解(Singular Value Decomposition,SVD)的标准矩阵分解技术来计算的,可以将训练集矩阵X分解为三个矩阵UΣVT的乘积

4.4 学习曲线

如果你的模型欠拟合训练数据,添加更多训练示例将无济于事。你需要使用更复杂的模型或提供更好的特征。

改善过拟合模型的一种方法是向其提供更多的训练数据,直到验证误差达到训练误差为止。

统计学和机器学习的重要理论成果是以下事实:模型的泛化误差可以表示为三个非常不同的误差之和:

  • 偏差
  • 方差
  • 不可避免的误差

增加模型的复杂度通常会显著提升模型的方差并减少偏差。反过来,降低模型的复杂度则会提升模型的偏差并降低方差。这就是为什么称其为权衡。

4.5 正则化线性模型

减少过拟合的一个好方法是对模型进行正则化(即约束模型):它拥有的自由度越少,则过拟合数据的难度就越大。正则化多项式模型的一种简单方法是减少多项式的次数。

Lasso回归的一个重要特点是它倾向于完全消除掉最不重要特征的权重(也就是将它们设置为零)。

通常来说,有正则化——哪怕很小,总比没有更可取一些。所以大多数情况下,你应该避免使用纯线性回归。岭回归是个不错的默认选择,但是如果你觉得实际用到的特征只有少数几个,那就应该更倾向于Lasso回归或是弹性网络,因为它们会将无用特征的权重降为零。一般而言,弹性网络优于Lasso回归,因为当特征数量超过训练实例数量,又或者是几个特征强相关时,Lasso回归的表现可能非常不稳定。

对于梯度下降这一类迭代学习的算法,还有一个与众不同的正则化方法,就是在验证误差达到最小值时停止训练,该方法叫作提前停止法。

通过早期停止法,一旦验证误差达到最小值就立刻停止训练。这是一个非常简单而有效的正则化技巧,所以Geoffrey Hinton称其为“美丽的免费午餐”。

4.6 逻辑回归

一些回归算法也可用于分类(反之亦然)。逻辑回归(Logistic回归,也称为Logit回归)被广泛用于估算一个实例属于某个特定类别的概率。(比如,这封电子邮件属于垃圾邮件的概率是多少?)如果预估概率超过50%,则模型预测该实例属于该类别(称为正类,标记为“1”),反之,则预测不是(称为负类,标记为“0”)。这样它就成了一个二元分类器。

逻辑回归模型经过推广,可以直接支持多个类别,而不需要训练并组合多个二元分类器。这就是Softmax回归,或者叫作多元逻辑回归。

原理很简单:给定一个实例x,Softmax回归模型首先计算出每个类k的分数sk(x),然后对这些分数应用softmax函数(也叫归一化指数),估算出每个类的概率。

Softmax回归分类器一次只能预测一个类(即它是多类,而不是多输出),因此它只能与互斥的类(例如不同类型的植物)一起使用。你无法使用它在一张照片中识别多个人。

第5章 支持向量机

支持向量机(Support Vector Machine,SVM)是一个功能强大并且全面的机器学习模型,它能够执行线性或非线性分类、回归,甚至是异常值检测任务。

SVM特别适用于中小型复杂数据集的分类。

5.3 SVM回归

SVM算法非常全面:它不仅支持线性和非线性分类,而且还支持线性和非线性回归。诀窍在于将目标反转一下:不再尝试拟合两个类之间可能的最宽街道的同时限制间隔违例,SVM回归要做的是让尽可能多的实例位于街道上,同时限制间隔违例(也就是不在街道上的实例)。

第6章 决策树

与SVM一样,决策树是通用的机器学习算法,可以执行分类和回归任务,甚至多输出任务。它们是功能强大的算法,能够拟合复杂的数据集。

决策树也是随机森林的基本组成部分,它们是当今最强大的机器学习算法之一。

6.2 做出预测

Scikit-Learn使用的是CART算法,该算法仅生成二叉树:非叶节点永远只有两个子节点(即问题答案仅有是或否)。但是,其他算法(比如ID3生成的决策树),其节点可以拥有两个以上的子节点。

6.4 CART训练算法

Scikit-Learn使用分类和回归树(Classification and Regression Tree,CART)算法来训练决策树(也称为“增长树”)。

该算法的工作原理是:首先使用单个特征k和阈值tk(例如,“花瓣长度”≤2.45cm”)将训练集分为两个子集。

一旦CART算法成功地将训练集分为两部分,它就会使用相同的逻辑将子集进行分割,然后再分割子集,以此类推。一旦达到最大深度(由超参数max_depth定义),或者找不到可减少不纯度的分割,它将停止递归。

如你所见,CART是一种贪婪算法:从顶层开始搜索最优分裂,然后每层重复这个过程。几层分裂之后,它并不会检视这个分裂的不纯度是否为可能的最低值。贪婪算法通常会产生一个相当不错的解,但是不能保证是最优解。

6.6 基尼不纯度或熵

熵的概念源于热力学,是一种分子混乱程度的度量:如果分子保持静止和良序,则熵接近于零。后来这个概念推广到各个领域,其中包括香农的信息理论,它衡量的是一条信息的平均信息内容[1]:如果所有的信息都相同,则熵为零。在机器学习中,它也经常被用作一种不纯度的测量方式:如果数据集中仅包含一个类别的实例,其熵为零。

6.7 正则化超参数

为避免过拟合,需要在训练过程中降低决策树的自由度。现在你应该知道,这个过程被称为正则化。

减小max_depth可使模型正则化,从而降低过拟合的风险。

第7章 集成学习和随机森林

如果你随机向几千个人询问一个复杂问题,然后汇总他们的回答。在许多情况下,你会发现,这个汇总的回答比专家的回答还要好,这被称为群体智慧。

同样,如果你聚合一组预测器(比如分类器或回归器)的预测,得到的预测结果也比最好的单个预测器要好。这样的一组预测器称为集成,所以这种技术也被称为集成学习,而一个集成学习算法则被称为集成方法。

例如,你可以训练一组决策树分类器,每一棵树都基于训练集不同的随机子集进行训练。做出预测时,你只需要获得所有树各自的预测,然后给出得票最多的类别作为预测结果。这样一组决策树的集成被称为随机森林,尽管很简单,但它是迄今可用的最强大的机器学习算法之一。

7.1 投票分类器

当预测器尽可能互相独立时,集成方法的效果最优。获得多种分类器的方法之一就是使用不同的算法进行训练。这会增加它们犯不同类型错误的机会,从而提升集成的准确率。

7.2 bagging和pasting

前面提到,获得不同种类分类器的方法之一是使用不同的训练算法。还有另一种方法是每个预测器使用的算法相同,但是在不同的训练集随机子集上进行训练。

采样时如果将样本放回,这种方法叫作bagging[1](bootstrap aggregating[2]的缩写,也叫自举汇聚法)。采样时样本不放回,这种方法则叫作pasting[3]。

7.5 提升法

提升法(boosting,最初被称为假设提升)是指可以将几个弱学习器结合成一个强学习器的任意集成方法。大多数提升法的总体思路是循环训练预测器,每一次都对其前序做出一些改正。可用的提升法有很多,但目前最流行的方法是AdaBoost[1](Adaptive Boosting的简称)和梯度提升。

这种依序学习技术有一个重要的缺陷就是无法并行(哪怕只是一部分),因为每个预测器只能在前一个预测器训练完成并评估之后才能开始训练。因此,在扩展方面,它的表现不如bagging和pasting方法。

第8章 降维

8.1 维度的诅咒

简而言之,训练集的维度越高,过拟合的风险就越大。

8.2 降维的主要方法

减少维度的两种主要方法:投影和流形学习。

许多降维算法通过对训练实例所在的流形进行建模来工作。这称为流形学习。它依赖于流形假设(也称为流形假说),该假设认为大多数现实世界的高维数据集都接近于低维流形。通常这是根据经验观察到的这种假设。

简而言之,在训练模型之前降低训练集的维度肯定可以加快训练速度,但这并不总是会导致更好或更简单的解决方案,它取决于数据集。

8.3 PCA

主成分分析(PCA)是迄今为止最流行的降维算法。首先,它识别最靠近数据的超平面,然后将数据投影到其上

比较原始数据集与其轴上的投影之间的均方距离,使这个均方距离最小的轴是最合理的选择,这也正是PCA背后的简单思想。

轴的数量与数据集维度数量相同。

第i个轴称为数据的第i个主要成分(PC)。

那么如何找到训练集的主要成分呢?幸运的是,有一种称为奇异值分解(SVD)的标准矩阵分解技术,该技术可以将训练集矩阵X分解为三个矩阵UΣVT的矩阵乘法,其中V包含定义所有主要成分的单位向量。

8.5 LLE

局部线性嵌入(LLE)是另一种强大的非线性降维(NLDR)技术[1]。它是一种流形学习技术,不像以前的算法那样依赖于投影。简而言之,LLE的工作原理是首先测量每个训练实例如何与其最近的邻居(c.n.)线性相关,然后寻找可以最好地保留这些局部关系的训练集的低维表示形式(稍后会详细介绍)。这种方法特别适合于展开扭曲的流形,尤其是在没有太多噪声的情况下。

8.6 其他降维技术

线性判别分析(LDA)
LDA是一种分类算法,但是在训练过程中,它会学习各类之间最有判别力的轴,然后可以使用这些轴来定义要在其上投影数据的超平面。这种方法的好处是投影将使类保持尽可能远的距离,因此LDA是在运行其他分类算法(例如SVM分类器)之前降低维度的好技术。

第9章 无监督学习技术

9.1 聚类

你可能需要植物学家告诉你什么是物种,但你当然不需要专家来识别外观相似的物体组。这称为聚类:识别相似实例并将其分配给相似实例的集群或组。

与分类不同,聚类是一项无监督任务。

聚类是无监督学习任务

K-Means通常是最快的聚类算法之一。

尝试选择k时,惯性不是一个好的性能指标,因为随着k的增加,惯性会不断降低。实际上,集群越多,每个实例将越接近其最接近的中心点,因此惯性将越低。

9.2 高斯混合模型

如果你将正常实例的重建误差与异常的重建误差进行比较,则后者通常会大得多。这是一种简单且通常非常有效的异常检测方法

第二部分 神经网络与深度学习

第10章 Keras人工神经网络简介

10.1 从生物神经元到人工神经元

事实证明,可以通过堆叠多个感知器来消除感知器的某些局限性。所得的ANN称为多层感知器(MLP)。

MLP由一层(直通)输入层、一层或多层TLU(称为隐藏层)和一个TLU的最后一层(称为输出层)组成

靠近输入层的层通常称为较低层,靠近输出层的层通常称为较高层。除输出层外的每一层都包含一个偏置神经元,并完全连接到下一层。

对于每个训练实例,反向传播算法首先进行预测(正向传递)并测量误差,然后反向经过每个层以测量来自每个连接的误差贡献(反向传递),最后调整连接权重以减少错误(梯度下降步骤)。

10.2 使用Keras实现MLP

如果你对模型的性能不满意,则应回头调整超参数。首先要检查的是学习率。如果这样做没有帮助,请尝试使用另一个优化器(并在更改任何超参数后始终重新调整学习率)。如果性能仍然不佳,则尝试调整模型超参数(例如层数、每层神经元数以及用于每个隐藏层的激活函数的类型)。你还可以尝试调整其他超参数,例如批处理大小

在测试集上获得比在验证集上略低的性能是很常见的,因为超参数是在验证集而不是测试集上进行调优的

切记不要调整测试集上的超参数,否则你对泛化误差的估计将过于乐观。

Keras使用HDF5格式保存模型的结构(包括每一层的超参数)和每一层的所有模型参数值(例如,连接权重和偏置)。

它还可以保存优化器(包括其超参数及其可能具有的任何状态)。

在这种情况下,你不仅应该在训练结束时保存模型,还应该在训练过程中定期保存检查点,以免在计算机崩溃时丢失所有内容。

10.3 微调神经网络超参数

Google还使用了一种进化方法,不仅用于搜索超参数,而且还为该问题寻找最佳神经网络架构。

实际上,进化算法已成功用于训练单个神经网络,从而取代了无处不在的梯度下降!

尽管取得了这些令人振奋的进步以及这些工具和服务,但是了解每个超参数的合理值,仍然有助于你构建快速原型并限制搜索空间。

对于许多问题,你可以从单个隐藏层开始并获得合理的结果。只要具有足够的神经元,只有一个隐藏层的MLP理论上就可以对最复杂的功能进行建模。但是对于复杂的问题,深层网络的参数效率要比浅层网络高得多:与浅层网络相比,深层网络可以使用更少的神经元对复杂的函数进行建模,从而使它们在相同数量的训练数据下可以获得更好的性能。

较低的隐藏层对低层结构(例如形状和方向不同的线段)建模,中间的隐藏层组合这些低层结构,对中间层结构(例如正方形、圆形)进行建模,而最高的隐藏层和输出层将这些中间结构组合起来,对高层结构(例如人脸)进行建模。

这种分层架构不仅可以帮助DNN更快地收敛到一个好的解,而且还可以提高DNN泛化到新数据集的能力。例如,如果你已经训练了一个模型来识别图片中的人脸,并且现在想训练一个新的神经网络来识别发型,则可以通过重用第一个网络的较低层来开始训练。你可以将它们初始化为第一个网络较低层的权重和偏置值,而不是随机初始化新神经网络前几层的权重和偏置值。这样,网络就不必从头开始学习大多数图片中出现的所有底层结构。只需学习更高层次的结构(例如发型)。这称为迁移学习。

总而言之,对于许多问题,你可以仅从一两个隐藏层开始,然后神经网络就可以正常工作。例如,仅使用一个具有几百个神经元的隐藏层,就可以轻松地在MNIST数据集上达到97%以上的准确率,而使用具有相同总数的神经元的两个隐藏层,可以在大致相同训练时间上轻松达到98%以上的精度。对于更复杂的问题,你可以增加隐藏层的数量,直到开始过拟合训练集为止。非常复杂的任务(例如图像分类或语音识别)通常需要具有数十层(甚至数百层,但不是全连接的网络)的网络,并且它们需要大量的训练数据。你几乎不必从头开始训练这样的网络:重用一部分类似任务的经过预训练的最新网络更为普遍。这样,训练就会快得多,所需的数据也要少得多

输入层和输出层中神经元的数量取决于任务所需的输入类型和输出类型。例如,MNIST任务需要28×28=784个输入神经元和10个输出神经元。

对于隐藏层,通常将它们调整大小以形成金字塔状,每一层的神经元越来越少,理由是许多低层特征可以合并成更少的高层特征。

就像层数一样,你可以尝试逐渐增加神经元的数量,直到网络开始过拟合为止。但是在实践中,选择一个比你实际需要的层和神经元更多的模型,然后使用提前停止和其他正则化技术来防止模型过拟合,通常更简单、更有效。

学习率可以说是最重要的超参数。一般而言,最佳学习率约为最大学习率的一半(即学习率大于算法发散的学习率

因此一种策略是尝试使用大批量处理,慢慢增加学习率,如果训练不稳定或最终表现令人失望,则尝试使用小批量处理。

通常,ReLU激活函数是所有隐藏层的良好的默认设置。

在大多数情况下,实际上不需要调整训练迭代次数,只需使用提前停止即可。

最佳学习率取决于其他超参数,尤其是批量大小,因此如果你修改了任何超参数,请确保也更新学习率。

第11章 训练深度神经网络

11.1 梯度消失与梯度爆炸问题

随着算法向下传播到较低层,梯度通常会越来越小。结果梯度下降更新使较低层的连接权重保持不变,训练不能收敛到一个良好的解。我们称其为梯度消失问题。

在某些情况下,可能会出现相反的情况:梯度可能会越来越大,各层需要更新很大的权重直到算法发散为止。这是梯度爆炸问题,它出现在递归神经网络中。更笼统地说,深度神经网络很受梯度不稳定的影响,不同的层可能以不同的速度学习。

那么,你应该对深度神经网络的隐藏层使用哪个激活函数呢?尽管你的目标会有所不同,但通常SELU>ELU>leaky ReLU(及其变体)>ReLU>tanh>logistic。如果网络的架构不能自归一化,那么ELU的性能可能会优于SELU(因为SELU在z=0时不平滑)。如果你非常关心运行时延迟,那么你可能更喜欢leaky ReLU。如果你不想调整其他超参数,则可以使用Keras使用的默认α值(例如,leaky ReLU为0.3)。如果你有空闲时间和计算能力,则可以使用交叉验证来评估其他激活函数,例如,如果网络过拟合,则为RReLU;如果你的训练集很大,则为PReLU。也就是说,由于ReLU是迄今为止最常用的激活函数,因此许多库和硬件加速器都提供了ReLU特定的优化。因此,如果你将速度放在首位,那么ReLU可能仍然是最佳选择。

批量归一化的作用就像正则化一样,减少了对其他正则化技术的需求。

但是,批量归一化确实增加了模型的复杂性

由于每一层都需要额外的计算,因此神经网络的预测速度较慢。幸运的是,经常可以在训练后将BN层与上一层融合,从而避免了运行时的损失。

这是通过更新前一层的权重和偏置来完成的,以便它直接产生适当缩放和偏移的输出。

BatchNormalization已成为深度神经网络中最常用的层之一,以至于在图表中通常将其省略,因为假定在每层之后都添加了BN。

缓解梯度爆炸问题的另一种流行技术是在反向传播期间裁剪梯度,使它们永远不会超过某个阈值。这称为梯度裁剪[12]。这种技术最常用于循环神经网络,因为在RNN中难以使用批量归一化,

11.2 重用预训练层

一般而言,当输入具有类似的低级特征时,迁移学习最有效。

任务越相似,可重用的层越多(从较低的层开始)。对于非常相似的任务,请尝试保留所有的隐藏层和只是替换掉输出层。

首先尝试冻结所有可重复使用的层(使其权重不可训练,这样梯度下降就不会对其进行修改),训练模型并查看其表现。然后尝试解冻上部隐藏层中的一两层,使反向传播可以对其进行调整,再查看性能是否有所提高。你拥有的训练数据越多,可以解冻的层就越多。当解冻重用层时,降低学习率也很有用,可以避免破坏其已经调整好的权重。

但是由于新的输出层是随机初始化的,它会产生较大的错误(至少在前几个轮次内),因此将存在较大的错误梯度,这可能会破坏重用的权重。为了避免这种情况,一种方法是在前几个轮次时冻结重用的层,给新层一些时间来学习合理的权重。为此,请将每一层的可训练属性设置为False并编译模型

冻结或解冻层后,你必须总是要编译模型。

解冻重用层之后,降低学习率通常是个好主意,可以再次避免损坏重用的权重

事实证明,迁移学习在小型密集型网络中不能很好地工作,大概是因为小型网络学习的模式很少,密集网络学习的是非常特定的模式,这在其他任务中不是很有用。迁移学习最适合使用深度卷积神经网络,该神经网络倾向于学习更为通用的特征检测器(尤其是在较低层)。

自我监督学习是指你从数据本身自动生成标签,然后使用有监督学习技术在所得到的“标签”数据集上训练模型。由于此方法不需要任何人工标记,因此最好将其分类为无监督学习的一种形式。

11.3 更快的优化器

到目前为止,我们已经知道了四种加快训练速度(并获得了更好的解决方法)的方法:对连接权重应用一个良好的初始化策略,使用良好的激活函数,使用批量归一化,以及重用预训练网络的某些部分(可能建立在辅助任务上或使用无监督学习)。

动量优化的一个缺点是它增加了另一个超参数来调整。但是,动量值为0.9通常在实践中效果很好,几乎总是比常规的“梯度下降”更快。

对于简单的二次问题,AdaGrad经常表现良好,但是在训练神经网络时,它往往停止得太早。学习率被按比例缩小,以至于算法在最终达到全局最优解之前完全停止了。

Adam[5]代表自适应矩估计,结合了动量优化和RMSProp的思想:就像动量优化一样,它跟踪过去梯度的指数衰减平均值。就像RMSProp一样,它跟踪过去平方梯度的指数衰减平均值

所有的优化算法都产生了密集模型,这意味着大多数参数都是非零的。如果你在运行时需要一个非常快的模型,或者需要占用更少的内存,那么你可能更喜欢使用一个稀疏模型。

实现这一点的一个简单方法是像往常一样训练模型,然后去掉很小的权重(将它们设置为零)。注意,这通常不会导致非常稀疏的模型,而且可能会降低模型的性能。一个更好的选择是在训练时使用强1正规化

因为它会迫使优化器产生尽可能多的为零的权重

表11-2比较了我们目前讨论的所有优化器(*不好,**平均,***好)。

表11-2:优化器比较

11.4 通过正则化避免过拟合

深度神经网络通常具有数万个参数,有时甚至有数百万个。这给它们带来了难以置信的自由度,意味着它们可以拟合各种各样的复杂数据集。但是,这种巨大的灵活性也使网络易于过拟合训练集。我们需要正则化。

最好的正则化技术之一:提前停止。

即使“批量归一化”被设计用来解决不稳定的梯度问题,它的作用也像一个很好的正则化。

其他流行的神经网络正则化技术:l1和l2正则化、dropout和最大范数正则化。

对于深度神经网络,dropout是最受欢迎的正则化技术之一。

只需要增加dropout,即使最先进的神经网络也能获得1~2%的准确率提升。

在实践中,你通常只可以对第一至第三层(不包括输出层)中的神经元应用dropout。

dropout确实会明显减慢收敛速度,但是如果正确微调,通常会导致更好的模型。因此,花额外的时间和精力是值得的。

简而言之,MC Dropout是一种出色的技术,可以提升dropout模型并提供更好的不确定性估计。当然,由于这只是训练期间的常规dropout,所以它也像正则化函数。

对于神经网络而言,另一种流行的正则化技术称为最大范数正则化

11.5 总结和实用指南

你如果可以找到解决类似问题的神经网络,那应该尝试重用部分神经网络;如果有大量未标记的数据,则应使用无监督预训练;如果有相似任务的大量标记的数据,则应该在辅助任务上使用预训练。

如果你需要低延迟的模型(执行闪电般快速预测的模型),则可能需要使用更少的层,将批量归一化层融合到先前的层中,并使用更快的激活函数,例如leaky ReLU或仅仅使用ReLU。拥有稀疏模型也将有所帮助。最后,你可能想把浮点精度从32位降低到16位甚至8位

如果你要构建风险敏感的应用,或者推理延迟在你的应用中不是很重要,则可以使用MC Dropout来提高性能并获得更可靠的概率估计以及不确定性估计。

第12章 使用TensorFlow自定义模型和训练

12.1 TensorFlow快速浏览

越来越多的ML论文连同实现一起发布,有时甚至带有预训练的模型。查看https://paperswithcode.com/,可以轻松找到它们。

12.2 像NumPy一样使用TensorFlow

TensorFlow的API一切都围绕张量,张量从一个操作流向另一个操作,因此命名为TensorFlow。张量非常类似NumPy的ndarray,它通常是一个多维度组,但它也可以保存标量(简单值,例如42)。

请注意,默认情况下NumPy使用64位精度,而TensorFlow使用32位精度。这是因为32位精度通常对于神经网络来说绰绰有余,而且运行速度更快且使用的RAM更少。因此,当你从NumPy数组创建张量时,请确保设置dtype=tf.float32。

类型转换会严重影响性能,并且自动完成转换很容易被忽视。为了避免这种情况,TensorFlow不会自动执行任何类型转换:如果你对不兼容类型的张量执行操作,会引发异常。

当然当你确实需要转换类型时,可以使用tf.cast()

第13章 使用TensorFlow加载和预处理数据

13.3 预处理输入特征

词汇表外(outof-vocabulary,oov)桶

为什么要使用oov桶?如果类别数量很大(例如邮政编码、城市、单词、产品或用户)并且数据集也很大,或者它们一直在变化,那么得到类别的完整列表可能不是很方便。一种解决方法是基于数据样本(而不是整个训练集)定义词汇表,并为不在数据样本中的其他类别添加一些桶。你希望在训练期间找到的类别越多,就应该使用越多的oov桶。如果没有足够的oov桶,就会发生冲突:不同的类别最终会出现在同一个桶中,因此神经网络将无法区分它们(至少不是基于这个特征)。

根据经验,如果类别数少于10,则通常采用独热编码方式。(但数字可能会有所不同!)如果类别数大于50(通常这种情况需要使用哈希桶),通常最好使用嵌入。在10到50个类别中,你可能需要尝试两种方法,然后看看哪种最适合你。

训练使嵌入成为类别的有用表征。这称为表征学习

确保深度学习算法的公平性是重要且活跃的研究课题。

第14章 使用卷积神经网络的深度计算机视觉

14.2 卷积层

CNN的最重要的构建块是卷积层[1]:第一卷积层的神经元不会连接到输入图像中的每个像素(如前文所述),而只与其接受野内的像素相连接(见图14-2)。反过来,第二卷积层的每个神经元仅连接到位于第一层中的一个小矩阵内的神经元。这种架构允许网络集中在第一个隐藏层的低阶特征中,然后在下一个隐藏层中将它们组装成比较高阶的特征,等等。CNN在图像识别方面效果很好的原因之一是这种层次结构在现实世界的图像中很常见。

在CNN中,每一层都以2D表示,这使得将神经元与其相应的输入进行匹配变得更加容易。

特征图中所有的神经元共享相同的参数,大大减少了模型中的参数数量。CNN一旦学会了在一个位置识别模式,就可以在其他任何位置识别模式。相反,常规DNN学会了在一个位置识别模式,它就只能在那个特定位置识别它。

在推理期间(即对新实例进行预测时),只要计算了下一层,就可以释放由前一层占用的RAM,因此你只需要两个连续层所需的RAM。但是在训练过程中,需要保留正向传播过程中计算出的所有内容以供反向传播,因此所需的RAM量至少是所有层所需的RAM总量。

14.3 池化层

平均池化层曾经很受欢迎,但是现在人们通常都使用最大池化层,因为它们通常表现更好。这似乎令人惊讶,因为计算均值通常比计算最大值损失更少的信息。但是另一方面,最大池化仅保留最强的特征,将所有无意义的特征都丢弃掉,因此下一层可获得更清晰的信号来使用。而且与平均池化相比,最大池化提供了更强的变换不变性,并且所需计算量略少于平均池化。

14.4 CNN架构

与常规卷积层相比,可分离卷积层使用更少的参数、更少的内存和更少的计算,而且通常它们表现更好,因此,你应默认使用它们(除了在通道较少的层之后)。

14.8 分类和定位

评估模型对边界框的预测能力不是一个很好的指标。最常用的度量指标是“交并比”(Intersection over Union,IoU):预测边界框和目标边界框之间的重叠面积除以它们的联合面积(见图14-23)。

图14-23:边界框的交并比(IoU)度量

第15章 使用RNN和CNN处理序列

15.1 循环神经元和层

循环神经网络看起来非常像前馈神经网络,除了它还具有指向反向的连接。

由于在时间步长t时递归神经元的输出是先前时间步长中所有输入的函数,因此你可以说它具有记忆的形式。在时间步长上保留某些状态的神经网络的一部分称为记忆单元(或简称为单元)。

许多研究人员更喜欢在RNN中使用双曲正切(tanh)激活函数,而不是ReLU激活函数。

15.3 预测时间序列

典型的任务是预测未来值,这称为预测。另一个常见任务是填补空白:预测过去的缺失值。这称为插补。

15.4 处理长序列

简而言之,LSTM单元可以学会识别重要的输入(这是输入门的作用),将其存储在长期状态中,只要需要就保留它(即遗忘门的作用),并在需要时将其提取出来。这就解释了为什么这些单元在识别时间序列、长文本、录音等的长期模式方面取得了惊人的成功。

门控循环单元(Gated Recurrent Unit,GRU)是LSTM单元的简化版,它的性能似乎也不错

LSTM和GRU单元是RNN成功的主要原因之一。尽管它们可以处理比简单RNN更长的序列,但它们的短期记忆仍然非常有限,而且很难学习100个或更多时间步长序列中的长期模式,例如音频样本、长时间序列或长句子。解决这个问题的一种方法是缩短输入序列,例如使用一维卷积层。

第16章 使用RNN和注意力机制进行自然语言处理

16.3 神经机器翻译的编码器-解码器网络

在同一输入上运行两个循环层,一层从左至右读取单词,另一层从右至左读取单词。然后,只需在每个时间步长上简单地组合它们的输出,通常将它们合并即可。这称为双向循环层

集束搜索:它跟踪k个最有希望的句子(例如前三个)的一个短列表,在每个解码器步长中尝试用一个单词它们扩展,仅仅保留k个最有可能的句子。参数k称为集束宽度。

第17章 使用自动编码器和GAN的表征学习和生成学习

生成式对抗网络(GAN)

GAN由两个神经网络组成:一个试图生成看起来与训练数据相似数据的生成器,以及一个试图从虚假数据中分辨出真实数据的判别器。这种架构在深度学习中非常创新的,因为生成器和判别器在训练期间相互竞争:生成器就像是一个试图做假币的犯罪分子,而判别器就像是一个警察,试图从真币中分辨出假币。对抗训练(训练竞争性神经网络)被广泛认为是近年来最重要的想法之一。

17.2 使用不完整的线性自动编码器执行PCA

一个模型可以用作另一个模型中的一个层

第18章 强化学习

18.7 马尔可夫决策过程

无记忆的随机过程,称为马尔可夫链。这个过程具有固定数量的状态,并且在每个步骤中它都从一种状态随机演变到另一种状态。它从状态s演变为状态s’的概率是固定的,并且仅取决于(s,s’),而不取决于过去的状态(这就是为什么我们说系统没有记忆)。

18.8 时序差分学习

时序差分学习(TD学习)算法与值迭代算法非常相似,但进行了调整,以考虑到智能体仅仅对MDP有部分了解的事实。

TD学习与随机梯度下降有很多相似之处,特别是它一次处理一个样本。而且,就像随机梯度下降一样,它只有在逐渐降低学习率的情况下才能真正收敛(否则它将不断在最佳Q值附近震荡)。

第19章 大规模训练和部署TensorFlow模型

19.2 将模型部署到移动端或嵌入式设备

图19-8:使用对称量化,从32位浮点数到8位整数

在运行时,量化权重在使用之前会转换回浮点数(这些恢复的浮点数与原始浮点数并不完全相同,但相差不大,因此精度损失通常是可以接受的)。

为避免始终对它们进行重新计算,将对被恢复的浮点进行缓存,因此不会减少RAM使用量。而且计算速度也不会降低。

19.3 使用GPU加速计算86

Compute Unified Device Architecture(CUDA)库,该库使开发人员可以利用支持CUDA的GPU来进行各种计算(不仅是图形加速),还有CUDA深度神经网络库(cuDNN),这是GPU加速的DNN算子库。cuDNN提供了常见DNN计算的优化实现,例如激活层、归一化、前向和后向卷积以及池化

TensorFlow使用CUDA和cuDNN来控制GPU卡并加速计算(见图19-10)。

图19-10:TensorFlow使用CUDA和cuDNN来控制GPU和加速DNN

19.4 跨多个设备的训练模型

在多个设备上训练单个模型的主要方法有两种:模型并行(模型在设备之间划分)和数据并行(模型在每个设备上复制,每个副本在数据子集上训练)。

跨设备通信速度很慢(尤其是当设备位于不同的计算机上时),这很可能会完全抵消并行计算的好处。

深度循环神经网络(见第15章)可以在多个GPU之间更有效地拆分。

模型并行化可以加快运行或训练某些类型的神经网络的速度,但不是全部,需要特别注意和调整,例如确保需要通信最多的设备在同一台机器上运行

0%