首先說,GBDT (Gradient Boosting Decision Tree) 是一種集成學習算法,它通過組合多個決策樹模型來提升預測效果。這個名字有點復雜,但其實它背后的思想并不難理解。

我們從頭講起,分幾個步驟來說明 GBDT 的工作原理,并結合一個簡單的例子來幫助大家理解。

簡介GBDT

什么是決策樹?

決策樹是一個常見的機器學習模型,它的結構就像一棵樹,通過逐步問一些問題,來將數據分類或做出預測。舉個例子,如果你要預測今天要不要帶傘,決策樹可能問:

這些問題的答案會引導你做出最終的決定(帶傘或不帶傘)。決策樹很直觀,但它的表現可能不夠完美,有時候一棵樹會有偏差,不能準確預測。

什么是集成學習?

集成學習的想法是,把多個模型的預測結果結合起來,往往能比單個模型表現得更好。你可以把它想象成:一群人一起做決定,可能比一個人單獨做決定更靠譜。GBDT 就是使用多棵決策樹來改進模型效果。

GBDT 的核心思想

GBDT 全稱為梯度提升決策樹。它是這樣工作的:

1. 訓練第一棵樹:一開始,GBDT 訓練一棵簡單的決策樹,盡量去擬合數據。比如我們預測房價,第一棵樹的預測結果可能不是很準確,但它會盡力給出一個初步的房價預測。

2. 計算誤差:接著,GBDT 會計算這棵樹的預測和真實結果的差距(誤差)。這個誤差告訴我們模型哪里做得不好。

3. 訓練第二棵樹:第二棵樹的任務是專門去修正第一棵樹的錯誤。換句話說,第二棵樹學習的是第一棵樹的殘差,即它未能正確預測的部分。這樣,第二棵樹就能幫助第一棵樹做出更好的預測。

4. 重復過程:這個過程會重復很多次,每次新加入的樹都會去修正前一棵樹的錯誤。最終,所有樹的預測結果會被加起來,組合成一個更準確的預測。

簡單案例

假設你想預測某個同學期末考試的成績,模型會通過一些因素,比如平時的作業成績、出勤率和模擬考試的分數,來預測最終成績。

通過這樣的迭代過程,每棵樹都在修正前面的誤差,最終模型的預測會越來越接近真實值。

GBDT 就像是多個決策樹的團隊協作,每棵樹專注于改正前面樹犯的錯誤,逐步逼近正確的預測結果。通過這個「修正誤差」的過程,GBDT 的預測效果通常非常好,尤其在處理回歸問題和分類問題時。

有了這個淺顯易懂的解釋后,咱們下面詳細聊聊其原理性的內容~

GBDT原理及案例

要詳細推導 GBDT 的公式,并實現一個從零開始的 Python 案例,首先我們需要一步步從 GBDT 的核心思想出發,包括梯度的概念、損失函數的優化過程,最后手動實現算法。然后通過一個 Kaggle 數據集進行實踐,分析數據并繪制可視化圖形。這里我們分成幾個步驟:

1. GBDT 原理

GBDT 是基于梯度提升算法決策樹回歸。它的核心思想是通過構建多個弱學習器(通常是決策樹),并且每次都去擬合當前模型的殘差,從而逐步逼近真實值。

梯度提升的基本步驟

1. 初始化模型

首先,我們可以用一個常數來初始化模型:

2. 迭代訓練

3. 更新模型

4. 最終模型

經過 次迭代后,最終的模型是所有樹的線性組合:

2. GBDT 案例

導入并預處理數據

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split

# 讀取數據
df = pd.read_csv('house-prices-advanced-regression-techniques.csv')

# 查看數據概況
print(df.head())

數據預處理

# 簡單選擇特征和目標值
features = ['OverallQual', 'GrLivArea', 'GarageCars', 'TotalBsmtSF', 'FullBath', 'YearBuilt']
X = df[features].fillna(0)
y = df['SalePrice']

# 數據標準化
X = (X - X.mean()) / X.std()

# 劃分訓練集和測試集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

GBDT 基本實現

我們手動實現一個 GBDT 模型,從零開始訓練決策樹,計算殘差,并逐步更新模型。

# 自定義一個簡單的決策樹樁
class DecisionTreeStump:
def __init__(self):
self.feature_index = None
self.threshold = None
self.left_value = None
self.right_value = None

def fit(self, X, y):
# 找到最佳的分裂點
best_loss = float('inf')
for feature_index in range(X.shape[1]):
thresholds = np.unique(X[:, feature_index])
for threshold in thresholds:
left_mask = X[:, feature_index] <= threshold
right_mask = X[:, feature_index] > threshold
left_value = np.mean(y[left_mask])
right_value = np.mean(y[right_mask])
loss = np.sum((y[left_mask] - left_value) ** 2) + np.sum((y[right_mask] - right_value) ** 2)
if loss < best_loss:
best_loss = loss
self.feature_index = feature_index
self.threshold = threshold
self.left_value = left_value
self.right_value = right_value

def predict(self, X):
predictions = np.zeros(X.shape[0])
left_mask = X[:, self.feature_index] <= self.threshold
right_mask = X[:, self.feature_index] > self.threshold
predictions[left_mask] = self.left_value
predictions[right_mask] = self.right_value
return predictions

# 梯度提升算法
class GradientBoostingRegressor:
def __init__(self, n_estimators=100, learning_rate=0.1):
self.n_estimators = n_estimators
self.learning_rate = learning_rate
self.trees = []

def fit(self, X, y):
# 初始化模型為樣本均值
F_m = np.mean(y)
self.F_0 = F_m
residuals = y - F_m
for _ in range(self.n_estimators):
tree = DecisionTreeStump()
tree.fit(X, residuals)
predictions = tree.predict(X)
residuals = residuals - self.learning_rate * predictions
self.trees.append(tree)

def predict(self, X):
# 開始預測
F_m = np.ones(X.shape[0]) * self.F_0
for tree in self.trees:
F_m += self.learning_rate * tree.predict(X)
return F_m

模型訓練與評估

我們可以訓練模型并進行預測:

# 訓練模型
model = GradientBoostingRegressor(n_estimators=50, learning_rate=0.1)
model.fit(X_train.values, y_train.values)

# 預測
y_pred = model.predict(X_test.values)

# 評估結果
from sklearn.metrics import mean_squared_error
mse = mean_squared_error(y_test, y_pred)
print(f'MSE: {mse}')

可視化分析

將結果與實際值進行可視化對比,并展示殘差的變化趨勢。

# 繪制預測值與實際值的對比圖
plt.figure(figsize=(10,6))
plt.scatter(y_test, y_pred, color='red')
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'k--', lw=2, color='blue')
plt.xlabel('Actual Prices')
plt.ylabel('Predicted Prices')
plt.title('Actual vs Predicted House Prices')
plt.show()
# 繪制殘差分布
residuals = y_test - y_pred
plt.figure(figsize=(10,6))
sns.histplot(residuals, kde=True, color="green")
plt.title('Residuals Distribution')
plt.xlabel('Residuals')
plt.ylabel('Frequency')
plt.show()

完整代碼

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error

# 讀取數據
df = pd.read_csv('house-prices-advanced-regression-techniques.csv')

# 簡單選擇特征和目標值
features = ['OverallQual', 'GrLivArea', 'GarageCars', 'TotalBsmtSF', 'FullBath', 'YearBuilt']
X = df[features].fillna(0)
y = df['SalePrice']

# 數據標準化
X = (X - X.mean()) / X.std()

# 劃分訓練集和測試集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=

42)

# 自定義一個簡單的決策樹樁
class DecisionTreeStump:
def __init__(self):
self.feature_index = None
self.threshold = None
self.left_value = None
self.right_value = None

def fit(self, X, y):
# 找到最佳的分裂點
best_loss = float('inf')
for feature_index in range(X.shape[1]):
thresholds = np.unique(X[:, feature_index])
for threshold in thresholds:
left_mask = X[:, feature_index] <= threshold
right_mask = X[:, feature_index] > threshold
left_value = np.mean(y[left_mask])
right_value = np.mean(y[right_mask])
loss = np.sum((y[left_mask] - left_value) ** 2) + np.sum((y[right_mask] - right_value) ** 2)
if loss < best_loss:
best_loss = loss
self.feature_index = feature_index
self.threshold = threshold
self.left_value = left_value
self.right_value = right_value

def predict(self, X):
predictions = np.zeros(X.shape[0])
left_mask = X[:, self.feature_index] <= self.threshold
right_mask = X[:, self.feature_index] > self.threshold
predictions[left_mask] = self.left_value
predictions[right_mask] = self.right_value
return predictions

# 梯度提升算法
class GradientBoostingRegressor:
def __init__(self, n_estimators=100, learning_rate=0.1):
self.n_estimators = n_estimators
self.learning_rate = learning_rate
self.trees = []

def fit(self, X, y):
# 初始化模型為樣本均值
F_m = np.mean(y)
self.F_0 = F_m
residuals = y - F_m
for _ in range(self.n_estimators):
tree = DecisionTreeStump()
tree.fit(X, residuals)
predictions = tree.predict(X)
residuals = residuals - self.learning_rate * predictions
self.trees.append(tree)

def predict(self, X):
# 開始預測
F_m = np.ones(X.shape[0]) * self.F_0
for tree in self.trees:
F_m += self.learning_rate * tree.predict(X)
return F_m

# 訓練模型
model = GradientBoostingRegressor(n_estimators=50, learning_rate=0.1)
model.fit(X_train.values, y_train.values)

# 預測
y_pred = model.predict(X_test.values)

# 評估結果
mse = mean_squared_error(y_test, y_pred)
print(f'MSE: {mse}')

# 繪制預測值與實際值的對比圖
plt.figure(figsize=(10,6))
plt.scatter(y_test, y_pred, color='red')
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'k--', lw=2, color='blue')
plt.xlabel('Actual Prices')
plt.ylabel('Predicted Prices')
plt.title('Actual vs Predicted House Prices')
plt.show()

# 繪制殘差分布
residuals = y_test - y_pred
plt.figure(figsize=(10,6))
sns.histplot(residuals, kde=True, color="green")
plt.title('Residuals Distribution')
plt.xlabel('Residuals')
plt.ylabel('Frequency')
plt.show()

通過手動實現完整的 GBDT 算法,展示了從零開始如何構建一個簡單的 GBDT 回歸模型,同時通過可視化展示了預測效果和殘差的分布。

文章轉自微信公眾號@深夜努力寫Python

上一篇:

GBDT、XGBoost、LightGBM,樹模型全面對比 ??!

下一篇:

突破最強算法模型,XGBoost!!
#你可能也喜歡這些API文章!

我們有何不同?

API服務商零注冊

多API并行試用

數據驅動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內容創意新穎性、情感共鳴力、商業轉化潛力

25個渠道
一鍵對比試用API 限時免費

#AI深度推理大模型API

對比大模型API的邏輯推理準確性、分析深度、可視化建議合理性

10個渠道
一鍵對比試用API 限時免費