GBDT回归的原理及Python实现

提到GBDT回归相信大家应该都不会觉得陌生,本文就GBDT回归的基本原理进行讲解,并手把手、肩并肩地带您实现这一算法。完整实现代码请参考本人的github。

一、原理篇

我们用人话而不是大段的数学公式来讲讲GBDT回归是怎么一回事。

1.1 温故知新 回归树是GBDT的基础,之前的一篇文章曾经讲过回归树的原理和实现。链接如下:

回归树的原理及Python实现

1.2 预测年龄 仍然以预测同事年龄来举例,从《回归树》那篇文章中我们可以知道,如果需要通过一个常量来预测同事的年龄,平均值是最佳选择之一。

1.3 年龄的残差 我们不妨假设同事的年龄分别为5岁、6岁、7岁,那么同事的平均年龄就是6岁。所以我们用6岁这个常量来预测同事的年龄,即[6, 6, 6]。每个同事年龄的残差 = 年龄 - 预测值 = [5, 6, 7] - [6, 6, 6],所以残差为[-1, 0, 1]

1.4 预测年龄的残差 为了让模型更加准确,其中一个思路是让残差变小。如何减少残差呢?我们不妨对残差建立一颗回归树,然后预测出准确的残差。假设这棵树预测的残差是[-0.9, 0, 0.9],将上一轮的预测值和这一轮的预测值求和,每个同事的年龄 = [6, 6, 6] + [-0.9, 0, 0.9] = [5.1, 6, 6.9],显然与真实值[5, 6, 7]更加接近了, 年龄的残差此时变为[-0.1, 0, 0.1],预测的准确性得到了提升。

1.5 GBDT 重新整理一下思路,假设我们的预测一共迭代3轮 年龄:[5, 6, 7]

第1轮预测:6, 6, 6

第1轮残差:[-1, 0, 1]

第2轮预测:6, 6, 6 + -0.9, 0, 0.9 = [5.1, 6, 6.9]

第2轮残差:[-0.1, 0, 0.1]

第3轮预测:6, 6, 6 + -0.9, 0, 0.9 + -0.08, 0, 0.07 = [5.02, 6, 6.97]

第3轮残差:[-0.08, 0, 0.03]

看上去残差越来越小,而这种预测方式就是GBDT算法。

1.6 公式推导 看到这里,相信您对GBDT已经有了直观的认识。这么做有什么科学依据么,为什么残差可以越来越小呢?前方小段数学公式低能预警。

因此,我们需要通过用第m-1轮残差的均值来得到函数fm,进而优化函数Fm。而回归树的原理就是通过最佳划分区域的均值来进行预测。所以fm可以选用回归树作为基础模型,将初始值,m-1颗回归树的预测值相加便可以预测y。

二、实现篇

本人用全宇宙最简单的编程语言——Python实现了GBDT回归算法,没有依赖任何第三方库,便于学习和使用。简单说明一下实现过程,更详细的注释请参考本人github上的代码。

2.1 导入回归树类 回归树是我之前已经写好的一个类,在之前的文章详细介绍过,代码请参考:

https://github.com/tushushu/imylu/blob/master/imylu/tree/regression_tree.py
from ..tree.regression_tree import RegressionTree

2.2 创建GradientBoostingBase类 初始化,存储回归树、学习率、初始预测值和变换函数。(注:回归不需要做变换,因此函数的返回值等于参数)

class GradientBoostingBase(object):
    def __init__(self):
        self.trees = None
        self.lr = None
        self.init_val = None
        self.fn = lambda x: x

2.3 计算初始预测值 初始预测值即y的平均值。

def _get_init_val(self, y):
    return sum(y) / len(y)

2.4 计算残差

def _get_residuals(self, y, y_hat):
    return [yi - self.fn(y_hat_i) for yi, y_hat_i in zip(y, y_hat)]

2.5 训练模型

训练模型的时候需要注意以下几点: 1. 控制树的最大深度max_depth; 2. 控制分裂时最少的样本量min_samples_split; 3. 训练每一棵回归树的时候要乘以一个学习率lr,防止模型过拟合; 4. 对样本进行抽样的时候要采用有放回的抽样方式。

def fit(self, X, y, n_estimators, lr, max_depth, min_samples_split, subsample=None):
    self.init_val = self._get_init_val(y)

    n = len(y)
    y_hat = [self.init_val] * n
    residuals = self._get_residuals(y, y_hat)

    self.trees = []
    self.lr = lr
    for _ in range(n_estimators):
        idx = range(n)
        if subsample is not None:
            k = int(subsample * n)
            idx = choices(population=idx, k=k)
        X_sub = [X[i] for i in idx]
        residuals_sub = [residuals[i] for i in idx]
        y_hat_sub = [y_hat[i] for i in idx]

        tree = RegressionTree()
        tree.fit(X_sub, residuals_sub, max_depth, min_samples_split)

        self._update_score(tree, X_sub, y_hat_sub, residuals_sub)

        y_hat = [y_hat_i + lr * res_hat_i for y_hat_i,
                    res_hat_i in zip(y_hat, tree.predict(X))]

        residuals = self._get_residuals(y, y_hat)
        self.trees.append(tree)

2.6 预测一个样本

def _predict(self, Xi):
    return self.fn(self.init_val + sum(self.lr * tree._predict(Xi) for tree in self.trees))

2.7 预测多个样本

def predict(self, X):
    return [self._predict(Xi) for Xi in X]

三、效果评估

3.1 main函数

使用著名的波士顿房价数据集,按照7:3的比例拆分为训练集和测试集,训练模型,并统计准确度。

@run_time
def main():
    print("Tesing the accuracy of GBDT regressor...")

    X, y = load_boston_house_prices()

    X_train, X_test, y_train, y_test = train_test_split(
        X, y, random_state=10)

    reg = GradientBoostingRegressor()
    reg.fit(X=X_train, y=y_train, n_estimators=4,
            lr=0.5, max_depth=2, min_samples_split=2)

    get_r2(reg, X_test, y_test)

3.2 效果展示 最终拟合优度0.851,运行时间5.0秒,效果还算不错~

3.3 工具函数 本人自定义了一些工具函数,可以在github上查看

https://github.com/tushushu/imylu/blob/master/imylu/utils.py
  1. run_time - 测试函数运行时间
  2. load_boston_house_prices - 加载波士顿房价数据
  3. train_test_split - 拆分训练集、测试集
  4. get_r2 - 计算拟合优度

四、总结

GBDT回归的原理:平均值加回归树

GBDT回归的实现:加加减减for循环

点击参与Python编程学习

本文作者

李小文:先后从事过数据分析、数据挖掘工作,主要开发语言是Python,现任一家小型互联网公司的算法工程师。Github: https://github.com/tushushu

投稿邮箱:pythonpost@163.com

欢迎点击申请Python中文社区新专栏作者计划

Python中文社区作为一个去中心化的全球技术社区,以成为全球20万Python中文开发者的精神部落为愿景,目前覆盖各大主流媒体和协作平台,与阿里、腾讯、百度、微软、亚马逊、开源中国、CSDN等业界知名公司和技术社区建立了广泛的联系,拥有来自十多个国家和地区数万名登记会员,会员来自以公安部、工信部、清华大学、北京大学、北京邮电大学、中国人民银行、中科院、中金、华为、BAT、谷歌、微软等为代表的政府机关、科研单位、金融机构以及海内外知名公司,全平台近20万开发者关注。

原文发布于微信公众号 - Python中文社区(python-china)

原文发表时间:2018-09-11

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏CreateAMind

变分自编码器(Variational Autoencoder, VAE)通俗教程,细节、基础、符号解释很齐全

现在有一个数据集DX(dataset, 也可以叫datapoints),每个数据也称为数据点。

1003
来自专栏云时之间

NLP系列学习:数据平滑

各位小伙伴们大家好,数据平滑这个地方我想使用宗成庆老师的书中的一个例子进行开始,从而引出这一篇文章的主题,我们为什么要需要数据平滑以及常用的数据平滑的模型,话不...

3067
来自专栏AI2ML人工智能to机器学习

变の贝叶斯

前面我们提到 拉普拉斯Laplace (参考 “拉近似”),也提到了 吉布斯Gibbs (参考 “给能量以自由吧!”), 当Laplace携手Gibbs,再携手...

711
来自专栏Vamei实验室

统计02:怎样描绘数据

作者:Vamei 出处:http://www.cnblogs.com/vamei 严禁转载。

1072
来自专栏专知

【NIPS2017前沿】半监督学习需要Bad GAN,清华特奖学霸与苹果AI总监提出(附Ruslan教授深度学习教程pdf下载)

【导读】CMU博士杨植麟与导师同时也是苹果首任AI总监Ruslan Salakhutdinov 在NIPS2017上合作提出新的GAN生成模型,大幅度提高对抗生...

6596
来自专栏iOSDevLog

人工智能-数学基础总结

6254
来自专栏大数据挖掘DT机器学习

机器学习有很多关于核函数的说法,核函数的定义和作用是什么?

作者:蓦风 链接:https://www.zhihu.com/question/30371867/answer/73508853 机器学习,具体以RBF网络里...

4525
来自专栏机器之心

专栏 | 阿里IJCAI 2017 Workshop论文:使用深度强化学习方法求解一类新型三维装箱问题

机器之心专栏 阿里菜鸟物流人工智能部 据机器之心了解,阿里巴巴有 11 篇论文入选如今正在墨尔本进行的 IJCAI 2017 大会,其中 6 篇来自阿里巴巴-浙...

9416
来自专栏专知

【论文笔记】基于实体、属性和关系的知识表示学习

2253
来自专栏深度学习入门与实践

机器学习基础与实践(一)----数据清洗

本博客所有内容以学习、研究和分享为主,如需转载,请联系本人,标明作者和出处,并且是非商业用途,谢谢!   想写这个系列很久了,最近刚好项目结束了闲下来有点时间,...

5306

扫码关注云+社区

领取腾讯云代金券