背景
最近,即将要实习的公司经理给了一个关于 Prophet 预测模型的案例,让我去学习一下怎么调参数。正好我早就想体验一下传说中“会一手调参数,就可以在硕士圈混”的感受了,????????????。本文主要通过简单介绍 Prophet 模型及其重要参数,再结合京东订单量的例子,加深对 Prophet 模型的了解。
认识 Prophet 模型
简介
Prophet 模型,英文直译为“先知”模型,是 Facebook 公司于 2017 开源的一个基于 Python 和 R 语言的时间序列预测算法。它适用于具有趋势性、多种周期性(每年每月每周每日每小时等)、节假日效应,以及部分异常值的时间序列,该模型不需要使用者掌握深厚的时间序列分析的统计学知识,Facebook 表示,默认配置的 Prophet 就可以生成媲美经验丰富的专业数据分析师的预测,即使不是时序分析专家,也可以理解该模型的重要参数。
模型结构
Prophet 模型本质上是一个可加性回归模型,其基本形式如下:
其中:
- :趋势项,表示非周期性的变化,如:饱和增长、分段线性两种
- :周期项,表示周期性的变化(傅立叶级数),如:每年每月每周每日每小时
- :节假日项,表示节假日或重大事件的变化,如春节、双十一
- :噪声项,表示无法建模和预测的随即波动,服从高斯分布,如:政府政策变动,世界末日
重要参数
下面分别为 Prophet 模型(Python)中三大模型相关的主要参数,以下所有参数 =
等号后面的值均为默认值,对历史数据进行拟合和预测时可以根据具体的业务情况对这些参数进行调整。
趋势项模型 g(t)
-
growth='linear'
Prophet 模型默认使用分段线性模型,如果使用饱和增长模型 (growth='logistic'
),必须给出饱和最大值cap
, 饱和最小值floor
默认为 0 -
changepoint_prior_scale=0.05
趋势模型拟合的灵活度,值越大,灵活度越高 -
changepoint_range=0.8
表示突变点(即斜率突变的点)所在的范围,如所有的突变点是基于前 80% 的历史数据的 -
n_changepoints=25
表示突变点的个数,如在前 80% 的历史数据中,通过等分方法找到 25 个变点 -
changepoints=None
手动设置突变点的位置,如changepoints=['2014-01-01']
-
interval_width=0.80
设置不确定性区间的宽度
周期项模型 s(t)
-
seasonality_prior_scale=10.0
周期性模型拟合的灵活度,值越大,灵活度越高 -
yearly_seasonality='auto'
表示周期性年份的傅立叶级数,当变化频率很高时,可以增大傅立叶级数 -
weekly_seasonality='auto'
表示周期性周份指定傅立叶级数 -
daily_seasonality='auto'
表示周期性日期的傅立叶级数 -
period
表示需要预测的点数 -
fourier_order
表示傅立叶项数 -
prior_scale
表示个别周期影响的程度 -
seasonality_mode='additive'
表示周期性模型使用的拟合模型,默认使用加法模型,可通过设置seasonality_mode='multiplicative'
设置为乘法模型
节假日项模型 h(t)
-
holidays_prior_scale=10.0
表示节假日模型拟合的灵活度,值越大,灵活度越高 -
lower_window=0
/upper_window=1
表示节假日影响的前后天数,注意:lower_window 取值范围为 0 或负整数 -
prior_scale
表示个别节假日影响的程度
Prophet 模型的应用
现在有京东 2015.1.1——2018.3.13 的订单量数据,如下图,用 Prophet 模型对此数据集进行拟合,并且预测未来一年的订单量。
….
拿到一个数据集时,首先对其进行数据清洗,剔除掉数据的异常值,提高数据的质量,这样才更有利于数据的分析。数据处理之后,大致观察此数据集,可以对此数据集做简单的图表分析,如下图为这三年京东的订单量的趋势变化。
通过此折线图可以观察到,每年都有四个时期,订单量出现的巨大的波动(三个波峰一个波谷)。接着,再仔细观察某一年中这四个具体的时段,如下图所示,
得知,这四个时期分别对应着春节、京东 618 购物节、双十一、双十二,从而可以在拟合和预测时,增加节假日模型的成分。
如下图为直接利用 python 进行拟合作图的结果(源码在最后附录):
可以看出,拟合的效果整体上还是挺不错的嘛 ????
如果想查看预测的各成分分析,可以使用 Prophet.plot_components 方法。默认情况下,将展示趋势、时间序列的年度季节性和周季节性,如果包含了节假日,也会展示出来,如下图所示:
结束语
- 简单友好。Prophet 模型,只需要简单的步骤和懂得一些重要参数的调整,就可以得到很好的拟合和预测,对于统计学知识不太深厚的我,很是友好了,一开始以为要先补一下时间序列分析相关的统计学知识。不过还得感谢 Facebook 的大佬们开发出了这么牛逼的算法,并且开源了出来。
- 参数的理解。参数对模型的结果影响是很大的,一般理解了一个模型的参数,基本上就可以使用这个模型了。
- 什么时候使用?当带有周期性和趋势性很强的时间序列,并且可以预先知道异常日期的场景下,Prophet 模型是个不错的分析工具。
参考文献
- Prophet 的官方网站
- Prophet 的中文翻译版的官方网站
- FACEBOOK 时间序列预测算法 PROPHET 的研究 ——张戎 2018.11.30
- Prophet 的英文版论文
- 京东订单量数据来源
- 其他 Prophet 实战例子
附录
Prophet 模型的算法在 python 中不是 prophet,而是叫 fbprophet,它在 python 的安装方法,在 Github 上的介绍已经很详细了:
https://github.com/facebook/p…
本文案例的 python 源码
import pandas as pd
from fbprophet import Prophet
import matplotlib.pyplot as plt
%matplotlib inline
# 节假日数据
# 因为春节法定节日有一个星期,所以将春节的前后假日扩大至 7 天
# Spring Festival
# 春节
TF_Spring=pd.DataFrame({
'holiday':'Spring Festival',
'ds':pd.to_datetime(['2015-2-19','2016-2-8','2017-1-28','2018-2-16','2019-2-5']),
'lower_window': -3,
'upper_window': 4,
})
# E-commerce festival 618
ECF_618 = pd.DataFrame({
'holiday': 'ECF_618',
'ds': pd.to_datetime(['2015-6-18','2016-6-18','2017-6-18','2018-6-18']),
'lower_window': 0,
'upper_window': 1,
})
# E-commerce festival 1111
ECF_1111 = pd.DataFrame({
'holiday': 'ECF_1111',
'ds': pd.to_datetime(['2015-11-11','2016-11-11','2017-11-11','2018-11-11']),
'lower_window': 0,
'upper_window': 1,
})
# E-commerce festival 1212
ECF_1212 = pd.DataFrame({
'holiday': 'ECF_1212',
'ds': pd.to_datetime(['2015-12-12','2016-12-12','2017-12-12','2018-12-12']),
'lower_window': 0,
'upper_window': 1,
})
# 合并所有节假日,作为 Prophet 的形参
holidays=pd.concat((TF_Spring,ECF_618,ECF_1111,ECF_1212))
# 导入数据集,用 pandas 库来读取 excel 数据
df=pd.read_excel('./data.xlsx')
# 转换订单量的数据类型
df['y']=df['y'].astype(float)
# 初始化模型,设定 Prophet 模型的参数
m=Prophet(holidays=holidays,holidays_prior_scale=10.0,changepoint_prior_scale=0.15,daily_seasonality=True)
# 进行拟合
m.fit(df)
# 构建待预测的日期数据,periods=365 表示从历史数据的最后一天再往后推 365 天
future=m.make_future_dataframe(periods=365)
future.tail()
# 预测结果
forecast=m.predict(future)
# 绘制预测结果
m.plot(forecast)
# 预测的成分分析绘图,展示时间序列的分量
m.plot_components(forecast);
x1=forecast['ds']
y1=forecast['yhat']
plt.plot(x1,y1)
plt.show()