• 谈谈模型融合之三 —— GBDT


    前言

    本来应该是年后就要写的一篇博客,因为考完试后忙了一段时间课设和实验,然后回家后又在摸鱼,就一直没开动。趁着这段时间只能呆在家里来把这些博客补上。在之前的文章中介绍了 Random Forest 和 AdaBoost,这篇文章将介绍介绍在数据挖掘竞赛中,最常用的算法之一 —— GBDT(Gradient Boosting Decision Tree)。

    GBDT

    原理

    GBDT 实际上是 GBM(Gradient Boosting Machine) 中的一种,采用 CART 树作为基学习器,所以称为 GBDT。与 AdaBoost 一样,GBDT 为 Boosting 家族中的一员。其表达式为

    [f_m(x) = sum_{m=1}^{M} T(x;Theta_m) ]

    其中(T(x;Theta_m))表示决策树;(Theta_m)为决策树参数;M为树的个数。

    这里来回顾下 AdaBoost,AdaBoost 通过不断调整样本权重,使得上一次分类错误的样本权重变大,最后训练出 m 个弱分类器,然后对 m 个弱分类器加权结合形成强分类器。

    而 GBDT 又是另一思想,当我们假设前一轮迭代得到的学习器为 (f_{m-1}(x)) ,损失函数为 (L(y, f_{m-1}(x))) ,那么,本轮迭代的目标就是使损失函数 (L(y, f_{m-1}(x) + h_m(x))) 的值尽可能的小。

    我们先将损失函数假设为最常用的平方损失

    (r = y - f_{m-1}(x)) ,那么第 m 步的目标就是最小化 (L(y, f_m(x)) = frac{1}{2}(y-f_m(x))^2=frac{1}{2}(r-h_m(x))^2)

    到这里,似乎发现了点什么,我们对损失函数求导,发现:

    [frac{partial{L}}{partial{h_m(x)}}=h_m(x)-r ]

    看出什么来了没?对其取个负号就变成 (r-h_m(x)) ,即我们常说的残差 ,所以,当为平方损失函数时,弱学习器拟合的目标为之前拟合结果的残差。那到这里代码就很好写了,但是,我们实际中有很多其它的损失函数,而且在很多问题中,这些损失函数比平方损失要好得多。那这时候,如果我们还采用同样的思路,那就没办法像上面那样直接展开并拟合残差了,这时候该怎么办?

    这里别忘了,我们最终的目标是使得 (L(y, f_m(x))) 最小,那么只需要保证 (L(y, f_{m-1}(x)+h_m(x))) 的值比 (L(y, f_{m-1}(x))) 小就好了。

    [max[L(y,f_{m-1}(x))-L(y,f_{m-1}(x)+h(x))] ]

    检验大一高数学的怎么样的时候到了 orz

    我们前面说了第 m 轮迭代的损失函数为 (L(y, f_{m-1}(x) + h_m(x))) ,换一种形式,写成 (L(f_{m-1}(x) + h_m(x))) ,对其进行一阶泰勒展开,得

    [L(f_{m-1}(x)+h_m(x)) approx L(f_{m-1}(x)) + L'(f_{m-1}(x))h_m(x) ]

    所以,我们只需使得满足

    [max L'(f_{m-1}(x))h_m(x) \ L'(f_{m-1}(x))h_m(x)<0 ]

    那我们的 (h_m(x)) 到底要拟合什么呢?别忘了,我们是要求梯度的,在这里我们已知的是 (L'(f_{m-1}(x))) ,我们肯定是根据上一次的拟合的结果来拟合这一次的结果,所以,要使得结果最大,自然就是梯度方向。那么 (h_m(x)=-L'(f_{m-1}(x))) , 这样原先的 (r) 也就变成了梯度。这里如果把损失函数看作是平方损失,我们得到的结果也恰好就是我们所说的残差!!

    此时也总算明白了之前面腾讯的时候我说 GBDT 是拟合残差的时候面试官让我再回去重新康康这个算法的原因了。

    算法步骤

    输入: 训练数据集 (T = {(x_1, y_1),(x_2, y_2), ..., (x_N, y_N)}, x_i in X subset R^n, y_i in Y subset R); 损失函数 L(y,f(x)),树的个数M.

    输出: 梯度提升树(F_M(x))

    (1) 初始化 (f_0(x) = argmin_c Sigma_{i=1}^N L(y_i,c)).

    (2) 对 (m=1,2,...,M)

    ​ (a) 对(i =1,2,...,N),计算, (r_{mi} = - [frac{partial L(y_i, f(x_i))}{partial f(x_i)}]_{f(x) = F_{m-1}(x)}).

    ​ (b) 拟合残差(r_{mi})学习一个回归树,得到(f_m(x)).

    ​ (c) 更新(F_m(x) = F_{m-1}(x) + f_m(x)).

    (3) 得到回归问题提升树 (F_M(x) = Sigma_{i=0}^M f_i(x))

    代码

    这里代码是采用了平方损失的方法来写的,且解决的是分类问题

    def sigmoid(x):
        """
        计算sigmoid函数值
        """
        return 1 / (1 + np.exp(-x))
    
    
    def gbdt_classifier(X, y, M, max_depth=None):
        """
        用于分类的GBDT函数
        参数:
            X: 训练样本
            y: 样本标签,y = {0, +1}
            M: 使用M个回归树
            max_depth: 基学习器CART决策树的最大深度
        返回:
            F: 生成的模型
        """
        # 用0初始化y_reg
        y_reg = np.zeros(len(y))
        f = []
        
        for m in range(M):
            # 计算r
            r = y - sigmoid(y_reg)
            
            # 拟合r
            # 使用DecisionTreeRegressor,设置树深度为5,random_state=0
            f_m = DecisionTreeRegressor(max_depth=5, random_state=0)
            # 开始训练
            f_m.fit(X, r)
            # 更新f
            f.append(f_m)
            
            y_reg += f_m.predict(X)
        
        def F(X):
            num_X, _ = X.shape
            reg = np.zeros((num_X))
            
            for t in f:
                reg += t.predict(X)
            
            y_pred_gbdt = sigmoid(reg)
            
            # 以0.5为阈值,得到最终分类结果0或1
            one_position = y_pred_gbdt >= 0.5
            y_pred_gbdt[one_position] = 1
            y_pred_gbdt[~one_position] = 0
            
            return y_pred_gbdt
        
        return F
    

    小节

    到这里 GBDT 也就讲完了,从决策树 ID3 开始一直到 GBDT,后面终于要迎来最开始想要梳理的数据挖掘的两大杀器 XGBoost 和 LightGBM 了,下一篇将介绍 XGBoost。

  • 相关阅读:
    手势识别 ios
    无题
    核心动画笔记
    Quartz2D的学习2
    Quartz2D的学习1
    NSURLsessionTask
    NSURLSession
    POST请求的两种方式
    网络第一天
    NSThread
  • 原文地址:https://www.cnblogs.com/csu-lmw/p/12254174.html
Copyright © 2020-2023  润新知