'GBDT迴歸的原理及Python實現'

Python GitHub 算法 文章 編程語言 物聯網新資訊 2019-08-19
"

簡介: 提到GBDT迴歸相信大家應該都不會覺得陌生,本文就GBDT迴歸的基本原理進行講解,並手把手、肩並肩地帶您實現這一算法。完整實現代碼請參考本人的github。一、原理篇我們用人話而不是大段的數學公式來講講GBDT迴歸是怎麼一回事。

提到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已經有了直觀的認識。這麼做有什麼科學依據麼,為什麼殘差可以越來越小呢?前方小段數學公式低能預警。


"

簡介: 提到GBDT迴歸相信大家應該都不會覺得陌生,本文就GBDT迴歸的基本原理進行講解,並手把手、肩並肩地帶您實現這一算法。完整實現代碼請參考本人的github。一、原理篇我們用人話而不是大段的數學公式來講講GBDT迴歸是怎麼一回事。

提到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已經有了直觀的認識。這麼做有什麼科學依據麼,為什麼殘差可以越來越小呢?前方小段數學公式低能預警。


GBDT迴歸的原理及Python實現


因此,我們需要通過用第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循環

"

相關推薦

推薦中...