フリーランチ食べたい

No Free Lunch in ML and Life. Pythonや機械学習のことを書きます。

【機械学習】混同行列を表示するのに便利な「pycm」

機械学習のクラス分類モデルを評価する際に用いる混同行列を簡単に表示できるちょっとしたライブラリがあったので紹介します。

f:id:ikedaosushi:20190526171950p:plain

混同行列とは、機械学習で予測したクラス分類の結果をまとめた行列です。ちょうど上の画像のように「どのクラスに分類されて」「実際はどのクラスだったか」がひと目でわかる表のことですね。

課題

scikit-learnにも混同行列を表示する関数があるのですが、ラベル情報が含まれていないので、例えば第三者に結果を報告する際には、わかりやすく加工することが必要でした。

scikit-learn.org

from sklearn.metrics import confusion_matrix
y_true = ["cat", "ant", "cat", "cat", "ant", "bird"]
y_pred = ["ant", "ant", "cat", "cat", "ant", "cat"]
print(confusion_matrix(y_true, y_pred))
# [[2 0 0]
#  [0 0 1]
#  [1 0 2]]

そのため、整形するために独自に関数を作っている方もQiitaで見つけました。

qiita.com

この問題を解決するのがpycmです。

pycm

pycmは混同行列の取扱いをサポートするライブラリです。用途がはっきりしていてわかりやすいかと思います。

github.com

例えば今scikit-learnで表示した混同行列はpycmだと下記のように表示することができます。

from pycm import ConfusionMatrix
y_true = ["cat", "ant", "cat", "cat", "ant", "bird"]
y_pred = ["ant", "ant", "cat", "cat", "ant", "cat"]
cm = ConfusionMatrix(actual_vector=y_true, predict_vector=y_pred)
cm.print_matrix()
# Predict    ant        bird       cat        
# Actual
# ant        2          0          0          
# 
# bird       0          0          1          
# 
# cat        1          0          2   

ラベル付きで表示することができました。 ちょっとしたライブラリなんですが、こういうものが意外と重宝するんですよね。

その他の機能も

ただ表示するだけでは終わりません。その他にも便利な機能もあるので紹介します。

混同行列の正規化

cm.print_normalized_matrix()
# Predict       0             1             2             
# Actual
# 0             0.9661        0.0339        0.0           
# 
# 1             0.0           0.97183       0.02817       
# 
# 2             0.0           0.02083       0.97917  

「1 vs それ以外」の表示

cm.print_matrix(one_vs_all=True, class_name=0)
# Predict   0         ~         
# Actual
# 0         57        2         
# 
# ~         0         119  

辞書型への変換

cm.table
# {0: {0: 57, 1: 2, 2: 0}, 1: {0: 0, 1: 69, 2: 2}, 2: {0: 0, 1: 1, 2: 47}}

数値とラベルのマッピング

cm.relabel(mapping: dict) を使うことで簡単にクラスの名前にもマッピングすることができます。

y_true = [0, 0, 1, 0, 2, 2, 1, 0, 0, 1, 2, 1]
y_pred = [1, 0, 1, 2, 2, 2, 1, 1, 2, 2, 0, 1]

cm = ConfusionMatrix(actual_vector=y_true, predict_vector=y_pred)
cm.print_matrix()

# Predict 0       1       2       
# Actual
# 0       1       2       2       
# 
# 1       0       3       1       
# 
# 2       1       0       2       

mapping = {0: "クラス1", 1: "クラス2", 2: "クラス3"}
cm.relabel(mapping=mapping) # マッピング
cm.print_matrix()

# Predict    クラス1       クラス2       クラス3       
# Actual
# クラス1       1          2          2          
# 
# クラス2       0          3          1          
# 
# クラス3       1          0          2   

マッピングしたあとに cm.table を呼び出すと前述したQiitaの記事と同じことができます。

import pandas as pd
pd.DataFrame(cm.table)

f:id:ikedaosushi:20190526180349p:plain

混同行列同士の比較も

またもう1つ便利な機能として、「混同行列同士の比較」があります。 ここでは、「ロジスティクス回帰とSVMで分類結果を比較する」というユースケースを例として考えてみましょう。

from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC

lr = LogisticRegression()
lr.fit(X_train, y_train)
y_lr_pred = lr.predict(X_test)

svc = SVC()
svc.fit(X_train, y_train)
y_svc_pred = svc.predict(X_test)

y_true = y_test

この分類結果を混同行列として次のように簡単に比較することができます。 Compare クラスを使います。

from pycm import Compare
cm_lr = ConfusionMatrix(actual_vector=y_true, predict_vector=y_lr_pred)
cm_svc = ConfusionMatrix(actual_vector=y_true, predict_vector=y_svc_pred)
cp = Compare({"lr": cm_lr,"svc": cm_svc})
print(cp)
# Best :lr
# 
# Rank  Name      Class-Score   Overall-Score
# 1     lr     3.0           4.0
# 2     svc    0.6           1.11667

今回の例ではロジスティク回帰の方が良い結果だったようです。

さいごに

混同行列を表示するのに便利な「pycm」を紹介しました。汎用的というわけではありませんが、かゆいところに手が届く孫の手的なライブラリですね。こういう日常に転がっている問題に気づけるようになりたいと常々思っています。

今回使ったコードはすべてGitHubにあげてあります。

github.com