ベイズ推論パッケージPyMCと需要予測パッケージ Prophet を紹介する.

Prophetとは

Prophet https://facebook.github.io/prophet/ は需要予測のためのパッケージである. ここでは,Prophetを用いた需要予測の方法について述べる. また,需要予測の基本原理と, Prophetの基礎になるベイズ推論をPyMCパッケージを用いて解説する.

需要予測

筆者は,以前は最適化の仕事は引き受けても予測の仕事は引き受けないことにしていた.予測は現場で長年働いている人の経験を加味して行うべきものであり,門外漢がいくらテクニックを駆使してもそれを超えることは難しいかと考えていたからだ.

しかし最近になって,需要予測は当たらないといけないという考えを捨てて,誤差の管理を行うことだと割り切って考えることにして,予測に積極的に関与するようになった.多くの最適化モデルは,予測がある程度合っているという前提で構築される.予測をいいかげんにされると,最適化モデル自体が役に立たないものになる.ゴミを入れればゴミが出てくるからだ.

誤差を管理することによって,需要予測を「点」で行うのではなく,「範囲」で行うことが可能になる.また,需要の分布も特定できるようになる.範囲内での最適化はロバスト最適化,確率分布を仮定した最適化は確率最適化という枠組みで解決可能になる.

以下では,サプライ・チェイン最適化に関連した需要予測の基本と重要なモデルについて解説する.

予測の公理

最近,サプライ・チェインの現場において,予測に関する多くの誤りが浸透していることに気づいた.ここでは,このような誤用を減らして正しい予測手法を適用するための,予測の公理について述べる.

  • 予測は予測のためならず

しばしば,予測を目的として仕事をしている人たちを見かける.予測は,ほかの重要な意思決定を行うための基礎となる手段であり,予測そのものを目的としてはならない.実際には,予測よりもその誤差を評価することの方が重要である.誤差が増えているのか,減っているのか,その理由は何かを考えることが,需要予測の真の目的なのである.

たとえば,小売りの現場で需要予測を行うことは,在庫費用の削減や品切れ損出の回避などを目的としたものであり,予測の精度だけを問題にするのではなく,どの程度外れているのかという誤差の管理と,外れた場合の影響や,緊急発注などの回避手段とあわせて考える必要がある.

また,予測するだけでなく,なぜそのような値になったのかを究明することも重要である.需要が0という日が続いた場合には,それが,本当に需要がなかったのか,それとも在庫がないために売れなかったのか,陳列場所が悪かったために売れなかったのか,などをあわせて原因を分析する必要がある.

多くのメーカーでは,需要予測をするためだけの部署を設けているが,これもナンセンスなのでできるだけ早くほかの部署と連携をとるように改めるべきである.需要は当てるものではなくコントロールするものであり,需要予測を当てることだけを目標としている部署は,廃止すべきである.

  • 予測は外れるもの

しばしば予測が当たったとか外れたとかいう言葉を現場の人から聞くが,経営はギャンブルではないので的中というのはありえない.この人なら当たるとかいうのは迷信であり,たまたま当たったときに声を張り上げて宣伝しているか,誤差が大きいにもかかわらず当たったと宣伝しているかの何れかである.サプライ・チェインからはちょっと外れるが,地震の予測(予知ともいう)も似たようなものであり,日本中のどこかで地震が発生すると予測し,そのうち1つが当たると的中と宣伝していたりする.ましてや,株や競馬の予想的中などの宣伝は,たまたま当たったときの結果だけを掲載し,外れたときのものを消去して,予想的中の証拠として提出していたりする.いまだに,こんな宣伝にだまされる人がいるのかと感心するが,社内で需要予測が的中する人がいるという会社も似たような詐欺に遭っていると言える.

重要なことは,どの程度外れたのかを時系列的に管理することである.誤差が増えている際には,その原因を追求し,予測手法を改善するなり,在庫を増やして品切れを回避するなりの行動をとるのが正しい方法である.

  • 集約すれば精度が上がる

往々にして,個々のものの予測は難しいが,それをまとめたものの予測は容易になる.たとえば,特定の場所で特定のマグニチュードの地震が明日発生することを予測するのは難しいが,日本のどこかでマグニチュード4以上の地震が来年発生することは容易に予測できる.前者はほぼ$0$%であるが,後者はほぼ$100$%の精度で予測できる.これが集約の力である.

サプライ・チェインでも同様であり,商品を集約して商品群にすれば精度があがり,個々の店舗での売上でなく,地域内のすべての店舗の売上を集約すれば精度が上がる. 時間軸でも同様であり,1時間以内の需要を予測精度は,日,週,月,年単位と集約していくにしたがってあがっていく.

予測精度だけを議論するのであれば,どんどん集約した方が得であるが,もちろんどんどん役に立たなくなる.前述したように,重要なことは,その予測を何に使うかであり,使用法にあわせて「適切に」集約を行うことである.

また,製品設計や在庫地点を考慮することによって,物理的に集約を行うこともできる.たとえば,製品のモジュール化や遅延差別化やリスク共同管理がこれに相当する.これらはモダンなサプライ・チェインの基本戦略であり,そのすべてがこの単純な公理(集約した方が予測精度が上がる)に基づくものであることは興味深い.

  • 目先の需要は当たりやすく,遠い未来の需要は予測しにくい

明日の天気はある程度予測できるが,1年後の天気は予測しにくい.不確実性は時間が経過するにしたがって増大するからである.需要予測も同様である.たとえば,カップラーメンなどは一部の定番品を除いて,来年店頭に並んでいるかどうかも怪しい.

近い未来の予測は,短期のオペレーショナルな意思決定に用いるので,比較的正確性が必要であるが,遠い未来の予測は長期のストラテジックな意思決定に用いるので,おおよそで構わない.さらに,長期の意思決定の際には,データは集約して行われるので,予測精度も上がる. 要は,目的のために適切な精度で管理できるように,時間軸を集約することが推奨される.たとえば,近い未来は予測精度がよいので,集約をせずに日単位で予測し,未来に行くに従って時間軸の集約を行い,週単位,月単位,年単位としていく訳である.このテクニックはテレスコーピングと呼ばれ,時間軸を含んだ実際問題を解くときによく用いられる.

予測手法と使い分け

古典的な予測手法は statsmodelsに入っている。種々の指数平滑法の拡張や、ARIMAなどは簡単にできる。 いずれも高速なので、補助的なデータがない時系列データに対して、簡易的な予測をしたい場合には便利だが、予測精度は期待すべきではない。

機械(深層)学習を用いた予測は、 scikit-learn(最近ではpycaret)やfastaiのTabularモデルやLSTMを用いることによって容易にできる。 補助的なデータが豊富な場合には、これらの手法が推奨される。

補助的なデータが少ない時系列データの場合には、prophetを用いたベイズ推論が推奨される。ベイズ推論だと、予測を点でするのではなく、不確実性の範囲まで得られるので、 サプライ・チェインのモデルと相性が良い。例えば、在庫モデルにおいては、不確実性の情報が不可欠だからだ。

ベイズ推論

ここでは, ベイズ推論 (Bayesian inference) をPyMCパッケージ https://www.pymc.io/ を用いて解説する. PyMCはGoogle Colabにプレインストールされている.

ベイズの公式

ベイズ推論は,高校で学習するベイズの公式を利用する.

  • $P(A | B)$ : $B$ という事象が発生したきに $A$ が起こる条件付き確率
  • $P( A \cap B)$: 事象 $A$ と $B$ が同時に発生する確率
$$ P(A|B) = \frac{P( A \cap B)}{P(B)} $$

同様に, $$ P(B|A) = \frac{P( A \cap B)}{P(A)} $$

ベン図を使うと簡単に理解できる.

事前分布と事後分布と尤度関数

  • $P(\bar{B})$: 事象 $B$ が発生しない確率
  • $P(B) + P(\bar{B}) =1$

ベイズの公式から, $$ P(B|A) = \frac{P(A|B) P(B) }{P(A)} = \frac{P( A|B) p(B) }{P(A|B) P(B) + P(A|\bar{B}) P(\bar{B}) } $$

$B$ をモデルのパラメータ $\theta$, $A$ を(観測された)データとすると, $$ P(\theta| data) = \frac{P( data|\theta) P(\theta) }{P(data)} $$

  • $P(\theta)$: 事前分布 (prior)
  • $P( data|\theta)$ : パラメータ $\theta$ に対する尤度関数 $L(\theta)$ (likelihood)
  • $P(\theta| data)$ : 事後分布 (posterior)

事後分布は,尤度と事前分布の積に比例する.

$$ P(\theta| data) \propto P( data|\theta) P(\theta) = Likelyhood \times Prior $$

ベイズ線形回帰

例として, 通常の線形回帰をベイズ推論を用いて行う. 以下の2変数の線形モデルを仮定し,データを生成する.

$$ \begin{array}{l l} Y &\sim N(\mu, \sigma^2) \\ \mu &= \alpha + \beta_1 X_1 + \beta_2 X_2 \end{array} $$
import arviz as az
import numpy as np
import pymc as pm

alpha, sigma = 1, 1
beta = [1, 2.5]
size = 100

X1 = np.random.randn(size)
X2 = np.random.randn(size) * 0.2

Y = alpha + beta[0] * X1 + beta[1] * X2 + np.random.normal(size=size) * sigma

ベイズ推論

ベイズ推論は, 事前分布と尤度関数によって定義された生成モデルがあれば,ベイズの公式を用いて,データが与えられた条件下での事後分布の推定が可能であることを利用する.

線形回帰の例では,事前分布は $\alpha, \beta$ は正規分布, $\sigma$ は負の部分を除いた半正規分布とし, 生成モデルは $\mu = \alpha + \beta_1 X_1 + \beta_2 X_2$, 尤度関数は $Y \sim N(\mu, \sigma^2)$ とする.

model = pm.Model()
with model:
    # 事前分布
    alpha = pm.Normal("alpha", mu=0, sigma=10)
    beta = pm.Normal("beta", mu=0, sigma=10, shape=2)
    sigma = pm.HalfNormal("sigma", sigma=1)

    # 生成モデル
    mu = alpha + beta[0] * X1 + beta[1] * X2

    # 尤度関数
    Y_obs = pm.Normal("Y_obs", mu=mu, sigma=sigma, observed=Y)

MCMC (Markov chain Monte Carlo)法

実際の分布の推定には, MCMC (Markov chain Monte Carlo)法を利用する. <!-- 詳細は,以下を参照.

https://en.wikipedia.org/wiki/Markov_chain_Monte_Carlo -->

MCMC法は,以下に示すように改悪も許した確率的な探索を行うことによって,分布のサンプリングを行う.

ベイズ線形回帰の事後分布をサンプリングによって生成する.

with model:
    # 事後分布を 1000 個サンプル(時間がかかるのでしばらく待つ)
    idata = pm.sample(draws=1000)
100.00% [2000/2000 00:02<00:00 Sampling chain 0, 0 divergences]
100.00% [2000/2000 00:01<00:00 Sampling chain 1, 0 divergences]

結果の表示

az.plot_trace(idata, combined=True, figsize=(10,10));
az.summary(idata, round_to=2)
mean sd hdi_3% hdi_97% mcse_mean mcse_sd ess_bulk ess_tail r_hat
alpha 1.03 0.09 0.86 1.21 0.00 0.00 3101.07 1585.09 1.0
beta[0] 1.06 0.09 0.89 1.22 0.00 0.00 2642.38 1633.52 1.0
beta[1] 2.44 0.45 1.64 3.31 0.01 0.01 3164.36 1788.72 1.0
sigma 0.94 0.07 0.81 1.07 0.00 0.00 2338.74 1457.89 1.0

元のデータは,以下のコードによって生成していたので,良い近似になっていることが確認できる.

alpha, sigma = 1, 1
beta = [1, 2.5]

諸パッケージのインポート

Prophetで予測するために必要なパッケージをインポートしておく. vega_datasetsのデータを用いるので,インストールしておく.

https://github.com/altair-viz/vega_datasets

#import plotly.io as pio
#pio.renderers.default = "colab"
#!pip install prophet
#!pip install -U vega_datasets

import pandas as pd
from prophet import Prophet
from vega_datasets import data
import plotly.express as px
import prophet.plot as fp
import plotly

Prophetの基本

ProphetをPythonから呼び出して使う方法は,機械学習パッケージscikit-learnと同じである。

  1. Prophetクラスのインスタンスmodelを生成
  2. fitメソッドで学習(引数はデータフレーム)
  3. predictメソッドで予測(引数は予測したい期間を含んだデータフレーム)

例題:Wikiアクセス数

例としてアメリカンフットボールプレーヤのPayton ManningのWikiアクセス数のデータを用いる。

df = pd.read_csv("http://logopt.com/data/peyton_manning.csv")
df.head()
ds y
0 2007-12-10 9.590761
1 2007-12-11 8.519590
2 2007-12-12 8.183677
3 2007-12-13 8.072467
4 2007-12-14 7.893572

Prophetモデルのインスタンスを生成し,fitメソッドで学習(パラメータの最適化)を行う.fitメソッドに渡すのは,上で作成したデータフレームである.このとき、ds(datestamp)列に日付(時刻)を、y列に予測したい数値を入れておく必要がある (この例題では,あらかじめそのように変更されている).

model = Prophet()
model.fit(df)
09:28:44 - cmdstanpy - INFO - Chain [1] start processing
09:28:45 - cmdstanpy - INFO - Chain [1] done processing
<prophet.forecaster.Prophet at 0x7ff1c82f31f0>

make_future_dataframeメソッドで未来の時刻を表すデータフレームを生成する。既定値では、予測で用いた過去の時刻も含む。 引数は予測をしたい期間数periodsであり,ここでは、1年後(365日分)まで予測することにする。

future = model.make_future_dataframe(periods=365)
future.tail()
ds
3265 2017-01-15
3266 2017-01-16
3267 2017-01-17
3268 2017-01-18
3269 2017-01-19

predict メソッドに予測したい時刻を含んだデータフレームfuture を渡すと、予測値を入れたデータフレームforecastを返す。このデータフレームは、予測値yhatの他に、予測の幅などの情報をもった列を含む。以下では,予測値yhatの他に,予測の上限と下限(yhat_lowerとyhat_upper)を表示している.

forecast = model.predict(future)
forecast[["ds", "yhat", "yhat_lower", "yhat_upper"]].tail()
ds yhat yhat_lower yhat_upper
3265 2017-01-15 8.212625 7.490936 8.952181
3266 2017-01-16 8.537635 7.801412 9.285417
3267 2017-01-17 8.325071 7.604013 9.018835
3268 2017-01-18 8.157723 7.458736 8.901977
3269 2017-01-19 8.169677 7.469157 8.876939

matplotlibを用いた描画は,plotメソッドで行う.

model.plot(forecast)

一般化加法モデル

Prophetにおける予測は一般化加法モデルを用いて行われる.これは,傾向変動,季節変動,イベント情報などの様々な因子の和として予測を行う方法である.

$$ y_t =g_t + s_t + h_t + \epsilon_t $$
  • $y_t$ : 予測値
  • $g_t$ : 傾向変動(trend);傾向変化点ありの線形もしくはロジスティック曲線
  • $s_t$ : 季節変動;年次,週次,日次の季節変動をsin, cosの組み合わせ(フーリエ級数)で表現
  • $h_t$ : 休日などのイベント項
  • $\epsilon_t$ : 誤差項

因子ごとに予測値の描画を行うには,plot_componentsメソッドを用いる.既定では,以下のように,上から順に傾向変動,週次の季節変動,年次の季節変動が描画される.また,傾向変動の図(一番上)には,予測の誤差範囲が示される.季節変動の誤差範囲を得る方法については,後述する.

model.plot_components(forecast)

対話形式に,拡大縮小や範囲指定ができる動的な図も,Plotlyライブラリを用いて得ることができる.

fig = fp.plot_plotly(model, forecast) 
plotly.offline.plot(fig);

例題: $CO_2$ 排出量のデータ

データライブラリから二酸化炭素排出量のデータを読み込み,Plotly Expressで描画する.

co2 = data.co2_concentration()
co2.head()
Date CO2
0 1958-03-01 315.70
1 1958-04-01 317.46
2 1958-05-01 317.51
3 1958-07-01 315.86
4 1958-08-01 314.93
fig = px.line(co2,x="Date",y="CO2")
plotly.offline.plot(fig);

列名の変更には,データフレームのrenameメソッドを用いる.引数はcolumnsで,元の列名をキーとし,変更後の列名を値とした辞書を与える.また,元のデータフレームに上書きするために,inplace引数をTrueに設定しておく.

co2.rename(columns={"Date":"ds","CO2":"y"},inplace=True)
co2.head()
ds y
0 1958-03-01 315.70
1 1958-04-01 317.46
2 1958-05-01 317.51
3 1958-07-01 315.86
4 1958-08-01 314.93

make_future_dataframeメソッドで未来の時刻を表すデータフレームを生成する。既定値では、(予測で用いた)過去の時刻も含む。 ここでは、200ヶ月先まで予測することにする。

そのために,引数 periods を200に,頻度を表す引数 freq をMonthを表す M に設定しておく

predict メソッドに予測したい時刻を含んだデータフレームfuture を渡すと、予測値を入れたデータフレームforecastを返す。このデータフレームは、予測値yhatの他に、予測の幅などの列を含む。

最後にplotメソッドで表示する.

model = Prophet()
model.fit(co2)
future = model.make_future_dataframe(periods=200, freq=`M`)
forecast = model.predict(future)
model.plot(forecast);
09:49:04 - cmdstanpy - INFO - Chain [1] start processing
09:49:04 - cmdstanpy - INFO - Chain [1] done processing

予測は一般化加法モデルを用いて行われる.

これは,傾向変動,季節変動,イベント情報などの様々な因子の和として予測を行う方法である.

上に表示されているように,週次と日次の季節変動は無視され,年次の季節変動のみ考慮して予測している.

因子ごとに予測値の描画を行うには,plot_componentsメソッドを用いる.既定では,以下のように,上から順に傾向変動,週次の季節変動,年次の季節変動が描画される.また,傾向変動の図(一番上)には,予測の誤差範囲が示される.季節変動の誤差範囲を得る方法については,後述する.

model.plot_components(forecast);

Plotlyで描画すると, 一部を拡大,期の選択などが可能になる.

fig = fp.plot_plotly(model, forecast)
plotly.offline.plot(fig);

例題:航空機乗客数のデータ

Prophetの既定値では季節変動は加法的モデルであるが、問題によっては乗法的季節変動の方が良い場合もある。 例として、航空機の乗客数を予測してみよう。最初に既定値の加法的季節変動モデルで予測し,次いで乗法的モデルで予測する.

passengers = pd.read_csv("http://logopt.com/data/AirPassengers.csv")
passengers.head()
Month #Passengers
0 1949-01 112
1 1949-02 118
2 1949-03 132
3 1949-04 129
4 1949-05 121
fig = px.line(passengers,x="Month",y="#Passengers")
plotly.offline.plot(fig);
passengers.rename(inplace=True,columns={"Month":"ds","#Passengers":"y"})
passengers.head()
ds y
0 1949-01 112
1 1949-02 118
2 1949-03 132
3 1949-04 129
4 1949-05 121

季節変動を乗法的に変更するには, モデルの seasonality_mode 引数を乗法的を表す multiplicative に設定する.

なお,以下のデータは月次のデータであるので,make_future_dataframeの freq 引数を M (Month)に設定する.

model = Prophet().fit(passengers)
future = model.make_future_dataframe(periods=20, freq="M")
forecast = model.predict(future)
model.plot(forecast);
09:58:45 - cmdstanpy - INFO - Chain [1] start processing
09:58:45 - cmdstanpy - INFO - Chain [1] done processing
model = Prophet(seasonality_mode="multiplicative").fit(passengers)
future = model.make_future_dataframe(periods=20, freq="M")
forecast = model.predict(future)
model.plot(forecast);
09:56:17 - cmdstanpy - INFO - Chain [1] start processing
09:56:17 - cmdstanpy - INFO - Chain [1] done processing

結果から,乗法的季節変動の方が,良い予測になっていることが確認できる.

問題(小売りの需要データ)

以下の,小売りの需要データを描画し,予測を行え. ただし,モデルは乗法的季節変動で,月次で予測せよ.

retail = pd.read_csv(`http://logopt.com/data/retail_sales.csv`)
retail.head()
ds y
0 1992-01-01 146376
1 1992-02-01 147079
2 1992-03-01 159336
3 1992-04-01 163669
4 1992-05-01 170068

例題: 1時間ごとの気温データ

ここではシアトルの気温の予測を行う.

climate = data.seattle_temps()
climate.head()
date temp
0 2010-01-01 00:00:00 39.4
1 2010-01-01 01:00:00 39.2
2 2010-01-01 02:00:00 39.0
3 2010-01-01 03:00:00 38.9
4 2010-01-01 04:00:00 38.8

このデータは, date 列に日付と1時間ごとの時刻が, temp 列に気温データが入っている.

Prophetは, 日別でないデータも扱うことができる。 date列のデータ形式は、日付を表すYYYY-MM-DDの後に時刻を表すHH:MM:SSが追加されている。 未来の時刻を表すデータフレームは、make_future_dataframeメソッドで生成するが、このとき引数freqで時間の刻みを指定する。 ここでは1時間を表す H を指定する。

climate["Date"] = pd.to_datetime(climate.date)
climate.rename(columns={"Date":"ds","temp":"y"},inplace=True)
model = Prophet().fit(climate)
future = model.make_future_dataframe(periods=200, freq="H")
forecast = model.predict(future)
model.plot(forecast);
10:06:08 - cmdstanpy - INFO - Chain [1] start processing
10:06:11 - cmdstanpy - INFO - Chain [1] done processing

因子ごとに予測値を描画すると,傾向変動と週次の季節変動の他に,日次の季節変動(1日の気温の変化)も出力される.

model.plot_components(forecast);

問題(サンフランシスコの気温データ)

以下のサンフランシスコの気温データを描画し,時間単位で予測を行え.

sf = data.sf_temps()
sf.head()
temp date
0 47.8 2010-01-01 00:00:00
1 47.4 2010-01-01 01:00:00
2 46.9 2010-01-01 02:00:00
3 46.5 2010-01-01 03:00:00
4 46.0 2010-01-01 04:00:00

傾向変化点

「上昇トレンドの株価が,下降トレンドに移った」というニュースをよく耳にするだろう.このように,傾向変動は,時々変化すると仮定した方が自然なのだ.Prophetでは,これを傾向の変化点として処理する.再び,Peyton Manningのデータを使う.

add_changepoints_to_plotを使うと、変化した点(日次)と傾向変動を図に追加して描画できる。引数は軸(axis),モデル(model),予測データフレーム(forecast)であり, 軸は図オブジェクトのgca(get current axis)メソッドで得る.

df = pd.read_csv(`http://logopt.com/data/peyton_manning.csv`)
model = Prophet().fit(df)
future = model.make_future_dataframe(periods=366)
forecast = model.predict(future)
fig = model.plot(forecast)
a = fp.add_changepoints_to_plot(fig.gca(),model,forecast);
11:27:07 - cmdstanpy - INFO - Chain [1] start processing
11:27:08 - cmdstanpy - INFO - Chain [1] done processing

変化点の数を制御するための引数は changepoint_prior_scale であり、既定値は $0.05$ である。これを増やすと変化点が増え、予測の自由度が増すため予測幅が大きくなる。

model = Prophet(changepoint_prior_scale=0.5).fit(df)
future = model.make_future_dataframe(periods=366)
forecast = model.predict(future)
fig = model.plot(forecast)
fp.add_changepoints_to_plot(fig.gca(), model, forecast);
11:28:49 - cmdstanpy - INFO - Chain [1] start processing
11:28:51 - cmdstanpy - INFO - Chain [1] done processing

傾向変化点のリストをchangepoints引数で与えることもできる。以下の例では、1つの日だけで変化するように設定している。

model = Prophet(changepoints=[`2014-01-01`]).fit(df)
future = model.make_future_dataframe(periods=366)
forecast = model.predict(future)
fig = model.plot(forecast)
fp.add_changepoints_to_plot(fig.gca(), model, forecast);
11:28:59 - cmdstanpy - INFO - Chain [1] start processing
11:28:59 - cmdstanpy - INFO - Chain [1] done processing

例題 SP500データ

株価の予測を行う.

傾向変化点の候補は自動的に設定される。既定値では時系列の最初の80%の部分に均等に設定される。これは、モデルのchangepoint_range引数で設定する. この例では,期間の終わりで変化点を設定したいので,0.95に変更する.

年次の季節変動の変化の度合いは、yearly_seasonality(既定値は $10$) で制御できる。この例では,このパラメータを $5$ に変更することによって年間の季節変動を抑制して予測を行う.

sp500 = data.sp500()
sp500.tail()
date price
118 2009-11-01 1095.63
119 2009-12-01 1115.10
120 2010-01-01 1073.87
121 2010-02-01 1104.49
122 2010-03-01 1140.45
sp500.rename(inplace=True,columns={"date":"ds","price":"y"})
model = Prophet(changepoint_prior_scale=0.5, changepoint_range=0.95,yearly_seasonality=5).fit(sp500)
future = model.make_future_dataframe(periods=200, freq=`D`)
forecast = model.predict(future)
model.plot(forecast);
11:30:30 - cmdstanpy - INFO - Chain [1] start processing
11:30:30 - cmdstanpy - INFO - Chain [1] done processing
model.plot_components(forecast);
from prophet.plot import add_changepoints_to_plot
fig = model.plot(forecast)
a = add_changepoints_to_plot(fig.gca(), model, forecast)

例題: 個別銘柄の株価の予測

stocksデータでは,symbol列に企業コードが入っている.

  • AAPL アップル
  • AMZN アマゾン
  • IBM IBM
  • GOOG グーグル
  • MSFT マイクロソフト

まずは可視化を行う。

stocks = data.stocks()
stocks.tail()
symbol date price
555 AAPL 2009-11-01 199.91
556 AAPL 2009-12-01 210.73
557 AAPL 2010-01-01 192.06
558 AAPL 2010-02-01 204.62
559 AAPL 2010-03-01 223.02
fig = px.line(stocks,x="date",y="price",color="symbol")
plotly.offline.plot(fig);

以下では,マイクロソフトの株価を予測してみる.

msft = stocks[ stocks.symbol == "MSFT"]
msft.head()
symbol date price
0 MSFT 2000-01-01 39.81
1 MSFT 2000-02-01 36.35
2 MSFT 2000-03-01 43.22
3 MSFT 2000-04-01 28.37
4 MSFT 2000-05-01 25.45
msft = msft.rename(columns={"date":"ds","price":"y"})
msft.head()
symbol ds y
0 MSFT 2000-01-01 39.81
1 MSFT 2000-02-01 36.35
2 MSFT 2000-03-01 43.22
3 MSFT 2000-04-01 28.37
4 MSFT 2000-05-01 25.45
model = Prophet(changepoint_prior_scale=0.5, changepoint_range=0.95,yearly_seasonality=5).fit(msft)
future = model.make_future_dataframe(periods=200, freq=`D`)
forecast = model.predict(future)
model.plot(forecast);
11:33:31 - cmdstanpy - INFO - Chain [1] start processing
11:33:31 - cmdstanpy - INFO - Chain [1] done processing
model.plot_components(forecast);

問題(株価)

上の株価データのマイクロソフト以外の銘柄を1つ選択し,予測を行え.

stocks = data.stocks()
stocks.head()
symbol date price
0 MSFT 2000-01-01 39.81
1 MSFT 2000-02-01 36.35
2 MSFT 2000-03-01 43.22
3 MSFT 2000-04-01 28.37
4 MSFT 2000-05-01 25.45

発展編

以下では,Prophetの高度な使用法を解説する.

ロジスティック曲線による予測

Prophetによる予測の既定値は線形モデルであるが、ロジスティック曲線を用いることもできる。これによって,上限や下限に漸近する時系列データの予測を行うことができる.

上限を規定するためには、データフレームのcap列に上限値(容量(capacity)の略でcap)を入力する。(下限値を設定する場合には、floor列に下限値を入力する.) これは行(データ)ごとに設定しなければならない.

次いで、引数growthlogisticに設定してProphetモデルを生成すると、ロジスティック曲線に当てはめを行う。

logistic = pd.read_csv("http://logopt.com/data/logistic.csv")
model = Prophet(growth="logistic") 
logistic["cap"] = 8.5 
model.fit(logistic)    
future = model.make_future_dataframe(periods=1826)
future[`cap`] = 8.5
forecast = model.predict(future)
model.plot(forecast);                      
11:34:53 - cmdstanpy - INFO - Chain [1] start processing
11:34:53 - cmdstanpy - INFO - Chain [1] done processing

外れ値の影響

外れ値(outlier)を除去すると予測の精度が向上する場合がある。以下の例では、2010年あたりに大きな変化があるため、予測の幅が広がっている。

outliers1 = pd.read_csv("http://logopt.com/data/outliers1.csv")
model = Prophet()
model.fit(outliers1)
future = model.make_future_dataframe(periods=1096)
forecast = model.predict(future)
model.plot(forecast);
11:35:45 - cmdstanpy - INFO - Chain [1] start processing
11:35:47 - cmdstanpy - INFO - Chain [1] done processing

2010年のデータを除外することによって、予測が改善される。

outliers1.loc[(outliers1[`ds`] > `2010-01-01`) & (outliers1[`ds`] < `2011-01-01`), `y`] = None
model =Prophet()
model.fit(outliers1)
forecast = model.predict(future)
model.plot(forecast);
11:35:52 - cmdstanpy - INFO - Chain [1] start processing
11:35:52 - cmdstanpy - INFO - Chain [1] done processing

上では、外れ値を除外することによって予測が改善されたが、これがいつでも成立するとは限らない。以下の例では2015年6月に外れ値が観察される。 

outliers2 = pd.read_csv("http://logopt.com/data/outliers2.csv")
model = Prophet()
model.fit(outliers2)
future = model.make_future_dataframe(periods=1096)
forecast = model.predict(future)
model.plot(forecast);
11:36:21 - cmdstanpy - INFO - Chain [1] start processing
11:36:21 - cmdstanpy - INFO - Chain [1] done processing

上の予測では2015年6月のデータを予測に用いず,外れ値として処理しているので,外れ値を除外すると予測の幅が広がる。

outliers2.loc[(outliers2[`ds`] > `2015-06-01`) & (outliers2[`ds`] < `2015-06-30`), `y`] = None
model = Prophet()
model.fit(outliers2)
future = model.make_future_dataframe(periods=1096)
forecast = model.predict(future)
model.plot(forecast);
11:36:26 - cmdstanpy - INFO - Chain [1] start processing
11:36:27 - cmdstanpy - INFO - Chain [1] done processing

休日(特別なイベント)を考慮した予測

休日や特別なイベントをモデルに追加することを考える。そのためには、holidayds(datestamp)を列名としたデータフレームを準備する必要がある。holiday列にはイベント名を、dsにはそのイベントが発生する日時を入力する。

以下では、holiday 列に superbowlplayoff の2種類を入れる。

また、イベントの影響が指定した日時の前後何日まで影響を与えるかを示す2つの列lower_windowupper_windowを追加することができる。

例としてPeyton Manningの例題のデータを用いる.

df = pd.read_csv(`http://logopt.com/data/peyton_manning.csv`)
df.head()
ds y
0 2007-12-10 9.590761
1 2007-12-11 8.519590
2 2007-12-12 8.183677
3 2007-12-13 8.072467
4 2007-12-14 7.893572
playoffs = pd.DataFrame({
  `holiday`: `playoff`,
  `ds`: pd.to_datetime([`2008-01-13`, `2009-01-03`, `2010-01-16`,
                        `2010-01-24`, `2010-02-07`, `2011-01-08`,
                        `2013-01-12`, `2014-01-12`, `2014-01-19`,
                        `2014-02-02`, `2015-01-11`, `2016-01-17`,
                        `2016-01-24`, `2016-02-07`]),
  `lower_window`: 0,
  `upper_window`: 1,
})
superbowls = pd.DataFrame({
  `holiday`: `superbowl`,
  `ds`: pd.to_datetime([`2010-02-07`, `2014-02-02`, `2016-02-07`]),
  `lower_window`: 0,
  `upper_window`: 1,
})
holidays = pd.concat((playoffs, superbowls))
holidays.head()
holiday ds lower_window upper_window
0 playoff 2008-01-13 0 1
1 playoff 2009-01-03 0 1
2 playoff 2010-01-16 0 1
3 playoff 2010-01-24 0 1
4 playoff 2010-02-07 0 1

引数holidaysで休日を表すデータフレームを与えることによって、特別なイベントを考慮した予測を行うことができる。

model= Prophet(holidays=holidays)
model.fit(df)
future = model.make_future_dataframe(periods=365)
forecast = model.predict(future)
11:37:41 - cmdstanpy - INFO - Chain [1] start processing
11:37:42 - cmdstanpy - INFO - Chain [1] done processing

プレーオフやスーパーボールなどのイベント効果がある日だけ抜き出してデータフレームを表示する.

forecast[(forecast[`playoff`] + forecast[`superbowl`]).abs() > 0][
        [`ds`, `playoff`, `superbowl`]][-10:]
ds playoff superbowl
2190 2014-02-02 1.231269 1.189638
2191 2014-02-03 1.900381 1.461279
2532 2015-01-11 1.231269 0.000000
2533 2015-01-12 1.900381 0.000000
2901 2016-01-17 1.231269 0.000000
2902 2016-01-18 1.900381 0.000000
2908 2016-01-24 1.231269 0.000000
2909 2016-01-25 1.900381 0.000000
2922 2016-02-07 1.231269 1.189638
2923 2016-02-08 1.900381 1.461279

因子別に描画を行うと,イベントによって変化した量が描画される(上から2番目).

model.plot_components(forecast);

国(州)別の休日

add_country_holidaysを用いて,各国(州)の休日データを追加することができる。日本のデータもあるが、天皇誕生日がずれていたりするので、注意を要する。

model = Prophet(holidays=holidays)
model.add_country_holidays(country_name=`US`)
model.fit(df)
11:38:05 - cmdstanpy - INFO - Chain [1] start processing
11:38:05 - cmdstanpy - INFO - Chain [1] done processing
<prophet.forecaster.Prophet at 0x7f8ee9460580>

追加された休日名をtrain_holiday_names属性で確認しておく.

model.train_holiday_names
0                         playoff
1                       superbowl
2                  New Year's Day
3      Martin Luther King Jr. Day
4           Washington's Birthday
5                    Memorial Day
6                Independence Day
7                       Labor Day
8                    Columbus Day
9                    Veterans Day
10                   Thanksgiving
11                  Christmas Day
12       Christmas Day (Observed)
13        Veterans Day (Observed)
14    Independence Day (Observed)
15      New Year's Day (Observed)
dtype: object

米国の休日を考慮して予測を行い,因子別に描画してみる.上から2番目が,休日に対する影響を表している.

forecast = model.predict(future)
model.plot_components(forecast);

予測因子の追加

add_regressorメソッドを用いると、モデルに因子を追加できる。以下の例では、オンシーズンの日曜日にだけ影響がでる因子を追加している。

 def nfl_sunday(ds):
   date = pd.to_datetime(ds)
   if date.weekday() == 6 and (date.month > 8 or date.month < 2):
     return 1
   else:
     return 0
df = pd.read_csv(`http://logopt.com/data/peyton_manning.csv`)
df[`nfl_sunday`] = df[`ds`].apply(nfl_sunday)
model = Prophet()
model.add_regressor(`nfl_sunday`)
model.fit(df)
future = model.make_future_dataframe(periods=365)
future[`nfl_sunday`] = future[`ds`].apply(nfl_sunday)
forecast = model.predict(future)
model.plot_components(forecast); 
11:38:22 - cmdstanpy - INFO - Chain [1] start processing
11:38:22 - cmdstanpy - INFO - Chain [1] done processing

ユーザーが設定した季節変動

Prophetでは既定値の年次や週次の季節変動だけでなく、ユーザー自身で季節変動を定義・追加できる。以下では、週次の季節変動を除き,かわりに周期が30.5日の月次変動をフーリエ次数(seasonalityの別名)5として追加している。

model = Prophet(weekly_seasonality=False)
model.add_seasonality(name=`monthly`, period=30.5, fourier_order=5)
model.fit(df)
future = model.make_future_dataframe(periods=365)
forecast = model.predict(future)
model.plot_components(forecast);
11:38:38 - cmdstanpy - INFO - Chain [1] start processing
11:38:38 - cmdstanpy - INFO - Chain [1] done processing

他の要因に依存した季節変動

他の要因に依存した季節変動も定義・追加することができる。以下の例では、オンシーズンとオフシーズンごと週次変動を定義し、追加してみる。

def is_nfl_season(ds):
    date = pd.to_datetime(ds)
    return (date.month > 8 or date.month < 2)
df[`on_season`] = df[`ds`].apply(is_nfl_season)
df[`off_season`] = ~df[`ds`].apply(is_nfl_season)
model = Prophet(weekly_seasonality=False)
model.add_seasonality(name=`weekly_on_season`, period=7, fourier_order=3, condition_name=`on_season`)
model.add_seasonality(name=`weekly_off_season`, period=7, fourier_order=3, condition_name=`off_season`)
model.fit(df)
future = model.make_future_dataframe(periods=365)
future[`on_season`] = future[`ds`].apply(is_nfl_season)
future[`off_season`] = ~future[`ds`].apply(is_nfl_season)
forecast =model.predict(future)
model.plot_components(forecast);
11:38:48 - cmdstanpy - INFO - Chain [1] start processing
11:38:49 - cmdstanpy - INFO - Chain [1] done processing

休日と季節変動の効果の調整法

休日の影響を抑制するためには、holidays_prior_scaleを小さくすれば良い。このパラメータの既定値は $10$ であり、これはほとんど正則化を行わないことを意味する。 一般に, prior_scale を大きくするとそのパラメータの柔軟性が増し,小さくすると柔軟性が減る.以下では,holidays_prior_scaleを $0.05$ に設定して予測を行う.

df = pd.read_csv(`http://logopt.com/data/peyton_manning.csv`)
model= Prophet(holidays=holidays, holidays_prior_scale=0.05)
model.fit(df)
future = model.make_future_dataframe(periods=365)
forecast = model.predict(future)

forecast[(forecast[`playoff`] + forecast[`superbowl`]).abs() > 0][
    [`ds`, `playoff`, `superbowl`]][-10:]
11:39:23 - cmdstanpy - INFO - Chain [1] start processing
11:39:24 - cmdstanpy - INFO - Chain [1] done processing
ds playoff superbowl
2190 2014-02-02 1.203527 0.970955
2191 2014-02-03 1.851203 0.993308
2532 2015-01-11 1.203527 0.000000
2533 2015-01-12 1.851203 0.000000
2901 2016-01-17 1.203527 0.000000
2902 2016-01-18 1.851203 0.000000
2908 2016-01-24 1.203527 0.000000
2909 2016-01-25 1.851203 0.000000
2922 2016-02-07 1.203527 0.970955
2923 2016-02-08 1.851203 0.993308

スーパーボール(superbowl)の効果が抑制されていることが見てとれる. 同様に,季節変動の影響はseasonality_prior_scaleを小さくすることによって抑制できる。

不確実性の幅

Prophetは既定では傾向変動に対する不確実性の幅を予測する。このとき、引数interval_widthで予測の幅を設定できる。既定値は $0.8$ である。このパラメータを大きくすると幅が広がり、小さくすると幅が狭くなることが確認できる。

例として $CO_2$ 排出量のデータを用いる.

co2 = data.co2_concentration()
co2.rename(columns={"Date":"ds","CO2":"y"},inplace=True)
co2.head()
ds y
0 1958-03-01 315.70
1 1958-04-01 317.46
2 1958-05-01 317.51
3 1958-07-01 315.86
4 1958-08-01 314.93
model = Prophet(interval_width=0.95)
model.fit(co2)
future = model.make_future_dataframe(periods=200, freq=`M`)
forecast = model.predict(future)
model.plot(forecast);
11:39:35 - cmdstanpy - INFO - Chain [1] start processing
11:39:35 - cmdstanpy - INFO - Chain [1] done processing
forecast = Prophet(interval_width=0.5).fit(co2).predict(future)
model.plot(forecast);
11:39:37 - cmdstanpy - INFO - Chain [1] start processing
11:39:37 - cmdstanpy - INFO - Chain [1] done processing

季節変動に対する不確実性を予測するためには、マルコフ連鎖モンテカルロ法を行う必要がある、そのためには、引数 mcmc_samples をシミュレーションの反復回数に設定する。 このパラメータの既定値は $0$ である。

これによって、既定値の最大事後確率(MAP)推定の代わりにマルコフ連鎖モンテカルロ法によるサンプリングが行われる。これは、非常に時間がかかることもある。 要因別に図を描画してみると、季節変動に対しても不確実性の幅が示されていることが確認できる。

model = Prophet(mcmc_samples=100)
forecast = model.fit(co2).predict(future)
model.plot_components(forecast);
11:41:11 - cmdstanpy - INFO - CmdStan installation /Users/mikiokubo/Library/Caches/pypoetry/virtualenvs/analytics-v-sH3Dza-py3.8/lib/python3.8/site-packages/prophet/stan_model/cmdstan-2.26.1 missing makefile, cannot get version.
11:41:11 - cmdstanpy - INFO - Cannot determine whether version is before 2.28.
11:41:11 - cmdstanpy - INFO - CmdStan start processing
                                                                                                                                                                                                                                                                                                                                
11:41:18 - cmdstanpy - INFO - CmdStan done processing.
11:41:18 - cmdstanpy - WARNING - Non-fatal error during sampling:
Exception: normal_id_glm_lpdf: Matrix of independent variables is inf, but must be finite! (in '/Users/runner/work/prophet/prophet/python/stan/prophet.stan', line 137, column 2 to line 142, column 4)
	Exception: normal_id_glm_lpdf: Matrix of independent variables is inf, but must be finite! (in '/Users/runner/work/prophet/prophet/python/stan/prophet.stan', line 137, column 2 to line 142, column 4)
	Exception: normal_id_glm_lpdf: Scale vector is 0, but must be positive finite! (in '/Users/runner/work/prophet/prophet/python/stan/prophet.stan', line 137, column 2 to line 142, column 4)
	Exception: normal_id_glm_lpdf: Matrix of independent variables is inf, but must be finite! (in '/Users/runner/work/prophet/prophet/python/stan/prophet.stan', line 137, column 2 to line 142, column 4)
Exception: normal_id_glm_lpdf: Matrix of independent variables is inf, but must be finite! (in '/Users/runner/work/prophet/prophet/python/stan/prophet.stan', line 137, column 2 to line 142, column 4)
	Exception: normal_id_glm_lpdf: Matrix of independent variables is inf, but must be finite! (in '/Users/runner/work/prophet/prophet/python/stan/prophet.stan', line 137, column 2 to line 142, column 4)
	Exception: normal_id_glm_lpdf: Scale vector is 0, but must be positive finite! (in '/Users/runner/work/prophet/prophet/python/stan/prophet.stan', line 137, column 2 to line 142, column 4)
	Exception: normal_id_glm_lpdf: Scale vector is 0, but must be positive finite! (in '/Users/runner/work/prophet/prophet/python/stan/prophet.stan', line 137, column 2 to line 142, column 4)
Exception: normal_id_glm_lpdf: Matrix of independent variables is inf, but must be finite! (in '/Users/runner/work/prophet/prophet/python/stan/prophet.stan', line 137, column 2 to line 142, column 4)
	Exception: normal_id_glm_lpdf: Matrix of independent variables is inf, but must be finite! (in '/Users/runner/work/prophet/prophet/python/stan/prophet.stan', line 137, column 2 to line 142, column 4)
	Exception: normal_id_glm_lpdf: Scale vector is 0, but must be positive finite! (in '/Users/runner/work/prophet/prophet/python/stan/prophet.stan', line 137, column 2 to line 142, column 4)
	Exception: normal_id_glm_lpdf: Scale vector is 0, but must be positive finite! (in '/Users/runner/work/prophet/prophet/python/stan/prophet.stan', line 137, column 2 to line 142, column 4)
Exception: normal_id_glm_lpdf: Matrix of independent variables is inf, but must be finite! (in '/Users/runner/work/prophet/prophet/python/stan/prophet.stan', line 137, column 2 to line 142, column 4)
	Exception: normal_id_glm_lpdf: Matrix of independent variables is inf, but must be finite! (in '/Users/runner/work/prophet/prophet/python/stan/prophet.stan', line 137, column 2 to line 142, column 4)
	Exception: normal_id_glm_lpdf: Scale vector is 0, but must be positive finite! (in '/Users/runner/work/prophet/prophet/python/stan/prophet.stan', line 137, column 2 to line 142, column 4)
	Exception: normal_id_glm_lpdf: Scale vector is 0, but must be positive finite! (in '/Users/runner/work/prophet/prophet/python/stan/prophet.stan', line 137, column 2 to line 142, column 4)
Consider re-running with show_console=True if the above output is unclear!
11:41:18 - cmdstanpy - WARNING - Some chains may have failed to converge.
	Chain 1 had 49 divergent transitions (98.0%)
	Chain 2 had 44 divergent transitions (88.0%)
	Chain 2 had 6 iterations at max treedepth (12.0%)
	Chain 3 had 5 divergent transitions (10.0%)
	Chain 3 had 45 iterations at max treedepth (90.0%)
	Chain 4 had 46 divergent transitions (92.0%)
	Use function "diagnose()" to see further information.

検証と誤差の評価

Prophetでは、予測の精度を検証するための仕組みが組み込まれている。例として、Peyton Manningのデータセットを用いる。 このデータセットは、全部で $2905$ 日分のデータで構成されている。

交差検証のためにはcross_validationを用いる。

引数は以下の通り.

  • model: 予測を行うモデル;事前にfitメソッドで学習しておく必要がある.
  • horizon : 計画期間(予測を行う期間)
  • period : 予測の間隔;省略するとhorizonの半分が代入される.
  • initial : 交差検証を開始する最初の期;省略するとhorizonの3倍が代入される.

以下の例では、initialが $730$ 日なので、$729$ 日までの情報を用いて、その後の $365$(horizon)日の予測を行い、本当の値との誤差を評価し、 次いで $730+180$(period)日までの情報を用いて、その後の $365$ 日の予測を行い評価し、という手順を最後の日まで繰り返す。 $(2905-730-365)/180 = 10.05$ であるので、 $11$ 回の予測を行い評価することになる。cross_validationは、交差検証用のデータフレームを返す。

最初の検証は $730$ 日後である 2010-2-15(cutoff)までのデータを用いて,2010-2-16から $365$ (horizon)日分の予測で行われ、次の検証はその $180$(period)日後である2010-08-14日から行われる。最後の検証は2015-01-20日までのデータを用いて2016-01-20日まで行われる。

df = pd.read_csv(`http://logopt.com/data/peyton_manning.csv`)
model = Prophet()
model.fit(df);
11:43:07 - cmdstanpy - INFO - Chain [1] start processing
11:43:08 - cmdstanpy - INFO - Chain [1] done processing
from prophet.diagnostics import cross_validation
df_cv = cross_validation(model, initial=`730 days`, period=`180 days`, horizon = `365 days`)
df_cv.head()
11:43:17 - cmdstanpy - INFO - Chain [1] start processing
11:43:17 - cmdstanpy - INFO - Chain [1] done processing
11:43:17 - cmdstanpy - INFO - Chain [1] start processing
11:43:17 - cmdstanpy - INFO - Chain [1] done processing
11:43:18 - cmdstanpy - INFO - Chain [1] start processing
11:43:18 - cmdstanpy - INFO - Chain [1] done processing
11:43:19 - cmdstanpy - INFO - Chain [1] start processing
11:43:19 - cmdstanpy - INFO - Chain [1] done processing
11:43:19 - cmdstanpy - INFO - Chain [1] start processing
11:43:19 - cmdstanpy - INFO - Chain [1] done processing
11:43:20 - cmdstanpy - INFO - Chain [1] start processing
11:43:20 - cmdstanpy - INFO - Chain [1] done processing
11:43:20 - cmdstanpy - INFO - Chain [1] start processing
11:43:21 - cmdstanpy - INFO - Chain [1] done processing
11:43:21 - cmdstanpy - INFO - Chain [1] start processing
11:43:21 - cmdstanpy - INFO - Chain [1] done processing
11:43:22 - cmdstanpy - INFO - Chain [1] start processing
11:43:22 - cmdstanpy - INFO - Chain [1] done processing
11:43:23 - cmdstanpy - INFO - Chain [1] start processing
11:43:23 - cmdstanpy - INFO - Chain [1] done processing
11:43:24 - cmdstanpy - INFO - Chain [1] start processing
11:43:24 - cmdstanpy - INFO - Chain [1] done processing
ds yhat yhat_lower yhat_upper y cutoff
0 2010-02-16 8.959074 8.490492 9.469220 8.242493 2010-02-15
1 2010-02-17 8.725548 8.253267 9.210082 8.008033 2010-02-15
2 2010-02-18 8.609390 8.144968 9.107764 8.045268 2010-02-15
3 2010-02-19 8.531294 8.014229 9.048185 7.928766 2010-02-15
4 2010-02-20 8.273357 7.775097 8.779778 7.745003 2010-02-15

performance_metrics を用いてメトリクス(評価尺度)を計算する。評価尺度は、 平均平方誤差(mean squared error: MSE), 平均平方誤差の平方根 (root mean squared error: RMSE), 平均絶対誤差 (mean absolute error: MAE), 平均絶対パーセント誤差 (mean absolute percent error : MAPE), yhat_loweryhat_upper の間に入っている割合(被覆率: coverage) である。

既定値では予測期間の最初の$10$%は除外して示される。これは、引数rolling_window によって変更できる。

from prophet.diagnostics import performance_metrics
df_p = performance_metrics(df_cv, rolling_window=0.1)
df_p.head()
horizon mse rmse mae mape mdape smape coverage
0 37 days 0.494752 0.703386 0.505215 0.058538 0.049584 0.058826 0.677935
1 38 days 0.500521 0.707475 0.510201 0.059115 0.049373 0.059463 0.675423
2 39 days 0.522712 0.722988 0.516284 0.059713 0.049505 0.060187 0.672682
3 40 days 0.529990 0.728004 0.519131 0.060018 0.049231 0.060561 0.673824
4 41 days 0.537478 0.733129 0.520118 0.060096 0.049373 0.060702 0.681361

評価尺度は plot_cross_validation_metricで可視化できる。以下では平均絶対パーセント誤差(MAPE)を描画している.

from prophet.plot import plot_cross_validation_metric
plot_cross_validation_metric(df_cv, metric=`mape`);

主なパラメータと既定値

以下にProphetの主要なパラメータ(引数)とその既定値を示す.

  • growth=linear :傾向変動の関数.既定値は線形.ロジスティック曲線にするにはlogisticに設定
  • changepoints=None : 傾向変更点のリスト
  • changepoint_range = $0.8$ : 傾向変化点の候補の幅(先頭から何割を候補とするか)
  • n_changepoints=25 : 傾向変更点の数
  • yearly_seasonality=auto : 年次の季節変動を考慮するか否か
  • weekly_seasonality=auto : 週次の季節変動を考慮するか否か
  • daily_seasonality=auto : 日次の季節変動を考慮するか否か
  • holidays=None : 休日のリスト
  • seasonality_prior_scale= $10.0$ : 季節変動の事前分布のスケール値(パラメータの柔軟性を表す)
  • holidays_prior_scale= $10.0$ : 休日のの事前分布のスケール値(パラメータの柔軟性を表す)
  • changepoint_prior_scale= $0.05$ : 傾向変更点の事前分布のスケール値(パラメータの柔軟性を表す)
  • mcmc_samples= $0$ : MCMC法のサンプル数
  • interval_width= $0.8$ : 不確実性の幅
  • uncertainty_samples= $1000$ : 不確実性の幅を計算する際のサンプル数