読者です 読者をやめる 読者になる 読者になる

入門 機械学習による異常検知―Rによる実践ガイド: ノート2

読んでいる本(出典): 入門 機械学習による異常検知―Rによる実践ガイド | 井手 剛 |本 | 通販 | Amazon

前回: ノート1 / 次回: ノート3
目次: 入門 機械学習による異常検知―Rによる実践ガイド

読んだページ: 23~44ページ
以下、メモと雑談。

前回までのあらすじ

  • 観測値  x' の異常度  a(x') は、正常なデータ群  \mathcal{D} の分布に対する対数尤度  a(x') = - \ln p (x' | \mathcal{D}) で測るよ。
    • 例.サイコロを10回ふって出た目の和が20以下か50以上になる確率は1%未満なので、もしサイコロをふった結果そうなってしまったらあまり尤もらしくなく、異常なのではないか(サイコロかあなたが)。
      f:id:cookie-box:20170119123936p:plain:w630
  • 異常度の閾値をどう設定するべきかについて、観測データが i.i.d. に正規分布にしたがうなら、異常度が F 分布にしたがうよ(ホテリング理論)。
    • 例.東京の2017年1月1日~18日の日ごとの平均気温は以下だった。気象庁|過去の気象データ検索
      x <- c(7.4, 7.2, 8.0, 8.5, 6.7, 4.5, 4.1, 3.9, 7.2, 8.1, 6.9, 6.0, 6.2, 2.4, 0.7, 3.2, 5.4, 5.8)
      もし1月19日の平均気温が0℃だったら異常なのかどうか考える。日ごとの平均気温が i.i.d. に正規分布にしたがうと仮定するのは気象学的には駄目かもしれないけど、ここではいいことにして、「1% も起こりそうにないこと」を異常とみなすなら、0.30℃未満か 11.06℃超が異常になる。つまり、1月19日の平均気温が0℃なら異常である。
      > qnorm(mean=mean(x), sd=sqrt(var(x)*(18-1)/18), 0.005)
      [1] 0.2975372
      > qnorm(mean=mean(x), sd=sqrt(var(x)*(18-1)/18), 0.995)
      [1] 11.05802
      ただ、上の議論では、「1月1日~18日の平均気温は正規分布から歪んでいなかったのか」が考慮されていない。そもそも18点しかサンプルがないなら、平均や分散の推定値には区間幅があるだろう。それも考慮すれば、 (x' - \hat{\mu})^2 / \hat{\sigma}^2 \sim (N+1)/(N-1) \cdot \mathcal{F}(1, N-1) が成り立つので(ホテリング理論)、以下のように正常と判定される範囲が少し広がり、-0.72℃未満か 12.08℃超が異常になる。つまり、1月19日の平均気温が0℃ならぎりぎり異常ではない。あまり0℃になってほしくはないけど。
      > F_0.01 <- qf(df1=1, df2=18-1, 0.99)
      > F_0.01
      [1] 8.39974
      > mean(x) - sqrt(var(x) * (18+1) / 18 * F_0.01)
      [1] -0.7220735
      > mean(x) + sqrt(var(x) * (18+1) / 18 * F_0.01)
      [1] 12.07763
      なお、各日の異常度とF分布上側1%の閾値ラインをプロットすると以下。1月1日~18日の平均気温に異常値はなかった。15日の日曜日は特に寒かったけど、異常ラインに達するほどではなかった。
      f:id:cookie-box:20170119155541p:plain:w320
    • 1変数のホテリング理論の証明(26~36ページ)について。

今回のお話

  • データが多次元正規分布にしたがう場合も、同様に  a(x') = (x' - \hat{\mu}) ^{\rm T} \hat{\Sigma} ^{-1} (x' - \hat{\mu}) で異常度を定義できる。
    • これもマハラノビス距離の2乗。雑記 - クッキーの日記
    • 例.東京の2017年1月1日~18日の最低/最高気温は以下だった。気象庁|過去の気象データ検索
      x1 <- c(2.0, 3.8, 3.5, 3.6, 3.7, 1.5, 0.1, 1.6, 3.8, 3.5, 3.9, 0.7, 1.4, -1.3, -2.3, -2.0, 0.7, 1.1)
      x2 <- c(13.8, 13.3, 13.7, 14.0, 10.4, 8.8, 8.7, 6.0, 11.1, 12.7, 11.0, 12.1, 12.7, 6.3, 4.7, 8.0, 10.9, 10.3)
      どの日が異常っぽいというか、あまりまとまっていない…。
      f:id:cookie-box:20170119234027p:plain:w280
      とりあえず各日の異常度を求める。
      mu <- colMeans(cbind(x1, x2))
      xc <- cbind(x1, x2) - matrix(1, 18, 1) %*% mu
      sigma <- t(xc) %*% xc / 18
      a <- rowSums( (xc %*% solve(sigma) ) * xc)
      a <- a * (18 - 2) / ((18 + 1) * 2)
      th <- qf(df1=2, df2=18-2, 0.99)
      案の定、1%基準で異常ラインに達した日はなかった。特に寒かった15日よりも、上図においてデータ点が対角線から離れ気味の(最低気温と最高気温の差が他の日付に比べてかなり小さい)8日の方が異常度が微妙に大きかった。
      f:id:cookie-box:20170119235635p:plain:w400