にほんごのれんしゅう

日本語として伝えるための訓練を兼ねたテクログ

機械学習のスタックしていた案件をFacebook Prophetで3日で返済した話

機械学習のスタックしていた案件をFacebook Prophetで3日で返済した話

背景

  • 広告代理店業を行なっており、クライアント企業から予算を預かって、インターネット広告やマーケティング業をしているのだが、クライアントの予算消化の異常値を監視したい
  • 2016年半ばに外部のデータ分析専門の会社に、その日の予算消化が異常の場合、アラートを鳴らすシステムを外注開始、2016年10月に納品
  • 2017年9月半ばに進捗率が芳しくないことが判明した。終わる見込みが立たなかったので、私が解決に当たる  

(ついでに"Machine Learning: The High-Interest Credit Card of Technical Debt[2]"と呼ばれる負債化してしまう機械学習のシステムとはという評価軸があったので、これらから今回使えそうなプラクティスを取り出して適応してみたいというモチベーションがあった

さらに、開発速度感が出なかったのでUberが採用したMicroserviceのアーキテクチャの粒度に還元できるように再設計し、システムやアルゴリズム、データ収集などに対し横断的であった機械学習チームの役割を明示的に、httpでjsonで命令を受けると、jsonを返すだけのモジュールの開発に限定することで、スコープを機械学習アルゴリズムの開発・チューニングに絞った。)

全体の進捗感

  • 9/19で既存システムのサーベイランスと、問題点の洗い出し
  • 9/20でより使い勝手が良い設計へ変更、プロトタイプコーディング開始
  • 9/21でプロトタイプコーディング終了し、仕様要件の微細な差を共通認識をとり、私の手を離れて、本番実装・保守チームに移管完了(彼らはAPIのIFを付け加えるてテストを通し、デプロイし、最終的に運用に渡す)

既存システム

既存システムは個々のクライアントの予算消化を監視して、異常に多すぎる予算消化になる場合や、少なすぎる場合にアラートを鳴らすというシステムであった  

STLと呼ばれるRの周期成分分解するライブラリで、周期成分を消すとポアソン分布や正規分布に予算消化の幅が従うことから、ARIMAモデルで自己回帰で導出できるとしていたモデルで、自己回帰の予想した線からの乖離を標準偏差で表現して、異常値を定義するシステムであった。  

問題点

  • 当時、最先端とされていたAWSのデータパインプラインやそれに関するサブシステムを動員しており、できることが予算消化の異常値検知だけなはずなのだが、かなり大規模なシステムになっていた。  
  • 開発状況も、意思決定層にヒアリングしていた内容よりだいぶ未達であることがわかり、仮にこれを完成させて運用したとしても、多くの人間の労働力が消えていくと考えられ、根本的に見直す必要があった。  
  • STLを適用してARIMAで学習して異常値を検出するのに、計算時間がAWSのCPU最適化インスタンスで12時間と、スケールアウト性にも疑問があった。  
  • 弊チームは技術部に属しており、Pythonを使える人間は多くいるが、Rを十分に使える人は一人もいなく、誰も新人にアドバイスできず、放置状態であり、教育にもよくなかった。  
  • 評価指標がARIMAで導出した予想系列からの乖離をみて、標準偏差を計算しているのが、標準偏差のどの閾値でアラートを出せばいいのか不透明であった

提案システム

まず、委託した企業の調査報告書から、売り上げの時系列変化は周期性成分を消した状態では、正規分布ポアソン分布に従うとの報告を受けていたことが判明したので、この時点で信頼区間が出せるFacebook Prophetにリプレースするという選択肢が候補にあった(つまり、正規分布の信頼区間のようなものであろうという今回の仮定と一致する)。  

Prophetは時系列予想ツールで、トレンドと呼ばれる長期的な成分と、周期性成分と、イベントのように突発的な成分を表す合成関数でフィッティングすることで、信頼区間を考慮した状態で、予想できる便利なツールである  

また、Facebook Researchの一般的な検証結果で、今回のSTL + ARIMAで行なっていた学習と同等かそれ以上の結果が得られることがわかった

図1. 提案システム

メリット

  • 一つのライブラリに収まっているのでコード量も少なく済む
  • 標準技術セットのPythonで記述で可能であり、別途Rを学ぶ必要がなく、学習コストの低減と、属人化の回避が期待できる
  • STL+ARIMAで12時間かかっていた作業が10分に短縮された
  • 信頼区間という95%はカバレッジがある領域を推定するので、残り5%を異常値とすればよく、判断が容易であり、またこれを調整するのも容易
  • 雑にAWSの安いインスタンスでcronで動かせばよく、予算も、保守チームのコストも、IFの設計を行うチームへの負担も全て軽くなる

今回のアルゴリズムである、prophetは三つの系列の予想のアルゴリズムの和で表現できるとしている  

大局的なトレンドを予想する関数

a(t)は、ステップ関数のようになっており、特定の閾値では1,0を出力する関数

kは成長係数   δはa(t)の補正係数
bはオフセット係数 γは関数を継続する役割がある

周期性を予想する関数

Cnとnを推定する問題として扱える

イベントを予想する関数

Diはイベントの集合である

これらの合成関数で表現されていて、合成関数は既存の手法であるARIMAなどに比べて精度は良いようである

オプティマイザ自体は標準では準ニュートン法のソルバが利用され、非常に収束が早い  

MAPE誤差の定量的な評価では、既存の様々な手法より誤差は少ないようである  

実装

  • 基本的に前のシステムが学習に用いていたデータセットを変更させなくても、学習と予想部分だけ変更することで対応可能であることが判明したのでそのようにした。
  • 定期的に毎日、特定のcsvがgzで圧縮された状態で特定フォルダに投入されるので、それをPandasでパースして、Prophetで学習して、前日分のデータが異常値に該当すれば、jsonに出力し、該当しなければ出力しない(出力したjsonファイルは別チームがアラートシステムに投入する)

評価とチューニング

実際のデータを見ながらオーバフィットしないように注意しつつ、薄い水色の範囲(uncertainty intervals)が、特定の閾値で設定した値以上で起こる確率の範囲である。   

つまり、これを1.0にしてしまうと、今まで起こり得た系列は全て薄い水色になってしまうし、小さくしすぎても多くのアラートが飛んでしまうので、これも実用的でない。  

想定するエンドユーザから、180日分の過去のデータをもらっており、このうちの5%ぐらいは異常値としてアラート、ないし、ワーニングを飛ばして欲しいとのことであったので、そのように対応した。  

系列のトレンドはかなりこまめに変化していることがわかり、これは、アメリカの株式市場の挙動と似ていたので(ここは定性的)、株式市場のデータを用いてパラメータチューニングを行なった。  

図2. Googleの株価のtime series

図3. AMDの株価のtime series

株価にフィッティングするパラメータチューニングを行なったところ、実運用するべき異常値検知アルゴリズムに直接適応しても、悪くない結果が得られることが判明した。

トータルコストの変化

$76(c3.large) -> $9(t2.micro) 

まとめ

機械学習で結局何がやりたいのか、何ができるのか、またそのロジックはどうなっているのか、何らか簡易的なパッケージで表現可能なのか、システムとしてその選択は適切なのかというかなりヒューリスティクな判断が必要になる(と思っている)機械学習の実運用なのですが、このように適切な意思決定さえできれば一年近く進まず人月だけをひたすら溶かしていくような案件を3日で閉じることも可能になります。  

実際には多くの方のもう終わりにすべきだという強い意志と、私のやっていることを補助していただける発言でサクサク進んだところもあるので、周りの方々には本当に感謝しています。  

機械学習を通じて何らかシステムを作りたいが、どうにもこうにも進まないという企業さんもいらっしゃると思います。知見として多くの人に共有・公開することで、この件が一助になれば幸いです。

株価でチューニングに用いたコードはこちらです

Jupyter NotebookのStock.ipynbというファイルに記されています
(試行錯誤だらけで汚いです。。。)

参考論文