1. 历史及演进

提升学习算法,又常常被称为Boosting,其主要思想是集成多个弱分类器,然后线性组合成为强分类器。为什么弱分类算法可以通过线性组合形成强分类算法?其实这是有一定的理论基础的。1988年,Kearns和Valiant首先提出了“强可学习”和“弱可学习”的概念,他们指出,在概率近似正确(Probably Approximately Correct, PAC)学习的框架中,一个概念,如果存在一个多项式的学习算法能够学习它,并且正确率很高,那么就称这个概念是强可学习的;如果正确率只是稍比随机猜测好,那么就称这个概念是弱可学习的。在两年之后,Schapire证明了强可学习和弱可学习是等价的,也就是说,在PAC学习的框架下,一个概念的强可学习可以由弱可学习转换得到。但在此后很长一段时间里,都没有人能设计一种算法验证PAC理论。直到1996年,Schapire提出了AdaBoost算法才将这种僵局打破。AdaBoost算法将多个不同的决策树用一种非随机的方式组合起来,比传统决策树精度更高也更加稳定。AdaBoost的出现不仅拯救了有些式微的决策树算法,而且还开创了集成学习算法的先河。在AdaBoost的影响下,Friedman在1999年提出了梯度提升决策树算法,这种算法完美地将梯度提升算法和CART算法结合起来,不仅可以用于回归问题,同样也可以应用于分类问题中。GBDT被很多人认为是传统机器学习中位居Top3的算法。即使这样,GBDT也并不完美,相比于AdaBoost,因为后一棵树的训练依赖于前一棵树的残差,所以其并不能进行并行训练。XGBoost是近年来针对大规模机器学习需求对GBDT提出的改进方案。XGBoost是2016年由华盛顿大学在读博士生陈天奇发布的开源框架,相关论文 XGBoost: A Scalable Tree Boosting System 也发表在机器学习与数据挖掘顶级会议KDD2016上。XGBoost较传统的GBDT算法,加入了正则项,能够更好地防止模型过拟合,并且可以并行分布式计算,极大地提高了精度和训练效率。

2. AdaBoost

AdaBoost的关键在于如何生成多个不同的弱分类器,其主要思想是在下一轮迭代中强化那些被误分类的样例,而弱化那些被正确分类的样例。在实际设计时,通过调整每个样例的权重来达到给予不同样例不同关注程度的目的。每一轮的权重调整都是基于上一轮分类器对所有样例分类情况的误差评估,而每个分类器的权重则是由该分类器对样例总体精度决定。

这里我们以最简单的二类分类问题为例对AdaBoost算法进行介绍,其他分类和回归问题与此类似。假定二类分类问题的训练数据集为:$(X,y)=\{(x_1,y_1), (x_2,y_2),...,(x_n,y_n)\}$。其中每个训练样本由特征向量和标记组成,特征向量$x_i \in \mathbb{R}^d, i=1,2,...,n$,$d$为特征向量的维度,标记$y_i \in \{\pm1\}, i=1,2,...,n$。下面将介绍AdaBoost的详细算法流程。

1. 初始化训练数据的权值:

$$W_1=(w_{1,1},w_{1,2},...,w_{1,i},...,w_{1,n}),w_{1,i}=\frac{1}{n},i=1,2,...,n$$

在最开始的时候,我们并不知道训练数据的权值分布,只能假定每个训练样例在初始的基本分类器中的作用相同,所以设置每个样例的权重相同。

2. 对当前的权值分布$W_m$,使用训练数据进行训练,得到当前轮的基本分类器:

$$F_m(x):x\rightarrow \{\pm1\}$$

使用具有权重的训练数据进行训练,需要对训练方法的公式稍加修改。

3. 计算$F_m(x)$在训练数据集上的分类误差率:

$$e_m=P\left(F_m(x)\neq y_i\right)=\sum_{i=1}^{n}w_{m,i}\,I\left(F_m(x_i)\neq y_i\right)$$

$$ I\left(F_m(x_i)\neq y_i\right)=\left\{
\begin{aligned}
1&&     F_m(x_i)\neq y_i\\
0&&    F_m(x_i)=y_i\\
\end{aligned}
\right.
$$

因为每个训练样例都有权重,所以我们只需要将那些错误分类的样例的权重全部相加即可计算得出基本分类器在训练数据上的分类误差率。

4. 计算当前分类器$F_m(x)$的权重:

$$\alpha_m=\frac{1}{2}\ln\left(\frac{1-e_m}{e_m}\right)$$

可以看出,当$e_m<\frac{1}{2}$时,$\alpha_m>0$,并且随着$e_m$的减小而增大。即,分类误差率越大的权重应该越小,反而精度越高的权重应该越大。这其实很符合现实规律,比如三个人共同筹资创建一个公司,甲出资$30$万元,乙出资$15$万元,丙出资$5$万元,按出资比例,甲占股$60\%$,乙占股$30\%$,丙占股$10\%$。在公司股东会表决某项决议的时候,甲拥有$60\%$的投票权,而乙和丙分别拥有$30\%$和$10\%$的投票权。

5. 使用$w_{m,i}$,$\alpha_m$,$F_m(x_i)$和$y_i$更新样例$i$的权重:

$$w_{m+1,i}=\frac{w_{m,i}}{Z_m}exp\left(-\alpha_m \, y_i \,F_m\,(x_i)\right)$$

$Z_m$为规范化因子,使$W_{m+1}$称为一个合理的概率分布:

$$Z_m=\sum_{i=1}^{n}w_{m,i}\,exp\left(-\alpha_m \, y_i \,F_m\,(x_i)\right)$$

更新样本权重时,主要根据样例在当前基本分类器中的分类情况,如果$y_i=1$,$F_m(x)$越趋近于$1$的时候,$exp\left(-\alpha_m \, y_i \,F_m\,(x_i)\right)$越小,反而如果越接近$-1$时越大。这也正符合当前轮分得越差的样例在下一轮越受到重视的设计。

6. 对$m=1,2,...,M$($M$是基本分类器的个数),重复步骤2~5;

7. 构建基本分类器的线性组合$f(x)$及其最终分类器$F(x)$:

$$f(x)=\sum_{m=1}^{M}\alpha_m\, F_m(x)$$

$$F(x)=sign(f(x))=sign\left(\sum_{m=1}^{M}\alpha_m \,F_m(x)\right)$$

$sign(x)$为符号函数,满足以下性质:

$$ sign(x)=\left\{
\begin{aligned}
1&&     x\geq 0\\
-1&&    x<0\\
\end{aligned}
\right.
$$

你可能已经注意到,$F_m(x)$的系数计算及训练数据的权值分布直接定义了两个公式,并不能一眼看出其中的含义,那么为什么会想到这么定义呢?其实AdaBoost算法还有另外一种解释方法,即可以认为其是模型为加法模型、损失函数为指数函数、学习算法为前向分布算法时的二类分类学习方法。具体内容可参考李航的《统计机器学习》第$8$章 提升方法。

2. GBDT

GBDT是Gradient Boosting Decision Tree的简称,很多时候又称为MART(Multiple Additive Regression Tree)。不同于经典的集成学习算法Adaboost利用前一轮学习器的误差来更新下一轮学习的样本权重,GBDT每次都拟合上一轮分类器产生的残差。举个例子便于理解,比如一个人的年龄是50岁,第一棵树拟合的结果是35岁,第一轮的残差为15岁;然后第二棵数拟合的结果是10岁,两棵树相加总的拟合结果是45岁,第二轮的残差为5岁;第三棵数拟合的结果为2岁,三棵树相加拟合的结果是47岁,第三轮的残差是3岁......只要如此不断地进行下去,拟合结果就可以达到50岁,拟合残差的过程就是训练数据的过程。

对于一个给定的数据集$\{x_i,y_i\}, i=1,2,...,m$,其中特征向量$x_i \in \mathbb{R}^n$,标签$y_i \in \mathbb{R}$,可以用$x_{ij}, j=1,2,...,d来表示x_i的第j个特征值$。对于一个典型的回归决策树问题,需要遍历所有特征$j$的全部阈值$t$,找到最优的$j$和$t$使下面的等式最小化:

$$S_j=\sum_{i \in L}(y_i-\mu_L)^2+\sum_{i \in R}(y_i-\mu_R)^2$$

其中$x_{ij} \leq t$的所有样本落入左子树$L$中,其中$x_{ij} > t$的所有样本落入右子树$R$中,$\mu_L(\mu_R)$表示左子树(右子树)所有样例标签值的均值。如果这就是一棵最简单的拥有一个根节点、两个叶子节点的二叉回归树,那么只需要根据最优阈值切分为左右子树,并且分别计算左右子树的值$\gamma_l,l=1,2$即可。如果将划分子树的过程继续进行$L-1$次即可得到一棵包含$L$个叶子节点的回归树。

上面公式使用最小二乘法计算拟合误差,所以通过上面方法得到的模型又称为最小二乘回归树。其实不管误差的计算方式如何,我们都可以拟合出相应的回归树,唯一的区别是梯度的计算不同而已。

GBDT使用线性组合的方式将拟合的树结合起来,作为最后的输出:

$$F_n(x)=\sum_{i=1}^{N}\alpha_if_i(x)$$

$f_i(x)$是单棵回归树函数,$\alpha_i$是第$i$棵回归树的权重,一般采用相等权重即可。

在这里我们需要弄清楚为什么拟合残差就能不断减少拟合误差。假设拟合误差$C$是拟合函数$F_n$的函数$C(F_n)$。那么:

$$\delta C \approx \frac{\partial{C(F_n)}}{\partial{F_n}}\delta F_n$$

如果取$\delta F_n=-\eta \frac{\partial{C}}{\partial{F_n}}$,就可以得到$\delta C<0$。其中$\eta$是学习率,为正实数。所以只要函数$F_n$拟合误差函数的负梯度就可以不断降低拟合误差的值。

设标签向量$y=[y_1,y_2,...,y_m]^T$,如果用最小二乘的方式表示拟合误差,则:$$C=\frac{1}{2}(F_n-y)^2$$

那么$\delta F_n=-\eta \frac{\partial{C}}{\partial{F_n}}=-\eta (F_n-y)$。$F_n-y$其实就是每一轮拟合后的残差,所以拟合残差就可以不断减少拟合误差。

3. XGBoost

XGBoost,全称为eXtreme Gradient Boosting,是目前唯一能与LightGBM相媲美的提升树算法。XGBoost的出现解决了传统GBDT两个很明显的缺点:容易过拟合和对于大数据训练效率低。既然在一定程度上解决了过拟合问题,模型的训练精度也得到了很大的提升。在XGBoost刚发布的半年内,曾经一度包揽了Kaggle比赛的冠军,实力可见一斑。那么XGBoost是怎样解决过拟合问题从而提升模型精度的呢?

我们先从一般的机器学习问题入手,然后再逐步深入了解XGBoost的原理。定义机器学习问题的损失函数如下:

$$Obj(\Theta)=L(\Theta)+\Omega(\Theta)$$

其中的$L(\Theta)$表示训练误差,衡量当前机器学习模型与训练数据的拟合程度,训练误差越低表示模型拟合得越好,但并不一定代表模型的泛化能力就强,因为很有可能是因为模型在训练数据集上过拟合导致的。$\Omega(\Theta)$表示模型正则化项,衡量了模型本身的复杂程度。在“以简为美”的理念下,一般更倾向于选择简单的模型,认为越简单的模型越可能是数据真正的规律。同时优化这两项,就可以找到模型复杂度和模型对训练数据拟合程度之间的一个tradeoff。

对于有K棵树的GBDT模型来说,可以定义模型的损失函数如下:

$$Obj(\Theta)=\sum_{i=1}^{N}l(y_i,\hat{y_i})+\sum_{k=1}^{K}\Omega(f_k)$$

其中$\hat{y_i}=\sum_{k=1}^{K}f_k(x_i)$表示模型对样例$i$的预测值,$y_i$则表示样例$i$的标签。$\Omega(f_k)$表示第$k$棵树的复杂度。可以说以上的定义合乎情理,同时考虑了模型的拟合程度和树模型的总复杂度。

如何定义每棵树的拟合函数$f_k$呢?我们以陈天奇PPT里的图为例:

$f_k(x)=w_{q(x)}$,$w \in \mathbb{R}^T$,$T$为叶子节点个数,$q(x)$将样例映射到叶子节点。$f_k(x)$将每个样例映射到叶子节点,并且赋予一定的权重,但并没有定义树形结构,所以在后面优化时只能采取启发式的方法构建树。

在XGBoost中,定义单棵树的正则化项为:

$$\Omega(f_k)=\gamma T+\frac{1}{2}\lambda\sum_{j=1}^{T}w_{j}^{2}$$

第一项为树的叶子节点个数,第二项为所有叶子节点分数的$L2$正则化项。

很明显,以上定义的损失函数并不能直接求导,所以也就不能直接使用SGD等梯度下降算法进行求解,GBDT是一种可行的解法。

实际上,可以将GBDT看成是一种加法训练模型,在第$k$轮时,模型函数由第$k-1$轮和当前轮的拟合函数相加构成,也即:

$$\hat{y_i}^{(k)}=\hat{y_i}^{(k-1)}+f_k(x_i)$$

所以,可以改成损失函数为:

$$Obj^{(k)}=\sum_{i=1}^{N}l\left(y_i,\hat{y_i}^{(k)}\right)+\sum_{i=1}^{k}\Omega(f_i)=\sum_{i=1}^{N}l\left(y_i,\hat{y_i}^{(k-1)}+f_k(x_i)\right)+\Omega(f_k)+const$$

考虑最简单的平方误差,损失函数改写为:

$$Obj^{(k)}=\sum_{i=1}^{N}\left[y_i-(\hat{y_i}^{(k-1)}+f_k(x_i))\right]^2+\Omega(f_k)+const=\sum_{i=1}^{N}\left[2(\hat{y_i}^{(k-1)}-y_i)f_k(x_i)+f_k(x_i)^2\right]+\Omega(f_k)+const$$

回忆一下泰勒展开公式,二阶泰勒展开式为:

$$f(x+\Delta{x})\simeq f(x)+f'(x)\Delta{x}+\frac{1}{2}f''(x)\Delta{x}^2$$

那么目标损失函数可以改写为:

$$Obj^{(k)}\simeq \sum_{i=1}^{N}\left[l(y_i,\hat{y_i}^{(k-1)})+g_if_k(x_i)+\frac{1}{2}h_if_k^2(x_i)\right]+\Omega(f_k)+const$$

其中,

$$g_i=\frac{\partial l(y_i,\hat{y_i}^{(k-1)})}{\partial \hat{y_i}^{(k-1)}}$$

$$h_i=\frac{\partial^2 l(y_i,\hat{y_i}^{(k-1)})}{\partial (\hat{y_i}^{(k-1)})^2}$$

移除常数项,得到:

$$Obj^{(k)}=\sum_{i=1}^{N}\left[g_if_k(x_i)+\frac{1}{2}h_if_k^2(x_i)\right]+\Omega(f_k)$$

如果叶子节点$j$里的样例集合用$I_j$表示,可以改成损失函数如下所示:

$$Obj^{(k)}=\sum_{i=1}^{N}\left[g_if_k(x_i)+\frac{1}{2}h_if_k^2(x_i)\right]+\Omega(f_k)=\sum_{i=1}^{N}\left[g_iw_{q(x_i)}+\frac{1}{2}h_iw_{q(x_i)}^2\right]+\gamma T+\lambda \frac{1}{2}\sum_{j=1}^{T}w_j^2=\sum_{j=1}^{T}\left[(\sum_{i\in I_j}g_i)w_j+\frac{1}{2}(\sum_{i \in I_j}h_i+\lambda)w_j^2\right]+\gamma T$$

令$G_j=\sum_{i\in I_j}g_i, H_j=\sum_{i \in I_j}h_i$,则:

$$Obj^{(k)}=\sum_{j=1}^{T}\left[G_jw_j+\frac{1}{2}(H_j+\lambda)w_j^2\right]+\gamma T$$

如果上式中当前树形结构已经确定,那么可以在每个叶子节点优化权重,可以得到:

$$w_j^*=-\frac{G_j}{H_j+\lambda}$$

$$Obj=-\frac{1}{2}\sum_{j=1}^{T}\frac{G_j^2}{H_j+\lambda}+\gamma T$$

$Obj$评价了一棵树的好坏,值越小树模型越好。但是问题来了,如果构建这棵树呢?构建树的方式似乎可以有很多很多种,遍历所有的树形结构肯定是不可取的。回想一下在我们最开始学二叉树的时候,知道树是一层一层地构建出来的,这里其实也可以定义一个切分规则,采用一种启发式的方法,从上到下,一层层地将树结构构建出来。

决策树ID3采用信息增益划分节点,回归树使用基尼指数划分节点,这里可以根据损失函数来定义节点划分标准:

$$Gain=\frac{G_L^2}{H_L+\lambda}+\frac{G_R^2}{H_R+\lambda}-\frac{(G_L+G_R)^2}{(H_L+H_R)+\lambda}-\gamma$$

第一项为左子树的cost,第二项为右子树的cost,第三项为划分前的cost,最后一项定义为由于节点划分带给整棵树的复杂度提升,可以作为是否切分节点的标准。有了节点划分标准,就可以将树结构迭代地构建出来。

XGBoost可以说完美地将正则化融入到GBDT模型中。

提升学习算法简述:AdaBoost, GBDT和XGBoost的更多相关文章

  1. 机器学习总结(一) Adaboost,GBDT和XGboost算法

    一: 提升方法概述 提升方法是一种常用的统计学习方法,其实就是将多个弱学习器提升(boost)为一个强学习器的算法.其工作机制是通过一个弱学习算法,从初始训练集中训练出一个弱学习器,再根据弱学习器的表 ...

  2. 机器学习算法总结(四)——GBDT与XGBOOST

    Boosting方法实际上是采用加法模型与前向分布算法.在上一篇提到的Adaboost算法也可以用加法模型和前向分布算法来表示.以决策树为基学习器的提升方法称为提升树(Boosting Tree).对 ...

  3. 集成学习算法总结----Boosting和Bagging(转)

    1.集成学习概述 1.1 集成学习概述 集成学习在机器学习算法中具有较高的准去率,不足之处就是模型的训练过程可能比较复杂,效率不是很高.目前接触较多的集成学习主要有2种:基于Boosting的和基于B ...

  4. 浅谈树模型与集成学习-从决策树到GBDT

    引言   神经网络模型,特别是深度神经网络模型,自AlexNet在Imagenet Challenge 2012上的一鸣惊人,无疑是Machine Learning Research上最靓的仔,各种进 ...

  5. 常见算法(logistic回归,随机森林,GBDT和xgboost)

    常见算法(logistic回归,随机森林,GBDT和xgboost) 9.25r早上面网易数据挖掘工程师岗位,第一次面数据挖掘的岗位,只想着能够去多准备一些,体验面这个岗位的感觉,虽然最好心有不甘告终 ...

  6. Boosting学习笔记(Adboost、GBDT、Xgboost)

    转载请注明出处:http://www.cnblogs.com/willnote/p/6801496.html 前言 本文为学习boosting时整理的笔记,全文主要包括以下几个部分: 对集成学习进行了 ...

  7. 【学习笔记】 Adaboost算法

    前言 之前的学习中也有好几次尝试过学习该算法,但是都无功而返,不仅仅是因为该算法各大博主.大牛的描述都比较晦涩难懂,同时我自己学习过程中也心浮气躁,不能专心. 现如今决定一口气肝到底,这样我明天就可以 ...

  8. Adaboost\GBDT\GBRT\组合算法

    Adaboost\GBDT\GBRT\组合算法(龙心尘老师上课笔记) 一.Bagging (并行bootstrap)& Boosting(串行) 随机森林实际上是bagging的思路,而GBD ...

  9. RF、GBDT、XGBOOST常见面试算法整理

    1.  RF(随机森林)与GBDT之间的区别 相同点: 1)都是由多棵树组成的 2)最终的结果都是由多棵树一起决定 不同点: 1)  组成随机森林的树可以是分类树也可以是回归树,而GBDT只由回归树组 ...

随机推荐

  1. vue table-tree 组件

    最近接到一个需要使用table-tree开发 百度的一圈.什么的都有.感觉不怎么靠谱.最后找到一个感觉挺 huo shi 先附上demo和代码地址: 代码地址:https://github.com/s ...

  2. Java当中的IO二

    1.大文件的读写方法 由于文件很大,我们不能一下子把文件内的所有内容都读取出来,所以只能一段一段的读取 注意:在关闭read()和write()的时候可能会产生IOException,需要对其进行处理 ...

  3. Go语言基础之函数

    Go语言基础之函数 函数是组织好的.可重复使用的.用于执行指定任务的代码块.本文介绍了Go语言中函数的相关内容. 函数 Go语言中支持函数.匿名函数和闭包,并且函数在Go语言中属于“一等公民”. 函数 ...

  4. lodash 实现一些常见的功能

    排序 const sorted = _.orderBy(filtered, [sortColumn.path], [sortColumn.order]); 数组切片 普通的 slice 可传递两个参数 ...

  5. javascript 编程风格 部分精要

    1 换行保持两个缩进(通常是一行太长) 运算符前后加一个空格,包括赋值运算符和逻辑运算符 括号运算符,左括号之后,右括号之前不应该有空格 段代码无关,添加空行 命名驼峰式,一般首字母小写,其他单词首字 ...

  6. JavaScript / 本地存储

    cookie 首先让我们先了解一下,什么是cookie,cookie是浏览器提供的一种机制,他将document.cookie的接口提供给JavaScript使其可以对cookie进行控制,但cook ...

  7. 访问不了虚拟机 ubuntu中的myql,解决方案

    ============================================================== 1. 现象 2. 原因分析 2.1 访问虚拟机中的mysql的前提: 你的 ...

  8. C# WPF 使用委托修改UI控件

    近段时间在自学WPF,是一个完全不懂WPF的菜鸟,对于在线程中修改UI控件使用委托做一个记录,给自已以后查询也给需要的参考: 界面只放一个RichTextBox,在窗体启动时开起两个线程,调用两个函数 ...

  9. 深入理解JVM(七)——性能监控工具

    前言 工欲善其事必先利其器,性能优化和故障排查在我们大都数人眼里是件比较棘手的事情,一是需要具备一定的原理知识作为基础,二是需要掌握排查问题和解决问题的流程.方法.本文就将介绍利用性能监控工具,帮助开 ...

  10. ArrayList源码理解

    ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存. ArrayList不是线程安全的,只能用在单线程环境下,多线程环境下可以考虑用Col ...