Prophet与先进的机器学习算法如LGBM相比,Prophet作为一个时间序列的工具,优点就是不需要特征工程就可以得到趋势,季节因素和节假日因素,但是这同时也是它的缺点之一,它无法利用更多的信息,如在预测商品的销量时,无法利用商品的信息,门店的信息,促销的信息等。

一、串行交叉验证断

1.1 交叉验证

Prophet 包括时间序列交叉验证功能,可使用历史数据测量预测误差。这是通过选择历史中的截止点来完成的,并且对于每个截止点,只使用直到该截止点的数据来拟合模型。然后,我们可以将预测值与实际值进行比较。该图说明了对 Peyton Manning 数据集的模拟历史预测,其中该模型适合 5 年的初始历史,并且预测是在一年的范围内进行的。

深度学习时间序列预测交叉验证代码_拟合

 注:这里需要解释下horizon,initial和period的意义:initial代表了一开始的时间是多少,period代表每隔多长时间设置一个cutoff,horizon代表每次从cutoff往后预测多少天。

        这个交叉验证过程可以使用cross_validation函数自动完成一系列历史截断。我们指定预测水平(horizon),然后选择初始训练期(initial)的大小和截断之间的间隔(period)。默认情况下,初始训练期设置为horizon的三倍,每半个horizon就有一个截断。

     cross_validation的输出是一个dataframe,在每个模拟预测日期和每个截断日期都有真实值y和样本预测值yhat。特别地,对在cutoff 和cutoff + horizon之间的每一个观测点都进行了预测。然后,这个dataframe可以用来度量yhat和y的错误。

 cross_validation的使用方法:

cross_validation(model, horizon, period=None, initial=None, parallel=None, cutoffs=None, disable_tqdm=False)

要画出上图的样子,可以使用以下的代码:

from prophet import Prophet
import pandas as pd
from matplotlib import pyplot as plt
import logging
logging.getLogger('prophet').setLevel(logging.ERROR)
import warnings
from prophet.diagnostics import cross_validation
warnings.filterwarnings("ignore")
import itertools
import numpy as np
import pandas as pd

#读入测试数据
df = pd.read_csv('example_wp_log_peyton_manning.csv')
#创建一个Prophet时间序列模型
m = Prophet()
m.fit(df)
#要预测的时间,这里指定是366天
future = m.make_future_dataframe(periods=366)


df_cv = cross_validation(
    m, '365 days', initial='1825 days', period='365 days')
cutoff = df_cv['cutoff'].unique()[0]
df_cv = df_cv[df_cv['cutoff'].values == cutoff]
fig = plt.figure(facecolor='w', figsize=(10, 6))
ax = fig.add_subplot(111)
ax.plot(m.history['ds'].values, m.history['y'], 'k.')
ax.plot(df_cv['ds'].values, df_cv['yhat'], ls='-', c='#0072B2')
ax.fill_between(df_cv['ds'].values, df_cv['yhat_lower'],
                df_cv['yhat_upper'], color='#0072B2',
                alpha=0.2)
ax.axvline(x=pd.to_datetime(cutoff), c='gray', lw=4, alpha=0.5)
ax.set_ylabel('y')
ax.set_xlabel('ds')
ax.text(x=pd.to_datetime('2010-01-01'),y=12, s='Initial', color='black',
       fontsize=16, fontweight='bold', alpha=0.8)
ax.text(x=pd.to_datetime('2012-08-01'),y=12, s='Cutoff', color='black',
       fontsize=16, fontweight='bold', alpha=0.8)
ax.axvline(x=pd.to_datetime(cutoff) + pd.Timedelta('365 days'), c='gray', lw=4,
           alpha=0.5, ls='--')
ax.text(x=pd.to_datetime('2013-01-01'),y=6, s='Horizon', color='black',
       fontsize=16, fontweight='bold', alpha=0.8);

1.1.1 参数设定截断

     在这里,我们做交叉验证来评估预测horizon在365天的性能,从第一次截止时730天的训练数据开始,然后每180天进行一次预测。在这个8年的时间序列中,这相当于11个总预测。

     根据上面对horizon,initial和period的解释:容易得到为什么是11个总预测。因为最后一个也要预测365天,所有最后一个cutoff在2015-01-20,从2007-12-19数730天是2010-02-15,则在2010-02-15到2015-01-20共有1800天,对应着10个cutoff,最后1个cutoff在2015-01-20,因此共11个cutoff。

注:Peyton Manning数据一共大约8年多(2007/12/10 - 2016/01/20)的数据。

在 Python 中,字符串initial,period和horizon应该采用 Pandas Timedelta 使用的格式,它接受天或更短的单位。

df_cv = cross_validation(m, initial='730 days', period='180 days', horizon = '365 days')
import pandas as pd
import matplotlib.pyplot as plt
from fbprophet import Prophet
from prophet.plot import plot_plotly, plot_components_plotly
import time
from prophet.diagnostics import cross_validation
# 数据的读入,输入的数据的时间是从2007年12月10号到2016年1月20日
# 一共是2905天
df = pd.read_csv('example_wp_log_peyton_manning.csv')
# new_df = df[0:2896]
print(df)
# 使用原始的进行拟合,通过对一个 Prophet 对象进行实例化来拟合模型
# 调用拟合模型进行拟合
m = Prophet()
m.fit(df)
#使用交叉验证的方法进行验证
df_cv = cross_validation(m, initial='730 days', period='180 days', horizon = '365 days')
df_cv.head()
print(df_cv)

执行该段代码,运行的结果为,可以直白的看到,预测值yhat和真实值之间的关系

深度学习时间序列预测交叉验证代码_机器学习_02

 1.1.2 参数设定截断

cutoffs关键字cross_validation。例如,相隔六个月的三个截止需要以cutoffs日期格式传递给参数,例如:

# Python
cutoffs = pd.to_datetime(['2013-02-15', '2013-08-15', '2014-02-15'])
df_cv2 = cross_validation(m, cutoffs=cutoffs, horizon='365 days')
import pandas as pd
import matplotlib.pyplot as plt
from fbprophet import Prophet
from prophet.plot import plot_plotly, plot_components_plotly
import time
from prophet.diagnostics import cross_validation
# 数据的读入,输入的数据的时间是从2007年12月10号到2016年1月20日
# 一共是2905天
df = pd.read_csv('example_wp_log_peyton_manning.csv')
# new_df = df[0:2896]
print(df)
# 使用原始的进行拟合,通过对一个 Prophet 对象进行实例化来拟合模型
# 调用拟合模型进行拟合
m = Prophet()
m.fit(df)
#使用交叉验证的方法进行验证
cutoffs = pd.to_datetime(['2013-02-15', '2013-08-15', '2014-02-15'])
df_cv2 = cross_validation(m, cutoffs=cutoffs, horizon='365 days')
df_cv2.head()
print(df_cv2)

 执行结束后的结果为:

深度学习时间序列预测交叉验证代码_拟合_03

 1.2 计算预测性能

    performance_metrics实用程序可用于计算预测性能的一些有用统计数据(yhat、yhat_lower和yhat_upper比较y),作为与截止点的距离(预测距离未来多远)的函数。计算的统计数据是均方误差 (MSE)、均方根误差 (RMSE)、平均绝对误差 (MAE)、平均绝对百分比误差 (MAPE)、中值绝对百分比误差 (MDAPE)。yhat_lower和yhat_upper估计值的覆盖率。这些是在按水平(减)df_cv排序后在预测的滚动窗口上计算的。默认情况下,每个窗口中将包含 10% 的预测,但这可以通过参数进行更改。执行方法为:

from prophet.diagnostics import performance_metrics
df_p = performance_metrics(df_cv)
df_p.head()

执行代码为:

import pandas as pd
import matplotlib.pyplot as plt
from fbprophet import Prophet
from prophet.plot import plot_plotly, plot_components_plotly
import time
from prophet.diagnostics import cross_validation
from prophet.diagnostics import performance_metrics

# 数据的读入,输入的数据的时间是从2007年12月10号到2016年1月20日
# 一共是2905天
df = pd.read_csv('example_wp_log_peyton_manning.csv')
# new_df = df[0:2896]
print(df)
# 使用原始的进行拟合,通过对一个 Prophet 对象进行实例化来拟合模型
# 调用拟合模型进行拟合
m = Prophet()
m.fit(df)
#使用交叉验证的方法进行验证
cutoffs = pd.to_datetime(['2013-02-15', '2013-08-15', '2014-02-15'])
df_cv2 = cross_validation(m, cutoffs=cutoffs, horizon='365 days')
df_cv2.head()
print(df_cv2)
df_p = performance_metrics(df_cv2)
df_p.head()
print(df_p)

得到的结果为,这样可以知道不同的 误差值

深度学习时间序列预测交叉验证代码_机器学习_04

1.3 计算预测性能可视化

plot_cross_validation_metric,这里显示的是 MAPE。点显示 中每个预测的绝对百分比误差df_cv。蓝线显示 MAPE,其中平均值取自点的滚动窗口。我们看到,对于这个预测,大约 5% 的误差对于未来一个月的预测是典型的,而对于一年后的预测,误差会增加到 11% 左右。

from prophet.plot import plot_cross_validation_metric
fig = plot_cross_validation_metric(df_cv, metric='mape')

深度学习时间序列预测交叉验证代码_拟合_05

rolling_window指定在每个滚动窗口中使用的预测比例。默认值为 0.1,对应df_cv于每个窗口中包含的 10% 的行;增加这将导致图中的平均曲线更平滑。周期initial应该足够长,以捕捉模型的所有组成部分,特别是季节性和额外的回归量:每年的季节性至少一年,每周的季节性至少一周等。 

二、调参产生误差最小值

2.1 参数自动化设置

#设置改变点尺度参数和季节尺度的参数,参数越大说明影响越大
param_grid = {
    'changepoint_prior_scale': [0.001, 0.01, 0.1, 0.5],
    'seasonality_prior_scale': [0.01, 0.1, 1.0, 10.0],
}

# 生成所有的参数组合【0.001,0.01】【0.001,0.1】【0.001,1】【0.001,10】一共16种组合
all_params = [dict(zip(param_grid.keys(), v)) for v in itertools.product(*param_grid.values())]
#在这里存储每个参数的rmse
rmses = []

通过上面的代码。可以生成16种组合数据 

深度学习时间序列预测交叉验证代码_拟合_06

 2.2 使用设置好的参数组,测试最优解

# 使用交叉验证来计算所有参数
for params in all_params:
    m = Prophet(**params).fit(df)  #用给定的参数拟合模型
    df_cv = cross_validation(m, cutoffs=cutoffs, horizon='30 days', parallel="processes")
    df_p = performance_metrics(df_cv, rolling_window=1)
    rmses.append(df_p['rmse'].values[0])

# 求解误差最优解
tuning_results = pd.DataFrame(all_params)
tuning_results['rmse'] = rmses
print(tuning_results)
# 打印最佳的两个尺度值
best_params = all_params[np.argmin(rmses)]
print(best_params)

执行后的结果为: 

深度学习时间序列预测交叉验证代码_交叉验证_07

2.3 完整的代码 

from prophet import Prophet
import pandas as pd
from matplotlib import pyplot as plt
import logging
logging.getLogger('prophet').setLevel(logging.ERROR)
import warnings
warnings.filterwarnings("ignore")
import itertools
import numpy as np
import pandas as pd
from prophet.diagnostics import cross_validation
##############第一部分的运行的结果,可以直白的看到,预测值yhat和真实值之间的关系#############
#读入测试数据
df = pd.read_csv('example_wp_log_peyton_manning.csv')
#创建一个Prophet时间序列模型
m = Prophet()
m.fit(df)
#要预测的时间,这里指定是366天
future = m.make_future_dataframe(periods=366)
#使用交叉验证的方法进行验证
#我们做交叉验证来评估预测horizon在365天的性能,从第一次截止initial时730天的训练数据开始,
# 然后每180天进行一次预测。
df_cv = cross_validation(m, initial='730 days', period='180 days', horizon = '365 days')
df_cv.head()
###################################################################################
#######################第二部分,自定义截止日期########################################
# 相隔六个月的三个截止需要以cutoffs日期格式传递给参数
cutoffs = pd.to_datetime(['2013-02-15', '2013-08-15', '2014-02-15'])
df_cv2 = cross_validation(m, cutoffs=cutoffs, horizon='365 days')
###################################################################################
##################第三部分是预测性能的评价,可以得到所有评价准则的值#######################
from prophet.diagnostics import performance_metrics
#performance_metrics实用程序可用于计算预测性能的测试
df_p = performance_metrics(df_cv)
df_p.head()
###################################################################################
##################第四部分是将指定的性能评价显示出来####################################
from prophet.plot import plot_cross_validation_metric
#平均绝对百分比误差mape
fig = plot_cross_validation_metric(df_cv, metric='mape')
###################################################################################
##################第五部分:是一个通过参数的值设置测试评价指标的值,并得到最优的参数##########
#设置改变点尺度参数和季节尺度的参数,参数越大说明影响越大
param_grid = {
    'changepoint_prior_scale': [0.001, 0.01, 0.1, 0.5],
    'seasonality_prior_scale': [0.01, 0.1, 1.0, 10.0],
}

# 生成所有的参数组合【0.001,0.01】【0.001,0.1】【0.001,1】【0.001,10】一共16种组合
all_params = [dict(zip(param_grid.keys(), v)) for v in itertools.product(*param_grid.values())]
#在这里存储每个参数的rmse
rmses = []

# 使用交叉验证来计算所有参数
for params in all_params:
    m = Prophet(**params).fit(df)  #用给定的参数拟合模型
    df_cv = cross_validation(m, cutoffs=cutoffs, horizon='30 days', parallel="processes")
    df_p = performance_metrics(df_cv, rolling_window=1)
    rmses.append(df_p['rmse'].values[0])

# 求解误差最优解
tuning_results = pd.DataFrame(all_params)
tuning_results['rmse'] = rmses
print(tuning_results)
# 打印最佳的两个尺度值
best_params = all_params[np.argmin(rmses)]
print(best_params)

三,可调参数和不可调参数归类 

3.1 可调参数

参数

作用

changepoint_prior_scale

最有影响力的参数。它决定了趋势的灵活性。特别是趋势在趋势变化点的变化程度。太小欠拟合,太大过拟合。默认0.05,可以调整[0.001, 0.5] 的范围可能是正确的

seasonality_prior_scale

控制季节性的灵活性。较大的值允许季节性适应较大的波动,较小的值会缩小季节性的幅度。默认值为 10.,调整它的合理范围可能是 [0.01, 10];当设置为 0.01 时,您会发现季节性的幅度被迫非常小。

holidays_prior_scale

控制灵活性以适应假日效果,它默认为10.0。[0.01, 10] 的范围内进行调整

seasonality_mode

选项为 [ 'additive''multiplicative']。默认为'additive',乘法季节性的特点为随着时间序列的幅度而增长

changepoint_range

允许趋势变化的历史比例,默认为 0.8,即历史的 80%。模型将不适合时间序列最后 20% 的任何趋势变化。这是相当保守的,以避免在时间序列的最后没有足够的跑道来适应趋势变化时过度拟合。通过对截止值的交叉验证可能无法有效地调整此参数。模型从时间序列的最后 10% 的趋势变化中进行概括的能力将很难通过查看在最后 10% 中可能没有趋势变化的早期截止值来学习。所以,这个参数最好不要调整,除非可能是在大量的时间序列上。在这种情况下,[0.8, 0.95] 可能是一个合理的范围。

3.2 可能不会调整的参数 

参数

作用

growth

选项是“线性”和“逻辑”。这可能不会被调整;如果有一个已知的饱和点并且朝着该点增长,它将使用逻辑趋势,否则它将是线性的。

changepoints

用于手动指定变更点的位置。默认情况下无,它会自动放置它们

n_changepoints

这是自动放置的变更点的数量,默认值 25 应该足以捕捉典型时间序列中的趋势变化(至少 Prophet 无论如何都能很好地工作的类型)。与其增加或减少变更点的数量,不如专注于增加或减少这些趋势变化的灵活性可能更有效,这通过changepoint_prior_scale.

yearly_seasonality

默认情况下('auto'),如果有一年的数据,这将打开年度季节性,否则关闭。选项是 ['auto', True, False],这个值调节不如直接调整seasonality_prior_scale

weekly_seasonality

yearly_seasonality一样

daily_seasonality

yearly_seasonality一样

holidays

这是传入指定假期的数据框。节日效果将与holidays_prior_scale效果差不多

mcmc_samples

是否使用 MCMC 可能取决于时间序列的长度和参数不确定性的重要性等因素

interval_width

Prophetpredict返回每个组件的不确定区间。例如yhat_loweryhat_upper预测yhat。这些被计算为后验预测分布的interval_width分位数,并指定要使用的分位数。默认值 0.8 提供 80% 的预测区间。如果您想要 95% 的间隔,可以将其更改为 0.95。这只会影响不确定区间,根本不会改变预测yhat因此不需要调整。

uncertainty_samples

不确定性区间计算为后验预测区间的分位数,后验预测区间使用蒙特卡洛采样估计。此参数仅影响不确定性区间,更改它不会以任何方式影响预测yhat;它不需要调整。

stan_backend

如果 pystan 和 cmdstanpy 都设置了后端,则可以指定后端。预测将是相同的,这不会被调整。