の続き。入門機械学習による異常検知の多変量正規分布に基づく異常検知を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)] //weight, height let n = List.length weight //let data = DenseMatrix.init 2 n (fun i j -> if i == 0 then weight.Item j else height.Item j) 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) // \hat μ 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 // \hat Σ 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 // return an integer exit code