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