静かなる名辞

pythonとプログラミングのこと



sklearnのclassification_reportで多クラス分類の結果を簡単に見る

 多クラス分類をしていると、「どのクラスが上手く分類できてて、どのクラスが上手く行ってないんだろう」と気になることがままあります。

 そういった情報を簡単に要約して出力してくれるのがsklearnのclassification_reportで、簡単に使える割に便利なので実験中や開発中に威力を発揮します。

使い方

 ドキュメントを見るととても簡単そうです。
sklearn.metrics.classification_report — scikit-learn 0.19.1 documentation

sklearn.metrics.classification_report(y_true, y_pred, labels=None, 
target_names=None, sample_weight=None, digits=2)

 要するに真のラベルと予測ラベル、あとラベルに対応する名前を入れてあげればとりあえず使えます。文字列の返り値が出力になります。sample_weight, digitsはそれぞれサンプルの重みと結果に出力される桁数を表しますが、とりあえず入れなくても大した問題は普通はありません。

 さっそく使ってみましょう。

# coding: UTF-8

import numpy as np
from sklearn.datasets import load_iris
from sklearn.svm import SVC
from sklearn.metrics import classification_report
from sklearn.model_selection import StratifiedKFold as SKF

def main():
    # irisでやる
    iris = load_iris()

    # svmで分類してみる
    svm = SVC(C=3, gamma=0.1)

    # 普通の交差検証
    trues = []
    preds = []
    for train_index, test_index in SKF().split(iris.data, iris.target):
        svm.fit(iris.data[train_index], iris.target[train_index])
        trues.append(iris.target[test_index])
        preds.append(svm.predict(iris.data[test_index]))
        
    # 今回の記事の話題はここ
    print("iris")
    print(classification_report(np.hstack(trues), np.hstack(preds), 
                                target_names=iris.target_names))

if __name__ == "__main__":
    main()

 すると、次のような出力が得られます。

iris
             precision    recall  f1-score   support

     setosa       1.00      1.00      1.00        50
 versicolor       0.96      0.98      0.97        50
  virginica       0.98      0.96      0.97        50

avg / total       0.98      0.98      0.98       150

 setasoは100%分類できていますが、versicolorとvirginicaはどうも混ざっているようです。以前の記事でirisを二次元にした画像を作ったので、再掲します。

irisをPCAで二次元にしたもの
irisをPCAで二次元にしたもの

 RGBの順でsetaso, versicolor, virginicaに対応しているはずです。ということはsetasoが綺麗に分離できてversicolorとvirginicaが混ざるというのは極めて妥当な結果ということになりそうです。

 こんな感じで結果を理解することができます。

 ちなみに、一番下にあるavg/totalは、sample_weightを指定しないとクラスごとの重み付き平均になります。今回はどのクラスも50件ずつなので、単純な平均になっていると解釈して構いません。とにかくトータルでは0.98のF1値になりました、という結果が得られました、ということです。

便利さと限界とその先へ

 人の目で見る分にはclassification_reportの結果は素晴らしいですが、テキストファイルに出力して集計したいといった用途には向きません。

 この関数は内部的にはprecision_recall_fscore_supportという関数の出力を整形して文字列にしているはずです。結果のダンプにはこちらを使った方が良さそうです。ドキュメントはこちらになります。
sklearn.metrics.precision_recall_fscore_support — scikit-learn 0.19.1 documentation

 使い方はこんな感じです。 

from sklearn.metrics import precision_recall_fscore_support
precision_recall_fscore_support(y_true, y_pred, average=None)

 結果はこんな感じになります(上のプログラムを対象に計算し、返り値をpprintしました)。

(array([1.        , 0.96078431, 0.97959184]),
 array([1.  , 0.98, 0.96]),
 array([1.        , 0.97029703, 0.96969697]),
 array([50, 50, 50]))

 numpy配列を格納したタプルが返ってますね。それぞれのnumpy配列がprecision, recall, fscore, supportに対応します。numpy配列なので、DBに入れるなりpandasデータフレームに入れるなり、好きに料理すれば良いでしょう。

まとめ

 簡単に使えるから使ってみよう(小並感)。