转载请注明出处:http://blog.csdn.net/gamer_gyt
博主微博:http://weibo.com/234654758
Github:https://github.com/thinkgamer
公众号:搜索与推荐Wiki
个人网站:http://thinkgamer.github.io
研究GBDT的背景是业务中使用到了该模型,用于做推荐场景,当然这里就引出了GBDT的一个应用场景-回归,他的另外一个应用场景便是分类,接下来我会从以下几个方面去学习和研究GBDT的相关知识,当然我也是学习者,只是把我理解到的整理出来。本文参考了网上各路大神的笔记,在此感谢!
集成学习方法不是单独的一个机器学习算法,而是通过构建多个机器学习算法来达到一个强学习器。集成学习可以用来进行分类,回归,特征选取和异常点检测等。随机森林算法就是一个典型的集成学习方法,简单的说就是由一个个弱分类器(决策树)来构建一个强分类器,从而达到比较好的分类效果。
那么如何得到单个的学习器,一般有两种方法:
相对异质而言,同质学习期用的最为广泛,我们平时所讨论的集成学习方法指的就是同质个体学习器,同质个体学习器按照个体学习器之间的依赖关系分为串行(有强依赖关系)和并行(不存在关系或者有很弱的依赖关系),而在串行关系中有代表性的就是boosting系列算法,并行关系中具有代表性的就是bagging和随机森林(random forest)
上边简单的介绍了集成学习方法和boosting&bagging的区别,那么对于单个学习器采用何种策略才能得到一个强学习器呢?
Stacking算法:
基本思想:先从初始数据集训练出初级学习器,然后生成一个新数据集用于训练次级学习器。在这个新数据集中,初级学习器的输出被当作样例输入特征,而出事样本的标记仍被当作样例标记。
注意点:若直接用初级学习器的训练集来产生次级训练集,则过拟合风险会比较大;一般会通过交叉验证等方式,用训练初级学习器未使用的样本来产生次级学习器的训练样本。
Gradient Boosting是一种Boosting的方法,它主要的思想是,每一次建立模型是在之前建立模型损失函数的梯度下降方向。损失函数是评价模型性能(一般为拟合程度+正则项),认为损失函数越小,性能越好。而让损失函数持续下降,就能使得模型不断改性提升性能,其最好的方法就是使损失函数沿着梯度方向下降(讲道理梯度方向上下降最快)。
Gradient Boost是一个框架,里面可以套入很多不同的算法。
三种比较常见的分类决策树分支划分方式包括:ID3, C4.5, CART。
以C4.5分类树为例,C4.5分类树在每次分枝时,是穷举每一个feature的每一个阈值,找到使得按照feature<=阈值,和feature>阈值分成的两个分枝的熵最大的阈值(熵最大的概念可理解成尽可能每个分枝的男女比例都远离1:1),按照该标准分枝得到两个新节点,用同样方法继续分枝直到所有人都被分入性别唯一的叶子节点,或达到预设的终止条件,若最终叶子节点中的性别不唯一,则以多数人的性别作为该叶子节点的性别。
总结:分类树使用信息增益或增益比率来划分节点;每个节点样本的类别情况投票决定测试样本的类别。
回归树总体流程也是类似,区别在于,回归树的每个节点(不一定是叶子节点)都会得一个预测值,以年龄为例,该预测值等于属于这个节点的所有人年龄的平均值。分枝时穷举每一个feature的每个阈值找最好的分割点,但衡量最好的标准不再是最大熵,而是最小化均方差即(每个人的年龄-预测年龄)^2 的总和 / N。也就是被预测出错的人数越多,错的越离谱,均方差就越大,通过最小化均方差能够找到最可靠的分枝依据。分枝直到每个叶子节点上人的年龄都唯一或者达到预设的终止条件(如叶子个数上限),若最终叶子节点上人的年龄不唯一,则以该节点上所有人的平均年龄做为该叶子节点的预测年龄。
总结:回归树使用最大均方差划分节点;每个节点样本的均值作为测试样本的回归预测值。
Classification And Regression Trees,即既能做分类任务又能做回归任务,CART也是决策树的一种,是一种二分决策树,但是也可以用来做回归,CART同决策树类似,不同于 ID3 与 C4.5 ,分类树采用基尼指数来选择最优的切分特征,而且每次都是二分。至于怎么利用基尼系数进行最优的特征切分,大家可以参考这篇文章的详细介绍 决策树之 CART
机器学习中的损失函数有很多,常见的有
该损失函数的意义就是,当预测错误时,损失函数值为1,预测正确时,损失函数值为0。该损失函数不考虑预测值和真实值的误差程度,也就是只要预测错误,预测错误差一点和差很多是一样的。
取预测差距的平方
取预测值与真实值的差值绝对值,差距不会被平方放大
该损失函数用到了极大似然估计的思想。P(Y|X)通俗的解释就是:在当前模型的基础上,对于样本X,其预测值为Y,也就是预测正确的概率。由于概率之间的同时满足需要使用乘法,为了将其转化为加法,我们将其取对数。最后由于是损失函数,所以预测正确的概率越高,其损失值应该是越小,因此再加个负号取个反。
上面的损失函数仅仅是对于一个样本来说的。而我们的优化目标函数应当是使全局损失函数最小。因此,全局损失函数往往是每个样本的损失函数之和,即:
J ( w , b ) = 1 m ∑ i = 1 m L ( Y , f ( X ) ) J(w,b)=\frac{1}{m} \sum_{i=1}^m L(Y,f(X)) J(w,b)=m1i=1∑mL(Y,f(X))
对于平方损失函数,为了求导方便,我们可以在前面乘上一个1/2,和平方项求导后的2抵消,即:
J ( w , b ) = 1 2 m ∑ i = 1 m L ( Y , f ( X ) ) J(w,b)=\frac{1}{2m} \sum_{i=1}^m L(Y,f(X)) J(w,b)=2m1i=1∑mL(Y,f(X))
在逻辑回归中,我们采用的是对数损失函数。由于逻辑回归是服从伯努利分布(0-1分布)的,并且逻辑回归返回的sigmoid值是处于(0,1)区间,不会取到0,1两个端点。因此我们能够将其损失函数写成以下形式:
L ( y ^ , y ) = − ( y log y ^ + ( 1 − y ) log ( 1 − y ^ ) ) L(\hat y,y)=-(y\log{\hat y}+(1-y)\log(1-\hat y)) L(y^,y)=−(ylogy^+(1−y)log(1−y^))
以下部分学习于 GBDT算法原理深入解析 ,原文作者讲的很好,照搬过来,毕竟笔者不是推导数学公式的料,哈哈
GBDT 可以看成是由K棵树组成的加法模型:
(0) y ^ i = ∑ k = 1 K f k ( x i ) , f k ∈ F \hat{y}_i=\sum_{k=1}^K f_k(x_i), f_k \in F \tag 0 y^i=k=1∑Kfk(xi),fk∈F(0)
其中F为所有树组成的函数空间,以回归任务为例,回归树可以看作为一个把特征向量映射为某个score的函数。该模型的参数为: Θ = { f 1 , f 2 , ⋯   , f K } \Theta=\{f_1,f_2, \cdots, f_K \} Θ={f1,f2,⋯,fK}。于一般的机器学习算法不同的是,加法模型不是学习d维空间中的权重,而是直接学习函数(决策树)集合
上述加法模型的目标函数定义为: O b j = ∑ i = 1 n l ( y i , y ^ i ) + ∑ k = 1 K Ω ( f k ) Obj=\sum_{i=1}^n l(y_i, \hat{y}_i) + \sum_{k=1}^K \Omega(f_k) Obj=∑i=1nl(yi,y^i)+∑k=1KΩ(fk),其中 Ω \Omega Ω表示决策树的复杂度,那么该如何定义树的复杂度呢?比如,可以考虑树的节点数量、树的深度或者叶子节点所对应的分数的L2范数等等。
如何来学习加法模型呢?
解这一优化问题,可以用前向分布算法(forward stagewise algorithm)。因为学习的是加法模型,如果能够从前往后,每一步只学习一个基函数及其系数(结构),逐步逼近优化目标函数,那么就可以简化复杂度。这一学习过程称之为Boosting。具体地,我们从一个常量预测开始,每次学习一个新的函数,过程如下(latex不能正常显示,截图如下):
\begin{split}
\hat{y}_i^0 &= 0 \\
\hat{y}_i^1 &= f_1(x_i) = \hat{y}_i^0 + f_1(x_i) \\
\hat{y}_i^2 &= f_1(x_i) + f_2(x_i) = \hat{y}_i^1 + f_2(x_i) \\
& \cdots \\
\hat{y}_i^t &= \sum_{k=1}^t f_k(x_i) = \hat{y}_i^{t-1} + f_t(x_i) \\
\end{split}}
那么,在每一步如何决定哪一个函数 f f f被加入呢?指导原则还是最小化目标函数。
在第 t t t步,模型对 x i x_i xi的预测为: y ^ i t = y ^ i t − 1 + f t ( x i ) \hat{y}_i^t= \hat{y}_i^{t-1} + f_t(x_i) y^it=y^it−1+ft(xi),其中 f t ( x i ) f_t(x_i) ft(xi)为这一轮我们要学习的函数(决策树)。这个时候目标函数可以写为:
\begin{split}
Obj^{(t)} &= \sum_{i=1}^nl(y_i, \hat{y}_i^t) + \sum_{i=i}^t \Omega(f_i) \\
&= \sum_{i=1}^n l\left(y_i, \hat{y}_i^{t-1} + f_t(x_i) \right) + \Omega(f_t) + constant
\end{split}\tag{1}
举例说明,假设损失函数为平方损失(square loss),则目标函数为:
\begin{split}
Obj^{(t)} &= \sum_{i=1}^n \left(y_i - (\hat{y}_i^{t-1} + f_t(x_i)) \right)^2 + \Omega(f_t) + constant \\
&= \sum_{i=1}^n \left[2(\hat{y}_i^{t-1} - y_i)f_t(x_i) + f_t(x_i)^2 \right] + \Omega(f_t) + constant
\end{split}\tag{2}
其中, ( y ^ i t − 1 − y i ) (\hat{y}_i^{t-1} - y_i) (y^it−1−yi)称之为残差(residual)。因此,使用平方损失函数时,GBDT算法的每一步在生成决策树时只需要拟合前面的模型的残差。
泰勒公式:设 n n n是一个正整数,如果定义在一个包含 a a a的区间上的函数 f f f在点 a a a处 n + 1 n+1 n+1次可导,那么对于这个区间上的任意 x x x都有: f ( x ) = ∑ n = 0 N f ( n ) ( a ) n ! ( x − a ) n + R n ( x ) \displaystyle f(x)=\sum _{n=0}^{N}\frac{f^{(n)}(a)}{n!}(x-a)^ n+R_ n(x) f(x)=n=0∑Nn!f(n)(a)(x−a)n+Rn(x),其中的多项式称为函数在 a a a处的泰勒展开式, R n ( x ) R_ n(x) Rn(x)是泰勒公式的余项且是 ( x − a ) n (x-a)^ n (x−a)n的高阶无穷小。
根据泰勒公式把函数 f ( x + Δ x ) f(x+\Delta x) f(x+Δx)在点 x x x处二阶展开,可得到如下等式:
(3) f ( x + Δ x ) ≈ f ( x ) + f ′ ( x ) Δ x + 1 2 f ′ ′ ( x ) Δ x 2 f(x+\Delta x) \approx f(x) + f'(x)\Delta x + \frac12 f''(x)\Delta x^2 \tag 3 f(x+Δx)≈f(x)+f′(x)Δx+21f′′(x)Δx2(3)
由等式(1)可知,目标函数是关于变量 y ^ i t − 1 + f t ( x i ) \hat{y}_i^{t-1} + f_t(x_i) y^it−1+ft(xi)的函数,若把变量 y ^ i t − 1 \hat{y}_i^{t-1} y^it−1看成是等式(3)中的 x x x,把变量 f t ( x i ) f_t(x_i) ft(xi)看成是等式(3)中的 Δ x \Delta x Δx,则等式(1)可转化为:
(4) O b j ( t ) = ∑ i = 1 n [ l ( y i , y ^ i t − 1 ) + g i f t ( x i ) + 1 2 h i f t 2 ( x i ) ] + Ω ( f t ) + c o n s t a n t Obj^{(t)} = \sum_{i=1}^n \left[ l(y_i, \hat{y}_i^{t-1}) + g_if_t(x_i) + \frac12h_if_t^2(x_i) \right] + \Omega(f_t) + constant \tag 4 Obj(t)=i=1∑n[l(yi,y^it−1)+gift(xi)+21hift2(xi)]+Ω(ft)+constant(4)
其中 g i g_i gi,定义为损失函数的一阶导数,即 g i = ∂ y ^ t − 1 l ( y i , y ^ t − 1 ) g_i=\partial_{\hat{y}^{t-1}}l(y_i,\hat{y}^{t-1}) gi=∂y^t−1l(yi,y^t−1); h i h_i hi定义为损失函数的二阶导数,即 h i = ∂ y ^ t − 1 2 l ( y i , y ^ t − 1 ) h_i=\partial_{\hat{y}^{t-1}}^2l(y_i,\hat{y}^{t-1}) hi=∂y^t−12l(yi,y^t−1)。
假设损失函数为平方损失函数,则 g i = ∂ y ^ t − 1 ( y ^ t − 1 − y i ) 2 = 2 ( y ^ t − 1 − y i ) g_i=\partial_{\hat{y}^{t-1}}(\hat{y}^{t-1} - y_i)^2 = 2(\hat{y}^{t-1} - y_i) gi=∂y^t−1(y^t−1−yi)2=2(y^t−1−yi), h i = ∂ y ^ t − 1 2 ( y ^ t − 1 − y i ) 2 = 2 h_i=\partial_{\hat{y}^{t-1}}^2(\hat{y}^{t-1} - y_i)^2 = 2 hi=∂y^t−12(y^t−1−yi)2=2,把 g i g_i gi和 h i h_i hi代入等式(4)即得等式(2)。
由于函数中的常量在函数最小化的过程中不起作用,因此我们可以从等式(4)中移除掉常量项,得:
(5) O b j ( t ) ≈ ∑ i = 1 n [ g i f t ( x i ) + 1 2 h i f t 2 ( x i ) ] + Ω ( f t ) Obj^{(t)} \approx \sum_{i=1}^n \left[ g_if_t(x_i) + \frac12h_if_t^2(x_i) \right] + \Omega(f_t) \tag 5 Obj(t)≈i=1∑n[gift(xi)+21hift2(xi)]+Ω(ft)(5)
由于要学习的函数仅仅依赖于目标函数,从等式(5)可以看出只需为学习任务定义好损失函数,并为每个训练样本计算出损失函数的一阶导数和二阶导数,通过在训练样本集上最小化等式(5)即可求得每步要学习的函数 f ( x ) f(x) f(x),从而根据加法模型等式(0)可得最终要学习的模型。
关于GBDT在Scikit-learn中的实现原文在 点击查看
GBDT在sklearn中导入的包不一样,分类是 from sklearn.ensemble import GradientBoostingClassifier,回归是 from sklearn.ensemble import GradientBoostingRegressor
GBDT的参数分为boosting类库参数和弱学习器参数,其中有GBDT的弱学习器为CART,所以弱学习器参数基本为决策树的参数,参考点击阅读
### 类库参数
>>> from sklearn.datasets import make_hastie_10_2
>>> from sklearn.ensemble import GradientBoostingClassifier
>>> X, y = make_hastie_10_2(random_state=0)
>>> X_train, X_test = X[:2000], X[2000:]
>>> y_train, y_test = y[:2000], y[2000:]
>>> clf = GradientBoostingClassifier(n_estimators=100, learning_rate=1.0,
... max_depth=1, random_state=0).fit(X_train, y_train)
>>> clf.score(X_test, y_test)
0.913...
>>> import numpy as np
>>> from sklearn.metrics import mean_squared_error
>>> from sklearn.datasets import make_friedman1
>>> from sklearn.ensemble import GradientBoostingRegressor
>>> X, y = make_friedman1(n_samples=1200, random_state=0, noise=1.0)
>>> X_train, X_test = X[:200], X[200:]
>>> y_train, y_test = y[:200], y[200:]
>>> est = GradientBoostingRegressor(n_estimators=100, learning_rate=0.1,
... max_depth=1, random_state=0, loss='ls').fit(X_train, y_train)
>>> mean_squared_error(y_test, est.predict(X_test))
5.00...
from sklearn import cross_validation, metrics
metrics.accuracy_score(y.values, y_pred) # 准确度
metrics.roc_auc_score(y, y_predprob) # AUC大小
参考资料: