首页 > 其他分享 >监督学习集成模型——GBDT

监督学习集成模型——GBDT

时间:2022-08-18 21:23:44浏览次数:44  
标签:集成 min pred 模型 GBDT estimators self

一、梯度提升树

提升是一类将弱学习器提升为强学习器的算法总称。

提升树(boosting tree)就是弱学习器为决策树的提升方法。针对提升树模型,加性模型和前向分步算法的组合是典型的求解方式。当损失函数为平方损失和指数损失时,前向分步算法的每一步迭代都较为容易求解,但如果是一般的损失函数,前向分步算法的每一步迭代并不容易。使用损失函数的负梯度在当前模型的值来求解更为一般的提升树模型。这种基于负梯度求解提升树前向分步迭代过程的方法也叫梯度提升树(gradient boosting tree)

在AdaBoost中,我们可以使用任意单模型作为弱分类器,但提升树的弱分类器只能是决策树模型。

GBDT的全称为梯度提升决策树(gradient boosting decision tree),其基模型(弱学习器)为CART决策树,针对分类问题基模型为二叉分类树,对应梯度提升模型就叫GBDT;针对回归问题基模型为二叉回归树,对应的梯度提升模型叫作GBRT(gradient boosting regression tree)。

GBDT近年来在一些数据竞赛上大杀四方,并不断衍生出像XGBoost和LightGBM等更强大的版本。从名字上看,GBDT是由决策树、提升模型梯度下降一起构成的。所以,要搞清楚GBDT的基本原理,就必须对这三者及其相互作用有一个深入的理解。

二、GBDT基本原理

决策树的基本原理我们已经很清楚了,就是依据信息增益等原则不断选择特征构建树模型的过程,具体可参考https://www.cnblogs.com/wkfvawl/p/16587436.html。

Boosting则是一种集成学习模式,通过将多个单个决策树(弱学习器)进行线性组合构成一个强学习器的过程,Boosting以一个单模型作为作为弱分类器,GBDT中使用CART作为这种弱学习器(基模型)。而融入了梯度下降对Boosting树模型进行优化之后就有了梯度提升树模型。

我们先来用一个通俗的说法来理解GBDT。假设某位同学月薪10k,先用一个树模型拟合了6k,发现有4k的损失,然后再用一棵树模型拟合了2k,这样持续拟合下去,拟合值和目标值之间的残差会越来越小,而我们将每一轮迭代,也就是每一棵树的预测值加起来就是模型最终的预测结果。不停的使用单棵决策树组合就是Boosting的过程,使用梯度下降对Boosting树模型进行优化的过程就是Gradient Boosting。

  • GBDT是回归树,不是分类树
  • GBDT的核心在于累加所有树的结果作为最终结果
  • GBDT的关键点就是利用损失函数的负梯度去模拟(代替)残差,这样对于一般的损失函数,只要其一阶可导就行。

一个提升树模型的数学表达为:

\(f_M(x)=∑_{m=1}^M T(x│Θ_m)\)

其中\(T(x;Θ_m)\)为决策树表示的基模型(决策树不会很深),\(Θ_m\)为决策树参数,\(M\)为决策树棵数。

当确定初始提升树模型\(f_0(x)=0\),第m的模型表示为:

\(f_m(x)=f_{m−1}(x)+T(x;Θ_m)\)

其中\(f_{m−1}(x)\)为当前迭代模型,根据前向分步算法,可以使用经验风险最小化来确定下一棵决策树的参数\(Θ_m\):

\(\widehat{\Theta}_{m}=\underset{\theta_{m}}{\operatorname{argmin}} \sum_{i=1}^{N} L\left(y_{i}, f_{m-1}\left(x_{i}\right)+T\left(x_{i} ; \Theta_{m}\right)\right)\)

以梯度提升回归树为例,一棵回归树可以表示为:

\(T(x;Θ)=∑_{k=1}^K c_k I(x∈R_j)\)

根据加性模型,第0步、第m步和最终模型可以表示为:

\(f_0(x)=0\)

\(f_m(x)=f_{m−1}(x)+T(x;Θ_m)\)

\(f_M(x)=∑_{m=1}^MT(x;Θ_m)\)

在已知\(f_{m−1}(x)\)的情况下,求解前述优化式可得到当前迭代步的模型参数。假设回归树的损失函数为平方损失

\(L(y,f(x))=(y−f(x))^2\)

对应到GBRT中,损失可推导为:

\(L(y,f_{m−1}(x)+T(x;Θ_m))=[y−f_{m−1}(x)−T(x;Θ_m)]^2\)

  • \(y\)真实值
  • \(f_{m−1}(x)\)截止到m-1的预测值
  • T(x;Θ_m)第m棵树的输出

令:\(r=y−f_{m−1}(x)\):表示训练第M棵树时,截止到当前的残差值

有:\(L(y,f_{m−1}(x)+T(x│Θ_m))=[r−T(x│Θ_m)]^2\)

梯度提升树以梯度下降的方法,使用损失函数的负梯度在当前模型的值作为回归提升树中残差的近似值:

\[r_{m i}=-\left[\frac{\partial L\left(y_{i} f\left(x_{i}\right)\right)}{\partial f\left(x_{i}\right)}\right]_{f(x)=f_{m-1}(x)} \]

综合提升树模型、前向分步算法和梯度提升,给定训练数据集\(D={(x_1,y_1),(x_2,y_2),⋯,(x_N,y_N)},x_i∈χ,y_i∈Y⊆R^n\),GBDT算法的一般流程可归纳为如下步骤。

(1)初始化提升树模型:\(f_{0}(x)=\underset{c}{\operatorname{argmin}} \sum_{i=1}^{N} L\left(y_{i}, c\right)\)

(2)对\(m=1, 2,⋯,M\),有:

  • (a)对每个样本\(i=1, 2,⋯,N\),计算负梯度拟合的残差:$$r_{m i}=-\left[\frac{\partial L\left(y_{i} f\left(x_{i}\right)\right)}{\partial f\left(x_{i}\right)}\right]{f(x)=f_{m-1}(x)}$$

  • (b)将上一步得到的残差作为样本新的真实值,并将数据\((x_i,r_mi),i=1, 2,⋯,N\)作为下一棵树的训练数据,得到一棵新的回归树\(f_m(x)\),其对应的叶子结点区域为\(R_{mj},j=1, 2,⋯,J\)。其中J为回归树T的叶子结点的个数。

  • (c)对叶子区域\(j=1, 2,⋯,J\)计算最佳拟合值:\(c_{m j}=\underset{c}{\operatorname{argmin}} \sum_{x_{i} \in R_{m j}} L\left(y_{i}, f_{m-1}\left(x_{i}\right)+c\right)\)

  • (d)更新提升树模型:\(f_m(x)=f_{m−1}(x)+∑_{j=1}^J c_{mj}I(x∈R_{mj})\)

(3)得到最终的梯度提升树:$f(x)=f_M(x)=∑_{m=1}^M ∑_{j=1}^J c_{mj}I(x∈R_{mj}) $

三、GBDT算法实现

GBDT的基模型为CART,所以定义决策树结点和构建CART树至关重要,CART算法在前面决策树部分已经进行了初步实现。

当基模型构建好后,即可根据GBDT算法流程搭建GBDT和GBRT。除此之外,一些辅助函数的定义(最大熵/Gini指数计算),损失函数定义和模型可视化方法等辅助功能也应该一应俱全。

### GBDT定义
class GBDT(object):
    def __init__(self, n_estimators, learning_rate, min_samples_split,
                 min_gini_impurity, max_depth, regression):
        ### 常用超参数
        # 树的棵树
        self.n_estimators = n_estimators
        # 学习率
        self.learning_rate = learning_rate
        # 结点最小分裂样本数
        self.min_samples_split = min_samples_split
        # 结点最小基尼不纯度
        self.min_gini_impurity = min_gini_impurity
        # 最大深度
        self.max_depth = max_depth
        # 默认为回归树
        self.regression = regression
        # 损失为平方损失
        self.loss = SquareLoss()
        # 如果是分类树,需要定义分类树损失函数
        # 这里省略,如需使用,需自定义分类损失函数
        if not self.regression:
            self.loss = None
        # 多棵树叠加
        self.estimators = []
        for i in range(self.n_estimators):
            self.estimators.append(RegressionTree(min_samples_split=self.min_samples_split,
                                             min_gini_impurity=self.min_gini_impurity,
                                             max_depth=self.max_depth))
    # 拟合方法
    def fit(self, X, y):
        # 前向分步模型初始化,第一棵树
        self.estimators[0].fit(X, y)
        # 第一棵树的预测结果
        y_pred = self.estimators[0].predict(X)
        # 前向分步迭代训练
        for i in range(1, self.n_estimators):
            gradient = self.loss.gradient(y, y_pred)
            self.estimators[i].fit(X, gradient)
            y_pred -= np.multiply(self.learning_rate, self.estimators[i].predict(X))
            
    # 预测方法
    def predict(self, X):
        # 回归树预测
        y_pred = self.estimators[0].predict(X)
        for i in range(1, self.n_estimators):
            y_pred -= np.multiply(self.learning_rate, self.estimators[i].predict(X))
        # 分类树预测
        if not self.regression:
            # 将预测值转化为概率
            y_pred = np.exp(y_pred) / np.expand_dims(np.sum(np.exp(y_pred), axis=1), axis=1)
            # 转化为预测标签
            y_pred = np.argmax(y_pred, axis=1)
        return y_pred

GBDT分类树与回归树

### GBDT分类树
class GBDTClassifier(GBDT):
      def __init__(self, n_estimators=200, learning_rate=.5, min_samples_split=2,
                 min_info_gain=1e-6, max_depth=2):
            super(GBDTClassifier, self).__init__(n_estimators=n_estimators,
                                             learning_rate=learning_rate,
                                             min_samples_split=min_samples_split,
                                             min_gini_impurity=min_info_gain,
                                             max_depth=max_depth,
                                             regression=False)
      # 拟合方法
      def fit(self, X, y):
            super(GBDTClassifier, self).fit(X, y)
        
### GBDT回归树
class GBDTRegressor(GBDT):
      def __init__(self, n_estimators=300, learning_rate=0.1, min_samples_split=2,
                 min_var_reduction=1e-6, max_depth=3):
        super(GBDTRegressor, self).__init__(n_estimators=n_estimators,
                                            learning_rate=learning_rate,
                                            min_samples_split=min_samples_split,
                                            min_gini_impurity=min_var_reduction,
                                            max_depth=max_depth,
                                            regression=True)

定义回归树的平方损失

class SquareLoss():
    # 定义平方损失
    def loss(self, y, y_pred):
        return 0.5 * np.power((y - y_pred), 2)
    # 定义平方损失的梯度
    def gradient(self, y, y_pred):
        return -(y - y_pred)

对波士顿房价数据集进行预测,最后计算评估回归模型的均方误差

# 导入sklearn数据集模块
from sklearn import datasets
# 导入波士顿房价数据集
boston = datasets.load_boston()
# 打乱数据集
X, y = data_shuffle(boston.data, boston.target, seed=13)
X = X.astype(np.float32)
offset = int(X.shape[0] * 0.9)
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
# 创建GBRT实例
model = GBDTRegressor()
# 模型训练
model.fit(X_train, y_train)
# 模型预测
y_pred = model.predict(X_test)
# 计算模型预测的均方误差
mse = mean_squared_error(y_test, y_pred)
print ("Mean Squared Error of NumPy GBRT:", mse)

Mean Squared Error of NumPy GBRT: 84.29078032628252

使用sklearn提供的GBDT算法实现

# 导入sklearn GBDT模块
from sklearn.ensemble import GradientBoostingRegressor
# 创建模型实例
reg = GradientBoostingRegressor(n_estimators=200, learning_rate=0.5,
                                 max_depth=4, random_state=0)
# 模型拟合
reg.fit(X_train, y_train)
# 模型预测
y_pred = reg.predict(X_test)
# 计算模型预测的均方误差
mse = mean_squared_error(y_test, y_pred)
print ("Mean Squared Error of sklearn GBRT:", mse)

Mean Squared Error of sklearn GBRT: 14.885053466425939

标签:集成,min,pred,模型,GBDT,estimators,self
From: https://www.cnblogs.com/wkfvawl/p/16600147.html

相关文章

  • cesium模型的本地加载模型
    需求目前有一个需求就是需要从本地拖拽glb文件模型到cesium地球中显示模型由于相关js库较多本文章就不涉及拖拽功能了思路第一种方案cesium通过Model.fromGltf函数来......
  • 数据结构中的数颜色模型
    目录目录目录简介一些解法莫队/带修莫队珂朵莉树简介在练习数据结构中,我们经常看到有以下操作的题:求一个区间\([l,r]\)的数字种数。求一个区间\([l,r]\)某一个数......
  • 38、python并发编程之IO模型
    38、python并发编程之IO模型  目录:一IO模型介绍二阻塞IO(blockingIO)三非阻塞IO(non-blockingIO)四多路复用IO(IOmultiplexing)五异步IO(A......
  • tp5模型的定义
    1<?php2namespaceapp\index\model;34usethink\Model;56classUserextendsModel7{8}先定义一个模型类在database把表的前缀改了namespaceapp\ind......
  • 合作再升级!云原生加速器成员企业云霁科技获得阿里云产品生态集成认证
    近日,杭州云霁科技有限公司CloudIaC(v1.0.0)通过阿里云产品集成认证测试,与阿里云旗下的阿里云容器服务ACK、云数据库RDSMySQL版深度集成。这意味着在云原生领域云霁科技......
  • kubernetes网络模型
    Overview本文将探讨Kubernetes中的网络模型,以及对各种网络模型进行分析。UnderlayNetworkModel什么是UnderlayNetwork底层网络UnderlayNetwork顾名思义是指网络......
  • 借助HSDB查看Java类对应的klass模型
    问题一:Java的每个类被加载到JVM中,他们对应的C++类是什么?答:klass模型问题二:在JDK8中,Java类存储在方法区还是堆区?普通的Java类,在JVM中对应的C++类是InstanceKlass,存储......
  • 基于C++的OpenGL 14 之模型加载
    1.引言本文基于C++语言,描述OpenGL的模型加载前置知识可参考:基于C++的OpenGL13之Mesh-当时明月在曾照彩云归-博客园(cnblogs.com)笔者这里不过多描述每个名词......
  • rim seal 网格模型批处理流程
    用turbogrid画完动叶域和静叶域网格后导出def文件;将def文件导入到icem里,测出静叶域网格hub面与rimseal交界处的网格节点坐标(跨度为单流道的两个节点坐标就......
  • 高并发之网络IO模型
    你好,我是坤哥今天我们聊一下高并发下的网络IO模型高并发即我们所说的C10K(一个server服务1w个client),C10M,写出高并发的程序相信是每个后端程序员的追求,高并发架构......