Energy Consumption (能源消耗)数据集预测(4):拯救prophet

前言

第一篇文章咱们用一个小时从安装prophet 到完成预测,而且咱们将结果对比了naive的季节信号分解(这个能够看做是简版的prohet,纯原创),发现效果略逊色于naive的方法。bash

后来咱们用lightgbm 再次预测一样的数据集,发现结果远远赛过prophet。post

咱们也对比了咱们的预测结果与kaggle上公开的方法与测试结果,发现咱们的lightgbm的结果更好,多是由于咱们作了必定参数调优。学习

咱们的prophet 结果和kaggle大神的预测结果同样“好” (相比于lghtgbm都不好)。测试

可是facebook 大厂旗下prophet的super power 真的弱爆了吗?ui

一条code挽救prophet

对于深度学习来讲,没有标准化会直接让你的模型跑飞。url

对于树模型来讲,异常值有的时候让你的feature 变得莫名奇妙的重要。spa

对于prohet 来讲,彷佛没有什么特殊的注意事项,也没有要清理数据的必要。3d

可是问题每每就隐藏在其中。code

读取数据

代码依然保持不变。component

import pandas as pd
from fbprophet import Prophet, make_holidays
from sklearn.metrics import mean_absolute_error
from fbprophet.plot import add_changepoints_to_plot


file = 'data/PJME_hourly.csv'
raw_df = pd.read_csv(file)
print(raw_df.head(5))
复制代码

将string 时间转成pandas Timestamp 格式,而且预览head 和tail。

dt_format = '%Y-%m-%d %H:%M:%S'
raw_df['Datetime'] = pd.to_datetime(raw_df['Datetime'], format=dt_format)
raw_df.rename({'PJME_MW': 'y', 'Datetime': 'ds'}, axis=1, inplace=True)
print(raw_df.tail(5))
print(raw_df.describe())
复制代码

能够看到时间从2002年到2018年,并且顺序彷佛也对。

ds        y
8734 2002-01-01 01:00:00  30393.0
8735 2002-01-01 02:00:00  29265.0
8736 2002-01-01 03:00:00  28357.0
8737 2002-01-01 04:00:00  27899.0
8738 2002-01-01 05:00:00  28057.0
                        ds        y
140250 2018-08-02 20:00:00  44057.0
140251 2018-08-02 21:00:00  43256.0
140252 2018-08-02 22:00:00  41552.0
140253 2018-08-02 23:00:00  38500.0
140254 2018-08-03 00:00:00  35486.0
复制代码

为了保险起见,咱们对时间轴进行排序。

raw_df = raw_df.sort_values(by=['ds']) # sort by time
print(raw_df.tail(5))
print(raw_df.describe())
复制代码

根据时间点划分测试集与训练集

split_dt = pd.Timestamp('2015-01-01 00:00:00')
train_df = raw_df[raw_df['ds'] < split_dt]
test_df = raw_df[raw_df['ds'] >= split_dt]
复制代码

prophet 建模

这里咱们依然采用默认的参数。

model = Prophet()
model= model.fit(train_df)
y_hat  = model.predict(test_df)
y_test = model.predict(test_df)
fig = model.plot(y_test)
a = add_changepoints_to_plot(fig.gca(), model, y_test)
print(mean_absolute_error(test_df['y'].values, y_test['yhat']))
model.plot_components(y_test)
复制代码

结果

咱们能够看到偏差结果居然是3105。相比于咱们第2篇的模型以及kaggle大神的5183要好不少。

可是咱们的代码几乎没有变,也没有任何的参数调优啊?

3105.4775855473995
复制代码

缘由解析

其实提升的缘由很简单,数据有bug!!

其实不是bug,应该是脏数据,并且隐藏的有点深。 通常咱们预览head(5) 和tail(5),看到顺序是一致的,咱们内心就会默认这个数据是按照时间顺序排列的。其实不是!

当咱们预览前30行数据的时候就会发现,日期居然是倒序的。并且中间还加了莫名其妙的1月1号。

咱们代码中不起眼的一句 sort_value 居然让偏差巨幅下降。

而prophet 须要根据时间的前后顺序,提取必定的周期信号。因此观测对象在时间轴上的前后顺序无疑起着决定性做用。

相比于第3 篇介绍的lightgbm,它不会考虑时间的连续性,所以须要咱们显式的告诉它一些时间特性,好比咱们提取day,year 等信息。

因此prophet 没有那么差,只不过咱们须要对数据进行清理,尤为是时间前后顺序。

Datetime,PJME_MW
2002-12-31 01:00:00,26498.0
2002-12-31 02:00:00,25147.0
2002-12-31 03:00:00,24574.0
2002-12-31 04:00:00,24393.0
2002-12-31 05:00:00,24860.0
2002-12-31 06:00:00,26222.0
2002-12-31 07:00:00,28702.0
2002-12-31 08:00:00,30698.0
2002-12-31 09:00:00,31800.0
2002-12-31 10:00:00,32359.0
2002-12-31 11:00:00,32371.0
2002-12-31 12:00:00,31902.0
2002-12-31 13:00:00,31126.0
2002-12-31 14:00:00,30368.0
2002-12-31 15:00:00,29564.0
2002-12-31 16:00:00,29098.0
2002-12-31 17:00:00,30308.0
2002-12-31 18:00:00,34017.0
2002-12-31 19:00:00,34195.0
2002-12-31 20:00:00,32790.0
2002-12-31 21:00:00,31336.0
2002-12-31 22:00:00,29887.0
2002-12-31 23:00:00,28483.0
2003-01-01 00:00:00,27008.0
2002-12-30 01:00:00,27526.0
2002-12-30 02:00:00,26600.0
2002-12-30 03:00:00,26241.0
2002-12-30 04:00:00,26213.0
2002-12-30 05:00:00,26871.0

复制代码

高阶的prophet 参数配置

默认的prophet 配置可让咱们快速的创建baseline,可是对于时间序列,咱们还能够自定义参数。 这里咱们贴出代码,方便你们参考。

添加假日信息

好比咱们能够添加美国的假日信息。该假日信息会传递给prophet holiday 参数

holidays = make_holidays.make_holidays_df(range(2002, 2018, 1), 'US')
复制代码

自定义条件

对于同一个周期,咱们能够定义一些条件进行区分。

  • 好比一周的周期都是7天,可是前5天是工做日,后两天是周末。
  • 或者一天能够分为白天和晚上,它们的周期依然都是1天。
  • 一年可能会有淡季和旺季,好比夏天多是用电高峰期(制冷设备),咱们能够这样划分
raw_df['on_season'] = (raw_df['ds'].dt.month > 4) | (raw_df['ds'].dt.month < 8)
raw_df['off_season'] = ~(raw_df['ds'].dt.month > 4) | (raw_df['ds'].dt.month < 8)


raw_df['night'] = (raw_df['ds'].dt.hour > 20) | (raw_df['ds'].dt.hour < 8)
raw_df['daylight'] = ~(raw_df['ds'].dt.hour > 20) | (raw_df['ds'].dt.hour < 8)

raw_df['weekend'] = raw_df['ds'].dt.week > 5
raw_df['workday'] = ~raw_df['weekend']

复制代码

创建复杂prophet模型

咱们能够将模型默认的yearly_seasonality,weekly_seasonality,daily_seasonality 设置为False,而后添加本身定义的周期,或者叠加咱们定义的条件。 此外,对于每个周期,咱们能够定义他的权重(prior_scale)。

model = Prophet(
    holidays=holidays,
    changepoint_prior_scale=0.5,
    holidays_prior_scale=10,
    seasonality_prior_scale=10,
    yearly_seasonality=False,
    weekly_seasonality=False,
    daily_seasonality=False)
model.add_country_holidays(country_name='US')
model.add_seasonality(name='half_daily',period=0.5,fourier_order=12)
model.add_seasonality(name='daylight_daily',period=1,fourier_order=24,condition_name='daylight')
model.add_seasonality(name='night_daily',period=1,fourier_order=24,condition_name='night')
model.add_seasonality(name='weekend_weekly',period=7,fourier_order=14,prior_scale=10,condition_name='weekend')
model.add_seasonality(name='workday_weekly',period=7,fourier_order=14,prior_scale=10,condition_name='workday')
model.add_seasonality(name='on_season_half_year',period=182.625,fourier_order=24,prior_scale=10,condition_name='on_season')
model.add_seasonality(name='off_season_half_year',period=182.625,fourier_order=24,prior_scale=10,condition_name='off_season')
model.add_seasonality(name='on_season_year',period=365.25,fourier_order=24,prior_scale=10,condition_name='on_season')
model.add_seasonality(name='off_season_year',period=365.25,fourier_order=24,prior_scale=10,condition_name='off_season')
复制代码

运行结果为:

3043.2366637873324
复制代码

相比于默认的参数偏差有所下降。

咱们依然附上分解的结果:

总结

本文成功解决了prophet 建模时遇到的坑,将偏差从5000多下降到3000多。 经过本文能够了解到:

  • 时间序列的顺序对于prophet模型的重要性
  • 有必要对时间序列的顺序作出验证。
  • 能够自定义假日,周期,权重等参数,让模型更贴近预测的时间序列特性
相关文章
相关标签/搜索