natsutan.hatenablog.com
の続き。入門機械学習による異常検知の多変量正規分布に基づく異常検知をF#で書いてみました。
入門 機械学習による異常検知―Rによる実践ガイド | 剛, 井手 |本 | 通販 | Amazon
出てくる数式とキーワードはこんな感じで、詳細は本を買って読んでみてください。
平均値
共分散行列
異常度の定義、マハラノビス距離
実行結果
教科書と同じ結果になりました。
F#実装
MathNet周りでちょっと詰まったところを整理しました。
List、配列からMatrix型を作る
一度array2Dを作って、Matrix.Build.DenseOfArrayを呼び出せばOK。list0とlist1に同じ数のデータが入っているとして、このように書けばMatrixが作れます。
let arr = array2D [ list0; list1 ]
let data = Matrix.Build.DenseOfArray arr
Matrix型からRowやColumnを取り出す。
Column(i), Row(i)を使う。2次元のMatrix xから0番目のコラムと1番目のコラムを取り出すにはこのようにします。
let c0 = x.Column(0)
let c1 = x.Column(1)
Matrixの平均値を求める。
MathNet.Numerics.Statistics.meanを使う。体重の値がはいったMatrixから平均値を求めるにはこうします。
let weight_mean = Statistics.Mean weights
RowやColumnでイテレートする
2次元MatrixからRow方向にデータを取り出して処理するには、EnumerateRowsを使います。
for row_data in x.EnumerateRows() do
...
分からないこと
2次元配列のRow方向に一定の値を引く方法が良く分からないです。
数式だと のように、体重や身長からそれぞれの平均値を引く操作です。
Pythonだと何も考えずにできます。
>>> import numpy as np
>>> a = np.array([[1,2,3], [4,5,6]])
>>> b = [[1],[2]]
>>> a - b
array([[0, 1, 2],
[2, 3, 4]])
>>>
row方向にmapができれば良さそうだが、結局やり方分からず。
F#ソース
以下ソースです。
open FSharp.Data
open FSharp.Charting
open MathNet.Numerics.LinearAlgebra
open MathNet.Numerics.Statistics
open MathNet.Numerics.Distributions
let csv_path = @"C:\home\myproj\study_ml\abnormaly_detection\data\car2.csv"
let output_path = @"C:\home\myproj\study_ml\abnormaly_detection\fsharp\Hotelling\output\"
type CarDB = CsvProvider<"C:/home/myproj/study_ml/abnormaly_detection/data/car2.csv">
let plotAbnormality (xs :List<float>, th:float) =
let mutable chart_list = []
let n = Seq.length xs
let chart1 = Chart.Point [for i in 0 .. n - 1 -> (i + 1, xs.[i]) ]
let output_file = output_path + @"abnomality.png"
let chart2 = Chart.Line [ for x in 0.0 .. 200.0 -> (x, th) ]
let chart_comb = Chart.Combine [chart1; chart2]
Chart.Save output_file chart_comb
printf "%s\n" output_file
None
let getData =
let car = CarDB.Load(csv_path)
let weight = [ for row in car.Rows -> (float row.Weight)]
let height = [ for row in car.Rows -> (float row.Height)]
let n = List.length weight
let arr = array2D [ weight; height ]
let data = Matrix.Build.DenseOfArray arr
data.Transpose()
let calcMahalanobis(x:Vector<float>, mu_hat:Vector<float>, sig_hat_inv:Matrix<float>) =
let x_minus_mu_hat = x - mu_hat
let a = x_minus_mu_hat * sig_hat_inv * x_minus_mu_hat
a
let calcAbnormality (x:Matrix<float>) =
let n = float x.RowCount
let weights = x.Column(0)
let heights = x.Column(1)
let weight_mean = Statistics.Mean weights
let height_mean = Statistics.Mean heights
let mu_arr = [|weight_mean; height_mean|]
let mu_hat = Vector.Build.DenseOfArray mu_arr
let sig_arr = array2D [ weights - weight_mean; heights - height_mean ]
let x_minus_mu = Matrix.Build.DenseOfArray sig_arr
let sig_hat = (x_minus_mu * x_minus_mu.Transpose()) / n
let sig_hat_inv = sig_hat.Inverse()
let mutable abnormality = []
for d in x.EnumerateRows() do
let a = calcMahalanobis(d, mu_hat, sig_hat_inv)
abnormality <- a :: abnormality
List.rev abnormality
[<EntryPoint>]
let main argv =
let data = hdtwo.getData
let abnormality = hdtwo.calcAbnormality data
let threshhold = ChiSquared.InvCDF(1.0, 0.99)
hdtwo.plotAbnormality (abnormality, threshhold) |> ignore
0