先日の大学院の授業で異常検知について学習しました。ここでは、そのときに学んだ内容をRで実装したときのメモを備忘録として残しておきます。このページは以下の参考書の2章と7章の一部をまとめたものです。
- 参考書:入門 機械学習による異常検知 – Rによる実践ガイド –, 井手 剛 著, コロナ社, 2015年
setup
- ライブラリ読み込み
library(tidyverse)
library(car)
library(MASS)
(参考) 外れ値が含むようなデータを生成する方法
<- c(25, 35) # x,yの平均値
Mu2 <- matrix(c(25, 28, 28, 49), ncol=2) # 相関係数
Si2 <- mvrnorm(90, Mu2, Si2) # データ生成
dat2 <- c(10, 55) # 外れ値の平均値1
Muo1 <- c(40, 15) # 外れ値の平均値2
Muo2 <- matrix(c(4, 1, 1, 9), ncol=2) # 外れ値の分散・共分散
Sio <- mvrnorm(5, Muo1, Sio)
Err1 <- mvrnorm(5, Muo2, Sio)
Err2 <- rbind(dat2, Err1, Err2)
Dat <- data.frame(x = Dat[,1], y = Dat[,2])
Dat
::ggplot(Dat, aes(x = x ,y = y)) +
ggplot2geom_point()
2章 外れ値検出
- 外れ値検出 (Outlier detection)
?Davishead(Davis)
## sex weight height repwt repht
## 1 M 77 182 77 180
## 2 F 58 161 51 159
## 3 F 53 161 54 158
## 4 M 68 177 70 175
## 5 F 59 157 59 155
## 6 M 76 170 76 165
summary(Davis)
## sex weight height repwt repht
## F:112 Min. : 39.0 Min. : 57.0 Min. : 41.00 Min. :148.0
## M: 88 1st Qu.: 55.0 1st Qu.:164.0 1st Qu.: 55.00 1st Qu.:160.5
## Median : 63.0 Median :169.5 Median : 63.00 Median :168.0
## Mean : 65.8 Mean :170.0 Mean : 65.62 Mean :168.5
## 3rd Qu.: 74.0 3rd Qu.:177.2 3rd Qu.: 73.50 3rd Qu.:175.0
## Max. :166.0 Max. :197.0 Max. :124.00 Max. :200.0
## NA's :17 NA's :17
- ヒストグラムと箱ひげ図
::ggplot(Davis, aes(x = weight)) +
ggplot2geom_histogram(bins = 15)
::ggplot(Davis, aes(x = "", y = weight)) +
ggplot2stat_boxplot(geom = "errorbar", width = 0.3) + # ひげと横線を描写するコード
geom_boxplot() +
labs(x = "")
# 正規分布を重ねる
::ggplot(Davis, aes(x = weight)) +
ggplot2geom_histogram(aes(y=..density..),colour = "black", fill = "blue", alpha = 0.7) +
stat_function(fun = dnorm, args = fitdistr(Davis$weight,"normal")$estimate,size = 1.5,colour = "brown")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
- hotelling
<- mean(Davis$weight)
mu <- mean((Davis$weight - mu)^2)
s2
<- sqrt(s2)
sd c(mu, s2)
## [1] 65.80 226.72
<- (Davis$weight - mu)^2/s2 # 異常度
a <- qchisq(0.99, 1) # 閾値
th
<- data.frame(a)
d
::ggplot(d,aes(x=c(1:200),y=a)) +
ggplot2geom_point() +
geom_hline(yintercept = th, linetype = "dashed", colour = "blue") +
labs(title = "", x = "sample number", y = "anomaly")
7章 異常部位検知と変化点検知
- Keogh et al. (2005) の心電図データを以下から取得
<- read.table("qtdbsel102.txt")
dt head(dt)
## V1 V2 V3
## 1 200.000 4.770 2.100
## 2 200.004 4.820 2.135
## 3 200.008 4.805 2.190
## 4 200.012 4.750 2.285
## 5 200.016 4.710 2.440
## 6 200.020 4.695 2.620
- データを分割, 可視化する
<- dt[1:3000,2] #訓練データ
D.train <- dt[3001:6000,2] #検証データ
D.test plot(D.train, type="l", ylim=c(4.0,6.0)) #訓練データの図示
plot(D.test, type="l", ylim=c(4.0,6.0)) #検証データの図示
最近傍までの距離で異常部位検知 (Discord discovery)
- knnのためにFNNパッケージを読み込んでおく
library(FNN) # knn
<- 100
width <- 1
nk
<- embed(dt[1:3000, 2], width)
D.train <- embed(dt[3001:6000, 2], width)
D.test
<- FNN::knnx.dist(D.train, D.test, k = nk) # knn
d
<- data.frame(x = c(1:(3000 - width + 1)), y = d[,1])
df
<- ggplot2::ggplot(df,aes(x = x,y = y)) +
p geom_line() +
labs(title = "", x = "index", y = "anomaly")
plot(p)
変化点検知 (Change-point detection)
- 特異スペクトル変換法で計算した変化度による変化点検知
# 特異スペクトル変換
<- dt[3001:6000, 2]
xi <- 50 # width
w <- 2
m <- w/2
k <- k/2 # lag
L <- length(xi)
Tt <- rep(0, Tt)
score
for(t in (w+k):(Tt-L+1)) {
<- t-w-k+1
t.start <- t-1
t.end <- t(embed(xi[t.start:t.end], w))[w:1,] # trajectory matrix
X1 <- t(embed(xi[(t.start + L):(t.end + L)], w))[w:1,] # test matrix
X2
<- svd(X1)$u[,1:m] # X11 SVD
U1 <- svd(X2)$u[,1:m] # X12 SVD
U2 <- svd(t(U1) %*% U2)$d[1] # overlap
sig1 <- 1 - sig1^2 # change score
score[t]
}
<- data.frame(x = c(1:3000), y = score)
df head(df)
## x y
## 1 1 0
## 2 2 0
## 3 3 0
## 4 4 0
## 5 5 0
## 6 6 0
<- ggplot2::ggplot(df,aes(x = x,y = y)) +
p geom_line() +
labs(title = "", x = "time index", y = "change")
plot(p)
異常部位検出よりも明瞭に変化点が検出されている
まとめ
外れ値検出、異常部位検出、変化点検知について簡単に触ってみました。
今回の内容を元に、異常検知について (ざっくり) 俯瞰した note を後日またアップします。