
如何高效爬取全球新聞網站 – 整合Scrapy、Selenium與Mediastack API實現自動化新聞采集
首先說,GBDT (Gradient Boosting Decision Tree) 是一種集成學習算法,它通過組合多個決策樹模型來提升預測效果。這個名字有點復雜,但其實它背后的思想并不難理解。
我們從頭講起,分幾個步驟來說明 GBDT 的工作原理,并結合一個簡單的例子來幫助大家理解。
決策樹是一個常見的機器學習模型,它的結構就像一棵樹,通過逐步問一些問題,來將數據分類或做出預測。舉個例子,如果你要預測今天要不要帶傘,決策樹可能問:
這些問題的答案會引導你做出最終的決定(帶傘或不帶傘)。決策樹很直觀,但它的表現可能不夠完美,有時候一棵樹會有偏差,不能準確預測。
集成學習的想法是,把多個模型的預測結果結合起來,往往能比單個模型表現得更好。你可以把它想象成:一群人一起做決定,可能比一個人單獨做決定更靠譜。GBDT 就是使用多棵決策樹來改進模型效果。
GBDT 全稱為梯度提升決策樹。它是這樣工作的:
1. 訓練第一棵樹:一開始,GBDT 訓練一棵簡單的決策樹,盡量去擬合數據。比如我們預測房價,第一棵樹的預測結果可能不是很準確,但它會盡力給出一個初步的房價預測。
2. 計算誤差:接著,GBDT 會計算這棵樹的預測和真實結果的差距(誤差)。這個誤差告訴我們模型哪里做得不好。
3. 訓練第二棵樹:第二棵樹的任務是專門去修正第一棵樹的錯誤。換句話說,第二棵樹學習的是第一棵樹的殘差,即它未能正確預測的部分。這樣,第二棵樹就能幫助第一棵樹做出更好的預測。
4. 重復過程:這個過程會重復很多次,每次新加入的樹都會去修正前一棵樹的錯誤。最終,所有樹的預測結果會被加起來,組合成一個更準確的預測。
假設你想預測某個同學期末考試的成績,模型會通過一些因素,比如平時的作業成績、出勤率和模擬考試的分數,來預測最終成績。
通過這樣的迭代過程,每棵樹都在修正前面的誤差,最終模型的預測會越來越接近真實值。
GBDT 就像是多個決策樹的團隊協作,每棵樹專注于改正前面樹犯的錯誤,逐步逼近正確的預測結果。通過這個「修正誤差」的過程,GBDT 的預測效果通常非常好,尤其在處理回歸問題和分類問題時。
有了這個淺顯易懂的解釋后,咱們下面詳細聊聊其原理性的內容~
要詳細推導 GBDT 的公式,并實現一個從零開始的 Python 案例,首先我們需要一步步從 GBDT 的核心思想出發,包括梯度的概念、損失函數的優化過程,最后手動實現算法。然后通過一個 Kaggle 數據集進行實踐,分析數據并繪制可視化圖形。這里我們分成幾個步驟:
GBDT 是基于梯度提升算法的決策樹回歸。它的核心思想是通過構建多個弱學習器(通常是決策樹),并且每次都去擬合當前模型的殘差,從而逐步逼近真實值。
梯度提升的基本步驟
1. 初始化模型:
首先,我們可以用一個常數來初始化模型:
2. 迭代訓練:
3. 更新模型:
4. 最終模型:
經過 次迭代后,最終的模型是所有樹的線性組合:
導入并預處理數據
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 回歸模型,同時通過可視化展示了預測效果和殘差的分布。