XGBoost 和 LightGBM 都是目前十分风行的基于决策树的机器学习模型,它们都有着高效的性能体现,然而在某些状况下,它们也有着不同的特点。
XGBoost 和 LightGBM 简略比照
训练速度
LightGBM 相较于 xgboost 在训练速度方面有显著的劣势。这是因为 LightGBM 应用了一些高效的算法和数据结构,比方直方图算法和基于梯度单边采样算法(GOSS),这些算法使得 LightGBM 在训练大规模数据集时速度更快。
内存耗费
因为 LightGBM 应用了一些高效的算法和数据结构,因而其内存耗费绝对较小。而 xgboost 在解决大规模数据集时可能会须要较大的内存。
鲁棒性
xgboost 在解决一些不规则数据时更加鲁棒,比方一些缺失值和异样值。而 LightGBM 在这方面绝对较弱。
精度
在雷同的数据集和参数设置下,两个模型的精度大抵相当。不过在某些状况下,xgboost 可能体现得更好,比方在特色数较少的状况下,或者是须要更加平滑的决策树时。
参数设置
xgboost 的参数比拟多,须要依据理论状况进行调整。而 LightGBM 的参数绝对较少,大多数状况下应用默认参数即可。
XGBoost 和 LightGBM 算法比照
XGBoost 和 LightGBM 都是基于决策树的梯度晋升框架,它们的核心思想都是通过组合多个弱学习器来晋升模型的预测能力。它们在实现上有很多相似之处,但在算法方面有一些显著的不同:
决裂点抉择办法
在构建决策树时,xgboost 采纳的是一种贪婪算法,称为 Exact Greedy Algorithm,它会枚举每一个特色的每一个取值作为决裂点,而后计算对应的增益值,选取最大增益的决裂点作为最终的决裂点。
而 LightGBM 应用的是一种基于梯度单边采样(Gradient-based One-Side Sampling,GOSS)和直方图算法的决裂点抉择办法,它会先对数据进行预排序,而后将数据划分成若干个直方图,每个直方图蕴含多个数据点。在寻找最优决裂点时,LightGBM 只会在直方图中选取一个代表点(即直方图中的最大梯度值)进行计算,这样大大降低了计算量。
特色并行处理
xgboost 将数据按 特色 进行划分,而后将每个特色调配到不同的节点上进行计算。这种办法能够无效进步训练速度,但须要额定的通信和同步开销。
LightGBM 将数据按 行进行划分,而后将每个分块调配到不同的节点上进行计算。这种办法防止了通信和同步开销,但须要额定的内存空间。
解决缺失值
xgboost 会主动将缺失值调配到左右子树中概率更高的那一边。这种办法可能会引入一些偏差,但对于解决缺失值较多的数据集比拟无效。
LightGBM 则采纳的办法称为 Zero As Missing(ZAM),它将所有的缺失值都视为一个非凡的取值,并将其纳入其中一个子节点中。这种办法能够防止偏差,但须要更多的内存空间。
训练速度
LightGBM 在训练速度方面具备显著劣势,这是因为它应用了 GOSS 和直方图算法,缩小了计算量和内存耗费。而 xgboost 的计算速度绝对较慢,然而在解决较小的数据集时体现良好。
电力能源消耗预测
在当今世界,能源是次要的探讨点之一,可能精确预测能源生产需要是任何电力公司的要害,所以咱们这里以能源预测为例,对这两个目前最好的表格类数据的模型做一个比照。
咱们应用的是伦敦能源数据集,其中蕴含 2011 年 11 月至 2014 年 2 月期间英国伦敦市 5567 个随机抉择的家庭的能源消耗。咱们将这个集与伦敦天气数据集联合起来,作为辅助数据来进步模型的性能。
1、预处理
在每个我的项目中,咱们要做的第一件事就是很好地了解数据,并在须要时对其进行预处理:
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv("london_energy.csv")
print(df.isna().sum())
df.head()
“LCLid”是标识每个家庭的惟一字符串,“Date”就是咱们的工夫索引,“KWH”是在该日期破费的总千瓦时数,没有任何缺失值。因为咱们想要以个别形式而不是以家庭为单位来预测耗电量,所以咱们须要将后果按日期分组并均匀千瓦时。
df_avg_consumption = df.groupby("Date")["KWH"].mean()
df_avg_consumption = pd.DataFrame({"date": df_avg_consumption.index.tolist(), "consumption": df_avg_consumption.values.tolist()})
df_avg_consumption["date"] = pd.to_datetime(df_avg_consumption["date"])
print(f"From: {df_avg_consumption['date'].min()}")
print(f"To: {df_avg_consumption['date'].max()}")
咱们来做一个折线图:
df_avg_consumption.plot(x="date", y="consumption")
季节性特色非常明显。夏季能源需求很高,而冬季的耗费是最低的。这种行为在数据集中每年都会反复,具备不同的高值和低值。可视化一年内的稳定:
df_avg_consumption.query("date >'2012-01-01'& date <'2013-01-01'").plot(x="date", y="consumption")
训练像 XGBoost 和 LightGB 这样的模型,咱们须要本人创立特色。因为目前咱们只有一个特色: 日期。所欲须要依据残缺的日期提取不同的特色,例如星期几、一年中的哪一天、月份和其余日期:
df_avg_consumption["day_of_week"] = df_avg_consumption["date"].dt.dayofweek
df_avg_consumption["day_of_year"] = df_avg_consumption["date"].dt.dayofyear
df_avg_consumption["month"] = df_avg_consumption["date"].dt.month
df_avg_consumption["quarter"] = df_avg_consumption["date"].dt.quarter
df_avg_consumption["year"] = df_avg_consumption["date"].dt.year
df_avg_consumption.head()
‘ date ‘ 特色就变得多余了。然而在删除它之前,咱们将应用它将数据集宰割为训练集和测试集。与传统的训练相同,在工夫序列中,咱们不能只是以随机的形式宰割汇合,因为数据的程序十分重要,所以对于测试集,将只应用最近 6 个月的数据。如果训练集更大,能够用去年全年的数据作为测试集。
training_mask = df_avg_consumption["date"] < "2013-07-28"
training_data = df_avg_consumption.loc[training_mask]
print(training_data.shape)
testing_mask = df_avg_consumption["date"] >= "2013-07-28"
testing_data = df_avg_consumption.loc[testing_mask]
print(testing_data.shape)
可视化训练集和测试集之间的宰割:
figure, ax = plt.subplots(figsize=(20, 5))
training_data.plot(ax=ax, label="Training", x="date", y="consumption")
testing_data.plot(ax=ax, label="Testing", x="date", y="consumption")
plt.show()
当初咱们能够删除 ’ date ‘ 并创立训练和测试集:
# Dropping unnecessary `date` column
training_data = training_data.drop(columns=["date"])
testing_dates = testing_data["date"]
testing_data = testing_data.drop(columns=["date"])
X_train = training_data[["day_of_week", "day_of_year", "month", "quarter", "year"]]
y_train = training_data["consumption"]
X_test = testing_data[["day_of_week", "day_of_year", "month", "quarter", "year"]]
y_test = testing_data["consumption"]
2、训练模型
咱们这里的超参数优化将通过网格搜寻实现。因为是工夫序列,所以不能只应用一般的 k -fold 穿插验证。Scikit learn 提供了 TimeSeriesSplit 办法,这里能够间接应用。
from xgboost import XGBRegressor
import lightgbm as lgb
from sklearn.model_selection import TimeSeriesSplit, GridSearchCV
# XGBoost
cv_split = TimeSeriesSplit(n_splits=4, test_size=100)
model = XGBRegressor()
parameters = {"max_depth": [3, 4, 6, 5, 10],
"learning_rate": [0.01, 0.05, 0.1, 0.2, 0.3],
"n_estimators": [100, 300, 500, 700, 900, 1000],
"colsample_bytree": [0.3, 0.5, 0.7]
}
grid_search = GridSearchCV(estimator=model, cv=cv_split, param_grid=parameters)
grid_search.fit(X_train, y_train)
对于 LightGB,代码是这样的:
# LGBM
cv_split = TimeSeriesSplit(n_splits=4, test_size=100)
model = lgb.LGBMRegressor()
parameters = {"max_depth": [3, 4, 6, 5, 10],
"num_leaves": [10, 20, 30, 40, 100, 120],
"learning_rate": [0.01, 0.05, 0.1, 0.2, 0.3],
"n_estimators": [50, 100, 300, 500, 700, 900, 1000],
"colsample_bytree": [0.3, 0.5, 0.7, 1]
}
grid_search = GridSearchCV(estimator=model, cv=cv_split, param_grid=parameters)
grid_search.fit(X_train, y_train)
3、评估
为了评估测试集上的最佳估计量,咱们将计算:均匀绝对误差 (MAE)、均方误差(MSE) 和均匀相对百分比误差(MAPE)。因为每个指标都提供了训练模型理论性能的不同视角。咱们还将绘制一个折线图,以更好地可视化模型的性能。
from sklearn.metrics import mean_absolute_error, mean_absolute_percentage_error,\
mean_squared_error
def evaluate_model(y_test, prediction):
print(f"MAE: {mean_absolute_error(y_test, prediction)}")
print(f"MSE: {mean_squared_error(y_test, prediction)}")
print(f"MAPE: {mean_absolute_percentage_error(y_test, prediction)}")
def plot_predictions(testing_dates, y_test, prediction):
df_test = pd.DataFrame({"date": testing_dates, "actual": y_test, "prediction": prediction})
figure, ax = plt.subplots(figsize=(10, 5))
df_test.plot(ax=ax, label="Actual", x="date", y="actual")
df_test.plot(ax=ax, label="Prediction", x="date", y="prediction")
plt.legend(["Actual", "Prediction"])
plt.show()
而后咱们运行上面代码进行验证:
# Evaluating GridSearch results
prediction = grid_search.predict(X_test)
plot_predictions(testing_dates, y_test, prediction)
evaluate_model(y_test, prediction)
XGB:
LightGBM:
从图上能够看到,XGBoost 能够更精确地预测夏季的能源消耗,但为了量化和比拟性能,咱们计算误差指标。通过查看上面的表,能够很显著地看出 XGBoost 在所有状况下都优于 LightGBM。
应用内部辅助天气数据
该模型体现还不错,咱们看看还能不能进步呢?为了达到更好的成果,能够采纳许多不同的技巧和技巧。其中之一是应用与能源消耗间接或间接相干的辅助特色。例如,在预测能源需求时,天气数据能够施展决定性作用。这就是为什么咱们抉择应用伦敦天气数据集的天气数据的起因。
首先让咱们来看看数据的构造:
df_weather = pd.read_csv("london_weather.csv")
print(df_weather.isna().sum())
df_weather.head()
这个数据集中有各种缺失的数据须要填充。填充缺失的数据也不是一件简略的事件,因为每种不同的状况填充办法是不同的。咱们这里的天气数据,每天都取决于前几天和下一天,所以能够通过插值来填充这些值。另外还须要将 ’ date ‘ 列转换为 ’ datetime ‘,而后合并两个 DF,以取得一个加强的残缺数据集。
# Parsing dates
df_weather["date"] = pd.to_datetime(df_weather["date"], format="%Y%m%d")
# Filling missing values through interpolation
df_weather = df_weather.interpolate(method="ffill")
# Enhancing consumption dataset with weather information
df_avg_consumption = df_avg_consumption.merge(df_weather, how="inner", on="date")
df_avg_consumption.head()
在生成加强集之后,必须从新运行拆分过程取得新的 ’ training_data ‘ 和 ’ testing_data ‘。
# Dropping unnecessary `date` column
training_data = training_data.drop(columns=["date"])
testing_dates = testing_data["date"]
testing_data = testing_data.drop(columns=["date"])
X_train = training_data[["day_of_week", "day_of_year", "month", "quarter", "year",\
"cloud_cover", "sunshine", "global_radiation", "max_temp",\
"mean_temp", "min_temp", "precipitation", "pressure",\
"snow_depth"]]
y_train = training_data["consumption"]
X_test = testing_data[["day_of_week", "day_of_year", "month", "quarter", "year",\
"cloud_cover", "sunshine", "global_radiation", "max_temp",\
"mean_temp", "min_temp", "precipitation", "pressure",\
"snow_depth"]]
y_test = testing_data["consumption"]
训练步骤不须要做更改。在新数据集上训练模型后,咱们失去以下后果:
XGBoost
LightGBM
整合下面的表格:
咱们看到:天气数据大大提高了两个模型的性能。特地是在 XGBoost 场景中,MAE 缩小了近 44%,而 MAPE 从 19% 缩小到 16%。对于 LightGBM, MAE 降落了 42%,MAPE 从 19.8% 降落到 16.7%。
总结
xgboost 和 LightGBM 都是优良的梯度晋升框架,它们各自具备一些独特的长处和毛病,抉择哪一种算法应该依据理论利用场景和数据集的特色来决定。如果数据集中缺失值较多,能够抉择 xgboost。如果须要解决大规模数据集并谋求更快的训练速度,能够抉择 LightGBM。如果须要解释模型的特色重要性,xgboost 提供了更好的特色重要性评估办法,并且如果须要更加鲁棒的模型,能够优先选择 xgboost。
在本文中咱们还介绍了一种进步模型的办法,就是应用附加数据,通过附加咱们认为相干的辅助数据,也能够大大提高模型的性能。
除此以外,咱们还能够联合滞后特色或尝试不同的超参数优化技术(如随机搜寻或贝叶斯优化),来作为进步性能的尝试,如果你有任何新的后果,欢送留言。
以下是 2 个数据集的链接:
https://avoid.overfit.cn/post/bfcd5ca1cd7741acac137fade88bd747
作者:George Kamtziridis