異常検知手法のお勉強記事ができるまで その2
その1の続きです。
前回までのお話
ブログのアクセス数推移をネタに異常検知をやってみよう。
ARIMA+回帰で予測して、予測値と実測値の差を評価したら良いんじゃなかろうか。
早速実装してみる
データ作成
細かいコードは完結後にQiitaにでも書くとして、やろうとしていることはこういう感じです。まず、アクセス数データと今回考慮すべき休祝日データを引っ付けて準備します。
こんなイメージ。
もし予測精度を求められるような場合(予測自体が重要な場合とかKaggleやオプトのコンペとか)はここで休祝日以外にもひたすら変数を作り込んだりします。
利用データ定義
どのぐらいのデータを使って何日先を予測するか、というのを決めていきます。
1年分とかを学習データにしてしまうとそれだけデータ貯まらないと使えないという応用しづらいロジックになりそうですし、7日分ぐらいで学習したところで一週間前の数値がそのまま出るだけのモデルになりそうです。
ということで1ヶ月分にして見ます。7日周期性が気になるので正確には28日分にしておきます。
予測範囲は次の日でいいですね。
余談ですが、予想なんて当然次の日(次の1コマ)が対象だろうと思われるかもしれませんが、ビジネスの現場では「いや翌日の予測値出されてもすぐには使えないので三日後の予測値をピンポイントでくれ」みたいなこともよくあります。
あと「数ヶ月後の予測値出してくれ」と言われてぐぬぬ、、、とか。
予測実行してみる
細かいコードは完成後に(略
model <- arima(data.ts[,1], ←時系列のデータ列
order = c(7,1,7), ←モデルのパラメータ
method = "ML",
xreg = data.ts[,2], ←休祝日のデータ列
seasonal = list(order=c(0,0,0), period=NA))
R言語の例ですが、関数一発でできちゃうので非常に楽です。
図は実測(黒)&予測(青)のグラフですが、実際には28日分のデータから翌日の点予測して、ズラしてまた予測して、ズラして、というのを繰り返します。
正直、思った以上に予測できてるなぁ、ARIMAすげぇ、という印象。
祝日部分もちゃんと考慮されてるっぽい予測値になっています。
異常部分を定義する
さて、予測値が出せたところでどのくらい差があれば異常とみなすかを考えます。
・単純に差をとる
→ アクセスが少ない期間と多い期間があるので閾値が決めにくい
・誤差割合でみる
→ これもその日のアクセス数によってだいぶ意味合いが変わりそう。
・標準偏差を利用しよう
→ 予測モデルでは予測値に加えその点での標準偏差σを返してくれます。予測区間とか考えるときに必要なやつ。
ということで、時系列モデル難しくて理解が怪しいところも多々ありますが、3σを閾値にすれば1%ぐらいで起こりうる事象に線引きできそうです。
図はさらに閾値絞って、実測値が予測区間7σ以上離れていれば異常としてピックアップしたもの。
ちょっと拡大率はあれですが、なかなか?良い?ではありませんか?(混乱)
(本音:一発目でここまでできるとは思わなんだ)
目に見えてアクセス数が跳ねているところは捉えられていそうです。
面白いのは急激に跳ねている部分の後は多少数値が荒れても異常として検知しない期間があります。学習期間の変動幅がでかくなりすぎて予測区間が広くなったせいでしょう。しかしそれは都合が良い気がします。実際、一度大きく跳ねた後に多少荒れるのは人間の感覚的にも新たな異常ではなく異常が収束する過程と捉えそうな気がします。(全部異常としたいということなら別手法がいるかな)
また、変化度合いが少なくても学習期間部分が一定なら検知されています。いい感じ。
あ、あれ?もうこれでよくない?という思いにかられながらも、次回以降もうちょっと頑張ります。
おまけというかTODO
時系列モデルは結構難しいので理解が追いついてないところが多いです。
このあたりを参考にどうぞ。
(結構できてないとこある気がするなぁ)