Greykiteを触ってみた

8 min read読了の目安(約7800字

概要

先日、LinkedIn開発の時系列予測OSS Greykiteが公開されました。
Greykite: A flexible, intuitive, and fast forecasting library
GitHub
現状、時系列予測といえばProphetが有名だと思います。わたし自身Prophetは普段からよく使います。
Prophetと比べてどんな感じなのか気になったので触ってみました。

理論的な話はこの記事ではしません。ただのライブラリユーザとして使ってみた感じを書きます。気になる人はarxivとか見ると良いと思います。

実行環境

Ubuntu 18.04 LTS
Python 3.7.4
fbprophet==0.7.1
greykite==0.1.1
plotly==4.14.3
pystan==2.19.1.1

Greykiteのrequirementsが結構厳格で、たとえばprophetは2021/05/22現在v1.0となっており名称もfbprophetではなくprophetになっていますが、fbprophet==0.5が要求されます。
0.5は結構古くて、plot_components()がうまく行かなかったりするので0.7.1に上げています。
pystan等も似たような理由で、greykiteを入れたあとに手動でupgradeしたりしています。
気になる人はDockerなりpoetry等を活用することをお勧めします。

Notebook

notebook
本記事中のソースコード抜粋や図はこちらのNotebookから抜粋しています。コード全文を見る場合はこちらを参照してください。
基本的に、GreykiteのQuick Startをベースに手を加えています。

学習~予測まで

一番シンプルな例

ほぼQuick Startからの抜粋ですが、下記コードでdfを学習し予測まで行います。

# specify dataset information
metadata = MetadataParam(
    time_col='ts',  # name of the time column ("date" in example above)
    value_col='y',  # name of the value column ("sessions" in example above)
    freq='D'# "H" for hourly, "D" for daily, "W" for weekly, etc.
                # Any format accepted by `pandas.date_range`
)
forecaster = Forecaster()  # Creates forecasts and stores the result
result = forecaster.run_forecast_config(  # result is also stored as `forecaster.forecast_result`.
    df=df,
    config=ForecastConfig(
        model_template=ModelTemplateEnum.SILVERKITE.name,
        forecast_horizon=365,  # forecasts N steps ahead
        coverage=0.95,         # 95% prediction intervals
        metadata_param=metadata
    )
)

良いところは、 forecaster.run_forecast_config()を実行した時点でCVとGrid Searchが実行される点です。
自分で色々書かなくてもよしなにやってくれるのはありがたいですね。
ForecastConfig内のmodel_templateで、使用するモデルを選択します。大雑把に言うとSilverkiteか、Prophetかを選べます。上記コードの場合はSilverkiteを選択しています。
SilverkiteはLinkedInが開発した独自の時系列予測アルゴリズムです。

Fitting Algorithmの選択

Silverkiteを使用する場合、モデルを指定することができます。デフォルトは Ridge(sklearnのRidgeCV) が使われます。
詳細はCustom Parametersで確認できます。
たとえば、GBDTを使いたい場合下記のように設定します。(sklearnのGradientBoostingRegressorが使われます)

forecaster = Forecaster()  # Creates forecasts and stores the result
result = forecaster.run_forecast_config(  # result is also stored as `forecaster.forecast_result`.
    df=df,
    config=ForecastConfig(
        model_template=ModelTemplateEnum.SILVERKITE.name,
        forecast_horizon=365,  # forecasts N steps ahead
        coverage=0.95,         # 95% prediction intervals
        metadata_param=metadata,
        model_components_param=ModelComponentsParam(
            custom=dict(fit_algorithm_dict=dict(fit_algorithm="gradient_boosting"))
        )
    )
)

上記ページに記載されているとおり、リストで渡すことでGrid Searchに含めることもできます。便利ですね。
Fitting Algorithmに限らずいろんなパラメータをここで指定できます。

予測結果の可視化

可視化は基本的にPlotlyです。今風ですね。

backtest = result.backtest
fig = backtest.plot()
plotly.io.show(fig)


Prophetのplot_componentsと同様に、トレンド、周期性、イベント効果の各要素を分解して可視化する機能もあります。

fitting algorithmにRandomForestやGBDTを選択した場合使用できません。

forecast = result.forecast
fig = forecast.plot_components()
plotly.io.show(fig) 

Prophetとの違い

公式Documentを見る

Docにて、モデルの違いが説明されています。
Choose a model
まず一番のポイントはProphetより早いってことでしょうか。それ以外では、状況・要件に応じてSilverkiteとProphetを好きに選ぼうって感じですね。
共通のインターフェースでProphetもSilverkiteも使えるってのは使い勝手良いと思います。

seasonality_mode=multiplicativeがない

個人的にSilverkiteを触っていて一番気になった点です。
Prophetで結構好きな機能で、周期性が加算的ではなく乗算的なときにworkします。詳細はProphetのページを参照してください。
有名な例として、AirPassengersを使ってみましょう。
dataset
月単位の航空機の乗客数で、見ての通りトレンドの上昇に合わせて周期性も大きくなっていることがわかります。

こちらをSilverkiteで学習、予測してみます。

# specify dataset information
metadata = MetadataParam(
    time_col="Month",  # name of the time column ("date" in example above)
    value_col="#Passengers",  # name of the value column ("sessions" in example above)
    freq="MS"# "H" for hourly, "D" for daily, "W" for weekly, etc.
                # Any format accepted by `pandas.date_range`
)

forecaster = Forecaster()  # Creates forecasts and stores the result
result = forecaster.run_forecast_config(  # result is also stored as `forecaster.forecast_result`.
    df=df,
    config=ForecastConfig(
        model_template=ModelTemplateEnum.SILVERKITE.name,
        forecast_horizon=12,  # forecasts N steps ahead
        coverage=0.95,         # 95% prediction intervals
        metadata_param=metadata
    )
)
backtest = result.backtest
fig = backtest.plot()
plotly.io.show(fig)

forecast = result.forecast
fig = forecast.plot_components()
plotly.io.show(fig)


だめそうですね。component plotを見る限り、周期性をうまく捉えられていないようです。
ちなみにfitting algorithmをgradient_boostingに変えても、なんかoverfitはするものの同様にうまくいきません。

forecaster = Forecaster()  # Creates forecasts and stores the result
result = forecaster.run_forecast_config(  # result is also stored as `forecaster.forecast_result`.
    df=df,
    config=ForecastConfig(
        model_template=ModelTemplateEnum.SILVERKITE.name,
        forecast_horizon=12,  # forecasts N steps ahead
        coverage=0.95,         # 95% prediction intervals
        metadata_param=metadata,
        model_components_param=ModelComponentsParam(
            custom=dict(fit_algorithm_dict=dict(fit_algorithm='gradient_boosting'))
        )
    )
)
backtest = result.backtest
fig = backtest.plot()
plotly.io.show(fig)


このようなデータの場合、Prophetをseasonality_mode=multiplicativeで実行することでうまくいきます。

forecaster = Forecaster()  # Creates forecasts and stores the result
result = forecaster.run_forecast_config(  # result is also stored as `forecaster.forecast_result`.
 df=df,
 config=ForecastConfig(
     model_template=ModelTemplateEnum.PROPHET.name,
     forecast_horizon=12,  # forecasts 365 steps ahead
     coverage=0.95,         # 95% prediction intervals
     metadata_param=metadata,
     model_components_param=ModelComponentsParam(
         seasonality=dict(seasonality_mode='multiplicative')
     )
 )
)
backtest = result.backtest
fig = backtest.plot()
plotly.io.show(fig)

forecast = result.forecast
fig = forecast.plot_components()
fig.show()


状況に合わせて適切なモデルを選択しましょう、ってことですね。
(Silverkiteでもできるよ!って場合こっそり教えてください、、、わたしが調べた限りでは無理そうでした)

まとめ

  • GreykiteはモデルにSilverkiteもProphetも選べる便利なインターフェース
  • Silverkite VS Prophet というよりはデータに合わせて適切なモデルを検討できて便利
  • CV勝手に切ってくれるのも嬉しい
  • よしなに力とカスタマイズ性が両立していて使い勝手よさそう

総じて好印象でした。(なんか上から目線だな、、)今後も使ってみようと思います。
以上、読んでいただいてありがとうございました。