品读《吴恩达机器学习》第二部分
品读《吴恩达机器学习》第二部分

品读《吴恩达机器学习》第二部分

在《吴恩达机器学习》第二部分:高级学习算法(Advanced Learning Algorithms),我们将会学习:

  • 神经网络的推理或预测
  • 训练神经网络
  • 构建机器学习系统的一些实用技巧
  • 决策树

初识神经网络

神经网络就源于模拟人类大脑,但其需要的计算量很大。随着计算机硬件性能的提高,神经网络逐渐从衰落变为流行,如今已广泛地被应用在各行各业中。

需求预测

我们来举例说明神经网络是如何预测的。

我们使用符号 a 代表激活,它是一个来自神经科学的术语,指的是一个神经元向下游其他神经元发送的输出。一层(layer)是指一组神经元,它们接受相同或相似的特征并产生输出。神经网络有四个输入,然后中间层接受输入并计算出三个激活值,输出层接受三个激活值,并计算出一个结果,也就是神经网络的输出。

在上述神经网络中,对于每一个神经元,我们必须手动选择它接受上一层哪些输出,但当我们构建一个大型神经网络时,这需要大量的工作。所以在实践中的方式是,任一神经元可以接受上一层的所有特征,只需通过合适地设置参数来弄清楚与该神经元有关的特征子集是什么即可。

中间层也叫隐藏层,这是由于在给定训练集时,我们可以同时观察 x 和 y,也就是我们知道什么是正确的输入,什么是正确的输出,但是我们不知道中间层输出的激活,可以看作隐藏起来了,所以被称为隐藏层。隐藏层可以有多个。

另一种关于神经网络的思考方式是,我们把左半边遮住,只看右半边时,这是一个逻辑回归模型,但使用的特征不是原始特征,而是选择了一些更好的特征进行更准确的预测。所以神经网络所做的是不需要你手动设计特征,它可以自己学习选择合适的特征,这也是使神经网络成为当今世界上最强大的学习算法之一的的原因之一。当我们自己构建神经网络时,其中一个需要做的决定就是设计多少隐藏层和每层多少个神经元,这是神经网络架构的问题。

神经网络模型

以一个简单的神经网络模型为例:

隐藏层中可以有多个特征,并且需要通过逻辑回归模型进行计算。

为了区分不同的层,我们使用上标方括号索引到不同的层。上标表示第几个隐藏层,下标表示第几个神经元。
神经网络的工作原理:每一层都输入一个数字向量,对其应用一些逻辑回归单元,计算另一个数字向量,然后在层到层之间传输,直至得到最终的输出层结果。

更复杂的神经网络

上标 [l] 表示第几个隐藏层,下标 j 表示第几个神经元。注意 g(z) 中,a 是 l-1 ,因为这里的 a 代表上一层的输出。

使用前向传播作预测

以手写数字识别为例,说明神经网络是如何进行预测的:

计算序列首先取 x,然后计算 a1,然后计算 a2,然后计算 a3,这也是神经网络的输出。这个计算是从左到右进行的,所以这个算法被称为前向传播。这种最初有很多的隐藏单元,越靠近输出层,隐藏单元数量越少的神经网络架构是一个非常典型的网络架构。

TensorFlow实现

以手写数字识别为例子,说明TensorFlow中如何实现神经网络:

Dense 是全连接层,是网络层的一种类型。
Sequential 函数用于将神经网络的三层顺序串在一起。
compile 函数用于编译模型,需指定损失函数。
fit 函数用于使模型拟合给定的数据集,需指定训练集使用几次(epoch)。

注:TensorFlow看懂即可,今后学习多以Pytorch为主

前向传播的一般实现

dense 函数的作用是,输入前一层的激活,给定当前层的参数,他返回下一层的激活。
sequential 函数的作用是,将几个全连接层组合在一起,以在神经网络中实现前向传播。
我们通常用大写字母表示矩阵,小写字母表示向量和标量。

神经网络如何高效实现

神经网络可以矢量化,可以使用矩阵乘法非常有效地实现。

训练神经网络

仍以手写数字识别为例,训练步骤:

  1. 指定如何在给定输入特征 x 和参数 w 和 b 的情况下计算输出(定义模型)。
  2. 指定损失函数和代价函数。
  3. 使用算法特别是指梯度下降算法以最小化代价函数。

这段代码指定了神经网络的整个架构,其中,每一层的参数是随机初始化的。

这里使用的是和逻辑回归一样的损失函数,也叫二元交叉熵损失函数。
代价函数是神经网络中所有参数的函数,所以 W 和 B 都用大写表示。

如果使用梯度下降来更新参数,那么我们需要对每一层每一单元更新参数,关键就是计算偏导项,而 TensorFlow 是使用一种称为反向传播的算法来计算这些偏导项(在 fit 函数中已经实现)。

sigmoid激活函数的替换方案

激活函数的主要作用是改变之前数据的线性关系,如果网络中全部是线性变换,则多层网络可以通过矩阵变换,直接转换成一层神经网络。所以激活函数的存在,使得神经网络的“多层”有了实际的意义,使网络更加强大,增加网络的能力,使它可以学习复杂的事物,复杂的数据,以及表示输入输出之间非线性的复杂的任意函数映射。
通俗来说,激活函数的工作是:从前一层接收数据,通过激活函数的运转,输出本层的计算出的新数据。

上图中列出了三种最常用的激活函数:

  • 线性激活函数(Linear activation function)
  • S型激活函数(Sigmoid)
  • ReLU函数(线性整流函数:Rectification Linear Unit)

如何选择激活函数

输出层的激活函数

为输出层选择激活函数时,取决于预测的标签 y 是什么:
对于二分类问题,选择 sigmoid 激活函数
对于 y 可以取正值和负值的回归问题,选择线性激活函数
如果 y 只能取非负值的回归问题,选择 ReLu 激活函数

隐藏层的激活函数

事实证明,ReLU 激活函数是迄今为止神经网络中最常见的选择。
为何使用 ReLU 替代 Sigmoid 呢?
首先,ReLU 的计算速度更快一些;
第二,ReLU 函数仅在图像中的一部分变平,而 sigmoid 函数它在两个地方变平,如果我们使用梯度下降来训练神经网络,当函数有很多平坦的地方时,梯度下降很慢。
虽然梯度下降优化代价函数 J,而不是激活函数,但激活函数是计算的一部分,如果使用 sigmiod,导致在代价函数中的很多地方也是平坦的,梯度很小,减慢了学习的速度。

为什么模型需要激活函数

如果所有的激活函数都是用线性激活函数,那么神经网络将变得与线性回归模型没什么不同。
简单举个例子证明一下:

也就是说,线性函数的线性函数仍然是线性函数。

再来看一个例子,在隐藏层都使用线性激活函数,输出层使用 Sigmoid 激活函数的情况下:

隐藏层都使用线性激活函数,输出层如果是线性激活函数,那么神经网络等同于线性回归模型;输出层如果是 sigmoid 激活函数,那么神经网络等同于逻辑回归模型。
综上,隐藏层不要使用线性激活函数

多分类

多分类问题:有两个以上可能的输出标签的分类问题。

Softmax

Softmax回归算法是逻辑回归的泛化,这是一种针对多分类环境的二元分类算法。

Softmax回归公式

当 n = 2 时,softmax 回归会简化为逻辑回归,因为此时输出标签 y 仅有两个。

Softmax回归代价函数

当 y = j 时,aj(模型预测 y-hat = j 的概率)越接近 1,损失越小(观察图右下函数图像)。

Softmax与神经网络

softmax 激活函数与其他激活函数不同的是:
其他激活函数中,aj 只是 zj 的函数;而在 softmax 激活函数中,aj 是 z1 到 zn 的函数。

具体实现:

这里使用的损失函数是 SparseCategoricalCrossentropy。
categorical 是指我们这是个分类问题(比如手写数字分类问题)。
sparse 是指只能取有限类别中的一种(比如数字只能是 4 或 7,而不可能是 4 和 7)。
上述版本是可工作的,但不是最好的,所以不要使用这个版本

Softmax的改进实现

舍入误差

因为计算机只有有限的内存来存储每个数字,在这里称为浮点数,当我们使用不同方式计算 2.0 / 10000 时,结果会有较多较少的舍入误差。
减少数值舍入误差,使其在 TensorFlow 中进行更精确的计算。

逻辑回归的改进实现

当我们不计算 a 作为中间项时,而是使用展开的表达式,那么 TensorFlow 可以重新排列这个表达式中的项,并想出一种在数值上更准确的方法来计算这个损失函数。
改进代码的作用是:将输出层的激活函数改成线性激活函数,然后相当于是使用了展开的损失函数,将 z 直接代入损失函数,TensorFlow 可以重新排列这个表达式中的项,使计算变得更准确。
这段代码也有缺点:代码变得不清晰,难以阅读。
对于逻辑回归模型来说,改进前后都可以正常工作,但对于 softmax 回归来说,舍入误差较大,需要使用改进后的版本。

softmax 回归的改进实现

此时,神经网络输出的不再是概率 a1…a10而是z1…z10
所以在预测的时候要使用 Sigmoid函数进行预测:

含有多个输出的分类

多分类问题:有两个以上可能的输出标签的分类问题,比如手写数字识别,预测结果是一个类别。
多标签分类问题:每个输入关联了多个标签,比如下图所示,预测结果是一个向量。

如何使用神经网络解决多标签分类问题?
可以将其视为三个独立的问题,但是不太合理。
也可以训练一个网络同时检测三种物体。

输出层每个单元都使用 sigmoid 激活函数,分别对应是否有小汽车,是否有公共汽车,是否有行人3个二分类问题。

高级优化方法

Adam算法

有一种称为 Adam 的算法可以做到自己调整学习率,如果它发现学习率太小(只是朝着相似的方向迈出微小的步伐),则它会让学习率增大;如果它发现学习率太小(来回震荡着迈步),则它会让学习率减小。

Adam 对每个参数都使用了不同的学习率。

具体实现:

使用 Adam 算法时需要指定初始学习率。

其他的神经网络层类型

全连接层(dense layer):每个神经元都会以前一层的所有激活作为自己的输入。
卷积层(convolutional layer):每个神经元只以前一层的部分激活作为自己的输入。

为什么不让神经元看到前一层的所有像素,而只看一部分像素?
首先,加快计算速度
其次,需要更少的训练数据或者不太容易出现过拟合
举例说明卷积神经网络是如何工作的——通过心电图查看病人是否患有心脏病。

第一个卷积层每一个神经元查看 20 个输入,第二个卷积层每一个神经元查看 5 个输入,第三层是 sigmoid 层。
事实证明,对于卷积层,我们有很多种架构,比如单个神经元应该查看到输入窗口有多大,以及每层应该有多少个神经元。
通过有效地选择这些架构参数,我们可以构建比使用全连接层更有效的新版本神经网络。

机器学习实践建议

本节将分享一些关于如何在机器学习项目中决定下一步该做什么的实用建议。
有效构建机器学习算法的关键在于找到一种对于在哪里投入时间做出正确选择的办法。

模型评估

我们将数据集分成训练集和测试集两个子集,在训练集上训练模型并拟合参数,在测试集上测试性能。

对于线性回归:

首先是通过最小化\(J(\vec w,b)\)来拟合参数 w 和 b。
\(J_{train}\)衡量模型在训练集上的性能;\(J_{test}\)衡量模型在测试集上的性能。
对于分类问题:

除了和回归问题类似的计算方法,还有另一种方法:

计算在训练集或者测试集上分类错误的比例。
简单来说,上图中\(J_{train}\)是训练集中 0 被归为 1 ,和 1 被归为 0 的那部分,\(J_{test}\)是测试集中被错误归类的部分。

模型选择和训练&交叉验证&测试集

一旦模型参数 w 和 b 已经适合训练集,\(J_{train}\)要比实际的泛化误差(不在训练集中的新示例的平均误差)要低,而\(J_{test}\)能较好地(相对于\(J_{train}\))估计泛化误差。

直观上,我们在选择模型的时候会选择一个 Jtest 最小的模型,当我们想估计这个模型表现如何时,可以直接看 Jtest
但这个过程有一个缺陷就是 Jtest 很可能是泛化误差的乐观估计(低于实际的泛化误差)。
可以类比到使用训练集训练 w 和 b 会使 Jtrain 成为泛化误差的一个过度乐观估计,使用测试集训练 d (多项式次数) 也会使 Jtest 成为泛化误差的乐观估计。
结合网上资料后,通俗理解:使用训练集我们想选择的就是适当的 w 和 b 使得 Jtrain 最小,对于未知数据集来讲,这是一个最乐观的估计,使用测试集选择模型时,我们选择的也是合适的 d 使得 Jtest 最小,此时再用 Jtest 作为泛化误差,肯定也是偏小的。

为了弥补这种缺陷,我们改变了数据集的划分

我们将数据集分为训练集交叉验证集(cross validation)、测试集
交叉验证集用于检查不同模型的有效性和真实性,也叫做验证集、开发集。

为什么要采取这种数据集的划分方式呢?
我们通过训练集 Jtrain 来拟合参数 w 和 b ,通过交叉验证集 Jcv 来选择多项式次数 d ,从而得到合适的回归模型。
但是此时,我们没有将任何参数(w、b和d)放到测试集中,所以所选参数对应的 Jtest 将是这个模型泛化误差的公平估计。
所以我们选择合适模型的方法是:使用训练集拟合参数,使用交叉验证集选择模型,使用测试集估计泛化误差。

方差与偏差

诊断方差和偏差

我们在之前学习的,欠拟合也叫高偏差;过拟合也叫高方差。

不通过作图,我们也可以通过计算 Jtrain 和 Jcv 来判断模型是否具有高偏差或者高方差。

随着多项式次数(横轴)的增加,显示发生欠拟合、然后是恰好拟合、最后发生过拟合,Jtrain 越来越低,Jcv 先降低后升高。

综上,诊断学习算法的偏差和方差的方法:

  • 如果 Jtrain 很高,则具有高偏差。
  • 如果 Jcv 远高于 Jtrain,则具有高方差。
  • 如果 Jtrain 很高,并且 Jcv 远高于 Jtrain,则同时具有高偏差和高方差。

对于部分数据发生过拟合,对于部分数据发生欠拟合时,会出现同时具有高偏差和高方差的情况。

正则化&偏差方差

λ 很大时,代价函数会过于注重减小参数的值,极端情况参数(w)为 0,发生欠拟合;
λ 很小时,代价函数会过于注重拟合数据,极端情况 λ 为 0,发生过拟合。

和选择多项式次数 d 时的方法一样,计算不同 λ 值对应的 Jcv 值,选择最小的 Jcv 对应的 λ 值,然后以此时的 Jtest 作为泛化误差。

以 λ 为自变量和以 d 为自变量时对应的 Jtrain 和 Jcv 图像是非严格镜像的,因为 λ 较小时和 d 较大时,对应的是过拟合,相反,λ 较大和 d 较小时对应的是欠拟合。

制定一个用于性能评估的基准(baseline)

以语音识别为例看看学习算法是否具有高偏差或高方差,此时训练误差意味着音频中没有被正确识别的音频百分比。

Jtrain = 10.8% 意味着训练集中有 89.2% 的音频被正确识别。
直观上看来,训练集误差(training error)和交叉验证集误差(cross validation error)都很高,但事实上我们还有另一项有用的标准,人类表现性能(human level performance),即人类识别的正确率,训练误差仅比人类识别误差高 0.2%,而交叉验证误差比训练误差高 4.0%,因此可以看出此模型具有高方差问题而不是高偏差问题。

当判断训练误差是否高时,通常建立一个性能评估基准。

前两个数据之间的差距决定了是否存在高偏差问题,后两个数据之间的差距决定了是否存在高方差问题。

学习曲线

学习曲线是一种学习算法性能和实验数量之间的函数。

通常情况下,随着数据量的增多,交叉验证误差随之下降,而训练误差会上升,因为数据越多,模型很难完美的契合每个数据,但是训练误差会低于交叉验证误差,因为我们拟合数据的时候是希望训练误差尽量小。

如下图所示,当学习算法具有高偏差时,增加数据量不会降低训练误差。

如下图所示,当学习算法具有高方差时,增加数据量会有所帮助。

综上,当我们发现模型性能没有达到我们的预期时,不要盲目地增加数据量,要看看问题出现在了哪里,才能有效地解决。

决定下一步做什么

如果发现你的算法具有很高的方差,解决方法是:

  • 获得更多的训练数据。
  • 简化模型(获得更小的特征集或者增加正则化参数 λ)

如果发现你的算法具有很高的偏差,解决办法是:

  • 使模型更强大(获得更大的特征集、增大多项式次数或者减小正则化参数 λ)

神经网络&方差偏差

神经网络出现之前,机器学习工程师必须在偏差和方差之间权衡,即平衡多项式次数的复杂性或者正则化参数 λ 以使偏差和方差不会太高。而神经网络让我们摆脱了必须权衡偏差与方差的困境。

当神经网络足够大时,只要训练集不是太大,几乎总能适应训练集,唯一不足是会使速度减慢。
当模型在训练集上表现不好时,即高偏差问题,则需要使用更大的神经网络,直至在训练集上表现良好。
当在交叉验证集上表现不好时,即高方差问题,则需获取更多的数据并重新训练。
但这个方法也有局限性,训练更大的神经网络虽然会减少偏差,但是也会使计算速度变慢,而且有时我们无法得到更多的数据。

事实证明,一个具有良好正则化的大型神经网络通常要比较小的网络做得更好

如下图所示,是使用 Tensorflow 实现神经网络的正则化。

机器学习系统的开发过程

机器学习的迭代发展

选择模型 → 训练模型 → 诊断,这个过程可能重复多次

误差分析

错误分析是继偏差与方差之后最重要的诊断模型的方法。
错误分析是指手动查看出现错误的样例,并试图深入了解算法出错的地方。具体来说,经常做的是从交叉验证集中找到一组算法误差分析的样本,并且按照共同的属性/主题/特征将他们分组。

以医疗垃圾邮件为例,手动计算邮件中有多少垃圾邮件,并分析出这些邮件各自不同特征(例如推销、故意拼写错误或窃取密码等)的数量。
此时我们会发现什么问题是重要的,什么问题是相对次要的。
而当我们拥有一个很大的数据集时,通常是选择一部分数据进行分析。

添加数据

添加数据的类型

当我们在训练机器学习算法时,我们总是希望拥有更多的数据。因此,有时我们想获得更多所有类型的数据,但是有时候既慢又贵,此时我们专注于添加对我们的分析更有帮助的数据会更高效。
也就是说,如果我们有办法添加更多的各种类型的数据,那是最好,否则,如果错误分析表明在某些问题上我们的算法性能非常差,我们就专注于获取这部分数据可能会更有效。

数据增强(data augmentation)

定义:利用现有的训练样例创建一个新的训练样例
例如识别字母 A ,可能通过旋转、缩放、改变对比度、镜像等方式来进行数据增强。

也可以通过引入网格进行随机扭曲。

数据增强也适用于声音识别。

数据增强的一个技巧是对数据所做的更改或扭曲,例如扭曲字母 A 或在声音识别中添加指定噪声,应该代表测试集中的噪声类型;而对于数据纯粹随机无意义的噪声通常没有多大帮助。

数据合成

定义:不是通过修改现有的示例,而是从头构建全新的示例
以 photo OCR (自动让计算机读取图像中显示的文本)为例,用电脑生成的图片看起来也很逼真。
但是数据合成多数用于计算机视觉,而较少用于其他应用程序。

关注数据

机器学习在过去的几十年发展中,大多数机器学习研究人员的注意力都集中在以传统的模型为中心,也就是说他们下载固定的数据集,并专注于改进算法或模型。
而今天很多算法已经非常好了,并且适用于许多应用程序,因此,有时花更多时间采用以数据为中心的算法会更有成效。

迁移学习:使用其他任务中的数据

迁移学习原理

假设我们想识别手写数字,但没有那么多的数据,但是我们发现了一个非常大的数据集,我们就可以首先在这个大的数据集上训练一个神经网络,然后复制这个神经网络,但是要替换输出层。

也就是说,在迁移学习中,我们使用除输出层外的所有层的参数作为新神经网络参数的起点,然后运行优化算法,具体来说,有两种方法可以训练这个神经网络的参数。

  • 保持输出层以前的参数不变,只训练输出层参数
  • 以输出层以前的参数为起点,训练网络中的所有参数

第一种方法适用于非常非常小的数据集,第二种方法适用于稍大一些的数据集。
直觉上,我们希望神经网络通过学习大的数据集学到了一些东西,然后在新的数据集上稍微训练后,就能起到较好的效果。
总结一下,迁移学习分为两步:监督预训练微调

迁移学习有效的原因

如果正在训练一个用于从图像中检测不同物体的神经网络,那么神经网络的第一层会检测图像边缘;第二层会将边缘分组,检测图像角落;第三层会检测图像曲线或者其他一些基本的形状。
这就是为什么通过学习大量不同的图像,我们在教神经网络学习检测边缘、角落和基本形状,这些对于其他计算机视觉的任务也很有帮助。
进而,训练神经网络来检测猫、狗、车辆和人等不同事物,我们是在帮它学习检测图像的基本特征,所以迁移学习是有效的。
但是对于预训练和微调这两步,图片尺寸必须相同。

迁移学习总结

  1. 下载带有参数的神经网络,这些参数已经在与算法相同输入类型(例如图像、音频、文本)的大型数据集上预训练过了
  2. 根据自己的数据进一步训练或微调神经网络

机器学习项目的完整周期

一个完整的机器学习周期包括:

  • 确定项目范围,即决定你想要做什么。
  • 收集数据。
  • 训练模型。
  • 部署在生产环境中,这意味着可以供用户使用。

部署模型的一种常见方法就是采用你的机器学习模型并在服务器中实现它,移动应用程通过 API 调用,将音频传给服务器,服务器将翻译文本传回应用程序。

数据倾斜

误差指标

数据偏斜指的是数据集中正例和反例比例失衡。
只用准确率来衡量算法是否有效通常是不可靠的。

比如我们设计了一个学习算法来检测一种罕见疾病,准确率在 99%,但实际上这是一种非常罕见的疾病,但是患病的数据非常少,发病率仅有 0.5%;那如果我们让另一个算法只输出 0(即没患病),这实际上有 99.5% 的准确率或是 0.5% 的误差,这个愚蠢的算法看起来比我们的学习算法更好,但实际上很明显这不是一个更好的算法。
所以,一种常见的度量就是精准率召回率

我们构造一个2×2的混淆矩阵或表格,横轴是实际类,纵轴是预测类。
根据划分不同,给出下面四个定义:

  • 真阳性:实际患病,预测患病。
  • 假阳性:实际没患病,预测患病。
  • 假阴性:实际患病,预测没患病。
  • 真阴性:实际没患病,预测没患病。

精准率:所有我们预测患病的群体中,真正患病的比例,即 \(\frac{真阳性}{预测为阳性}\) 或 \(\frac{真阳性}{真阳性+假阳性}\)。
召回率:所有真正患病的群体中,我们预测患病的比例,即 \(\frac{真阳性}{真实为阳性}\) 或 \(\frac{真阳性}{真阳性+假阴性}\)。
使用这两个标准衡量算法时,如果算法只输出 0,那召回率将为 0,精准率为 0 / 0,有时也说其等于 0。

精准率和召回率的权衡

仍旧以预测患病为例,我们先以 0.5 作为是否患病的阈值,y=1 时患病, y=0 时没有患病。
一种情况是,如果患病的后果不那么严重,不采取积极治疗也可以,那么我们只在十分有把握的情况下预测 y=1 。在这种情况下,我们可以设置更高的阈值,只有超过这个阈值才能预测 y=1 。只有在确切的情况下才会预测 y=1 ,意味着精准度会增加;同时因为预测的次数变少,正确诊断出患病的人数减少,会导致召回率减少。
另一种情况是,如果患病后果很严重,最好采取治疗,我们会尽量预测 y=1 。在这种情况下,我们会设置更低的阈值。因为判定的标准更松,降低阈值会导致精准度的减少,同时会导致召回率的增加。

假设我们使用逻辑回归算法,当提高阈值时,准确率提高,召回率降低;降低阈值时,准确率降低,召回率提高。
根据不同的阈值我们可以画出精准率和召回率曲线。
除了手动选择阈值以外,如果我们想自动权衡精准率和召回率,我们可以采用另外一个指标,F1 分数

F1 分数是一种计算平均分数的方法,它更关注较低的分数,也叫调和均值。其中 P 和 R 是精准率和召回率。

决策树

举例解释决策树是如何工作的——猫分类。

这是一个二元分类任务,并且每个特征也只能取两个离散值,下面是训练决策树模型可能得到获得的模型示例。

根节点:树中最顶层的节点。
决策节点:图中的椭圆节点。
叶子节点:图中的矩形节点。

学习过程

在构建决策树的过程中,我们需要做出几个关键决定。

第一个关键决定是,如何选择在每个节点上使用哪些特征进行拆分。原则就是最大化纯度(使每个节点中尽量都是猫或者狗)。
第二个关键决定是,何时停止拆分:

  • 当一个节点只属于一类时
  • 当拆分节点导致超过树的最大深度时,限制决策树深度的一个原因是为了确保我们的树不会变得太大或者笨重,并且保持树小使它不太容易过拟合
  • 当拆分节点对纯度得改善很小时
  • 当节点的示例数低于某个阈值时

决策树学习

测量纯度

是衡量一组数据是否不纯的指标。
我们定义 p1 是样本中猫的比例,以 p1 为横轴,熵为纵轴,则熵和 p1 的函数图像如下:

定义 p0 为样本中不为猫的比例,则熵函数的实际方程如下:

注意,计算熵时采用的是以 2 为底而不是以 e 为底的对数,是为了让曲线的峰值等于 1,便于解释曲线的意义(杂质含量)。

选择拆分:信息增益

构建决策树时,在一个节点使用什么特征进行拆分取决于选择什么特征可以最大程度地减少熵。
熵的减少成为信息增益,下面来看看如何计算信息增益。

我们分别计算左右分支的熵之后,会发现每种分法有两个熵值,那么如何进行比较呢,事实上,如果一个节点上有很多高熵的样本往往比一个节点上只有几个高熵的样本更糟糕,所以通常计算一个加权平均,然后使用根节点的熵减去这个加权平均,即求出了熵的减少,即信息增益。
为什么要计算熵的减少?因为决定何时不进行拆分的标准之一就是如果熵减少的太小,增益太小,而拆分还会增加过拟合的风险,就停止拆分。

信息增益的一般公式如下:

p 是分支中猫的比例,w 是到分支中的样本(猫和狗)与上一层所有样本的比例。
信息增益越多,表明以这种特征的分法减少了更多的熵,从而增加了我们在决策树的左右子分支上得到的数据子集的纯度,纯度越高对我们的决策树越好。

整合

决策树构造过程如下:

  • 从根节点的所有示例开始
  • 计算所有可能的特征的信息增益,并选择信息增益最高的特征
  • 根据所选特征拆分数据集,并创建决策树的左右分支
  • 继续重复拆分过程,知道满足停止条件:
    • 当一个节点 100% 是同类时(有的达到了 0 熵)
    • 当分裂一个节点时会导致决策树超过最大深度
    • 额外拆分的信息增益小于阈值
    • 当节点中的示例低于阈值时

而构造决策树是一个递归(recursive)的过程:

One-hot 编码:分类特征

在之前的样例中,每个特征只有两个离散的取值,那么当取值大于两个时,我们怎么构建决策树呢?此时就需要使用 One-hot 编码

当耳型特征有三个离散取值时,我们把它拆开,创建三个新的特征,三个特征中只有一个能取 1,因此得名。
更详细一点,如果一个分类特征可以取 k 个值,那么我们将创建 k 个二进制特征来代替它
这样每个特征就只能取两个离散的值,回到了之前的情况。
One-hot 编码不仅仅适用于决策树,它也适用于神经网络(使用 0、1 对特征进行编码,以便作为输入)。

连续值特征

当我们面临一个具有连续值的特征时,通常对其拆分的方式是设置一个阈值,根据是否小于等于阈值进行拆分。

可能会有很多个不同的阈值选择,此时需要计算信息增益,选择带来的信息增益最大的那个阈值。
选择阈值时,一种常见的方法是,根据特征值大小对所有样例排序,取排序表中每两个连续点的中点值作为一种阈值选择,也就是说,当有 10 个样例时,会测试 9 个不同的阈值。

回归树

举例介绍回归树——预测动物体重。根据耳朵形状、面部形状、胡须来预测体重,因此是个回归问题。

为回归问题建立好的决策树如下图:

当预测一个新的样例时,从根节点开始根据样例特征进行决策,直至根节点,然后输出该叶节点动物的权重的平均值。

选择特征划分样例时,类比到尽量减少熵的方法,回归问题中是尽量减少方差

集成树学习

使用多个决策树

使用单个决策树的缺点之一是该决策对数据中的微小变化高度敏感。
而使算法不那么敏感的解决方案是构建很多决策树,我们称之为集成树(tree ensembles)。

下图中只更换了一个小猫的例子,就导致了两种完全不同的分类。

当构建多个决策树时,也就是集成树,对一个新的样例分别作出判断,然后投票决定最终结果。

有放回抽样

原始训练集中有 10 个动物,我们做 10 次有放回抽样,得到一个新的训练集。这个训练集中可能有重复的取样,这就是我们要的效果。

随机森林算法

如下图所示是生成一个集成树的算法:给定训练集大小为m,对于 b=1 到 B ,使用有放回抽样来创建一个大小为m的新训练集,然后在这个新训练集上训练决策树。

如下图,对该算法进行一点更改:
在每个节点上,当选择用于拆分的特征时,如果n个特征可用,则选择 k<n 特征的随机子集,并允许算法仅从该特征自己中进行选择。
换句话说,我们可以选择 k 个特征作为允许的特征,然后从 k 个特征中选出最大的信息增益作为选择的特征来进行划分。k 值的经典选择是取 \(\sqrt{k}\) 。
这种技术趋向于更多地用于具有大量特征的较大问题,这将进一步改变算法,最终得到随机森林。

XGBoost

对生成树集合的算法进行改进,除了第一次取样以外,每次优先选择在之前的随机树中预测错误的例子,也被称为刻意练习

XGBoost 优点有:

  • Boost树的源代码是开源的
  • 快速高效的实现
  • 默认拆分条件和对何时停止拆分条件的良好选择
  • 内置正规化,防止过拟合
  • 是在机器学习竞赛中极具竞争力的算法(例如Kaggle competitions)

由于XGBoost 的实现细节非常复杂,所以一般直接使用开源库。

何时使用决策树

总结一下:
决策树适合处理结构化数据,并且训练速度快,小型决策树还具有可解释性。
神经网络适合处理任何类型数据(包括非结构化数据,例如图片、音频等),通常和迁移学习一起应用,当需要多个模型联合使用时,使用神经网络也更容易一些。


至此,第二部分(高级学习算法)结束。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

index