python小白之路:第十九章 Boosting模型(二)

提升树模型(Boosting Decision Tree,BDT)

以决策树为基函数的提升方法。

对于分类问题的决策树是二叉分类树;对于回归问题是二叉回归树。

一个根结点直接连两个叶结点的简单决策树称为决策树桩。

1 算法

前向分步算法+加法模型

针对不同问题,损失函数有所不同

第m步模型:
f m ( x ) = f m − 1 ( x ) + T ( x i ; Θ m ) f_m(x)=f_{m-1}(x)+T(x_i;\Theta_m) fm(x)=fm1(x)+T(xi;Θm)
参数 Θ m \Theta_m Θm:
Θ ^ m = a r g min ⁡ Θ m ∑ i = 1 N L ( y i , f m − 1 ( x i ) + T ( x i ; Θ m ) ) \hat\Theta_m =arg\min_{\Theta_m}\sum_{i=1}^NL(y_i,f_{m-1}(x_i)+T(x_i;\Theta_m)) Θ^m=argΘmmini=1NL(yi,fm1(xi)+T(xi;Θm))

1.1 分类问题

对于二分类问题,只要使用二类分类树作为基分类器就行。

损失函数是指数损失函数

1.2 回归问题

思路:学习回归树拟合残差

损失函数是平方误差损失函数

在划分时可以按照SSE最小

树可表示为:

J J J是回归树的复杂度即叶结点个数, c j c_j cj是各划分的区域上的常数

T ( x ; Θ ) = ∑ j = 1 J c j I ( x ∈ R j ) T(x;\Theta)=\sum_{j=1}^Jc_jI(x\in R_j) T(x;Θ)=j=1JcjI(xRj)

初始化:
f 0 ( x ) = 0 f_0(x)=0 f0(x)=0
第m步的模型:
f m ( x ) = f m − 1 ( x ) + T ( x ; Θ m ) , m = 1 , 2 , . . . , M f_m(x)=f_{m-1}(x)+T(x;\Theta_m), \quad \text m=1,2,...,M fm(x)=fm1(x)+T(x;Θm),m=1,2,...,M
采用平方误差损失函数:

r = y − f m − 1 ( x ) r=y-f_{m-1}(x) r=yfm1(x)是当前模型拟合数据的残差(residual)

L ( y , f m − 1 ( x ) + T ( x i ; Θ m ) ) = [ y − f m − 1 ( x ) − T ( x i ; Θ m ) ] 2 = [ r − T ( x i ; Θ m ) ] 2 L(y,f_{m-1}(x)+T(x_i;\Theta_m))=[y-f_{m-1}(x)-T(x_i;\Theta_m)]^2\\ =[r-T(x_i;\Theta_m)]^2 L(y,fm1(x)+T(xi;Θm))=[yfm1(x)T(xi;Θm)]2=[rT(xi;Θm)]2

1.3 梯度提升(Gradient Boosting)

大致和上面步骤一样

对一般的损失函数,使用梯度提升算法。利用损失函数的负梯度在当前模型的值作为残差的近似值。

当损失函数是平方差损失函数时,残差就是负梯度。但是,平方差损失函数对于异常值太敏感。

残差近似值:
− [ ∂ L ( y , f ( x i ) ) ∂ f ( x i ) ] f ( x ) = f m − 1 ( x ) -[\frac{\partial L(y,f(x_i))}{\partial f(x_i)}]_{f(x)=f_{m-1}(x)} [f(xi)L(y,f(xi))]f(x)=fm1(x)
第m步:

  1. i = 1 , 2 , . . . , N i=1,2,...,N i=1,2,...,N:
    r m i = − [ ∂ L ( y , f ( x i ) ) ∂ f ( x i ) ] f ( x ) = f m − 1 ( x ) r_{mi}=-[\frac{\partial L(y,f(x_i))}{\partial f(x_i)}]_{f(x)=f_{m-1}(x)} rmi=[f(xi)L(y,f(xi))]f(x)=fm1(x)

  2. r m i r_{mi} rmi拟合一个回归树,得到第m棵树的叶结点区域 R m j R_{mj} Rmj

  3. j = 1 , 2 , . . . , J j=1,2,...,J j=1,2,...,J:
    c m j = a r g min ⁡ c ∑ x i ∈ R m j L ( y i , f m − 1 ( x i ) + c ) c_{mj}=arg\min_{c}\sum_{x_i\in R_{mj}}L(y_i,f_{m-1}(x_i)+c) cmj=argcminxiRmjL(yi,fm1(xi)+c)

  4. 更新:
    f m x = f m − 1 ( x ) + ∑ j = 1 J c m j I ( x ∈ R m j ) f_m{x}=f_{m-1}(x)+\sum^J_{j=1}c_{mj}I(x\in R_{mj}) fmx=fm1(x)+j=1JcmjI(xRmj)

最终回归树:
f ^ ( x ) = f M ( x ) = ∑ m = 1 M ∑ j = 1 J c m j I ( x ∈ R m j ) \hat f(x)=f_M(x)=\sum^M_{m=1}\sum^J_{j=1}c_{mj}I(x\in R_{mj}) f^(x)=fM(x)=m=1Mj=1JcmjI(xRmj)

2 代码

# 以李航老师的《统计学习方法》(第二版)的p168例子为例

import numpy as np

x = [1,2,3,4,5,6,7,8,9,10]
y = [5.56,5.70,5.91,6.40,6.80,7.05,8.90,8.70,9.00,9.05] 

def BDT(MS,x,y):
    ms_f = 1000     # 初始化提升树拟合数据的平方损失误差
    f = [0]*len(x)  # 初始化提升树
    n = 1           # 记录迭代次数
    r = y           # 第一次残差为y
    while ms_f >= MS:   # 平方损失误差要求
        ms_f = 0
        R1_best = []
        R2_best = []
        ms_min = 1000   # 初始化划分的最小平方误差
        s_best = 0      # 初始化最优划分点
        c1_best = 0     # 初始化最优划分时使叶结点内部平方损失误差最小的参数
        c2_best = 0     # 初始化最优划分时使叶结点内部平方损失误差最小的参数
        # 根据最小化损失函数求最优的划分点和对应的系数
        for s in np.linspace(1.5,9.5,9):
            ms = 0
            R1 = []
            R2 = []
            c1 = 0
            c2 = 0
            n1,n2,ms1,ms2 = 0,0,0,0
            for i in range(0,len(x)):
                if x[i] < s or x[i] == s:
                    R1.append(x[i])
                    c1 += r[i]
                    n1 += 1
                else:
                    R2.append(x[i])
                    c2 += r[i]
                    n2 += 1
            
            c1 = c1/n1 # 使R1平方误差最小时的参数
            c2 = c2/n2 # 使R2平方误差最小时的参数
            for i in R1:
                ms1 += (r[i-1] - c1)**2
            for i in R2:
                ms2 += (r[i-1] - c2)**2 

            ms = ms1 + ms2 # 损失函数
            if ms_min > ms:
                ms_min = ms 
                s_best = s
                c1_best = c1
                c2_best = c2
                R1_best = R1
                R2_best = R2
        
        # 回归树  
        T = []
        for i in range(0,len(x)):
            if x[i] < s_best:
                T.append(c1_best)
            else:
                T.append(c2_best)
        
        # 提升树
        for i in range(0,len(T)):
            f[i] += T[i]
        
        # 残差及拟合残差的平方损失误差
        r = []
        for i in range(0,len(f)):
            r.append(y[i]-f[i])
            ms_f += r[i]**2
        
        # 记录

        with open('record_BDT.txt','a+',encoding='utf-8') as fp:
            fp.write(
                f'第{n}次:当s={s_best}时,m(s)达到最小值{ms_min},此时R1={R1_best},R2={R2_best},c1={c1_best},c2={c2_best},回归树为{T},提升树为{f},残差为{r},平方损失误差为{ms_f}\n\n'
            )
            fp.close()
      
        n += 1

        
    return f

BDT(0.5,x,y)

# 结果
#[5.87,5.87,5.87,6.603333333333333,6.603333333333333,6.603333333333333,8.9125,8.9125,8.9125,8.9125]1:

当s=6.5时,m(s)达到最小值1.9300083333333338,此时R1=[1, 2, 3, 4, 5, 6],R2=[7, 8, 9, 10],c1=6.236666666666667,c2=8.912500000000001,回归树为[6.236666666666667, 6.236666666666667, 6.236666666666667, 6.236666666666667, 6.236666666666667, 6.236666666666667, 8.912500000000001, 8.912500000000001, 8.912500000000001, 8.912500000000001],提升树为[6.236666666666667, 6.236666666666667, 6.236666666666667, 6.236666666666667, 6.236666666666667, 6.236666666666667, 8.912500000000001, 8.912500000000001, 8.912500000000001, 8.912500000000001],残差为[-0.6766666666666676, -0.5366666666666671, -0.3266666666666671, 0.1633333333333331, 0.5633333333333326, 0.8133333333333326, -0.012500000000001066, -0.21250000000000213, 0.08749999999999858, 0.1374999999999993],平方损失误差为1.93000833333333382:

当s=3.5时,m(s)达到最小值0.8006750000000016,此时R1=[1, 2, 3],R2=[4, 5, 6, 7, 8, 9, 10],c1=-0.513333333333334,c2=0.219999999999999,回归树为[-0.513333333333334, -0.513333333333334, -0.513333333333334, 0.219999999999999, 0.219999999999999, 0.219999999999999, 0.219999999999999, 0.219999999999999, 0.219999999999999, 0.219999999999999],提升树为[5.723333333333334, 5.723333333333334, 5.723333333333334, 6.456666666666666, 6.456666666666666, 6.456666666666666, 9.1325, 9.1325, 9.1325, 9.1325],残差为[-0.163333333333334, -0.023333333333333428, 0.18666666666666654, -0.056666666666665755, 0.3433333333333337, 0.5933333333333337, -0.23249999999999993, -0.432500000000001, -0.13250000000000028, -0.08249999999999957],平方损失误差为0.80067500000000153:

当s=6.5时,m(s)达到最小值0.47800833333333437,此时R1=[1, 2, 3, 4, 5, 6],R2=[7, 8, 9, 10],c1=0.1466666666666668,c2=-0.2200000000000002,回归树为[0.1466666666666668, 0.1466666666666668, 0.1466666666666668, 0.1466666666666668, 0.1466666666666668, 0.1466666666666668, -0.2200000000000002, -0.2200000000000002, -0.2200000000000002, -0.2200000000000002],提升树为[5.87, 5.87, 5.87, 6.603333333333333, 6.603333333333333, 6.603333333333333, 8.9125, 8.9125, 8.9125, 8.9125],残差为[-0.3100000000000005, -0.16999999999999993, 0.040000000000000036, -0.20333333333333226, 0.1966666666666672, 0.4466666666666672, -0.01249999999999929, -0.21250000000000036, 0.08750000000000036, 0.13750000000000107],平方损失误差为0.47800833333333437

# sklearn的GBDT算法api
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.ensemble import GradientBoostingRegressor
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值