前两篇文章,咱们分析分别用了prophet 和 seasonal_decompose 对信号进行了分解。机器学习中一直流程的一段话:模型和特征工程决定告终果的极限,调参只是逼近这个极限。python
因此本人通常不喜欢简单粗暴的调参,而是喜欢在模型上多试几回,或者在特征上变一下。git
今天咱们来用lightgbm 进行该数据集的预测。github
为何是ligbtgbm呢? 个人理由有如下几点:算法
既然实战,咱们就假设你听过lightgbm。若是是纯新手,能够看看官方文档。 据说有中文版的翻译,不过里面有少量的坑,好比由于更新不及时,致使缺乏一些metrics的翻译等。 咱们以前有用ligtgbm 硬train 不平衡数据集。有兴趣的能够在阅读完本文去翻阅。两个文章属于不一样的用法,一个是分类,一个是回归。bash
首先导入一些库。这几个库都是必须的, lightgbm 能够创造模型,pandas处理数据,pyplot 画图,metrics 用来评判咱们的模型。机器学习
import lightgbm as lgb
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.metrics import mean_absolute_error
复制代码
首先读入数据,而且将时间字符串转换为Timestamp 格式。ide
file = 'data/PJME_hourly.csv'
raw_df = pd.read_csv(file)
dt_format = '%Y-%m-%d %H:%M:%S'
raw_df['Datetime']= pd.to_datetime(raw_df['Datetime'],format=dt_format)
print(raw_df.head())
复制代码
预览数据:函数
Datetime PJME_MW
0 2002-12-31 01:00:00 26498.0
1 2002-12-31 02:00:00 25147.0
2 2002-12-31 03:00:00 24574.0
3 2002-12-31 04:00:00 24393.0
4 2002-12-31 05:00:00 24860.0
复制代码
咱们能够看到特征其实只有一个Datetime,PJME_MW 是咱们要预测的目标(target)。很明显这样的特征个数没法达到咱们的理想要求。 因此咱们要提取特征。 对于时间序列的特征,咱们通常的处理方法就是从中提取其余周期的信息,好比只要小时,只要日期,只要星期。 为何呢?post
咱们回想以前prophet 和seasonal_decompose的算法,这两个算法都是想在原始信号中分解出某个特定周期的信号,可是对于树类型的模型,它是基于分割点的,没法提取到周期信息。学习
因此咱们须要显式的告诉数模型,可能有哪些周期,而后让树对这个周期特征进行分割。
这里重点须要介绍pandas 库中给的series.dt 类,这个类其实基本类是CombinedDatetimelikeProperties, 它综合了三大类属性:DatetimeProperties, TimedeltaProperties, PeriodProperties。 今天咱们主要用到的PeriodProperties(周期属性)。 由于这个数据集只有一个特征,因此这里咱们尽量多的创造更多的特征,好让lightgbm发挥他的特长。数据集最小的采集频率是每小时,因此咱们能够依次建立:
具体能够参考代码,咱们们直接写了一个小函数。输入dataframe,返回带有更多特征的dataframe。
# create time series period features
def time_period_features(df,time_col):
# in hour level
df['hour'] = df[time_col].dt.hour
# in day level
df['dayofweek'] = df[time_col].dt.dayofweek
df['days_in_month'] = df[time_col].dt.days_in_month
df['dayofyear'] = df[time_col].dt.dayofyear
# week levels
df['weekday'] = df[time_col].dt.weekday
df['week'] = df[time_col].dt.week
df['weekofyear'] = df[time_col].dt.weekofyear
# month level
df['month'] = df[time_col].dt.month
df['is_month_start'] = df[time_col].dt.is_month_start
df['is_month_end'] = df[time_col].dt.is_month_end
# quarter level
df['quarter'] = df[time_col].dt.quarter
# year level
df['year'] = df[time_col].dt.year
return df
复制代码
而后咱们调用该函数进行特征工程。
raw_df = time_period_features(raw_df,'Datetime')
raw_df = raw_df.sort_values(by=['Datetime'])
复制代码
这个和以前文章的思路一致,直接上代码。
split_dt = pd.Timestamp('2015-01-01 00:00:00')
train_df = raw_df[raw_df['Datetime']< split_dt]
test_df = raw_df[raw_df['Datetime']>= split_dt]
train_Y = train_df['PJME_MW'].copy()
test_Y = test_df['PJME_MW'].copy()
train_df.drop(['Datetime','PJME_MW'],axis=1,inplace=True)
test_df.drop(['Datetime','PJME_MW'],axis=1,inplace=True)
复制代码
咱们几乎采用模型全部默认的参数,来“硬train一发”。 为何?
由于我会用这个做为该模型的baseline。以后咱们能够有方向的调整参数达到更好的效果。
这里仅仅要注意的是objective 参数,值为regression,由于咱们要作的是一个回归模型。 另外,metric 我会采用l1, 由于l1 其实就是mean_absolute_error(MAE)。 采用这个metric,咱们能够和其余模型的结果进行对比。
代码就是经典的lightgbm代码示例,这里不作更多解释,详细官方文档会写的更清楚。
# initial trial
dtrain = lgb.Dataset(train_df.values, label=train_Y.values)
dtest = lgb.Dataset(test_df.values, label=test_Y.values)
param = {
'max_depth': 6,
'eta': 0.05,
'objective': 'regression',
'verbose': 0,
'metric': ['l1'],
}
evals_result = {}
valid_sets = [dtrain, dtest]
valid_name = ['train', 'eval']
feature_name = list(train_df.columns)
model = lgb.train(param, dtrain, num_boost_round=500, feature_name=feature_name,
valid_sets=valid_sets, valid_names=valid_name, evals_result=evals_result)
print(f'fitting done')
y_hat = model.predict(test_df.values)
print(mean_absolute_error(test_Y.values,y_hat))
fig,ax = plt.subplots(2,1)
ax[0].plot(y_hat)
ax[1].plot(test_Y.values)
plt.show()
metric = 'l1'
fig, ax = plt.subplots()
ax.plot(evals_result['train'][metric], label='Train')
ax.plot(evals_result['eval'][metric], label='Test')
ax.legend()
plt.ylabel(f'{metric}')
plt.title(f'XGBoost {metric}')
plt.show()
复制代码
结果展现:
fitting done
3032.011693891576
复制代码
这几乎是让我震惊的结果,由于它比蓝老大(facebook)的模型结果5183.653998193845 提升了好多。
咱们在plot如下训练和测试的error历史曲线。
咱们用了默认的参数,结果已经很出乎个人意料了。主要是由于baseline弱爆了!! lightgbm咱们还能够再提升一下: 很明显从趋势来看,咱们出现了过拟合训练偏差还有降低的趋势,可是测试偏差已经反弹了。 下降过拟合,最快的方法就是调整max_depth。 另一个方法,就是花点时间看看lightgbm官方文档的tuning 指南。 咱们不是用来参赛或者提升给最终客户的成品,这里咱们简单调参。
param = {
'boosting':'dart',
'max_depth': 4,
'num_leaves':32,
'eta': 0.05,
'objective': 'regression',
'verbose': 0,
'metric': ['l1'],
}
复制代码
结果果真好于默认值,2666.2777024354377这个值是什么水平呢?咱们一样对比kaggle上的“大神”的结果。kaggle上kernals 第一名分享的结果是采用xgboost ( xgboost 通常会比lightgbm结果要更好),它的预测结果是2848.891429322955。
看来咱们的结果还不错。并且还666。。。 做为一篇学习类的文章,咱们不就用继续调参了。
2666.2777024354377
复制代码
咱们贴上主要特征图,也能够看到天天影响很大,再次是每小时的耗电量。
本文中咱们采用lightgbm预测能源消耗数据集,取得了相对不错的效果。相比于prophet,lightgbm的结果更好一些。这主要是由于咱们显式的提取了一些特征。
蓝老大说: 我能够再试一试!(请见下一篇,救活prohet)。