【机器学习】基于GBDT的数据回归及python实现


本博文中GBDT(Gradient Boosting Decison Tree,梯度提升决策树)的决策树采用CART(Classification and Regression Tree,分类回归树)。CART主要参考 《python机器学习算法》,在这里就不赘述了,有需要的同学可以看大神的书。本博文首先介绍集成学习中的Boosting技术,然后介绍GBDT的原理及应用,在GBDT的应用部分重点介绍基于GBDT的数据回归技术,并顺便提及基于GBDT的数据分类思想(只需对回归做一点小的改进即可)。

一、Boosting技术

在面对一个复杂的问题时,我们可以训练多个模型,将这些模型的结果集成起来作为最终的结果,这种处理问题的思想被称为集成学习(参考资料【2】),集成学习分为两类:Bagging和Boosting。Bagging技术是指在样本中有放回的随机抽取几组样本数据,用这几组样本数据训练几个模型,最终由这几个模型投票决定分类结果或取均值得到回归结果,随机森林(参考资料【3】)就是一个典型的Bagging模型。Boosting技术同样是训练多个弱性能模型,但不同于Bagging技术中弱性能模型是并行且独立训练的,在Boosting中多个弱性能模型是串行的。Boosting对样本进行不同的赋值,对错误学习的样本的权重设置的较大,在后续的学习中集中处理难学的样本,最终得到一系列的预测结果,每个预测结果有一个权重。

二、GBDT原理与应用

2.1、GBDT思想

GBDT由两部分组成,分别是GB(Gradient Boosting)和DT(Decison Tree)。博文开头已经说明,DT采用CART的回归树。GB指的是沿着梯度方向,构造一系列的弱分类器函数,并以一定权重组合起来,形成最终决策的强分类器(参考资料【4】)。

2.2、基于GBDT的数据回归

假设总共有 m m 个串行CART回归树模型,每个CART模型单独拿出来的预测结果为:

f i ( X ) , { i = 1 , 2 ,   , m } {f_i}\left( X \right),\left\{ {i = 1,2, \cdots ,m} \right\}

i i 个模型串行在一起的预测结果为:

F i ( X ) , { i = 1 , 2 ,   , m } {F_i}\left( X \right),\left\{ {i = 1,2, \cdots ,m} \right\}

其中 F i ( X ) = F i 1 ( X ) + f i ( X ) {F_i}\left( X \right) = {F_{i - 1}}\left( X \right) + {f_i}\left( X \right)

假设样本标签为Y,前 i 1 i-1 个模型串行的预测输出与标签真实值之间的损失函数为:

L o s s ( Y , F i 1 ( X ) ) = 1 N j = 1 N ( y j F i 1 ( x j ) ) 2 Loss\left( {Y,{F_{i-1}}\left( X \right)} \right) = \frac{1}{N}\sum\limits_{j = 1}^N {{{\left( {{y_j} - {F_{i-1}}\left( {{x_j}} \right)} \right)}^2}}

当训练第 i i 个回归树模型时,我们的目的是前 i i 个模型串行预测输出与样本标签真实值之间的损失函数 L o s s ( Y , F i 1 ( X ) + f i ( X ) ) Loss\left( {Y,{F_{i-1}}\left( X \right) + {f_{i }}\left( X \right)} \right) 最小。

为了训练出模型 f i ( X ) f_i\left( X \right) ,GB方法利用最速下降的近似方法,即利用损失函数的负梯度在当前模型的值,作为回归问题中提升树算法的残差的近似值,去拟合当前的回归树模型。

r i j = [ L o s s ( Y , f ( x j ) ) f ( x j ) ] f ( x ) = f i 1 ( x ) {r_{ij}} = - {\left[ {\frac{{\partial Loss\left( {Y,f\left( {{x_j}} \right)} \right)}}{{\partial f\left( {{x_j}} \right)}}} \right]_{f\left( x \right) = {f_{i - 1}}\left( x \right)}}

r i j r_{ij} 表示求第 i i 个回归树模型时,第 j j 个样本特征对应的残差值。利用 { ( x 1 , r i 1 ) , ( x 2 , r i 2 ) ,   , ( x N , r i N ) } \left\{ {\left( {{x_1},{r_{i1}}} \right),\left( {{x_2},{r_{i2}}} \right), \cdots ,\left( {{x_N},{r_{iN}}} \right)} \right\} 去拟合第 i i 个CART模型。

2.3、基于GBDT的数据分类

以GBDT二分类为例,样本类别为 0 , 1 (0,1) ,将每一轮模型训练后的预测的结果利用sigmoid函数映射到0到1之间,最后预测值大于等于0.5的样本为类别1,预测值小于0.5的样本为类别0。

三、基于GBDT的数据回归的python实现

python代码与样本地址:https://github.com/shiluqiang/GBDT_regression
基于GBDT的数据回归中训练每一个CART模型,直接采用前面所有模型的串行输出与样本标签的残差为训练目标(参考资料【5】)。
GBDTpython代码如下

import CART_regression_tree
import numpy as np

def load_data(data_file):
    '''导入训练数据
    input:  data_file(string):保存训练数据的文件
    output: data(list):训练数据
    '''
    data_X = []
    data_Y = []
    f = open(data_file)
    for line in f.readlines():
        sample = []
        lines = line.strip().split("\t")
        data_Y.append(float(lines[-1]))
        for i in range(len(lines) - 1):
            sample.append(float(lines[i]))  # 转换成float格式
        data_X.append(sample)
    f.close()    
    return data_X,data_Y

class GBDT_RT(object):
    '''GBDT回归算法类
    '''
    def __init__(self):
        self.trees = None ##用于存放GBDT的树
        self.learn_rate = learn_rate ## 学习率,防止过拟合
        self.init_value = None ##初始数值
        self.fn = lambda x: x
        
    def get_init_value(self,y):
        '''计算初始数值为平均值
        input:y(list):样本标签列表
        output:average(float):样本标签的平均值
        '''
        average = sum(y)/len(y)
        return average
    
    def get_residuals(self,y,y_hat):
        '''计算样本标签标签与预测列表的残差
        input:y(list):样本标签列表
              y_hat(list):预测标签列表
        output:y_residuals(list):样本标签标签与预测列表的残差
        '''
        y_residuals = []
        for i in range(len(y)):
            y_residuals.append(y[i] - y_hat[i])
        return y_residuals
    
    def fit(self,data_X,data_Y,n_estimators,learn_rate,min_sample, min_err):
        '''训练GBDT模型
        input:self(object):GBDT_RT类
              data_X(list):样本特征
              data_Y(list):样本标签
              n_estimators(int):GBDT中CART树的个数
              learn_rate(float):学习率
              min_sample(int):学习CART时叶节点的最小样本数
              min_err(float):学习CART时最小方差
        '''
        ## 初始化预测标签和残差
        self.init_value = self.get_init_value(data_Y)
        
        n = len(data_Y)
        y_hat = [self.init_value] * n ##初始化预测标签
        y_residuals = self.get_residuals(data_Y,y_hat)
        
        self.trees = []
        self.learn_rate = learn_rate
        ## 迭代训练GBDT
        for j in range(n_estimators):
            idx = range(n)
            X_sub = [data_X[i] for i in idx] ## 样本特征列表
            residuals_sub = [y_residuals[i] for i in idx] ## 标签残差列表
            
            tree = CART_regression_tree.CART_RT(X_sub,residuals_sub, min_sample, min_err).fit()
            res_hat = [] ##残差的预测值
            for m in range(n):
                res_hat.append(CART_regression_tree.predict(data_X[m],tree))
            ## 计算此时的预测值等于原预测值加残差预测值
            y_hat = [y_hat[i] + self.learn_rate * res_hat[i] for i in idx]
            y_residuals = self.get_residuals(data_Y,y_hat)
            self.trees.append(tree)
            
    def GBDT_predict(self,xi):
        '''预测一个样本
        '''
        return self.fn(self.init_value + sum(self.learn_rate * CART_regression_tree.predict(xi,tree) for tree in self.trees))
    
    def GBDT_predicts(self,X):
        '''预测多个样本
        '''
        return [self.GBDT_predict(xi) for xi in X]

def error(Y_test,predict_results):
    '''计算预测误差
    input:Y_test(list):测试样本标签
          predict_results(list):测试样本预测值
    output:error(float):均方误差
    '''
    Y = np.mat(Y_test)
    results = np.mat(predict_results)
    error = np.square(Y - results).sum() / len(Y_test)
    return error


if __name__ == '__main__':
    print ("------------- 1.load data ----------------")
    X_data,Y_data = load_data("sine.txt") 
    X_train = X_data[0:150]
    Y_train = Y_data[0:150]
    X_test = X_data[150:200]
    Y_test = Y_data[150:200]
    print('------------2.Parameters Setting-----------')
    n_estimators = 4
    learn_rate = 0.5
    min_sample = 30
    min_err = 0.3
    print ("--------------3.build GBDT ---------------")
    gbdt_rt = GBDT_RT()
    gbdt_rt.fit(X_train,Y_train,n_estimators,learn_rate,min_sample, min_err)
    print('-------------4.Predict Result--------------')
    predict_results = gbdt_rt.GBDT_predicts(X_test)
    print('--------------5.Predict Error--------------')
    error = error(Y_test,predict_results)
    print('Predict error is: ',error)

参考资料

1、《python机器学习算法》
2、https://blog.csdn.net/google19890102/article/details/46507387
3、https://blog.csdn.net/qq547276542/article/details/78304454
4、https://blog.csdn.net/legendavid/article/details/78904353
5、GBDT回归的原理及Python实现

猜你喜欢

转载自blog.csdn.net/Luqiang_Shi/article/details/85017280