概要
機械学習の精度を複数の視点で確認するのに、Confusion Matrix(混同行列)を用いるが、sklearn.metrics
パッケージのconfusion_matrix
モジュールはこの集計を自動で行ってくれる。
使い方
引数
confusion_matrix(y_true, y_pred, labels=None, normalize=None)
y_true
- ターゲットの正解の配列を与える。
y_pred
- 予測されたターゲットの配列を与える。
labels
- 表示される順番を変更したいときに、ターゲット値をリストで指定する。
normalize
- 合計値に対する比率で表示する。正解の合計に対する場合は
'true'
、予測結果の合計に対する場合は'pred'
、全体の合計に対する場合は'all'
を指定する。
戻り値
戻り値は[n_class, n_class]
の2次元配列で、各行が正解の各クラス、各列が予測された各クラスに対応する。各クラスの並びは、数値なら昇順、文字列なら辞書順で、行・列とも同じ並びになる。
実行例
データの準備とモデルによる予測
Breast Cancerデータセットで使い方を見ていく。まず、cancerデータを読み込み、訓練データとテストデータに分割する。予測モデルにはLogistic回帰を用いて、訓練データについてターゲットを予測する。以降、訓練データに関する正解ターゲットと予測ターゲットを使う。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import numpy as np import pandas as pd from sklearn.datasets import load_breast_cancer from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression from sklearn.metrics import confusion_matrix ds = load_breast_cancer() X_train, X_test, y_train, y_test = train_test_split( ds.data, ds.target, stratify=ds.target, random_state=42) logreg = LogisticRegression(solver='liblinear').fit(X_train, y_train) y_train_pred = logreg.predict(X_train) |
ここでデータの内容を確認しておく。正解データ、予測データとも0/1の2クラスで、0が悪性(malignant)、1が良性(begnign)と定義されている。
1 2 3 4 5 6 7 8 9 |
np.set_printoptions(threshold=1, edgeitems=3) print("Target data") print("Actual data (size={}):{}".format(y_train.size, y_train)) print("Predicted data (size={}):{}".format(y_train_pred.size, y_train_pred)) print(ds.target_names) # Actual data (size=426):[0 1 0 ... 0 0 1] # Predicted data (size=426):[0 1 0 ... 0 0 1] # ['malignant' 'benign'] |
また、0/1の数値によるクラス表現を文字列表現にした配列を別に作っておく。
1 2 3 4 5 6 7 8 |
y_train_named = np.array([ds.target_names[x] for x in y_train]) y_train_pred_named = np.array([ds.target_names[x] for x in y_train_pred]) print("Actual data (size={}):{}".format(y_train.size, y_train_named)) print("Predicted data (size={}):{}".format(y_train_pred.size, y_train_pred_named)) # Actual data (size=426):['malignant' 'benign' 'malignant' ... 'malignant' 'malignant' 'benign'] # Predicted data (size=426):['malignant' 'benign' 'malignant' ... 'malignant' 'malignant' 'benign'] |
基本的な使い方
要素のみを得る
基本的な使い方は、confusion_matrix()
の引数に正解データと予測データをコレクションで与える。結果は行・列とも昇順で並べられる。以下の例では、1行目が正解・悪性、2行目が正解・良性、1列目が予測・悪性、2列目が予測・良性となっている。
1 2 3 4 5 |
mat = confusion_matrix(y_train, y_train_pred) print(mat) # [[148 11] # [ 9 258]] |
クラスが文字列で表現されている場合は、文字列の辞書順なので、行・列とも'benign'
、'malignant'
の順で並べられる。この結果、数値表現の場合に対して行・列とも入れ替わっている。
1 2 3 4 5 |
mat_named = confusion_matrix(y_train_named, y_train_pred_named) print(mat_named) # [[258 9] # [ 11 148]] |
要素の並び順を変更する
引数labels
にリストでクラスの並びを指定できる。以下の例ではデフォルトの昇順の並びを変更している。
1 2 3 4 5 6 7 8 9 10 |
print(confusion_matrix(y_train, y_train_pred, labels=[1, 0])) # [[258 9] # [ 11 148]] print(confusion_matrix(y_train_named, y_train_pred_named, labels=['malignant', 'benign'])) # [[148 11] # [ 9 258]] |
要素を正規化する~比率で表す
引数normalize
で合計に対する比率の計算の仕方を指定できる。
normalize='true'
の場合、正解の各行の合計に対する比率が計算される。以下の例では行の合計で各要素が除され、各行の合計が1となっている。
1 2 3 4 5 6 7 |
mat = confusion_matrix(y_train, y_train_pred, normalize='true') print(mat) print(mat.sum(axis=1)) # [[0.93081761 0.06918239] # [0.03370787 0.96629213]] # [1. 1.] |
normalize='pred'
の場合、予測の各列の合計に対する比率が計算される。以下の例では列の合計で各要素が除され、各列の合計が1となっている。
1 2 3 4 5 6 7 |
mat = confusion_matrix(y_train, y_train_pred, normalize='pred') print(mat) print(mat.sum(axis=0)) # [[0.94267516 0.04089219] # [0.05732484 0.95910781]] # [1. 1.] |
normalize='all'
の場合、すべての要素の合計に対する比率が計算される。以下の例では、全要素の合計が1となっている。
1 2 3 4 5 6 7 |
mat = confusion_matrix(y_train, y_train_pred, normalize='all') print(mat) print(mat.sum()) # [[0.34741784 0.0258216 ] # [0.02112676 0.6056338 ]] # 1.0 |
なお、normalize='all'
を指定した場合の対角要素の和は、全要素に対する正解要素の比率になり、score()
メソッドの値と等しい。
1 2 3 4 5 |
print("Accuracy :{}".format(mat[0, 0] + mat[1, 1])) print("Training score:{}".format(logreg.score(X_train, y_train))) # Accuracy :0.9530516431924883 # Training score:0.9530516431924883 |
DataFrameによる扱い
ラベルの追加
pandas
のDataFrame
を使うと、行・列のラベルが表示されるので見やすくなる。行(正解)のラベルはindex
で、列(予測)のラベルはcolumns
で指定し、同じ内容のコレクションを与える。
1 2 3 4 5 6 7 |
mat = confusion_matrix(y_train, y_train_pred) result_label = ['malignant', 'benign'] df = pd.DataFrame(mat, columns=result_label, index=result_label) # malignant benign # malignant 148 11 # benign 9 258 |
合計欄
DataFrame
のsum()
メソッドで、行・列の合計を計算して追加すると見やすい。sum()
メソッドの引数を省略するとデフォルトのaxis=0
となり、列ごとの合計が1次元配列で得られる。引数をaxis=1
とすると行単位の合計が1次元配列で得られる。
以下の例では、まず列方向の合計(各予測クラスの合計)を最後の行に加え、その行も含めて行方向の合計(各正解クラス、予測クラス合計の合計)を最後の列に加える。
1 2 3 4 5 6 7 8 9 10 11 12 |
sums_in_col = df.sum() df.loc['Total'] = sums_in_col sums_in_row = df.sum(axis=1) df['Total'] = sums_in_row print(df) # malignant benign Total # malignant 148 11 159 # benign 9 258 267 # Total 157 269 426 |
Multiindex
DataFrameのMultiindexを使うと、正解・予測を表示できるのでより分かりやすくなる。ただし行・列・要素の指定が少し煩雑になる。
1 2 3 4 5 6 7 8 9 |
actual_label = ['Actual'] * 2 pred_label = ['Prediction'] * 2 df = pd.DataFrame(mat, columns=[pred_label, result_label], index=[actual_label, result_label]) print(df) # Prediction # malignant benign # Actual malignant 148 11 # benign 9 258 |
以下はMultiindexの場合に合計欄を加える例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
sums_in_col = df.sum() df.loc[('Actual', 'Total'), :] = sums_in_col sums_in_row = df.sum(axis=1) df[('Prediction', 'Total')] = sums_in_row df = df.astype('int') print(df) # Prediction # malignant benign Total # Actual malignant 148 11 159 # benign 9 258 267 # Total 157 269 426 |