梯度提升树算法GBDT

梯度提升树算法实际上是提升算法的扩展版,在原始的提升算法中,如果损失函数为平方损失或者指数损失,求解损失函数的最小值问题会非常简单,
但如果损失函数为更一般的函数(如绝对值函数),目标值的求解就会相对复杂许多。

所以,梯度提升算法诞生,也就是在第m轮基础模型中,利用损失函数的负梯度值作为该轮基础模型损失值(残差)的近似,利用这个近似值构建下一轮基础模型。

GBDT(梯度提升树)属于一种有监督的集成学习算法,可用于分类问题的识别和预测问题的解决。该集成算法体现了三方面的优势,分别是提升Boosting、梯度Gradient、决策树Decision Tree。

  • 提升是指将多个弱分类器通过线下组合实现强分类器的过程。
  • 梯度是指算法在Booting过程中求解损失函数时增强了灵活性和便捷性。
  • 决策树是指算法所使用的弱分类器为CART决策树(该决策树简单直观通俗易懂)

随机森林实质上是利用Bootstrap抽样技术生成多个数据集,然后通过这些数据集构造多颗决策树,进而运用投票或者平均的思想实现分类和预测问题的解决,但是随机性会导致树与树之间并没有太多的相关性,往往会导致随机森林算法在拟合效果上遇到瓶颈。

为了解决随机森林中这个问题,Friedman提出“提升”概念,也就是通过改变样本点的权值和各个弱分类器的权重,并将这些弱分类器完成组合、实现预测准确性的突破;为了使提升算法在求解损失函数的时候更加容易求解和方便,该大佬提出了梯度提升树算法GBDT。

GBDT模型对数据类型不做任何限制,既可以是连续的数值型,也可以是离散的字符型(一般需要将字符型变量做数值化或哑变量处理)。
GBDT模型相对于SVM来说有较少的参数以及更高准确率和更少运算时间,GBDT在面对异常数据是具有更强的稳定性。

GBDT算法是梯度提升的经典算法、可以解决分类和预测问题。

GBDT分类算法原理

当因变量为离散的类别变量时,无法直接利用各个类别值拟合残差(因为残差是连续的数值型),解决方法是将GBDT算法的损失函数设置为指数损失函数或者对数似然损失函数,进而实现残差的数值化;本质上:如果损失函数选择指数损失函数,GBDT算法退化为AdaBoost算法;如果选择对数似然损失函数,GBDT算法的残差类似于Logistic回归算法的对数似然损失。

选择对数似然损失函数、以二分类问题为例,推导计算GBDT的过程:

  • 首先初始化一个弱分类器(CART也可以):f0(x) = argminc *sum(L(yi,c)) 其中,i=1~n
  • 计算损失函数的负梯度值:rmi = -(L(yi,c)对f(xi)的导数)f(x) = f(m-1)(x) = yi/(1+exp(-yif(xi)))
  • 利用数据集(xi,rmi)拟合下一轮基础模型,得到对应的J个叶子节点Rmj,j=1,2,…J;计算每个叶子节点Rmj的最佳拟合值,用以估计残差rmi;
    也就是得到1.fm(x)=sum(cmj*I(xi属于Rmj)); 2.cmj=argminc sum(log(1+exp(-yi(f(m-1)(xi))+c)))
  • 重复步骤2和3,并利用m个基础模型,构建梯度提升模型。FM(x) = F(M-1)(x)+fm(x) = sum(sum(cmj*I(xi属于Rmj),j=1J),m=1M)

GBDT回归算法原理

如果因变量为数值型变量、问题其实会简单许多,因为输出的残差值本身就是数值型的。GBDT算法的损失函数可以有表较多的选择,例如平方损失函数、绝对值损失函数、分位数回归损失函数、Huber损失函数。

以平方损失函数为例,推导计算GBDT回归的过程:

  • 初始化一个弱分类器 f0(x) = argminc *sum(L(yi,c)) 其中,i=1~n
  • 计算损失函数的负梯度值:rmi = -(L(yi,c)对f(xi)的导数)*f(x) = f(m-1)(x) = yi-f(xi)
  • 利用数据集(xi,rmi)拟合下一轮基础模型:fm(x)=sum(cmjI(xi属于Rmj),j=1~J); cmj = argminc sum(1/2(yi-(f(m-1)(xi)+c))**2) 其中xi属于Rmj
  • 重复第二步和第三步,并利用m个基础模型,构建梯度提升模型 FM(x) = F(M-1)(x)+fm(x) = sum(sum(cmj*I(xi属于Rmj),j=1J),m=1M)
sklearn中的子模块有GradientBoostingClassifier类和GradientBoostingRegressor类来实现GBDT算法。算法实现以及参数解释如下。
* ensemble.GradientBoostingClassifier(loss='deviance',learning_rate=0.1,n_estimators=100,subsample=1.0,criterion='friedman_mse',  min_samples_split=2,min_samples_leaf=1,min_weight_fraction_leaf=0.0,max_depth=3,min_impurity_decrease=0.0,min_impurity_split=None,init=None,
random_state=None,max_features=None,verbose=0,max_leaf_nodes=None,warm_start=False,presort='auto')


* ensemble.GradientBoostingRegressor(loss='ls',learning_rate=0.1,n_estimators=100,subsample=1.0,criterion='friedman_mse',min_samples_split=2,
min_samples_leaf=1,min_weight_fraction_leaf=0.0,max_depth=3,min_impurity_decrease=0.0,min_impurity_split=None,init=None,random_state=None,
max_features=None,verbose=0,max_leaf_nodes=None,warm_start=False,presort='auto')

loss:用于指定损失函数;对于分类可以选择'deviance'(对数似然损失)和'exponential'(指数损失);对于回归预测、可以选择'ls'(平方损失)、'lad'(绝对值损失)、'huber'(Huber函数损失,即前两种的结合体,误差较小使用平方损失、误差较大使用绝对值损失,误差标准度量用alpha参数指定)、'quantile'(分位数回归损失,需要通过alpha参数设定分位数)。

learning_rate 用于指定模型迭代的学习率或步长,即FM(x)=F(m-1)(x)+v *fm(x) 中的v这个值,通常需要利用交叉验证确定、连同基础模型。

n_estimators用于指定基础模型的个数。

subsample用于指定构建基础模型所使用的抽样比率,默认为1表示使用原始数据构建每一个基础模型;小于1时表示构建随机梯度提升树模型、通常导致方差降低、偏差提高。

criterion 用于指定分割质量的度量,默认为'friedman_mse'均方误差,还可以使用'mse'均方根误差和'mae'绝对误差。

min_samples_split用于指定每个基础模型的根节点或中间节点能够继续分割的最小样本量,默认为2.

min_samples_leaf用于指定每个基础模型的叶节点所包含的最小样本量、默认为1.

min_weight_fraction_leaf 用于指定每个基础模型叶节点最小的样本权重,默认为0表示不考虑叶节点的样本权值。

max_depth表示指定每个基础模型所包含的最大深度,默认为3层,

min_impurity_decrease 用于指定每个基础模型的节点是否继续分割的最小不纯度值,默认为0;如果不纯度超过指定的阈值,则节点需要分割否则不分割。

min_impurity_split 该参数在sklearn0.21版本之后删除、功能与min_impurity_decrease类似

init 用于指定初始的基础模型。用于执行初始的分类或预测。

max_features用于指定每个基础模型所包含的最多分割字段数,默认为None表示使用所有的字段。如果为具体的整数则考虑使用对应的分割字段数;如果为0~1的浮点数则考虑对应百分比的字段个数;如果为'sqrt'则表示最多考虑根号P个字段、与指定'auto'效果一致;如果为'log2'则表示最多使用log2 P 个字段;其中P表示所有自变量的个数。

verbose用于指定GBDT算法在计算过程中是否输出日志信息、默认为0表示不输出。

alpha当loss参数为'huber''quantile'时,该参数有效、分别用于指定误差的阈值和分位数。

max_leaf_nodes用于指定每个基础模型最大的叶子节点个数,默认为None,表示对叶子节点个数不做任何限制。

warm_start :bool类型参数 是否使用上一轮的训练结果,默认为False

presort:bool类型参数、表示在构建时是否对数据进行预排序(用于快速寻找最佳分割点),默认为'auto'。当数据密集时、'auto'自动对数据集做预排序;当数据集比较稀疏时则无需预排序,对于稀疏数据来说设置该参数为True反而会提高模型的错误率。
小项目实战: 数据集为客户信用卡违约数据
 

分别使用GBDT算法与AdaBoost算法对数据进行建模训练,并对比结果。

读取数据:

from sklearn.model_selection import GridSearchCV
from sklearn import ensemble
from sklearn.model_selection import train_test_split
from sklearn import metrics
import pandas as pd
import matplotlib.pyplot as plt

# 读入数据
default = pd.read_excel(r'default of credit card clients.xls')
default.head(5)

将数据集拆分为训练集和测试集:

# 导入第三方包
from sklearn import model_selection
from sklearn import ensemble
from sklearn import metrics

# 排除数据集中的ID变量和因变量,剩余的数据用作自变量X;因为ID只是一个达到索引的作用
X = default.drop(['ID','y'], axis = 1)
y = default.y

# 数据拆分
X_train,X_test,y_train,y_test = model_selection.train_test_split(X,y,test_size = 0.25, random_state = 1234)

初次建模以筛选掉重要性比较低的因素:

# 构建AdaBoostt提升分类树算法的类
AdaBoost1 = ensemble.AdaBoostClassifier()

# 算法在训练数据集上的拟合
AdaBoost1.fit(X_train,y_train)

# 算法在测试数据集上的预测
pred1 = AdaBoost1.predict(X_test)

# 返回模型的预测效果
print('模型的准确率为:
',metrics.accuracy_score(y_test, pred1))
print('模型的评估报告:
',metrics.classification_report(y_test, pred1))


# 自变量的重要性排序
importance = pd.Series(AdaBoost1.feature_importances_, index = X.columns)
importance.sort_values().plot(kind = 'barh')
plt.show()
# 自变量的重要性排序
importance = pd.Series(AdaBoost1.feature_importances_, index = X.columns)
importance.sort_values().plot(kind = 'barh')
plt.show()

# 取出重要性比较高的自变量建模
predictors = list(importance[importance>0.02].index)
print(predictors)

# 取出重要性比较高的自变量建模
predictors = list(importance[importance>0.02].index)
print(predictors)

# 通过网格搜索法选择基础模型所对应的合理参数组合
# 导入第三方包
from sklearn.model_selection import GridSearchCV
from sklearn.tree import DecisionTreeClassifier

#先选出合适的深度
max_depth = [3,4,5,6]
params1 = {'base_estimator__max_depth':max_depth}
base_model = GridSearchCV(estimator = ensemble.AdaBoostClassifier(base_estimator = DecisionTreeClassifier()),
                          param_grid= params1, scoring = 'roc_auc', cv = 5, n_jobs = 4, verbose = 1)

base_model.fit(X_train[predictors],y_train)
# 返回参数的最佳组合和对应AUC值
# base_model.best_params_, base_model.best_score_


# 基于CART树参数调优之后的结果进行提升分类树的参数调优
# 通过网格搜索法选择提升树的合理参数组合
# 导入第三方包
from sklearn.model_selection import GridSearchCV

n_estimators = [100,200,300]
learning_rate = [0.01,0.05,0.1,0.2]
params2 = {'n_estimators':n_estimators,'learning_rate':learning_rate}
adaboost = GridSearchCV(estimator = ensemble.AdaBoostClassifier(base_estimator = DecisionTreeClassifier(max_depth = 3)),
                        param_grid= params2, scoring = 'roc_auc', cv = 5, n_jobs = 4, verbose = 1)

adaboost.fit(X_train[predictors] ,y_train)
# 返回参数的最佳组合和对应AUC值
# adaboost.best_params_, adaboost.best_score_

# 使用最佳的参数组合构建AdaBoost模型
AdaBoost2 = ensemble.AdaBoostClassifier(base_estimator = DecisionTreeClassifier(max_depth = 3),
                                       n_estimators = 300, learning_rate = 0.01)
# 算法在训练数据集上的拟合
AdaBoost2.fit(X_train[predictors],y_train)
# 算法在测试数据集上的预测
pred2 = AdaBoost2.predict(X_test[predictors])

# 返回模型的预测效果
print('模型的准确率为:
',metrics.accuracy_score(y_test, pred2))
print('模型的评估报告:
',metrics.classification_report(y_test, pred2))
# 模型仅仅提高了0.36%(另一篇文章讲述了AdaBoos原理以及使用)

下面将使用GBDT算法进行建模训练、然后与AdaBoost进行对比

使用交叉验证挑选最佳的参数组合:

# 运用网格搜索法选择梯度提升树的合理参数组合
learning_rate = [0.01,0.05,0.1,0.2]
n_estimators = [100,300,500]
max_depth = [3,4,5,6]

params = {'learning_rate':learning_rate,'n_estimators':n_estimators,'max_depth':max_depth}
gbdt_grid = GridSearchCV(estimator = ensemble.GradientBoostingClassifier(),
                         param_grid= params, scoring = 'roc_auc', cv = 5, n_jobs = 4, verbose = 1)

gbdt_grid.fit(X_train[predictors],y_train)
# 返回参数的最佳组合和对应AUC值
gbdt_grid.best_params_,  gbdt_grid.best_score_

基于最佳参数组合的GBDT模型,对测试数据集进行预测:

pred = gbdt_grid.predict(X_test[predictors])
# 返回模型的预测效果
print('模型的准确率为:
',metrics.accuracy_score(y_test, pred))
print('模型的评估报告:
',metrics.classification_report(y_test, pred))


# 计算违约客户的概率值,用于生成ROC曲线的数据
y_score = gbdt_grid.predict_proba(X_test[predictors])[:,1]
fpr,tpr,threshold = metrics.roc_curve(y_test, y_score)
# 计算AUC的值
roc_auc = metrics.auc(fpr,tpr)

# 绘制面积图
plt.stackplot(fpr, tpr, color='steelblue', alpha = 0.5, edgecolor = 'black')
# 添加边际线
plt.plot(fpr, tpr, color='black', lw = 1)
# 添加对角线
plt.plot([0,1],[0,1], color = 'red', linestyle = '--')
# 添加文本信息
plt.text(0.5,0.3,'ROC curve (area = %0.2f)' % roc_auc)
# 添加x轴与y轴标签
plt.xlabel('1-Specificity')
plt.ylabel('Sensitivity')
# 显示图形
plt.show()

GBDT结果几乎与AdaBoost算法结果一致。
希望大家给予意见和建议。谢谢喔。

原文地址:https://www.cnblogs.com/chenruhai/p/12464205.html