哈喽,我是cos大壮!~
今儿和大家聊聊关于GBDT算法模型的一个案例:房价预测。
本身房价预测是一个非常经典的回归问题,在金融、房地产市场中有广泛应用。机器学习中的梯度提升决策树(Gradient Boosting Decision Trees,GBDT)被认为是解决该类问题的强大工具之一,尤其擅长处理非线性关系和特征之间的复杂交互。
今天就使用GBDT模型通过构建虚拟数据集进行房价预测,完整介绍该模型的原理、算法、代码实现,并对结果进行可视化分析。
文末可取本文PDF版本~
GBDT原理
GBDT是一种集成学习方法,通过多个弱学习器(通常是决策树)集成来提高预测的准确性。GBDT的核心思想是通过逐步修正模型的预测误差来优化损失函数。具体来说,GBDT的每棵树都根据前一轮的残差进行训练,从而一步步提升模型的性能。
GBDT的基本思想可以通过以下步骤进行概述:
1. 初始化模型
假设我们有一个目标函数 ,初始化时我们用一个常数值模型来预测目标值。
其中, 是损失函数,通常对于回归问题使用的是均方误差(MSE):
初始化模型的常数项通常是目标值的均值。
2. 迭代训练
在每一步迭代中,模型尝试拟合前一步的残差(即误差)。假设当前的模型为 ,模型会生成一个新的决策树来拟合残差。
然后通过学习器(例如决策树)拟合这个残差,更新模型:
其中, 是学习率,控制每次更新的步幅大小, 是第 m 棵决策树。
3. 最终模型
经过 M 次迭代后,模型会收敛到一个能够较好地拟合数据的函数。
完整案例
为演示GBDT的房价预测模型,这里我们构建一个虚拟数据集。
数据集包含以下特征:
面积( Area
):房子的面积,单位是平方英尺卧室数( Bedrooms
):房间数量距市中心距离( Distance_to_city_center
):房子距离市中心的距离,单位是公里房龄( House_age
):房子的年龄,单位是年
房价(Price
)作为预测目标,满足如下线性关系,并带有一些噪声:
其中, 是服从正态分布的噪声项。
import torch
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
# 设置随机种子
np.random.seed(42)
# 生成虚拟数据集
n_samples = 1000
area = np.random.uniform(500, 5000, n_samples) # 面积 500 到 5000 平方英尺
bedrooms = np.random.randint(1, 6, n_samples) # 卧室数量 1 到 5
distance_to_city_center = np.random.uniform(1, 20, n_samples) # 距市中心距离 1 到 20 公里
house_age = np.random.uniform(0, 50, n_samples) # 房龄 0 到 50 年
# 定义房价计算公式,添加噪声项
noise = np.random.normal(0, 50000, n_samples)
price = 3000 * area + 50000 * bedrooms - 1000 * distance_to_city_center - 2000 * house_age + noise
# 构建 DataFrame
data = pd.DataFrame({
'Area': area,
'Bedrooms': bedrooms,
'Distance_to_city_center': distance_to_city_center,
'House_age': house_age,
'Price': price
})
# 查看数据集
print(data.head())
数据生成后,我们将其分为训练集和测试集,以便后续训练模型和评估模型的泛化能力。
# 分割数据集为训练集和测试集
X = data[['Area', 'Bedrooms', 'Distance_to_city_center', 'House_age']]
y = data['Price']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
模型训练
使用pytorch
实现一个GBDT模型。为简化起见,我们手动实现一个GBDT版本,重点展示模型的工作机制。
虽然pytorch
本身不是GBDT的常规实现工具,但我们将使用它来说明决策树的训练过程。
class GBDT(torch.nn.Module):
def __init__(self, n_estimators, learning_rate, y_train_mean):
super(GBDT, self).__init__()
self.n_estimators = n_estimators
self.learning_rate = learning_rate
self.trees = []
self.y_train_mean = y_train_mean # 保存y_train的均值
def forward(self, x):
# 初始化预测为常数值(使用训练集的目标均值)
pred = torch.ones(x.shape[0], dtype=torch.float32) * self.y_train_mean
for tree in self.trees:
pred += self.learning_rate * tree(x).squeeze() # 使用 .squeeze() 来匹配形状
return pred
def fit(self, X_train, y_train):
residuals = y_train
for i in range(self.n_estimators):
# 拟合残差
tree = torch.nn.Linear(X_train.shape[1], 1)
criterion = torch.nn.MSELoss()
optimizer = torch.optim.Adam(tree.parameters(), lr=0.01)
for epoch in range(100): # 简单训练 100 个 epoch
optimizer.zero_grad()
output = tree(X_train) # 这里可以不使用 .squeeze(),因为我们要使用形状为 [batch_size, 1]
loss = criterion(output, residuals.unsqueeze(1)) # 调整 residuals 的形状
loss.backward(retain_graph=True) # 保留计算图
optimizer.step()
# 更新残差
self.trees.append(tree)
residuals = residuals - self.learning_rate * tree(X_train).squeeze()
def predict(self, X):
return self.forward(X)
# 计算 y_train 的均值
y_train_mean = torch.mean(torch.tensor(y_train.values, dtype=torch.float32))
# 实例化模型,设置学习率和弱学习器数量
n_estimators = 500
learning_rate = 0.1
model = GBDT(n_estimators, learning_rate, y_train_mean)
# 将数据转化为 PyTorch 张量
X_train_tensor = torch.tensor(X_train.values, dtype=torch.float32)
y_train_tensor = torch.tensor(y_train.values, dtype=torch.float32)
# 训练模型
model.fit(X_train_tensor, y_train_tensor)
# 预测结果
X_test_tensor = torch.tensor(X_test.values, dtype=torch.float32)
predictions = model.predict(X_test_tensor).detach().numpy()
# 打印一些预测结果
print(predictions[:5])
结果分析和可视化
接下来,我们使用Matplotlib绘制分析图表,以帮助解释GBDT模型的效果。
1. 特征与目标之间的关系散点图:展示各个特征与房价之间的关系。
2. 残差图:展示预测误差的分布情况,以便观察模型的偏差和方差。
3. 特征重要性图:展示每个特征对模型预测结果的重要性。
# 1. 特征与房价的关系
fig, axs = plt.subplots(2, 2, figsize=(10, 10))
axs[0, 0].scatter(data['Area'], data['Price'], alpha=0.5)
axs[0, 0].set_title('Area vs Price')
axs[0, 0].set_xlabel('Area')
axs[0, 0].set_ylabel('Price')
axs[0, 1].scatter(data['Bedrooms'], data['Price'], alpha=0.5)
axs[0, 1].set_title('Bedrooms vs Price')
axs[0, 1].set_xlabel('Bedrooms')
axs[0, 1].set_ylabel('Price')
axs[1, 0].scatter(data['Distance_to_city_center'], data['Price'], alpha=0.5)
axs[1, 0].set_title('Distance to City Center vs Price')
axs[1, 0].set_xlabel('Distance to City Center')
axs[1, 0].set_ylabel('Price')
axs[1, 1].scatter(data['House_age'], data['Price'], alpha=0.5)
axs[1, 1].set_title('House Age vs Price')
axs[1, 1].set_xlabel('House Age')
axs[1, 1].set_ylabel('Price')
plt.tight_layout()
plt.show()
# 2. 残差图
residuals = y_test - predictions
plt.figure(figsize=(6, 4))
plt.scatter(predictions, residuals, alpha=0.5)
plt.axhline(y=0, color='r', linestyle='--')
plt.title('Residual Plot')
plt.xlabel('Predicted Price')
plt.ylabel('Residuals')
plt.show()
# 3. 特征重要性图(使用树的权重或均方误差降低量作为特征重要性的近似)
# 简单用回归系数的绝对值作为近似
feature_importance = np.abs([tree.weight.item() for tree in model.trees])
plt.figure(figsize=(6, 4))
plt.barh(X.columns, feature_importance)
plt.title('Feature Importance')
plt.xlabel('Importance')
plt.show()
下面,咱们分享一下呈现的数据分析图:
1. 特征与目标之间的关系散点图:通过各个特征与房价的散点图,我们可以直观地看到面积、卧室数量与房价呈正相关,距离市中心和房龄与房价呈负相关。
2. 残差图:残差图可以帮助我们查看模型的拟合效果。如果残差围绕零轴随机分布,说明模型的误差是随机的,拟合较好;若残差呈现某种系统性的模式,则模型可能存在偏差。
3. 特征重要性图:特征重要性展示了各个特征对模型预测结果的贡献程度。较大的值表明该特征对模型预测有更大的影响。
模型优化与调参
在GBDT模型中,几个关键的超参数对模型性能有显著影响:
树的数量(n_estimators):增加树的数量通常会提高模型的表现,但过多的树可能会导致过拟合。 学习率(learning_rate):学习率决定了每棵树对最终模型的贡献。较小的学习率可以获得更好的效果,但需要更多的树。 最大深度(max_depth):树的深度控制了每棵树的复杂度,较大的深度可以拟合复杂的关系,但也更容易过拟合。
调参过程可以使用网格搜索(Grid Search)或随机搜索(Random Search)进行自动化搜索。
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import GradientBoostingRegressor
# 定义超参数范围
param_grid = {
'n_estimators': [50, 100, 200],
'learning_rate': [0.01, 0.05, 0.1],
'max_depth': [3, 5, 7],
}
# 实例化GBDT模型
gbdt = GradientBoostingRegressor()
# 网格搜索
grid_search = GridSearchCV(estimator=gbdt, param_grid=param_grid, cv=5, scoring='neg_mean_squared_error')
grid_search.fit(X_train, y_train)
# 最优超参数
print(f"Best parameters: {grid_search.best_params_}")
# 使用最优超参数训练模型
best_model = grid_search.best_estimator_
best_model.fit(X_train, y_train)
# 预测测试集
y_pred_best = best_model.predict(X_test)
# 计算测试集误差
test_mse = np.mean((y_test - y_pred_best) ** 2)
print(f"Test MSE with best parameters: {test_mse}")
最后
本文使用GBDT模型进行了房价预测的完整实现,并展示了模型的训练过程、超参数调优、结果分析与可视化。GBDT通过迭代地拟合残差,能够很好地处理非线性回归问题。实际应用中,通过合理的调参和优化,可以显著提高模型性能。
大家有问题可以直接在评论区留言即可~
喜欢本文的朋友可以收藏、点赞、转发起来!
推荐阅读
原创、超强、精华合集 100个超强机器学习算法模型汇总 机器学习全路线 机器学习各个算法的优缺点 7大方面,30个最强数据集 6大部分,20 个机器学习算法全面汇总 铁汁,都到这了,别忘记点赞呀~