いい加減時代の潮流に乗ろうということで機械学習を学びたいと思う。またRはともかくとしてPythonは未経験であるため、丁度良い書籍として
を用いることにする。
2. 分類
有限個の数値を目的変数とするような分類の問題を取り扱う。
2.4. k近傍法
訓練データは有限集合)から規則を生成するのではなく、新しいデータに最も近い個の多数決によりそのデータのを予測する方法である。
として(各が通りの値を取る)、から見て最も距離の近い個の標本におけるの頻度を調べ、そのうち最も大きいをに対応すると見なす*1。すなわち
たとえば
- 利用する標本の数
という条件設定であれば、新たにテストデータが観測されたとき以下のアルゴリズムでそのテストデータのを決定する:
(1) | 各に対して距離を計算する。 | |
(2) | (1)で計算した各距離を昇順に並び替え、番目から番目のテストデータを選び、これらをとする。 | |
(3) | を集計し、のうち多い方をとすれば、とする。 |
近傍法のイメージ
2.4.1 Rでのスクリプト
# k近傍法 knn_1dim <- function(mt_x, vc_y, mt_z, num_k){ # 入力の準備 mt_x <- as.matrix(mt_x) num_r <- nrow(mt_x) num_c <- ncol(mt_x) vc_distance <- array(dim = num_r) # 近傍の判定 for(i in 1:num_r){ vc_distance[i] <- norm(x = mt_z - mt_x[i,],type = "2") # 2-norm(特異値)を距離とする } S <- order(vc_distance)[1:num_k] # 距離の小さいk個の添字iの集合 vc_u <- sort(table(vc_y[S]),decreasing = T) #k個のうち最頻のmt_y[i]と頻度 v <- names(vc_u)[1] # 最頻のmt_y[i] # タイデータがあった場合の処理 if(length(vc_u)>1){ w <- names(vc_u)[2] } while(length(vc_u)>1 && v==w){ num_k <- num_k - 1 vc_u <- vc_u[num_k] v <- names(vc_u)[1] w <- names(vc_u)[2] } return(v) } # k近傍法:判定対象が複数の場合、各対象に1dimを呼び出す knn <- function(mt_x, vc_y, mt_z, num_k){ num_n <- nrow(mt_z) vc_w <- array(dim = num_n) for(i in 1:num_n){ vc_w[i] <- knn_1dim(mt_x, vc_y, mt_z[i,], num_k) } return(vc_w) } # データ data(iris) num_n <- 150 # 訓練データ数は全体の3割 vc_train <- sample(1:num_n, round(0.3 * num_n,digits = 0), replace = F) vc_test <- setdiff(1:num_n, vc_train) # 入力の準備 mt_x <- as.matrix(iris[vc_train,1:4]) vc_y <- as.vector(iris[vc_train,5]) mt_z <- as.matrix(iris[vc_test,1:4]) vc_ans <- as.vector(iris[vc_test,5]) # k近傍法 w <- knn(mt_x = mt_x, vc_y = vc_y, mt_z = mt_z, num_k = 3) table(w,vc_ans)
2.4.2 Pythonによるスクリプト*2
%matplotlib inline import numpy as np import pandas as pd import matplotlib.pyplot as plt from sklearn.model_selection import train_test_split # アヤメデータセット読み込み from sklearn.datasets import load_iris iris = load_iris() # 特徴量 X = iris.data # 目的変数 Y = iris.target # データ表示(特徴量) print("データ数 = %d 特徴量 = %d" % (X.shape[0], X.shape[1])) pd.DataFrame(X, columns=iris.feature_names).head() # トレーニング・テストデータ分割 X_train, X_test, Y_train, Y_test = train_test_split(X, Y, random_state=0) # # K-近傍法 # from sklearn.neighbors import KNeighborsClassifier list_nn = [] list_score = [] for k in range(1, 101): # KNeighborsClassifier knc = KNeighborsClassifier(n_neighbors=k) knc.fit(X_train, Y_train) # 予測 Y_pred = knc.predict(X_test) # 評価 R^2 score = knc.score(X_test, Y_test) print("[%d] score: {:.2f}".format(score) % k) list_nn.append(k) list_score.append(score) # プロット plt.ylim(0.5, 1.0) plt.xlabel("n_neighbors") plt.ylabel("score") plt.plot(list_nn, list_score)
を変えたときの決定係数の推移
2.5 ROC曲線
分類問題に当たっては事後確率最大化、すなわち誤り率最小という意味で妥当ではある。しかし問題によっては敢えて別の面での性能を向上させたい場合がある。
たとえば2値問題において、一方に誤判断する方がもう一方に誤判断するよりも深刻な損害が出る場合がある。たとえば致命的な病気の診断において、実際にはそれに罹患しているのに罹患していないと誤判断することの方が生命の危機という観点で被害が大きいと考えられる。この場合、
病気である | 病気でない | ||
病気だと診断 | 真陽性(True Positive) | 偽陽性(False Positive) | |
病気でないと診断 | 偽陰性(False Negative) | 真陰性(True Negative) |
というパターンが存在する。
をそれぞれ病気に罹患しているか否か(罹患している,罹患していない)、診断で陽性と出る()か陰性と出るか()を表す確率変数だとする。このときFalse Positiveとする誤りを第1種の誤り、False Negativeとする誤りを第2種の誤りとし、それらの確率を
とする。このとき
と定義する。
偽陽性率を与えたときに、検出力を最大化するように分類を設計することを考える*3。このとき、その分類の良さを検証するのに、偽陽性率を横軸、各偽陽性率に対応する検出力を縦軸として曲線を描くことが良く行われる。これを曲線という。軸と曲線とが囲む部分の面積がに近いほど良い検査を行っているものだと考えられる。
曲線のイメージ