集成学习的提出,有时也被笼统的称作提升(Boosting)方法,很广泛的应用与分类和回归任务,也就是说我们在学习集成学习的时候,重点要关注的应用场景不是单一的回归或者分类,而是二者都有。他的最初的思想:使用一些方法可以改变训练样本分布,从而构建多个不同的分类器,并将这些分类器线性组合得到一个更加强大的分类器。
在进行集成学习器的时候,我们会接触到弱学习器和强学习器,那么我们先要搞清楚所有只是架构的基础,就先来看看这两者是如何定义的:
集成学习的理论基础来自于Kearns和Valiant提出的基于PAC(probably approximately correct)的可学习性理论 ,PAC 定义了学习算法的强弱:
弱学习算法:识别错误率小于1/2(即准确率仅比随机猜测略高的算法)
强学习算法:识别准确率很高并能在多项式时间内完成的算法
集成学习一开始的目的是可以借助多个弱学习器进行线性组合,从而极大的提高他们的泛化能力,但是我们也可以将强学习器进行线性组合。
那么根据上面的描述,我们大概可以看到集成学习的关键点就在于:
1. 如何改变数据的分布或者权重
2. 如何将多个弱分类器组合成一个强分类器
针对上面的两个问题,目前主流的方法有三种:
1. Boosting方法:指序列化方式,常见的为:Adaboosting,GBDT,XGBoost等
2. Bagging方法:指并行化方式,常见的为:随机森林
3. Stacking算法
下面我们就按照上面主流方法的顺序来一一介绍这些算法
1 Boosting
根据boosting算法的基本原理,我们可以知道,我们是
1.1 Adaboosting
1.1.1 公式推导
在boosting算法中,常见的为adaboost。我们之前已经知道,boosting算法的基本思想就是在已知之前分类器的分类误差之后,我们可以改变下一个分类器的样本分布权重,来突出被错误分类的样本,改善下一次的学习器的性能。
我们先贴出来Adaboosting的算法过程,然后再一一通过推导公式来证明算法中的参数迭代公式:
算法流程为:
之后的图片是我个人在推导公式的时候的具体过程,这里建议大家自己推导一下,好搞清楚每一步的推导原因
下图表示的是为什么我们可以使用指数损失函数作为Adaboosting的损失函数
接下来的几幅图分别对应了 αt,Dt α t , D t 的迭代公式推导
αt α t 迭代公式:
Dt D t 样本分布参数迭代公式:
至此,我们就完成了对于Adaboosting的公式推导
1.1.2 Adaboosting优缺点
优点:
- adaboosting算法是一个有很高精度的分类器,通过多个弱分类器的线性组合,提高了整个的泛化性能
- adaboosting提供了一个非常好的框架和基本思路,在应用不同的基分类器,就会产生不一样的算法效果。
- 如果使用弱分类器,弱分类器会有结构简单的优点,只要通过其线性组合就会极大的提高泛化性能
- 不需要担心过拟合,因为会在权重上有所改变,泛化性能很高
缺点:
- 根据计算过程,我们知道,每一次的样本参数分布的确定都要根据上一次分类器的错误率有关,而且和每个样本都有关,这样的话会受到outlier等极端点的影响很大,对outlier非常敏感。
1.2 GBDT算法(梯度提升树)
GBDT算法仍然使用加法模型与前向分布算法。GBDT的基学习器是回归树(不是分类树),GBDT用来做回归预测,调整之后也可以用分类
- GBDT与Adaboost的区别:
- 最主要区别在于如何识别模型的问题。Adaboost利用识别错分点来识别问题,通过在循环迭代中对错分点的样本权重进行调整,来增加后续分类器的对于错分样本点的识别改进;GBDT是利用负梯度来识别问题,通过计算负梯度来改进模型
- GBDT是思想使其具有天然优势可以发现多种有区分性的特征以及特征组合。
1.2.1 回归树:
回归树总体流程类似于分类树,区别在于,回归树的每一个节点都会得一个预测值,以年龄为例,该预测值等于属于这个节点的所有人年龄的平均值。分支时穷举每一个feature的每个阈值找最好的分割点,但是注意这里我们用到的优化函数不再是信息熵/信息增益等,因为这个是用来依据分类标签来判断节点属性是用于分类的,但是回归树是用来预测的,每一个节点都是一个预测值。所以我们用最小化均方误差来找到分支依据
来自李航的《统计学习方法》中的CART Tree回归树算法的推导算法截图为:
借用一个博客的代码生成的图,我们可以对比一下回归树与线性回归的比较
import numpy as np
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeRegressor
from sklearn import linear_model
x = np.array(list(range(1, 11))).reshape(-1, 1)
y = np.array([5.56, 5.70, 5.91, 6.40, 6.80, 7.05, 8.90, 8.70, 9.00, 9.05]).ravel()
model1 = DecisionTreeRegressor(max_depth=1)
model2 = DecisionTreeRegressor(max_depth=3)
model3 = linear_model.LinearRegression()
model1.fit(x, y)
model2.fit(x, y)
model3.fit(x, y)
X_test = np.arange(0.0, 10.0, 0.01)[:, np.newaxis]
y_1 = model1.predict(X_test)
y_2 = model2.predict(X_test)
y_3 = model3.predict(X_test)
plt.figure()
plt.scatter(x, y, s=20, edgecolor="black",
c="darkorange", label="data")
plt.plot(X_test, y_1, color="cornflowerblue",
label="max_depth=1", linewidth=2)
plt.plot(X_test, y_2, color="yellowgreen", label="max_depth=3", linewidth=2)
plt.plot(X_test, y_3, color='red', label='liner regression', linewidth=2)
plt.xlabel("data")
plt.ylabel("target")
plt.title("Decision Tree Regression")
plt.legend()
plt.show()
结果图如下
我们注意到使用回归树进行预测的话,如果回归树深度过大会有如图中绿线所显示的容易过拟合,但是如果深度过小的话,那么会有极大的欠拟合风险。那么我们那么我们该如何防止过拟合呢??可以查看我之前的博客关于决策树一节的讲解。 1. 我们可以使用CCP代价复杂度剪枝的方法,自下而上的对原始树进行剪枝,生成一系列子树序列,接着采用交叉验证的方法对这一系列子树序列进行验证,选出一个最优子树。 2. 想要得到更好的预测,处理更加复杂的问题,回归树是不够用的,所以我们就有了GBDT,xgboost等算法
1.2.2 提升树算法(Boosting Decision Tree)
CART回归树的生成树,是一个二叉决策树,每一个二叉节点的分支都是由其特征属性进行最小化均方误差得来的,每一个节点与分类决策树不同的是都是目标属性的预测值而不是选择分类的属性。 提升树是迭代多棵回归树来共同决策。当采用平方误差损失函数时,每一个回归树学习的是之前所有树的残差(残差 = 真实值- 预测值)。拟合得到一个当前的残差回归树,提升树即是将整个迭代过程生成的回归树的累加 注意:这里专门就针对的是回归问题,而不是分类问题(当然分类问题可以通过回归预测值来确定,我们之后讨论)
1.2.3 梯度提升树算法(GBDT):
1.2.3.1GBDT的基本思想:
与Adaboost使用误差率来更新样本权重迭代生成基学习器和其权重值的方法不同,GBDT普遍基于CART回归树使用样本损失值来不断学习后面的CART树,使本轮的损失最小。本轮迭代找到决策树,要让样本的损失尽量变小。 注意:损失函数多种多样,我们应该使用一个什么样的普遍的通用的思想来去拟合呢?重点内容
1.2.3.2 负梯度拟合:
Freidman提出了使用损失函数的负梯度来拟合本轮损失的近似值,进而拟合一个CART树的思想。我们将其损失函数的负梯度表示为:
rti=−∂L(yi,ft−1(xi))∂ft−1(xi)) r t i = − ∂ L ( y i , f t − 1 ( x i ) ) ∂ f t − 1 ( x i ) )
利用
(xi,rti)(i=1,2,3...,m) ( x i , r t i ) ( i = 1 , 2 , 3... , m ) ,我们可以拟合一颗CART回归树,得到了第t颗回归树,其对应的叶节点区域Rtj,j=1,2,…,J。其中J为叶子节点的个数。
针对每一个叶子节点里的样本,我们求出使损失函数最小,也就是拟合叶子节点最好的的输出值ctj如下:
ctj=argminc∑x∈RtjL(yi,ft−1(x)+c) c t j = a r g min c ∑ x ∈ R t j L ( y i , f t − 1 ( x ) + c )
根据计算,在损失函数为最小二乘均方误差时,最好的输出值为其
均值
于是我们就得到了本轮训练的CART决策树的方程:
ht(x)=∑j=1JctjI(x∈Rtj) h t ( x ) = ∑ j = 1 J c t j I ( x ∈ R t j )
于是本轮得到的强学习器为:
ft(x)=ft−1(x)+ht(x) f t ( x ) = f t − 1 ( x ) + h t ( x )
注意一点的是:Freidman的负梯度拟合本轮损失近似值的思想不是必须使用DT,其他的基学习器一样可以,同样的损失函数一样不一定是均方误差,我们还可以使用其他的损失函数。那么为什么我们要使用DT呢??我对比其他的常见的学习器有如下的猜测(暂定):
1. DT对于线性非线性数据样本集都有很好的分类预测效果,即使分类效果在无噪音数据集中不如SVM等,但是GBDT通过拟合多个DT也能达到很好的效果
2. DT不需要对数据进行归一化处理
3. DT是一个if-then结构非常容易理解而且容易实现
4. DT正如在决策树一章中提到的,对于缺失值的处理,也是其他常见的SVM等所不具备的
5. DT自然而然的对特征进行不同的组合
1.2.3.3GBDT回归算法:
GBDT回归算法如下:
input:训练样本D = {(x1,y1),(x2,y2),…,(xm,ym)},最大迭代次数,损失函数L
output:强学习器f(x)
算法流程:
1. 初始化弱学习器:
f0(x)=argminc∑i=1mL(yi,c) f 0 ( x ) = a r g min c ∑ i = 1 m L ( y i , c )
2. 对迭代轮数t = 1,2,3…T:
a.对样本1,2,…,m计算负梯度
rti=−[∂L(yi,ft−1(xi))∂ft−1(xi)] r t i = − [ ∂ L ( y i , f t − 1 ( x i ) ) ∂ f t − 1 ( x i ) ]
b.利用
(xi,rti) ( x i , r t i ) 训练得到一个CART回归树,其对应的叶子节点的区域为
Rtj,j=1,2,3...,J R t j , j = 1 , 2 , 3... , J J为叶子节点个数
c.每个叶子节点找到对应的预测值,该预测值满足
ctj=argminc∑xi∈RtjL(yi,ft−1(xi)+c) c t j = a r g min c ∑ x i ∈ R t j L ( y i , f t − 1 ( x i ) + c )
d. 更新强学习器:
ht(x)=∑j=1JctjI(x∈Rtj) h t ( x ) = ∑ j = 1 J c t j I ( x ∈ R t j )
ft(x)=ft−1(x)+ht(x) f t ( x ) = f t − 1 ( x ) + h t ( x )
3. 输出学习器f(x)
注意:算法中第二步迭代过程中涉及到的CART树生成时我们要注意避免过拟合,防止过拟合的方法有很多种,比如基于后验概率的PEP剪枝,限定树的深度,限制叶子节点的个数等等。
1.2.3.4GBDT分类算法:
注意到我们的GBDT使用的基学习器是CART回归树,所以我们输出的回事连续的值而不是离散的类别。所以我们无法直接从输出类别拟合误差。为了解决这个问题我现在想到的有两种
1. 损失函数采用指数函数,GBDT退化为Adaboost算法
2. 使用类别的预测概率值和真实概率值的差来拟合损失。也就是说,GBDT仍然是预测连续值,但是预测的连续值不再是类似于年龄这类的属性值,而是样本条件概率的值,那么我们就要使用类似于逻辑回归的对数似然函数的方法
1.2.3.4.1 GBDT二分类算法:
我们也知道,对于对数似然函数的分类有sigmoid和softmax分别对应了二分类和多分类,所以我们这里也有二分类和多分类。
首先对于二分类GBDT,我们同样的要明确我们要的损失函数是什么?
损失函数为:负二项对数似然损失函数
L(y,f(x))=log(1+exp(−2yf(x))),y∈1,1 L ( y , f ( x ) ) = l o g ( 1 + e x p ( − 2 y f ( x ) ) ) , y ∈ 1 , 1
其中。
F(x)=12log[P(y=1|x)P(y=−1|x)] F ( x ) = 1 2 l o g [ P ( y = 1 | x ) P ( y = − 1 | x ) ] ,注意这里的损失函数是和logistic回归的损失函数是一样的。至于为什么么??大家只要把正负标签一致之后就可以了。
接下来的就是求梯度了:
y~=−[∂L(f(x),y)∂f(x)]f(x)=fm−1(x)=2y1+exp(2yfm−1(x)) y ~ = − [ ∂ L ( f ( x ) , y ) ∂ f ( x ) ] f ( x ) = f m − 1 ( x ) = 2 y 1 + e x p ( 2 y f m − 1 ( x ) )
所以我们需要得到的叶子节点的最佳预测值为:
ctj=argminc∑xi∈RtjL(yi,ft−1(xi)+c)=argminc∑xi∈Rtjlog(1+exp(−2yi(ft−1(xi)+c)) c t j = a r g min c ∑ x i ∈ R t j L ( y i , f t − 1 ( x i ) + c ) = a r g min c ∑ x i ∈ R t j l o g ( 1 + e x p ( − 2 y i ( f t − 1 ( x i ) + c ) )
这个公式我们通过Newton-Raphson方法,就可以得到近似值
ctj=∑x∈Rtjy~i∑x∈Rtj|y~i|(2−|y~|) c t j = ∑ x ∈ R t j y ~ i ∑ x ∈ R t j | y ~ i | ( 2 − | y ~ | )
- GBDT二元分类算法:
- input:数据集 D=(x1,y1),(x2,y2),...,(xm,ym) D = ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . . , ( x m , y m ) ,迭代次数 T T ,CART回归树
- 算法过程:
- 初始化: f0(x)=argmixρ∑Ni=1L(y,ρ) f 0 ( x ) = a r g m i x ρ ∑ i = 1 N L ( y , ρ )
- for t from 1 up to T:
- y~=−[∂L(f(x),y)∂f(x)]f(x)=ft−1(x)=2y1+exp(2yft−1(x)) y ~ = − [ ∂ L ( f ( x ) , y ) ∂ f ( x ) ] f ( x ) = f t − 1 ( x ) = 2 y 1 + e x p ( 2 y f t − 1 ( x ) )
- 根据上述的负梯度偏差训练第t个CART回归树,并分成J个 Rtj R t j 区域
- 每个区域预测值 ctj=∑x∈Rtjy~i∑x∈Rtj|y~i|(2−|y~|) c t j = ∑ x ∈ R t j y ~ i ∑ x ∈ R t j | y ~ i | ( 2 − | y ~ | )
- 本轮得到的回归树为 ht(x)=∑Jj=1ctjI(xi∈Rtj) h t ( x ) = ∑ j = 1 J c t j I ( x i ∈ R t j ) ,则此轮的强学习器为 ft(x)=ft−1(x)+ht(x) f t ( x ) = f t − 1 ( x ) + h t ( x )
- 输出强学习器 f(x) f ( x )
1.2.3.4.1 GBDT多分类算法:
多分类GBDT同样的,二元分类的时候使用的负二项对数似然损失函数,多分类的时候我们使用softmax的损失函数:
L(y,f)=−∑k=1Kyklogpk(x) L ( y , f ) = − ∑ k = 1 K y k l o g p k ( x )
则同样的会得到其中的每一类的概率值
pk(x)=efk(x)∑Kl=1efl(x) p k ( x ) = e f k ( x ) ∑ l = 1 K e f l ( x )
所以我们在生成CART回归树的时候要对每一类都要生成一棵树
y~i,k=yi,k−pk,m−1(xi) y ~ i , k = y i , k − p k , m − 1 ( x i )
其中叶子节点的预测值为:
ctj=K−1K∑x∈Rtjy~i∑x∈Rtj|y~i|(2−|y~|) c t j = K − 1 K ∑ x ∈ R t j y ~ i ∑ x ∈ R t j | y ~ i | ( 2 − | y ~ | )