Prophet を使って問い合わせの件数を予想したはなし

この記事は [SmartHR Advent Calendar 2019](https://qiita.com/advent-calendar/2019/smarthr) の記事です。

こんにちは。SmartHRでサポートをしている狩野です。
今回は Facebook が公開している時系列予測機械学習ライブラリ Prophet を利用して問い合わせの件数を予測した話を書きます。


## そもそも課題

SmartHRでは、プロダクトのお問い合わせをチャットで受け付けております。そのお問い合わせの件数を予測することが課題でした。

特に年末調整時期は、件数が多くなり、いつ頃？どのくらい件数になるか？を知ることは、グループの人員計画や、メンバーのシフトを作成する上で極めて重要な情報です。

Excel 等のツールでモデルを作成して予測することもできますが、
複雑な数式、随時更新されるパラメータを保守し、精度を上げていくためには結構パワーがかかりそうでした。

いろいろ調べてみると、Facebook が提供している時系列分析ライブラリがあり、python か R の知識とドメイン知識があれば、簡単に予測ができるということで試してみました。


## Prophet について
Prophet は Facebook が公開しているオープンソースの時系列解析ライブラリです。

本家サイト
[Prophet \| Prophet is a forecasting procedure implemented in R and Python\. It is fast and provides completely automated forecasts that can be tuned by hand by data scientists and analysts\.](https://facebook.github.io/prophet/)

公式ドキュメント
[Quick Start \| Prophet](https://facebook.github.io/prophet/docs/quick_start.html)

ざっくり概要をまとめると、教師となる時系列データから、機械学習で周期性を割り出し、その精度を検証するためのツールまでがセットになったライブラリです。

公式ドキュメントは英語なのですが、和訳をしてくださっている人もいて、ありがたく参照させていただきました。


[時系列解析ライブラリProphet　公式ドキュメント翻訳１（概要＆特徴編）　 \- Qiita](https://qiita.com/japanesebonobo/items/96868e58d4da42d36807)

## 予測をはじめる

予測にあたってはあらかじめ欲しい状態を決めました。すなわち「日次・週次・月次の問い合わせを件数を予測する」です。

予測データの作成にあたっては Python (Jupyter notebook)を利用しました。


### Prophet のインストール
Python を利用している人には、インストールはとても簡単です。

```
# bash
$ pip install fbprophet
```

これでインストールが完了し、すぐ使い始めることができます。本当にすごい。


### 教師データを準備する
インストールが終わったら、教師データの準備です。
Prophet の教師データは DataFrame の形式で準備をする必要があります。

必要な項目は、`ds`, `y` の2つです。
Ds は集計の基準となる Date もしくは Timestamp の情報、Y は実績値です。

今回は、日次の予測結果が欲しかったので、ds に問い合わせを対応した日、y にその日に対応した問い合わせの件数が格納された DataFrame を準備しました。

![qiita1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547992/5a793d3c-7c51-2680-a8f6-cfeac0a4860f.png)

お問い合わせのデータは、BigQuery に格納されているため、日次の件数を Pandas で取得できるようにしました。便利！

```
import nampy as np
import pandas as pd

query = ‘’’
{sqlを書いてね！}
‘’’

cnv = pd.read_gbq(query, '{bigquery-project-id}', dialect='standard')
```


### モデルを設定する
続いては Prophet のモデルを設定していきます。

設定できるパラメータがいくつかあるのですが、初期設定の周期に加え、年間の周期、月間の周期を追加しました。

また、その日の問い合わせ件数が 0 であることはあっても マイナスになることはないので、下限値 floor と上限値 cap を追加しました。

下限値、上限値を設定する場合は、増加曲線を直線 `liner`
ではなく回帰線 `logistic` に設定する必要があります。

実際のコードにすると以下のような感じになります。
実行すると `forecast` に予測結果が格納された DataFrame が作成されます。

```
# ライブラリの読み込む
from fbprophet import Prophet

m = Prophet(
     growth='logistic' #増加を回帰的にさせる
    ,yearly_seasonality=True #年間の周期性を有効にする
)

# 月間の周期性を追加する
m.add_seasonality(name='monthly', period=30.5, fourier_order=5) 

pp['cap'] = 400	#1日あたりの上限
pp['floor'] = 0	#1日あたりの下限

m.fit(pp)

# 将来日付の DataFrame を作成する、次の繁忙期が常に予測に入るようにするために 14ヶ月先(427日後)まで予測する
future = m.make_future_dataframe(periods=427)
future['cap'] = 400	#1日辺りの上限
future['floor'] = 0	#1日辺りの下限

forecast= m.predict(future)
```


### 予測データの検証
Prophet には作成したモデルの精度を検証するためのライブラリが入っています。
モデルの検証方法は交差検証です。

交差検証とは、実績のある期間を複数に区切ってモデルの検証を行うことで、少ない教師データであっても精度の高い検証を行うためのやり方です。

<a href="https://aizine.ai/glossary-crossvalidation/">機械学習モデルの性能評価入門！交差検証（クロスバリデーション）とは | AIZINE（エーアイジン）</a>

Prophet では以下のようなコードで簡単に実行できる。

```
from fbprophet.diagnostics import cross_validation
from fbprophet.diagnostics import performance_metrics

df_cv = cross_validation(m, horizon='182 days')
df_p = performance_metrics(df_cv)
df_p
```


時系列分析で使われる主だった指標は自動で算出されるようになっています。

上のコードで作成される DataFrame はこちらです。

![qiita2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547992/8a77a8e6-2dfc-dcba-8ee5-9beabca6828c.png)

そのままだと評価しづらいので、 pandasの `describe()` で記述統計量を取得し評価します。

```
df_p.describe()
```

![qiita21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547992/47cfb754-acc3-b0c4-95a3-09b4b177a95a.png)

素人感覚だと、`mae` や `mape` といった指標が小さくなれば精度が高いと考えて進めましたが、
それで検証が十分かというとそうでもないらしく、この辺りは追加で勉強が必要だなーという印象でした。

[あまりに暑いので、ごく簡単に Prophet の分析の質を向上させる方法を書いた \- Qiita](https://qiita.com/s_katagiri/items/f579fe339cf7e664ea21)

### モデルの可視化

Prophet が楽しいのは、モデルを可視化してくれる点です。
予測に使用している各周期性をグラフで表示することができます。

```
m.plot(forecast)
m.plot_components(forecast)
```

![qiita3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547992/d47f4b5d-86e0-0539-4726-79891fdd1e35.png)


また、交差検証で利用した指標もグラフにすることができます。

```
from fbprophet.plot import plot_cross_validation_metric
plot_cross_validation_metric(df_cv, metric='mae')
```

![qiita33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547992/e79eec9d-320c-333b-0df4-e0204a56efad.png)

「こんな周期性で問い合わせきているんだー」など体感部分とすり合わせができ、気づかされることもありました。

### 実際の精度

予測で作成したデータを BigQuery に書き出して、予測データ、実績データを比較できるようにして利用しています。

この図は、年-月ごとに予測と実績表示するチャートです。 これとは別に日次のチャートも用意しています。

![qiita4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547992/437f44e4-147e-9103-8488-a147cc4abb5c.png)


日次で予測をした場合、その日その日ごとの増減を正確にとらえることはなかなか難しいと感じました。それは、機能リリースなどの固有イベントの有無で10件くらいは普段から増減するためと考えています。



なお、比較の期間を週間、月間とすると件数予測の精度は 90％前後となかなか悪くないと思える精度に収まっており、目安としては十分に機能しています。

今のところ、1%の誤差がそこまで影響が大きくないので、このくらいの精度でもよしと思えるが、問い合わせの件数が多くなると、もう少し精度を高める努力が必要になりそうです。

### おわりに

試した印象としては、教師データを用意してパラメータをいくつか設定すれば、予測結果を出してくれるのはやはり便利です。精度もそこそこ期待できます。
ただ、もっと精度を高めるためには、指標について理解したり、パラメータを調整したりといろいろ奥が深そうです。