吴恩达机器学习(二):高级算法学习
高级算法学习
一、神经网络的构建
人工神经网络(Artificial Neural Network,即ANN ),是20世纪80 年代以来人工智能领域兴起的研究热点。它从信息处理角度对人脑神经元网络进行抽象, 建立某种简单模型,按不同的连接方式组成不同的网络。在工程与学术界也常直接简称为神经网络或类神经网络。神经网络是一种运算模型,由大量的节点(或称神经元)之间相互联接构成。每个节点代表一种特定的输出函数,称为激励函数(activation function)。每两个节点间的连接都代表一个对于通过该连接信号的加权值,称之为权重,这相当于人工神经网络的记忆。网络的输出则依网络的连接方式,权重值和激励函数的不同而不同。而网络自身通常都是对自然界某种算法或者函数的逼近,也可能是对一种逻辑策略的表达。
1.1 需求预测
为了能更加直观地认识神经网络,我们可以从一个简单的需求预测案例开始。
加入我们现在已知一件衣服的价格,那么如何预测其是否畅销呢?
首先我们可以利用前面学到过的逻辑回归sigmoid函数进行拟合:
/demand_prediction1.png)
我们的输入是衣服的价格,输出则是由sigmoid函数得到的畅销概率,我们将记为,现在我们可以将这个单个的逻辑单元视为神经网络中的一个神经元,它的输入是衣服的价格,输出则是改衣服成为畅销产品的概率。
/demand_prediction2.png)
这就是神经网络中的一个最简单的神经元模型。
假如我们现在已知四种关于该衣服的参数,分别是:价格、运费、市场营销、材料。现在我们要利用这四个特征来对该种衣服的畅销度进行预测,那么我们首先可能会考虑到影响该件衣服畅销度的几个因素,比如:该衣服的负担能力、衣服的知名度以及该衣服的质量。其中,我们会认为影响负担能力的因素有价格和运费,影响知名度的因素有市场营销,而影响感知质量的因素有价格和衣服的材料。通过对这三个中间因素的分析,我们最后可以评测这件衣服是否能成为畅销品。
/demand_prediction3.png)
在神经网络中,我们将最初的四个特征划为一层,称之为输入层,最后的输出划为一层,称之为输出层,中间的三个特征我们称之为激活值,将他们三个为一层称之为隐藏层。在一个神经网络中,隐藏层可能只有一层,也可能有很多层。
/demand_prediction4.png)
当神经网络比较小的时候,我们可以很容易的找到输入层与隐藏层特征之间的关系,但是在大型神经网络中,由于输入特征有很多,我们无法一个个地选择隐藏层与输入层特征之间的关系,所以我们可以采用前面学到过的特征工程,使激活值可以访问输入的任何一个特征,从而实现神经网络的链接。
/demand_prediction5.png)
1.2 神经网络中的层
现在有一个神经网络,我们将向量作为输入值输入,在第一层神经网络中进行计算。在第一层中有三个神经元,这三个神经元每一个都可以实现一个小型的逻辑回归单元,每一个神经元都有两个参数:和,它们的作用是输出一些激活值,其中表示sigmoid函数。为了方便我们将第一个神经元的参数以及输出的激活值表示为,以此类推,最终得到该层的输出值。
/neural_network_layer1.png)
之后我们将该层的输出作为输出层的输入输入到下一层,经过输出层的神经元计算最后得出最终输出,再根据该输出对衣服的畅销度进行预测。
/neural_network_layer2.png)
为了更好的区分每一层的输出,我们一般会在右上角加上中括号以及该层的层数,如第一层的三个神经元参数分别记为,,,第一层输出的激活值为。
如果我们将输入层作为第0层,那么我们可以得到一个通用的公式:第层的第个神经元输出
/notation.png)
1.3 代码实现
1.3.1 代码实现神经网络
现在我们使用一个烘焙咖啡的案例来学习如何使用代码实现神经网络的层。
我们输入的特征值有两个,分别是烘焙温度和烘焙时间
/coffee_roasting1.png)
我们可以将其简化为一个三层的神经网络,
/coffee_roasting2.png)
我们的输入值为,它包含两个特征值,分别是烘焙温度和烘焙时间。该神经网络的第一层共有三个神经元,输出值为,用代码可表示为:
1 | x = np.array([[200.0, 17.0]]) # 其中200为烘焙温度,17为烘焙时间 |
之后将第一层的输出值作为第二层的输入值进行计算,输出为,用代码可以表示为:
1 | layer_2 = Dense(units=1, activation='sigmoid') # Dense是该层的名字,units=3表示该层的神经元个数为3个,activation='sigmoid'表示该层使用的激活函数为sigmoid |
最终我们可以通过输出值a2来判断烘焙温度和烘焙时间是否合适:
1 | if a2 >= 0.5: |
1.3.2 搭建一个神经网络
在之前的代码中,我们需要手动输入下一层的输入值,现在我们可以使用新的方法来更方便地搭建一个神经网络。
/model1.png)
在新的方法中,我们可以使用相同的方法来构建每一层,但与之前不同的是,我们现在可以使用顺序函数Sequential来将相邻的两层连接起来,实现代码如下:
1 | layer_1 = Dense(units=3, activation='sigmoid') |
1.4 前向传播
1.4.1 单层上的前向传播
在上节中我们分析了如何用代码实现神将网络的搭建,本节我们来具体分析一下实现的具体过程。
/forward_prop.png)
其中输入值为,第一层输出值为,第二层输出值为,代码内部实现如下:
1 | x = np.array([200, 17]) |
1.4.2 利用Numpy实现前向传播
/forward_prop_in_numpy.png)
密集函数Dense的实现:
1 | def dense(a_in,W,b,g): |
顺序函数Sequential的实现:
1 | def sequential(x): |
1.5 矩阵乘法
神经网络之所以能如此高效,是因为神经网络可以向量化,这使得我们可以使用矩阵乘法来进行计算。
在之前的学习中,我们在构建密集层dense时,再其内部使用for循环来取出矩阵W的每一列后在使用sigmoid函数进行计算:
/for_loops.png)
当我们使用matmul方法后,我们就可以直接使用矩阵乘法,而无需再将矩阵W的每一列取出后再计算,这样就可以大大提高神经网络的效率:
/matrix.png)
需要注意的是之前使用的是向量的内积,而当我们使用矩阵乘法时,矩阵A则需要进行转置,转置的代码为:
1 | A = np.array([[1,2], |
二、训练神经网络
2.1 模型训练
2.1.1 使用Tensorflow实现
在本节中我们使用一个新的案例:手写数字识别。我们需要做的是识别图中数字是0还是1。
/handwritten.png)
该模型一共有四层,分别为输入层,两个隐藏层以及一个输出层。第一个隐藏层包含25个神经元,第二个隐藏层包含15个神经元,输出层有1个神经元。
根据之前的学习,我们可以写出大致实现代码如下:
1 | # 首先导入需要使用的库 |
2.1.2 模型训练细节
2.1.2.1 逻辑回归模型
在之前逻辑回归中,我们训练逻辑回归模型的步骤一共有三步:
- 第一步:指定怎样在给定输入特征以及参数和的情况下计算输出。
我们一般会使用sigmoid函数来构建模型,即
1 | z = np.dot(w, x)+b |
- 第二步:指定损失函数和成本函数
损失函数用于评估单个训练示例,而成本函数则是在整个训练集上计算的所有示例的损失函数的平均值
1 | # logistic loss |
- 第三步:使用特定的算法对模型进行训练使得成本函数最小,以寻找最合适的和
在逻辑回归中,我们一般使用梯度下降算法来进行最小化成本函数
1 | w = w - alpha * dj_dw |
2.1.2.2 神经网络模型
在Tensorflow中训练神经网络模型中的步骤与在逻辑回归中相似:
- 第一步:指定怎样在给定输入特征以及参数和的情况下计算输出,也就是使用前向传播算法来构建模型
/training1.png)
1 | # 构建模型 |
- 第二步:制定损失函数,也就是模型的编译
在神经网络训练中,我们一般使用二元交叉熵函数来作为损失函数
/training2.png)
1 | # 模型编译 |
补充:上面问题是分类问题,所以使用二元交叉熵函数作为损失函数,如果我们处理的是回归问题的话,我们需要使用均方误差损失函数来作为损失函数
1 | from tensorflow.keras.losses import MeanSquaredError |
- 第三步:训练模型
在最小化成本函数时,我们依然可以使用梯度下降算法,但是在逻辑回归中,我们需要在代码中不断重复梯度下降的步骤:
而在tensorflow中,我们可以使用一种名为反向传播的算法来进行计算,在代码中我们只需要调用fit函数即可
/training3.png)
1 | model.fit(X, Y, epochs=100) # 对模型进行训练,epochs表示梯度下降的次数 |
2.2 激活函数
2.2.1 sigmoid激活函数的替代方案-Rule函数
在之前的神经元模型中,我们一直都使用sigmoid函数作为激活函数,但sigmoid函数只适用于二元分类问题,其结果只有0和1,所以我们需要了解一些其他函数以便于更好地构建模型。
比如在之前的判断衣服畅销度的问题中,我们把人们对于衣服的感知度分为0(不了解)和1(了解),但实际上人们对于衣服的感知度是有程度区分的,所以我们可以使用同一个新的函数ReLU函数(Linear rectification function)作为激活函数。
ReLU函数的表达式为,也就是当$z<z>0zg(z)=z$,也就相当于没有使用激活函数。
/functions.png)
2.2.2 如何选择合适的激活函数
我们现在已经知道了三个常用的激活函数:sigmoid函数、ReLU函数以及线性激活函数,那么我们该如何选择合适的激活函数来构建神经单元呢?
2.2.2.1 输出层激活函数的选择
对于输出层我们一般分为两种情况:
/output_layer.png)
- 当我们在处理输出结果为0或1的二元分类问题时,我们一般使用sigmoid函数作为激活函数,因为它能够很好地预测等于0或1的概率。
- 当我们在解决回归问题时,我们需要根据不同的问题来选择不同的激活函数:
- 比如,当我们需要预测明天股票的走势时,输出结果有可能为正数,也有可能为负数,所以最好的选择就是使用线性激活函数作为激活函数
- 假如我们需要预测房子的价格时,由于房子的价格只能为非负数,所以我们应该使用ReLU函数作为激活函数
- 总之,我们需要根据预测结果来选择合适的激活函数
2.2.2.2 隐藏层激活函数的选择
对于隐藏层,我们一般都会选择ReLU函数作为激活函数。
/hidden_layer.png)
这是因为:
- 相对于sigmoid函数来说,ReLU函数表达式更简单一些,所以在训练过程中会更快,效率会更高;
- sigmoid函数左右两侧都比较平坦,这可能会导致在梯度下降的过程很慢,减慢学习速度。
所以在实际应用中,我们一般会在隐藏层选择ReLU函数作为激活函数,而在输出层选择sigmoid函数作为激活函数
/choose.png)
2.2.3 为什么要使用激活函数
在神经网络的每一个神经元中,我们一般都会使用激活函数来对数据进行处理,那么我们为什么要使用激活函数呢?
假设我们在所有的神经元中都不使用激活函数,或者都是用线性激活函数,那么对于一个简单的神经网络,我们做如下分析:
/linear.png)
输入值经过第一层后输出
将作为输出层的输入值输入后得到
假如我们将看做,将看做,那么,即输出结果与输入结果仍然是呈线性关系,相当于我们模型中的隐藏层并未发挥实际作用,这也是为什么我们要选择使用激活函数,以及为什么在隐藏层中一般选择ReLU函数而不是选择线性激活函数作为激活函数的原因。
下面的例子同理:
/example.png)
2.3 多分类问题与Softmax
2.3.1 多分类问题
多分类问题是指在一个问题中有两个以上的结果种类,而不仅仅是0或1。
在之前的01二元分类问题中,我们一般需要做的是估计或的概率:
/multiclass1.png)
但是在多分类问题中,我们需要估计的就不仅仅是两个结果的概率,而是更多结果的概率:
/multiclass2.png)
在解决01二元分类问题中,我们一般会使用逻辑回归模型和sigmoid函数来进行处理,那么对于多分类问题我们应该如何处理呢,答案就是使用Softmax回归算法。
2.3.2 Softmax回归算法
在逻辑回归算法中,我们一般使用sigmoid函数作为输出层的激活函数,预测结果为1的概率为,其中,那么预测结果为0的概率就是。
而softmax的公式预测结果为j的概率:
其中:
并且
我们可以举一个简单的例子来更好地理解这个式子:加入我们的输出结果一共有四种可能,那么这四种可能的输出概率就分别为:
/4.png)
对于损失函数和成本函数,在逻辑回归中,我们使用的损失函数为:
成本函数为:
而在Softmax回归中,我们可以类比逻辑回归的损失函数,构造成Softmax回归中的损失函数:
也就是说,当时,预测的损失为,而的图像如下:
/loss.png)
那么也就是说当预测结果非常接近1时,那么损失就会很小,反之,当接近0时,损失就会很大。这就会激励算法使得尽可能地接近1。
但是需要注意的是在Softmax回归中,每个训练示例中的只能取一个值,所以每次只能计算出时的概率,而不能像逻辑回归中一次直接计算出和的概率。
2.3.3 神经网络的Softmax输出
当我们在处理二元分类问题时,输出层一般只有一个神经元,而当我们在处理多分类问题时,一个神经元显然已经满足不了我们的需要了,所以这时我们就需要使用多个神经元进行输出计算。
/softmax_output.png)
那么为什么我们需要多个神经元才能实现多分类问题呢,主要原因就是:在二元分类问题中,激活值只是关于的函数:
而在softmax回归中,是关于的函数,
所以我们在输出层需要多个神经元进行的计算。
2.3.4 Softmax的改进
根据我们前面所学的内容,使用tensorflow实现的代码如下:
1 | # 首先导入需要使用的库 |
但是这种代码在使用过程中会产生计算误差,导致结果不精准,那为什么会产生误差呢?
我们先来举一个很简单的例子:
假如我们现在要计算,那么我们可以有很多方法进行计算。一种是直接进行计算,而另一种是将其变化为再分步进行计算。
/2divide10000.png)
我们会发现,虽然两个式子化简后均为,但最后得到的结果却不相同,这是因为计算机中用于存储的字节位数是有限的,而且在计算机中小数也是用二进制来进行表示的,这就会导致二进制数在转化为小数时产生精度上的误差。
2.3.4.1 逻辑回归算法改进
那么如何解决这个由于硬件问题而产生的误差呢?答案就是尽量减少结果在中间出现的次数。
以逻辑回归为例:我们在逻辑回归的输出层中使用sigmoid函数作为激活函数,损失函数为,在代码运行执行过程中,我们就会先计算出的值,之后再将的值代入损失函数求得最后的损失结果。这就会导致计算出的出现误差,从而影响损失的值。
我们一般采取的办法就是直接将的表达式代入到函数之中,使得在代码执行过程中的值一直使用二进制来进行表示而不必在中间转化为小数,产生精度误差,虽然这样的代码看起来会比较复杂,但是能够提高计算结果的精度。
改进后的逻辑回归代码实现如下:
1 | # 首先导入需要使用的库 |
2.3.4.2 Softmax回归算法改进
同理在Softmax回归中,我们也可以使用这种方法来减少计算误差:
原Softmax回归:
改进后的Softmax回归:
具体实现代码为:
1 | # 首先导入需要使用的库 |
2.3.5 多标签分类问题
还有一种易于与多分类问题混淆的是多标签分类问题。多标签分类问题指的是对于单个输入的内容,我们需要同时输出多个不同标签的结果。
比如我们需要同时检测一张图片中是否有轿车、是否有公交车以及是否有行人。
/multi-label.png)
这时我们需要输出的就不是单个的数字,而是一个数组,这就是典型的多标签分类问题。
一种非常简单的处理办法就是将其分成三个完全独立的机器学习问题,也就是构建三个不同的神经网络用于处理三个不同的二元分类问题。
/multiple_classes1.png)
但这种方式会比较复杂,并且需要我们多次输入相同的输入值。
还有一种更为简单的方法就是训练一个具有三个不同输出的神经网络:
/multiple_classes2.png)
这样我们就只需要构建一个神经网络即可。
2.4 高级优化方法-Adam
梯度下降是一种广泛用于机器学习的优化算法,是线性回归和逻辑回归以及神经网络早期实现的基础。但实际上,除了梯度下降算法以外,还有一些其他的算法可以帮助我们更快更好地训练神经网络。
在使用梯度下降算法时,学习率过小可能会出现梯度下降虽然沿着正确的方向下降,但是下降速度过慢的问题,过大可能会出现梯度下降幅度过大而一直左右摇摆的问题。
![]() |
![]() |
|---|
那么我们可不可以通过实时调整从而避免这种问题呢,但是可以的,那就是使用Adma算法。
Adma算法的原理是:在原来的梯度下降算法中,参数的调整公式为,其中所有的参数调整使用的都是相同的学习率,而在Adma算法中,对于不同参数的调整,我们可以选用不同的学习率
/adam3.png)
Adam算法的原理就是当一个参数或一直在同一个方向移动时就增加;当参数在不断来回震荡时就减小,从而实现学习率的动态调整。
使用Adam算法后的代码如下:
1 | # 构建模型 |
三、模型评估
3.1 模型评估
3.1.1 为什么要进行模型评估
在构建神经网络模型中,我们基本上不可能一次就得到想要的模型,在训练模型的过程中一定会出现各种各样的问题,这就需要我们进行模型评估从而寻找解决问题的办法。
比如在之前的房价预测问题中,我们通过线性回归得到的模型为:
但是在使用过程中,模型的预测具有很大的误差,那么我们该怎样对模型进行调整呢?
其实有很多方法,比如:
- 获得更多的训练示例来对模型进行训练
- 尝试更小的特征集
- 尝试获得额外的特征
- 尝试增加多项式的次数
- 尝试增大或减小正则化参数
但实际上,不是每一种方法在调整模型时都会奏效,那么我们如何确定该使用哪种方法来对模型进行调整呢,那就需要对模型进行评估。
3.1.2 模型评估方法
以我们之前学习过的房价预测模型为例:
/Model_evaluation1.png)
虽然该模型可以很好的预测训练集中的数据,但实际上这是一个过拟合的模型,它无法很好地泛化到训练集以外的数据集中。
对于上面这个多项式回归模型,我们可以很容易的画出曲线图,从而通过图像清晰的进行模型评估,但是当我们需要添加多个特征时,我们就无法绘制多维度图像,也就没法利用图像来评估模型的好坏。这时我们就需要使用其他方法来进行模型评估。
最简单的方法就是训练集和测试集分割,也就是假如我们拥有一个包含10个示例的数据集,我们可以选择前7个数据作为训练集,后3个数据作为测试集。
/Model_evaluation2.png)
在训练模型的过程中,我们可以先使用训练集的数据对模型进行训练,然后再通过测试集的数据对模型进行测试,从而对模型进行评估。
比如对于一个线性回归模型,首先我们会通过最小化成本函数来寻找合适的参数:
之后计算测试集上的平均误差:
之后再计算训练集上的平均误差:
通过比较训练集误差和测试集误差,我们就可以判断出模型是否符合需求。
对于分类问题我们也可以使用同样的方法进行模型评估:
首先通过最小化成本函数来寻找合适的参数:
之后计算测试集上的平均误差:
之后再计算训练集上的平均误差:
之后通过对比训练集和测试集上的误差从而对模型进行评估。
对于二元分类问题,还有一种模型评估的方法就是统计训练集和测试集的误判率。
/Model_evaluation3.png)
此时表示测试集中被误判的部分,表示训练集中被误判的部分。
3.1.3 模型选择与交叉验证集
因为我们的模型是由训练集数据训练后得到的,所以使用训练集数据计算得到的平均误差没有什么评估的意义。相对于训练集数据,测试集数据能够更好地评估模型的泛化性能,所以我们可以使用测试集数据来选择模型。
/model_selection1.png)
在模型选择时,我们可以通过改变多项式次数来计算不同模型的测试集平均误差,从而选择平均误差最小的模型作为我们最终使用的模型,并且为了评估模型的好坏我们可以使用最终选择的模型的平均误差作为报告测试集误差。
但实际上有一个很大的问题就是实际上是对模型泛化能力的乐观估计。因为跟训练集时的问题一样,我们相当于使用测试集数据训练得到了最合适的参数:多项式次数,那么在进行误差计算就没有多大意义了。(因为我们选择的就是最小时的模型5,所以最后计算得到的一定是最小的,就好比我们在选择西瓜时就是根据西瓜的甜度选择了最甜的西瓜,那么最后吃西瓜的时候西瓜一定很甜)。
那么如何解决这个问题呢?一般我们使用的方法就是将原本的数据集分成三个集合:训练集(training set)、交叉验证集(cross validation set/development set/dev set)以及测试集(test set)。
/model_selection2.png)
在训练模型的过程中,我们使用训练集数据来对模型进行训练,从而得到合适的参数,之后使用交叉测试集选择合适的模型,也就是确定多项式次数,最后在使用测试集数据对模型进行评估。
/model_selection3.png)
由于测试集数据不参与模型的选择,所以通过测试集数据得到的误差就可以很好的反映出模型的泛化能力。
在构建神经网络模型时,我们也可以使用同样的方法进行模型的训练、选择与评估,从而得到最具有泛化能力的模型。
/model_selection4.png)
3.2 偏差与方差
3.2.1 多项式次数对偏差和方差的影响
在之前房价预测问题中,我们有很多不同的模型,当我们为给定的数据拟合一条直线时,其表现不佳的情况我们称之为欠拟合,也就是高偏差;当我们为给定的数据拟合一个四阶多项式时,他会出现过拟合现象,也就是高方差;而当我们使用二次多项式拟合数据时,那么模型的表现会很不错。
/bias_variance1.png)
相对于这种根据图像进行判断的方法,我们更系统的方法是查看算法在训练集和交叉验证集上的性能。
在欠拟合即高偏差时,我们可以发现模型既不能很好地拟合训练集数据,也不能很好地泛化到交叉测试集数据,所以它的和都会很大;在过拟合即高方差时,模型能够非常好地拟合训练集数据,但是无法泛化到交叉验证集数据,所以它的很低但很高;而对于二次多项式模型,它不仅可以很好地拟合训练集数据,也可以很好地泛化到交叉验证集数据,所以它的和都很低。
/bias_variance2.png)
也就是说当多项式次数很小时,和都很大,当多项式次数很大时,很小而很大,多项式次数关于和的图像如下:
/bias_variance3.png)
通过这种方法,我们就可以根据模型在训练集和交叉验证集上的误差表现来判断模型的偏差与方差情况。
3.2.2 正则化参数对偏差和方差的影响
与多项式次数类似,正则化参数的大小也会影响模型的偏差和方差。
对于一个四次多项式模型:
其成本函数为:
在模型训练过程中,如果我们选择较大的,那么正则化后会使得所有的很小甚至趋近于0,那么模型就会简化为常函数,那么就会出现高偏差(欠拟合),此时训练集和交叉测试集数据平均误差都会非常大;如果我们选择较小的,比如,那么就相当于我们没有对参数进行正则化处理,此时就会出现高方差(过拟合);当我们选择合适的时,正则化就能很好的起到应有的效果,从而得到我们想要的模型。
/bias_variance4.png)
最终我们可以得到正则化参数与偏差和方差的关系图如下:
/bias_variance5.png)
3.2.3 性能评估基准
我们已经学习了对于不同的多项式次数和正则化参数,模型的偏差和方差与和的关系。我们一直在说和高或者低,但是到底和多大的情况下算偏高或者偏低呢?这就需要我们设置一个性能评估基准。
我们以一个语音识别模型为例:
假如在此模型中,训练误差=10.8%,交叉验证误差=14.8%,在第一印象中,我们可能会觉得这个模型的和都很高,似乎该模型具有高偏差,但是当我们再加入一项新的指标——人类的表现水平(即正常人在听该语音集之后所得到的识别错误率)后,我们会发现:正常人类表现水平也有10.6%的错误率,那么相比较来说,训练集的误差就不是那么大了。产生这种情况的原因可能是该语音集中的语音背景比较嘈杂,这就使得正常人也无法完全地正确识别所有语音,所以我们也很难要求模型做得更好。
在这个例子中和人类的表现水平只相差了0.2%,而和之间就相差了4.0%,即实际上该模型在训练集上表现很好但在交叉测试集上表现很差,所以该模型存在更多的是方差问题。
接下来我们用一个简单的例子在分析一下实际可能遇到的几种情况:
/bias_variance6.png)
对于第一种情况:基准水平为10.6%,训练误差为10.8%,交叉验证误差为14.8%,由于和基准水平之间相差只有0.2%,而和之间相差4.0%,即该模型在训练集上表现很好但在交叉测试集上表现很差,所以该模型具有高方差问题。
对于第二种情况:基准水平为10.6%,训练误差为15.0%,交叉验证误差为15.5%,由于和基准水平之间相差4.4%,和之间只相差0.5%,即该模型在训练集和交叉测试集上表现都很差,所以该模型具有高偏差问题。
对于第三种情况:基准水平为10.6%,训练误差为15.0%,交叉验证误差为19.7%,由于和基准水平之间相差4.4%,而和之间相差4.7%,即该模型在训练集上表现很差并且在交叉测试集上表现更差,所以该模型同时具有高偏差和高方差问题。
3.2.4 学习曲线
学习曲线是一种重要的工具,它可以帮助我们评估模型的性能并帮助诊断模型的问题。
首先我们绘制一个关于二次多项式模型的学习曲线,该曲线的横轴是训练集的大小,纵轴是误差大小。
当训练集大小增加时,训练集误差会不断增加最后趋于平稳,而交叉验证集误差则会不断减小最后趋于平稳,如下图所示:
/learning_curves1.png)
出现这种情况的原因是:假如我们刚开始训练集只有一个示例,那么直线就可以很好的拟合训练集数据;当训练集数据继续增加为两个时,直线还是能够轻易地拟合所有数据;当训练集数据增加到三个时,我们可以使用二次函数进行很好的拟合;但是当训练集数据继续增多时,简单的二次函数就很难保证能够穿过所有的训练集数据点,所以训练集误差会随着训练集大小的增加而增加。而对于交叉验证集误差,虽然随着训练集大小的增加,模型很难拟合所有训练集数据,但是模型的泛化性会随着训练集的增多而不断增加,所以交叉验证误差就会不断减小。
需要注意的是虽然训练集误差在不断增大,交叉验证集误差在不断减小,但是交叉验证集误差会始终大于训练集误差,这是因为我们的模型是根据训练集数据训练得到的,那么模型在训练集上的表现肯定会优于模型在交叉验证集上的表现。
3.2.4.1 高偏差时的学习曲线
我们以一次多项式模型为例,在拟合训练集数据时很容易出现高偏差问题(欠拟合),这是因为直线很难拟合所有的训练集数据,当增加训练集数据时,训练集误差很明显会不断增加,因为不在模型上的点会越来越多;对于交叉验证集误差,虽然模型的复杂度没有改变,但是训练集大小的增加可以更好地平均化误差,从而使得交叉验证误差降低。
/learning_curves2.png)
如果我们将基准性能水平画在图上,我们会发现基准性能水平会在训练集误差和交叉验证集误差以下。
/learning_curves3.png)
那如果我们增加更多的训练集数据会发生什么情况呢?结果就是训练集误差和交叉验证集误差会一直继续变平延伸,不管增大到多大,训练集误差和交叉验证集误差都不会低于基准性能水平。这是因为模型的复杂度已经决定了模型表现的上限,不管再怎么增加数据,模型都不会表现得很好。所以我们可以得到一个重要的结论,那就是:对于高偏差问题,只增加训练集的大小是无法解决解决问题的。
3.2.4.2 高方差时的学习曲线
我们以四次多项式模型为例,在拟合训练集数据时很容易出现高方差问题(过拟合),同样的,当增加训练集数据时,训练集误差会不断增加,交叉验证集误差会不断降低,但是由于过拟合,所以交叉验证集误差会远远大于训练集误差。
/learning_curves4.png)
如果我们将基准性能水平画在图上,我们会发现基准性能水平会在训练集误差和交叉验证集误差之间。
/learning_curves5.png)
当我们增加更多的训练集数据时,训练集误差依旧会不断增加并趋于平稳,但是由于训练集大小的增加,模型的泛化性会得到提升,所以交叉验证集误差会不断减小最终趋于平稳,最终两者都会趋近于基准性能水平。所以我们可发现:对于高方差问题,增加训练集的大小可以提高模型的泛化性,从而有效地解决过拟合问题。
3.2.5 使用有效的方法来调整模型
通过上面几节的学习,我们可以根据模型的训练集误差和交叉测试集误差来判断模型是否具有高偏差或者高方差,从而根据判断来选择有效的方法对模型进行调整。
我们一般调整模型有以下几种方法:
- 获得更多的训练示例来对模型进行训练
- 尝试更小的特征集
- 尝试获得额外的特征
- 尝试增加多项式的次数
- 尝试减小正则化参数
- 尝试增大正则化参数
其中1.2.6可以用来解决高方差(过拟合)问题,3.4.5可以用来解决高偏差(欠拟合)问题。
3.2.6 神经网络中的偏差与方差
当我们将不同阶的多项式拟合到一个数据集上时,如果多项式过于简单就会产生高偏差问题,如果多项式过于复杂就会产生高方差问题,我们需要做的就是找到一个合适的模型使其不仅具有低偏差,同时具有低方差。
而对于神经网络,事实证明,只要神经网络足够大,模型就几乎总能够很好的适应训练集。这就使得我们不需要寻找一个偏差和方差都很低的点,而是可以根据需要尝试减少偏差或方差。
在实际训练中,我们可以首先根据训练集误差判断模型是否具有高偏差,如果有尝试使用更大的神经网络来对模型进行不断调整,最终使其不再具有高偏差;之后再根据交叉验证集误差判断模型是否具有高方差,如果有,可以通过增加数据集的方式对模型进行调整,最终使其具有低方差。就这样不断循环训练,我们就可以得到一个满意的神经网络模型。
/neural_network_bias_and_variance.png)
并且,事实证明,只要我们适当地正则化这个神经网络,我们就不用担心模型复杂导致的高方差问题。
3.3 机器学习开发
3.3.1 机器学习开发中的迭代循环
机器学习开发中的迭代循环一般有三步:
- 决定系统的整体框架,比如选择使用什么模型、使用哪些数据等等。
- 训练模型。
- 误差分析,包括偏差、方差以及误差的分析
/iterative_loop1.png)
训练一个神经网络往往需要经历很多次迭代循环,才能达到想要的性能。
我们以构建一个简单的垃圾邮件分类模型为例:
其中输入特征为电子邮件,输出结果为判断该邮件是否为垃圾邮件。我们可以列举10000个单词,通过统计这些单词在邮件中出现的次数来作为该邮件的输入特征
/iterative_loop2.png)
有了这些特征,我们就可以选择合适的模型进行训练了,比如逻辑回归模型或者是神经网络模型。在训练完模型之后,模型不一定会按照我们想象中地效果进行工作,这就需要我们对模型进行误差分析并进行调整。
我们调整的方式有许多,比如:
- 收集更多的数据对模型进行训练
- 基于邮件路由开发更复杂的特征
- 从邮件本体中定义更复杂的特征,比如将"discounting"和"discount"视为同一个单词
- 设计一个算法用来检查邮件中故意拼写错误的单词
不同的方法可能会对模型训练产生不同的效果,所以选择最有效的方法就非常重要,这就需要我们对模型进行误差分析。
3.3.2 误差分析
误差分析主要用于理解模型的性能和改进模型的质量。通过误差分析,我们能够识别和解释预测误差的来源,从而采取相应的措施进行优化。我们以垃圾邮件分类模型作为案例来解释误差分析的作用。
假如在该垃圾邮件分类模型中,对于500个检查验证集数据的预测,其中有100个示例被算法误判了,我们可以手动分析这100个示例,并深入了解算法出错的地方。
首先我们可以根据邮件的一些特征对其进行分类,比如:
在这100封误判邮件中,其中有21封药物垃圾邮件,有3封可能是因为故意的拼写错误而造成的误判,有7封是不常见的电子邮件路由,有18封试图窃取密码或网络钓鱼的电子邮件,还有5封是将垃圾邮件嵌入图片中造成的误判。
/error_analysis.png)
通过统计这些问题,我们会发现模型对于药物垃圾邮件以及窃取密码的垃圾邮件误判较多,而对于故意拼写错误、不常见电子邮件路由以及图片式垃圾邮件误判较少,所以当我们在对模型进行调整时,可以优先针对误判较多的问题进行调整,而对于误判较少的问题类型,我们就不必花费太多时间来解决这些问题,因为就算解决了对模型的提升也不大。
当我们的数据集很大时,误判的示例数量也会随之增加,这使得我们很难对所有的误判示例进行分类,这时我们就可以随机选取一部分误判示例进行误差分析。
3.3.3 获得更多数据
在训练机器学习模型的过程中,我们经常会想要获得更多的数据用作训练,但是获取更多的数据可能既昂贵又慢,所以我们需要一些其他方法来获取更多的数据。
3.3.3.1 数据增强
数据增强(data augmentation)是指通过对现有的数据进行各种变换,从而生成新的数据样本用以提高模型的泛化能力。
比如对于一个字母识别模型,我们现在有一个样本图片A,我们可以对它进行放大、缩小、旋转颜色变换等操作得到一个新的样本示例。
/add_data1.png)
如果再高级一点,我们可以引入一个网格,通过对网格的调整来扭曲原示例,从而得到新的数据样本。
/add_data2.png)
而对于一个语音识别模型,我们可以通过对原音频示例添加背景声音从而得到新的数据示例。
/add_data3.png)
需要注意的是,引入的失真应该代表测试集中的噪声/失真类型。比如对于图片中的字母A,我们想向每个像素都添加噪声,那么最终得到的图片与原示例看起来并没有多大的改变,那么这种新示例也就无法很好的提高模型的泛化性。或者是对于音频示例,如果我们只是随机地添加一些杂乱无章的背景,而不是在实际生活中存在的汽车或人群噪音,那么它也不会对我们训练模型起到很大的帮助。
/add_data4.png)
3.3.3.2 数据合成
数据合成(data synthesis)是指使用一些工具创建一个完全新的数据示例。
比如我们需要识别一张图片中的各种文字:
/add_data5.png)
当图片输入到模型中之后,模型会生成这样的数据:
/add_data6.png)
为了创造新的训练示例,我们就可以使用文本编辑中的更重不同的字体,通过对这些字体放大缩小、改变颜色等操作创建一个新的示例。
/add_data7.png)
3.3.4 迁移学习
当我们想要训练一个模型时,可能会遇到数据集不足的问题,这时如果我们有一些相似的数据集,那么我们就可以使用迁移学习(transfer learning)来构建我们的神经网络模型。
比如,当我们想构建一个手写数字识别的神经网络模型时,我们发现手上并没有足够的数据来进行训练,但是我们恰好有一百万个其他类型的图片,比如猫、狗、车、人等等。那么我们就可以使用迁移学习来构建我们的神经网络模型。
具体的做法是,我们首先使用这一百万个图片训练一个关于图像识别的神经网络模型,训练完之后,我们可以取该神经网络输出层之前的所有层作为我们手写数字识别模型的隐藏层,最后再加上一个新的输出层,这就构建了我们所需要的神经网络模型。之后我们在使用已有的手写数字数据集对模型进行训练即可。
当我们在训练迁移后的模型时,我们一般有两种训练方案:一种是只训练最后输出层中的参数,另一种方案是将原神经网络隐藏层的参数值作为新神经网络模型隐藏层参数的初始值,然后训练所有层的参数。
/transfer_learning1.png)
迁移学习之所以能实现这种任务主要是因为对于相似类型的神经网络模型,它们在前面隐藏层中完成的任务基本上是类似的,比如对于物体识别模型中中,它在第一层会检测物体的边缘,在第二层会检测物体的角落,在第三层会检测物体的曲线或形状,而对于一个手写数字识别模型,它在前几层完成的工作基本上也是这几步,它们之间的不同点就主要在于输出层中对物体拼接后的识别,所以才能实现迁移学习。
/transfer_learning2.png)
迁移学习的实现原理也说明了一个问题,就是迁移学习只能在相似模型之间迁移,最终实现的神经网络模型必须与预训练的神经网络模型是相同类型的,比如我们要得到一个手写数字识别模型,那么预训练的模型就应该也是这一类型的,而不能是语音识别模型,因为它们的输入示例类型就不同。
3.4 倾斜数据集的误差度量
3.4.1 精确率与召回率
当我们在构建一个机器学习模型时,如果其中正例与负例的比例差非常大,那么我们称之为倾斜数据集,对于倾斜数据集,通常的误差指标就不能达到很好的效果了。
比如我们有一个罕见病诊断模型,在我们对该模型进行测试后发现,该模型在测试集上上的正确率为99%,根据我们之前的模型评估标准,这个模型表现得很好,但是实际上数据集中真正有病的人只有0.5%,假如我们让模型不论输入什么都输出0(无病),那么实际上这个看起来不合理的模型却能达到99.5%的正确率,比我们辛辛苦苦训练得到的模型准确率还高。
所以在倾斜数据集中,误差很低并不意味着这个模型真正有用,那么我们应该用什么标准来评估这种模型呢?那就是精确率和召回率。
精确率是指模型预测为正类的样本中,实际为正类的比例,它回答了这样的问题:在所有被模型预测为正的实例中,有多少是真正的正类。
召回率是指实际为正类的样本中,被模型正确识别为正类的比例。它回答了这样的问题:在所有实际为正的实例中,有多少被模型正确识别为正类。
比如在一个模型预测结果中,我们将实际为1,模型预测也为1的样本叫做真阳性,实际为0,模型预测为1的样本叫做假阳性,实际为1,模型预测为0的样本叫做假阴性,实际为0,模型预测也为0的样本叫做真阴性。
/precision_recall1.png)
根据定义,精确率和召回率分别表示为:
这样我们就可以根据精确率和召回率来对倾斜数据集模型进行评估了。
3.4.2 精确率与召回率之间的权衡
理想情况下,我们肯定想要在高精确率的情况下同时获得高召回率。但是在实际过程中我们需要根据目标来做出平衡。
比如:在罕见病诊断时,如我们想尽可能准确地诊断出罕见病患者,那么我们就需要提高精确率,提高精确率的办法就是提高预测的阈值。
/precision_recall2.png)
比如我们在原来的模型中,模型输出结果大于等于0.5时预测结果为1,小于0.5时预测结果为0,我们就可以通过将阈值提高到0.7来使预测结果更为精准。
如果我们想让所有患病的患者都被预测到,那就需要提高回归率,这时我们就可以通过调低阈值来达到这一目的。
/precision_recall3.png)
还有一种常用的用于平衡精确率与召回率的方法是使用F1分数来进行模型评估。F1分数的计算公式为:
/precision_recall4.png)
通过计算每个模型的F1分数,我们就可以选出F1分数最高的,也就是最理想的模型。
四、决策树
4.1 决策树
4.1.1 决策树模型
决策树(decision tree)是一类常见的机器学习方法。以二分类任务为例,我们希望从给定的训练数据集中学得一个模型用以对新示例进行分类,把这个样本分类的任务,可看做对“当前样本属于正类吗”这个问题的“决策”或“判定”过程。
我们以一个猫分类为例。加入我们现在有一组关于猫和狗的样本示例,里面包括三个特征:耳朵形状、脸的形状以及胡须。其中每个特征都有两种可能的选择:耳朵形状:尖耳或圆耳,脸的形状:圆脸或不是圆脸,胡须:有胡须和没胡须。我们需要训练一个模型通过对这三个特征进行分析从而判断该动物是否为猫。
/decision_tree1.png)
下面就是关于该数据集得到的一个决策树模型,它其实是一个二叉树:
/decision_tree2.png)
在判断一个样本示例时,我们首先从根结点出发进行判断,例如一个尖耳圆脸没胡须的猫,由于它是尖耳,所以我们将它放到根结点的左子树中,再判断其脸的形状,由于它是个圆脸,所以我们再往左子树走,最终到达叶子结点,判断结果为它是猫。
在此决策树中,我们根结点是根据耳朵的形状进行判断的,如果我们使用其他特征作为根结点的分裂依据,我们还可以得到其他不同的决策树。
/decision_tree3.png)
在本节中我们需要学的不仅仅是如何构建决策树,还有如何选择最适合的决策树。
4.1.2 决策树构建过程
接下来我们以第一节中的猫识别模型为例构建一棵决策树。
在根节点中,我们一共有10个样本数据。
/decision_tree4.png)
首先我们需要选择一个特征值作为根结点的分裂依据,这里我们选择耳朵形状作为根结点的分裂依据,那么这10个样本示例就会按耳朵形状被分到左右子结点中。
、
然后我们先看左结点中一共有5个样本示例,我们再以脸部形状为分裂依据进行分裂,得到该结点的左右子结点。
/decision_tree6.png)
在其左结点中全部都是猫,在其右结点中全部都是狗,所以我们就可以停止分裂,左子树就分裂完成。
/decision_tree7.png)
之后我们再看根结点的右子树,右子树中也有5个样本示例,我们可以根据是否有胡须对其进行分裂,分裂之后由于该结点左子树全为猫,右子树全为狗,所以就不需要再继续分裂了。
/decision_tree8.png)
至此,该决策树构建完毕。
在构建决策树时,我们需要思考几个问题:
- 如何选择在每个结点中分裂的特征依据呢?
- 答案是使用该结点中分裂后的纯度变化为依据
- 何时停止分裂?
- 当一个结点中只有一种类型的样本时停止分裂
- 当一个结点分裂后会超过该树的最大深度时停止分裂
- 当分裂后的纯度提升低于某个阈值时停止分裂
- 当一个结点中的样本个数小于某个阈值时停止分裂
4.2 纯度和熵
4.2.1 纯度
在选择每个结点分裂时使用的特征时,我们一般需要判断分裂前后的纯度变化,衡量纯度的一种标准叫做熵(entropy)。
首先我们定义表示所有样本中正类所占的比例,假如现在有6个样本示例,其中3个狗3个猫,那么,熵通常表示为的函数,熵函数关于的曲线大致如下:
/entropy1.png)
需要注意的是:熵表示的只是样本的混乱程度,而不是与正类的比例成正比,样本中全是正类和全是负类时的熵均为0。
如果我们定义:
那么,熵的表达式就为:
其中,我们记。
4.2.2 信息增益
有了熵,我们就可以通过计算熵的变化来确定选择哪个特征作为分裂依据最合适,而信息增益(information gain)可以衡量由于分裂而导致的树中的熵的减少。
以之前的猫分类模型为例,在根结点中我们可以根据已知的三个特征中的一个来进行分裂,通过计算,它们的熵如下:
/entropy2.png)
在计算每棵树的熵时,我们一般使用加权平均数,其中权重为该结点中正类所占的比例。之所以用加权平均数是因为小的子节点(包含较少数据的结点)的熵值可能对总熵值产生不合理的影响,导致分裂选择出现偏差。通过加权平均可以确保大的子结点对最终的总熵值影响更大,更符合实际情况。
计算熵值的公式为:
其中,表示左子结点中示例个数占总示例个数的比例,表示左子结点中正例个数占左子结点中示例个数,和同理。
信息增益指的是根节点中熵值与分裂后子结点中熵值的变化量,其公式为:
信息增益越大,说明熵值减小越多,也就说明本次分裂的效果越好。
/entropy3.png)
4.2.3 整合
构建一个决策树的步骤如下:
- 将所有的样本示例从根结点开始
- 计算使用每个特征分裂时的信息增益,然后选择信息增益最高的特征
- 按照所选的特征进行分裂数据集,然后创建左右分支
- 重复分裂的步骤直到达到停止分裂时的条件
- 当一个结点中只有一种类型的样本时停止分裂
- 当一个结点分裂后会超过该树的最大深度时停止分裂
- 当分裂后的纯度提升低于某个阈值时停止分裂
- 当一个结点中的样本个数小于某个阈值时停止分裂
具体的构建过程参考4.1.2中过程,增加的只是选择特征的过程。
4.2.4 one-hot编码
在之前我们所见到的示例中,每个特征都只能采用两种可能值中的一种,但是在实际情况中,一定会有多中可能值的特征出现,这时我们可以采用的一种方法就是在结点处分裂为多个分支,类似于多叉树,但这种方法会使得整棵决策树变得十分复杂,所以我们常用的一种方法就是使用one-hot编码。
one-hot编码是指将一个可以取k个值的特征化为k个只能取0/1可能值的特征。比如对于之前的猫分类模型,假如耳朵形状可以有三种取值:尖耳、圆耳、椭圆耳。
/one_hot1.png)
那么通过one-hot编码,我们就可以将其分解为三个特征:有无尖耳、有无圆耳和有无椭圆耳,这样就有变成了之前所学的决策树模型。
/one_hot2.png)
同样的,我们也可以将脸步形状和有无胡须也用0/1表示:
/one_hot3.png)
这样就以便于将这些特征作为输入送到神经网络中训练模型。
4.2.5 连续值特征
之前我们见到的特征取值都是离散的,那么如果特征取值是连续的该怎么办呢?
依旧以猫分类模型为例,加入我们现在有一个新的特征:体重,那么我们该如何以体重作为特征值来分裂结点呢?
/continuous_feature1.png)
首先我们将其画在图中,横轴表示体重,纵轴表示猫和非猫。
/continuous_feature2.png)
之后我们可以将所有示例标在图中,之后我们可以划定一个阈值,当体重大于阈值时,我们判定其非猫,当体重小于阈值时,我们判定其为猫。
比如当我们划定阈值为8时,那么判定为猫的数量为2,非猫的数量为8,所以信息增量计算为,然后我们可以再将阈值设置为9进行计算,之后我们可以再将阈值设置为其他值进行计算,最终通过比较,阈值为9的时候信息增益最高,所以我们就可以选择9作为体重的阈值,然后按该阈值来分裂结点。
/continuous_feature3.png)
4.2.6 回归树模型
在之前的案例中,我们所做的之后是为了得到一个判断,而本节中的回归树可以帮助我们预测一个数字。
加入我们现在训练集中已知10个样本的四个特征:耳朵形状、脸部形状、有无胡须和体重,现在我们想通过判断前三个特征来预测一个新样本的体重,这时候我们就可以使用回归树。
/regression1.png)
与之前的决策树一样,我们可以通过使用不同的特征分裂出不同的决策树,但是我们该如何判断那个是最优的呢?那就是使用平均加权方差。
之所以使用平均加权方差是因为方差反映了数据的离散程度,能够很好地捕捉数据的分布特征,较小的方差意味着数据更加接近均值,代表节点内的样本更加集中,从而可以认为该节点的预测更加精准。
与之前的衡量方法相同,在这里我们也使用信息增益来进行评估,其公式为:
/regression2.png)
最后我们就可以选择信息增益最大的特征作为分裂特征。
4.3 随机森林
4.3.1 使用多个决策树
在之前的模型中我们一直都使用单个决策树,但是单个决策树有一个很大的问题,就是单个决策树对数据集中小的变化非常敏感。
比如我们现在有10个样本,经过计算信息增益,我们选用耳朵形状作为根结点的分裂特征,但是如果我们更换其中一个样本时,就会发现经过重新计算后,有无胡须变成了最适合根结点分裂的特征。我们仅仅更改了一个样本案例就使得整棵决策树变得完全不同,这足以说明单个决策树对数据的改变有多敏感。
/trees1.png)
我们常用的一个解决办法是使用决策树集成,也就是使用多个决策树,通过多个决策树进行判断后进行投票,从而获得最终的预测结果。
比如在这个决策树集成中,
/trees2.png)
第一颗决策树预测结果为猫,第二棵决策树预测结果为非猫,第三棵决策树预测结果为猫,所以最终的预测结果为猫。
4.3.2 替换采样
为了构建决策树集成,我们需要使用一种称为替换采样的技术。
替换采样(sampling with replacement)是一种从数据集中抽取样本的方法,其中每次抽取的数据点在被抽出后仍然可以被重新放入数据集中参与下次抽取,因此它有可能在后续的抽取中再次被选中。
比如现在袋子中有四种颜色的令牌,分别是红黄绿蓝,我们每次抽取一个令牌,一共抽取四次。
第一次抽取到绿色,之后我们将绿色放回进行第二次抽取,第二次抽取到黄色,之后我们再将黄色放回进行第三次抽取,依次类推,最终抽取到的结果为绿黄蓝蓝。
/sampling1.png)
我们使用这种方法的目的是为了得到不同的初始数据集,这样我们就可以根据不同的初始数据集训练得到不同的决策树,虽然这些数据集不同,但是其中的每个样本都属于原本的数据集,所以每棵决策树都能很好地进行预测。
4.3.3 随机森林
有了替换采样的技术我们就可以开始进行构建决策树集成了。
首先我们有包含m个样本的训练集,之后我们重复B次以下操作以构建单个决策树:
使用替换采样技术得到一个包含m个样本的训练集,之后用该训练集训练得到一棵决策树。重复B次后,我们就得到了一个包含B棵决策树的决策树集成。
但是这样做会有一个问题,那就是当决策树很多时,由于训练集可能相似,所以在根结点处使用的分裂特征就可能一样,这就使得最终可能有一些决策树很相似,也就造成了一些资源的浪费。
所以我们通常进行的一个改进就是:在每个结点中,当选择一个特征用于分裂结点时,如果原本有n个特征,那么我们就选取k(k< n)个特征作为特征子集,然后在构建决策树时从这k个特征子集中选择特征使用。通常情况下或。这种算法就称为随机森林算法。
随机森林算法表现好的原因主要归结于以下几个关键因素:
- 模型集成(Ensemble Learning):
• 降低方差:随机森林通过集成多个决策树的预测结果来减少单个决策树带来的过拟合问题。单棵决策树容易受训练数据中的噪声和细节影响,从而导致高方差,即模型对训练数据非常精准,但对新数据的泛化能力差。随机森林通过结合多个不同的决策树,平均它们的预测结果,降低了整体模型的方差,从而提高了泛化能力。
• 降低偏差:在某些情况下,随机森林也可以通过集成学习的方式降低模型的偏差,特别是在单棵决策树不足以捕捉数据的复杂模式时。 - 随机性引入(Randomization):
• Bagging(Bootstrap Aggregating):随机森林通过Bagging方法生成多个训练数据子集,每个子集通过替换采样(有放回地随机抽样)生成。这种方法不仅使每棵树对数据的不同子集进行训练,而且可以减小单个数据点对模型的影响,增强模型的稳健性。
• 随机特征选择:在构建每棵树时,随机森林不是在所有特征上寻找最佳分裂,而是随机选择部分特征来寻找最佳分裂点。这种随机选择进一步减少了不同树之间的相关性(即树之间的依赖性),从而增强了集成模型的多样性。模型的多样性有助于提高整体的预测能力,因为不同的树对同一数据集的处理方式不同,这样错误就不会被系统性地复制。 - 减少过拟合:
• 通过多数投票或平均:在分类问题中,随机森林通过多数投票机制来决定最终的预测类别;在回归问题中,随机森林通过平均所有树的预测值来给出最终预测。这种方式有效地平滑了单棵决策树可能出现的过拟合问题,因为过拟合的部分噪声和错误预测在集成模型中往往会被多数正确的预测所覆盖,从而提升模型的稳定性和准确性。 - 自动处理高维数据和特征选择:
• 随机森林对高维数据(即特征非常多的数据集)表现出色,部分原因是其随机特征选择的机制。即使数据集有许多无关或冗余的特征,随机森林的子树仍然可以有效地找到相关特征并进行合理分裂,最终仍然能做出准确的预测。
• 此外,随机森林还可以提供特征重要性度量(Feature Importance),帮助识别哪些特征对预测影响最大,从而有助于数据特征工程和理解模型。 - 鲁棒性和广泛适用性:
• 抗噪声能力强:由于每棵决策树都只使用部分数据和特征,随机森林对噪声和异常值不敏感。这意味着即使数据集存在一些异常点,随机森林依然能保持较好的预测性能。
• 适用性广泛:随机森林可以处理分类和回归问题,适用于各种类型的数据集,无论是线性可分还是非线性数据集。它的鲁棒性和广泛适用性使得它在众多应用场景中表现出色。 - 内置交叉验证机制:
• 随机森林在训练过程中可以利用袋外数据(Out-of-Bag, OOB)对模型进行内置的交叉验证,从而有效地评估模型的性能,而无需额外的验证集。这种内置评估机制使得模型调参更方便,并能在一定程度上防止过拟合。
总之,随机森林算法之所以表现好,是因为它通过集成多个决策树,并在训练过程中引入随机性,从而降低了模型的方差和偏差,提高了模型的泛化能力、鲁棒性和抗噪声能力。同时,它还能够自动处理高维数据、选择重要特征,并提供较好的解释性。这些特点使得随机森林在许多机器学习任务中都表现优异。
4.3.4 XGBoost
XGBoost(eXtreme Gradient Boosting,极端梯度提升)是现在常用的一种构建决策树集成的方法。与上节中介绍的构建决策树集成方法的不同点在于使用替换采样创建新的训练集时不再是从m个样本中等概率抽取了,而是更有可能选择到先前决策树表现不佳的样本,其实就是针对性地训练。
/XGBoost.png)
XGBoost有以下优点:
- 是提升决策树的开源实现
- 非常的快速和高效
- 有很好的选择默认拆分标准和停止拆分标准
- 内置了正则化以防止过拟合
- 广泛应用于机器学习的比赛中
XGBoost使用时的代码也非常简洁:
1 | # 分类问题 |

/adam1.png)
/adam2.png)
/cover.png)
/cover.jpg)