当前位置: 代码迷 >> 综合 >> 机器篇——集成学习(六) 细说 GBDT 算法
  详细解决方案

机器篇——集成学习(六) 细说 GBDT 算法

热度:53   发布时间:2023-12-15 08:54:01.0

返回主目录

返回集成学习目录

上一章:机器篇——集成学习(五) 细说 梯度提升(Gradient Boost)算法 

下一章:机器篇——集成学习(七) 细说 XGBoost 算法

 

本小节,细说 GBDT 算法,下一小节细说 XGBoost 算法

 

二. 具体算法

6. GBDT 算法 (Gradient Boosting Decision Tree, GBDT)

    GBDT 算法也是集成学习 Boosting 家族的成员,也是采用迭代弱学习器的方法,但是,弱学习器限定了只能使用 CART 回归树模型

    在 GBDT 的迭代中,假设前一轮迭代得到的强学习器是 ,损失函数是 ,则本轮迭代的目标是找到一个 CART 回归树模型的弱学习器 ,让本轮的损失函数:

               

    最小。也就是说,本轮迭代找到的决策树,要让样本的损失尽量变得更小。

    (1). GBDT 的负梯度拟合。

     ①. 第  轮的第  个样本的损失函数的负梯度表示为:

                

     ②. 当损失函数最小,也就是拟合叶子节点最好的输出值 :

                

                :为第  棵回归树对应的叶子节点区域

     ③. 得到本轮的决策树拟合函数

                

     ④. 本轮最终得到的强学习器表达式:

                

            通过损失函数的负梯度来拟合,可以找到一种通用的拟合损失误差的方法,这样无论是分类问题还是回归问题,通过其损失函数的负梯度拟合,就可以用 GBDT 来解决分类回归问题。区别仅仅在于损失函数不同导致的负梯度不同而已。

 

    (2). GBDT 回归算法

     输入是训练集样本 

     最大迭代次数 ,损失函数 

     输出是强学习器 

     ①. 初始化弱学习器

               

     ②. 对迭代轮数,

      a. 对样本 ,计算负梯度

                

      b. 利用  拟合一棵 CART 回归树,得到第  棵回归树,其对应的叶子节点区域为 ,,其中  为回归树  的叶子节点的个数。 

      c. 对叶子区域 ,计算最佳拟合值:

                

      d. 更新强学习器

                

     ③. 得到强学习器  的表达式。

                

 

    (3). GBDT 分类算法

     ①. GBDT 的分类算法从思想上和 GBDT 的回归算法没有区别,但是由于样本输出不是连续的值,而是离散的类别,导致无法直接从输出类别去拟合类别输出的误差。为了解决这个问题,主要有两个方法:一个是用指数损失函数,此时 GBDT 退化为 AdaBoost 算法。另一种方法是由类似逻辑回归的对数似然损失函数的方法。也就是说,用的是类别的预测概率值和真实概率的差来拟合损失。

     ②. 二元 GBDT 分类算法

      a. 对于二元 GBDT,如果用类似于逻辑回归的对数似然损失函数,则损失函数为:

               

          其中 

          此时的负梯度误差为:

                

                          

      b. 对于生成的决策树,各个叶子节点的最佳残差拟合值为:

                

      c. 由于上式比较难优化,一般使用近似值代替

                

          除了负梯度计算和叶子节点的最值残差拟合的线性搜索,二元 GBDT 和 GBDT 回归算法过程相同。

     ③. 多元 GBDT 分类算法

      a. 多元 GBDT 要比二元 GBDT 复杂一些,对应的是多元逻辑回归和二元逻辑回归的复杂度差别。假设类别数为 ,则此时的对数似然损失函数为:

               

         其中,如果样本输出类别为 ,则 

         第  类的概率  的表达式为:

                

      b. 由上式可以计算出第  轮的第  个样本对应类别  的负梯度误差为:

                

                            

           其中,这里的误差就是样本  对应类别  的真实概率和  轮预测概率的差值。

      c. 对于生成的决策树,各个叶子节点的最佳残差拟合值为:

                

      d. 由于上式较难优化,一般使用近似值代替

                 

            除了负梯度计算和叶子节点的最佳残差拟合的线性搜索,多元 GBDT 分类和二元 GBDT 分类及 GBDT 回归算法过程相同。

 

    (4). GBDT 常用损失函数

     ①. 对于分类算法,其损失函数一般有对数损失函数和指数损失函数两种:

      a. 如果是指数损失函数,则损失函数表达式为:

               

          其负梯度计算和叶子节点的最佳残差拟合与 AdaBoost 相同。

      b. 如果是对数损失函数,分为二元分类和多元分类两种。

     ②. 对于回归算法,常用损失函数有如下 4 种:

      a. 均方差,这个是最常见的回归损失函数

               

      b. 绝对损失

               

           对应负梯度误差为:

                

      c. Huber 损失,它是均方差和绝对损失的折中产物,对于远离中心的异常点,采用绝对损失,而中心附近的点采用均方差。这个界限一般用分位数点度量。损失函数为:

               

          对应的负梯度误差为:

               

      d. 分位数损失。它对应的是分位数回归的损失函数,表达式为:

               

           其中, 为分位数,需要在回归前指定

           对应的负梯度误差为:

                  

            对于 Huber 损失和分位数损失,主要用于健壮回归,也就是减少异常点损失函数的影响。

     

    (5). GBDT 的正则化

     和 AdaBoost 一样,GBDT 也需要进行正则化,防止过拟合。GBDT 的正则化主要有三种方式:

     ①. 第一种是和 AdaBoost 类似的正则化项,即学习率(learning rate)。定义为 ,对于前面的弱学习器迭代 

               

           加上正则化项,则有

                

            的取值范围额为 

           对于同样的训练集学习效果,较小的  意味着需要更多的弱学习器的迭代次数。通常用步长和迭代最大次数一起来决定算法的拟合效果。

     ②. 第二种正则化的方式是通过子采样比例(subsample),取值为 。注意这里的子采样和随机森林不一样,随机森林使用的是有放回抽样,而这里是不放回抽样。如果取值为 1,则全部样本都使用,等于没有采用子采样。如果取值小于 1,则只有一部分样本会去做 GBDT 的决策树拟合。

         选择小于 1 的比例可以减少方差,即防止过拟合,但是会增加样本拟合的偏差,因此取值不能太低。推荐在  之间。

         使用子采样的 GBDT 有时也称作随机梯度提升树(Stochastic Gradient Boosting Tree, SGBT)。由于使用了子采样,程序可以通过采样分到不同的任务去做 Boosting 的迭代过程,最后形成新树,从而减少学习器难以并行学习的弱点。

     ③. 第三种是对弱学习器即 CART 回归树进行正则化剪枝。

 

    (6). GBDT 的优缺点

     ①. 优点

      a. 可以灵活处理各种类型的数据,包括连续值和离散值。

      b. 在相对少的调参时间情况下,预测的准确率也可以比较高。这个是相对于 SVM 来说的。

      c. 使用一些健壮的损失函数,对异常值的鲁棒性非常强。比如 Huber 损失函数和 Quantile 损失函数。

     ②. 缺点

      a. 由于弱学习器之间存在依赖关系,难以并行训练数据。不过可以通过子采样的 SGBT 来达到部分并行。

     

    (7). 适用范围

     ①. GBDT 几乎可用于所有的回归问题(线性/非线性)

     ②. 亦可用于二分类问题(设定阈值,大于阈值为正例,小于阈值为负例)

     ③. 不太适用于多分类问题。

 

    (8). 代码演示

     某一汽车公司调查城市人口的基本情况,来判断该人 是否贷款买车,找出潜在客户,提高销售业绩。

     

    代码的目录结构如下:

    

    百度云数据下载:链接:https://pan.baidu.com/s/1WCtjdhEetOgGyiTF43ZwgQ 
                                    提取码:rrbu

 

     代码操作,和 前面的 机器篇——集成学习(二) 随机森林(Rondoom Forest) 算法 类似;都是用网格搜索进行调参,将得到的参数,代入最终的模型算法中,得到较好的预测结果。

#!/usr/bin/env python
# _*_ coding:utf-8 _*_
# ============================================
# @Time     : 2020/01/12 01:09
# @Author   : WanDaoYi
# @FileName : gbdt_demo.py
# ============================================import os
import pandas as pd
import numpy as np
from sklearn.ensemble import GradientBoostingClassifier
from sklearn import metrics
from sklearn.model_selection import GridSearchCV, train_test_splitclass GBDT(object):def __init__(self):self.base_path = os.getcwd()self.data_path_last = "data_info/car.csv"self.data_path = os.path.join(self.base_path, self.data_path_last)self.x_train, self.x_val, self.y_train, self.y_val = self.read_data()pass# 获取数据集def read_data(self):# 读取数据文件data_info = pd.read_csv(self.data_path, encoding="gbk")print(data_info.head())# 划分数据为训练集和验证集x = data_info.values[:, : -1]y = data_info.values[:, -1]x_train, x_val, y_train, y_val = train_test_split(x, y, test_size=0.3)return x_train, x_val, y_train, y_valpass# 网格搜索,调节参数:n_estimators(最大决策树的数量)、# learning_rate(学习率)、max_depth(最大深度)def best_n_estimators_lr_depth(self):# np.arange 可以生成 float 类型,range 只能生成 int 类型best_param = {'n_estimators': range(10, 201, 10),'learning_rate': np.arange(0.1, 1.1, 0.1),'max_depth': range(1, 20, 1)}best_gsearch = GridSearchCV(estimator=GradientBoostingClassifier(min_samples_split=300,min_samples_leaf=20,# n_estimators=80,# learning_rate=0.1,# max_depth=8,max_features='sqrt',subsample=0.8,random_state=10),param_grid=best_param, scoring='roc_auc', iid=False, cv=10)best_gsearch.fit(self.x_train, self.y_train)print("best_param:{0}".format(best_gsearch.best_params_))print("best_score:{0}".format(best_gsearch.best_score_))# best_param: {'learning_rate': 0.9, 'max_depth': 5, 'n_estimators': 180}# best_score: 0.9422813593203397return best_gsearch.best_params_pass# 网格搜索,调节参数:min_samples_split(内部节点再划分所需最小样本数)、# min_samples_leaf(叶子节点最少样本数)def best_samples_split_leaf(self, estimators=180, lr=0.9, depth=5):best_param = {'min_samples_split': range(80, 150, 10),'min_samples_leaf': range(10, 80, 10)}best_gsearch = GridSearchCV(estimator=GradientBoostingClassifier(n_estimators=estimators,learning_rate=lr,max_depth=depth,# min_samples_split=300,# min_samples_leaf=20,max_features='sqrt',subsample=0.8,random_state=10),param_grid=best_param, scoring='roc_auc', iid=False, cv=10)best_gsearch.fit(self.x_train, self.y_train)print("best_param:{0}".format(best_gsearch.best_params_))print("best_score:{0}".format(best_gsearch.best_score_))# best_param: {'min_samples_leaf': 20, 'min_samples_split': 110}# best_score: 0.9403354784793416return best_gsearch.best_params_pass# 网格搜索,调节参数:max_features(最大特征数)、# subsample(子采样)def best_features_subsample(self, estimators=180, lr=0.9, depth=5, samples_split=110,samples_leaf=20):best_param = {'max_features': range(2, 20, 1),'subsample': np.arange(0.1, 1.1, 0.1)}best_gsearch = GridSearchCV(estimator=GradientBoostingClassifier(min_samples_split=samples_split,min_samples_leaf=samples_leaf,n_estimators=estimators,learning_rate=lr,max_depth=depth,# max_features='sqrt',# subsample=0.8,random_state=10),param_grid=best_param, scoring='roc_auc', iid=False, cv=10)best_gsearch.fit(self.x_train, self.y_train)print("best_param:{0}".format(best_gsearch.best_params_))print("best_score:{0}".format(best_gsearch.best_score_))# best_param: {'max_features': 17, 'subsample': 1.0}# best_score: 0.9572549019607841return best_gsearch.best_params_pass# 网格搜索,调节参数:random_state(随机种子)def best_random(self, estimators=180, lr=0.9, depth=5, samples_split=110,samples_leaf=20, features=17, subsample=1.0):best_param = {'random_state': range(1, 300, 1)}best_gsearch = GridSearchCV(estimator=GradientBoostingClassifier(min_samples_split=samples_split,min_samples_leaf=samples_leaf,n_estimators=estimators,learning_rate=lr,max_depth=depth,# random_state=10,max_features=features,subsample=subsample),param_grid=best_param, scoring='roc_auc', iid=False, cv=10)best_gsearch.fit(self.x_train, self.y_train)print("best_param:{0}".format(best_gsearch.best_params_))print("best_score:{0}".format(best_gsearch.best_score_))# best_param: {'random_state': 1}# best_score: 0.9604184132423583return best_gsearch.best_params_pass# 用调节较好的参数,进行训练和预测def best_param_gbdt(self, estimators=80, lr=0.5, depth=12, samples_split=200,samples_leaf=20, features=5, subsample=1.0, state=1):best_model = GradientBoostingClassifier(min_samples_split=samples_split,min_samples_leaf=samples_leaf,n_estimators=estimators,learning_rate=lr,max_depth=depth,random_state=state,max_features=features,subsample=subsample)best_model.fit(self.x_train, self.y_train)y_pred = best_model.predict(self.x_val)acc_score = metrics.accuracy_score(self.y_val, y_pred)print("acc_score: {}".format(acc_score))print("score: {}".format(best_model.score(self.x_val, self.y_val)))print("AUC Score: {}".format(metrics.roc_auc_score(self.y_val, y_pred)))# y_proba = best_model.predict_proba(self.x_val)# # 预测为 0 的概率# y_zero = y_proba[:, 0]# # 预测为 1 的概率# y_one = y_proba[:, 1]# print("AUC Score2: {}".format(metrics.roc_auc_score(self.y_val, y_one)))passif __name__ == "__main__":demo = GBDT()# 调节参数函数,可以用这里连着调,也可以分开一步步去调# best_params_ne_lr_md = demo.best_n_estimators_lr_depth()# n_estimators = best_params_ne_lr_md["n_estimators"]# learning_rate = best_params_ne_lr_md["learning_rate"]# max_depth = best_params_ne_lr_md["max_depth"]## best_params_spl_lea = demo.best_samples_split_leaf(estimators=n_estimators,#                                                    lr=learning_rate,#                                                    depth=max_depth#                                                    )# min_samples_split = best_params_spl_lea["min_samples_split"]# min_samples_leaf = best_params_spl_lea["min_samples_leaf"]## best_params_fea_sub = demo.best_features_subsample(estimators=n_estimators,#                                                    lr=learning_rate,#                                                    depth=max_depth,#                                                    samples_split=min_samples_split,#                                                    samples_leaf=min_samples_leaf#                                                    )# max_features = best_params_fea_sub["max_features"]# subsample = best_params_fea_sub["subsample"]## best_params_random = demo.best_random(estimators=n_estimators,#                                       lr=learning_rate,#                                       depth=max_depth,#                                       samples_split=min_samples_split,#                                       samples_leaf=min_samples_leaf,#                                       features=max_features,#                                       subsample=subsample#                                       )demo.best_param_gbdt()pass

        调参之后,训练结果如下:

        

       这个结果是,我只随便调了一下参数,n_estimators(最大决策树的数量)、learning_rate(学习率)、max_depth(最大深度) 这三个参数,我只调了一次。因为,该方法放了 3 个参数进行调,比较耗时,我就没去第二次调了。读者们可以放 2 个参数,慢慢调,这样,会快很多的。而且,运行多次,可以找到一组相对较好的参数的。

 

 

 

                 

 

返回主目录

返回集成学习目录

上一章:机器篇——集成学习(五) 细说 梯度提升(Gradient Boost)算法 

下一章:机器篇——集成学习(七) 细说 XGBoost 算法

 

  相关解决方案