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已经有了直观的认识。这么做有什么科学依据么,为什么残差可以越来越小呢?前方小段数学公式低能预警。

0cdf3e510a9cd1fd9c422a3d6ab759c73615214c

因此,我们需要通过用第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
● run_time - 测试函数运行时间
● load_boston_house_prices - 加载波士顿房价数据
● train_test_split - 拆分训练集、测试集

● get_r2 - 计算拟合优度

四、总结

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

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


原文发布时间为:2018-09-7

本文作者:xxx

本文来自云栖社区合作伙伴“Python中文社区”,了解相关信息可以关注“Python中文社区”。

相关文章
|
22天前
|
机器学习/深度学习 Python
堆叠集成策略的原理、实现方法及Python应用。堆叠通过多层模型组合,先用不同基础模型生成预测,再用元学习器整合这些预测,提升模型性能
本文深入探讨了堆叠集成策略的原理、实现方法及Python应用。堆叠通过多层模型组合,先用不同基础模型生成预测,再用元学习器整合这些预测,提升模型性能。文章详细介绍了堆叠的实现步骤,包括数据准备、基础模型训练、新训练集构建及元学习器训练,并讨论了其优缺点。
41 3
|
22天前
|
机器学习/深度学习 算法 数据挖掘
线性回归模型的原理、实现及应用,特别是在 Python 中的实践
本文深入探讨了线性回归模型的原理、实现及应用,特别是在 Python 中的实践。线性回归假设因变量与自变量间存在线性关系,通过建立线性方程预测未知数据。文章介绍了模型的基本原理、实现步骤、Python 常用库(如 Scikit-learn 和 Statsmodels)、参数解释、优缺点及扩展应用,强调了其在数据分析中的重要性和局限性。
46 3
|
3天前
|
缓存 数据安全/隐私保护 Python
python装饰器底层原理
Python装饰器是一个强大的工具,可以在不修改原始函数代码的情况下,动态地增加功能。理解装饰器的底层原理,包括函数是对象、闭包和高阶函数,可以帮助我们更好地使用和编写装饰器。无论是用于日志记录、权限验证还是缓存,装饰器都可以显著提高代码的可维护性和复用性。
17 5
|
16天前
|
缓存 开发者 Python
深入探索Python中的装饰器:原理、应用与最佳实践####
本文作为技术性深度解析文章,旨在揭开Python装饰器背后的神秘面纱,通过剖析其工作原理、多样化的应用场景及实践中的最佳策略,为中高级Python开发者提供一份详尽的指南。不同于常规摘要的概括性介绍,本文摘要将直接以一段精炼的代码示例开篇,随后简要阐述文章的核心价值与读者预期收获,引领读者快速进入装饰器的世界。 ```python # 示例:一个简单的日志记录装饰器 def log_decorator(func): def wrapper(*args, **kwargs): print(f"Calling {func.__name__} with args: {a
29 2
|
24天前
|
机器学习/深度学习 人工智能 算法
强化学习在游戏AI中的应用,从基本原理、优势、应用场景到具体实现方法,以及Python在其中的作用
本文探讨了强化学习在游戏AI中的应用,从基本原理、优势、应用场景到具体实现方法,以及Python在其中的作用,通过案例分析展示了其潜力,并讨论了面临的挑战及未来发展趋势。强化学习正为游戏AI带来新的可能性。
61 4
|
1月前
|
搜索推荐 Python
快速排序的 Python 实践:从原理到优化,打造你的排序利器!
本文介绍了 Python 中的快速排序算法,从基本原理、实现代码到优化方法进行了详细探讨。快速排序采用分治策略,通过选择基准元素将数组分为两部分,递归排序。文章还对比了快速排序与冒泡排序的性能,展示了优化前后快速排序的差异。通过这些分析,帮助读者理解快速排序的优势及优化的重要性,从而在实际应用中选择合适的排序算法和优化策略,提升程序性能。
39 1
|
3月前
|
调度 Python
揭秘Python并发编程核心:深入理解协程与异步函数的工作原理
在Python异步编程领域,协程与异步函数成为处理并发任务的关键工具。协程(微线程)比操作系统线程更轻量级,通过`async def`定义并在遇到`await`表达式时暂停执行。异步函数利用`await`实现任务间的切换。事件循环作为异步编程的核心,负责调度任务;`asyncio`库提供了事件循环的管理。Future对象则优雅地处理异步结果。掌握这些概念,可使代码更高效、简洁且易于维护。
31 1
|
2月前
|
数据采集 调度 Python
Python编程异步爬虫——协程的基本原理(一)
Python编程异步爬虫——协程的基本原理(一)
22 0
|
2月前
|
数据采集 Python
Python编程异步爬虫——协程的基本原理(二)
Python编程异步爬虫——协程的基本原理(二)
25 0
|
2月前
|
Java C语言 Python
解析Python中的全局解释器锁(GIL):影响、工作原理及解决方案
解析Python中的全局解释器锁(GIL):影响、工作原理及解决方案
51 0