LoginSignup
5
6

More than 1 year has passed since last update.

sklearnの評価指標に特異度(specificity)がなくて困った!

Last updated at Posted at 2021-01-02

自分用の備忘録です。特異度っていうのはFalseの正解率ですね。
埋め込みリンクはどれもsklearnの公式ドキュメントです。

まずはさらっと混合行列のおさらい

混合行列機械学習モデルの性能を評価する指標の一つです。
主に二値分類問題におけるモデルを評価する際に使われることが多く、テストデータに対する予測結果を、真陽性、真陰性、偽陽性、偽陰性の4つのマトリクスで評価したものです。

真陽性(TP): 真に陽性のテストデータに対してモデルの予測結果も陽性と判別したケース
真陰性(TN): 真に陰性のテストデータに対してモデルの予測結果も陰性と判別したケース
偽陽性(FP): 真に陰性のテストデータに対してモデルの予測結果は陽性と判別したケース
偽陰性(FN): 真に陽性のテストデータに対してモデルの予測結果も陰性と判別したケース

となります。

可視化してみる

混合行列を作成するにはsklearnのconfusion_matrixを使います。
以下で実際に正解クラスラベルy_trueと、モデルの出力である予測クラスy_predを事前に与え、混合行列を可視化してみます。

y_true = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1]
y_pred = [0, 1, 1, 1, 1, 0, 0, 0, 1, 1]
cm = confusion_matrix(y_true, y_pred)
plt.figure(figsize = (10,7))
sns.heatmap(cm, annot=True)
plt.savefig('sklearn_confusion_matrix.png')
plt.show()

例えば真陽性(TP)は、y_tureは1でy_predも1のものを指すので、下の表からも2個あるとわかります。
偽陽性(FP)は、y_tureは0でy_predは1のものを指すので4個あるとわかりますね。

2020-12-31 (2).png

ちなみに、混合行列を可視化するためのplot_confusion_matrixこんな便利な関数も用意されています。

ここから本題のsklearn の評価指標の話

さて、よく用いられる指標として、

正解率(accuracy) 予測値が実際にそうである割合

Accuracy = \frac{TP+TN}{TP+FP+TN+FN} \\


適合率(precision) 陽と予測したもののうち、実際に陽である割合

Precision= \frac{TP}{TP+FP} \\


**再現率(recall、True Positive Rate, TPR)** 実際に陽であるもののうち、陽と予測できた割合
Recall= \frac{TP}{TP+FN} \\


**特異度(specificity、True Negative Rate, TNR)** 実際に陰であるもののうち, 陰と予測できた割合
Specificity= \frac{TN}{FP+TN} \\


**偽陽性率 (False Positive Rate, FPR)** 実際に陰であるもののうち、陽と予測してしまった割合
 \frac{FP}{FP+TN} \\


**F1スコア(F1-score)** 再現率と適合率の調和平均
 \frac{2Recall⋅Precision}{Recall+Precision} \\


などがあります。 式を見れば気づきますが、特異度=(1 - 偽陽性率)となっています。

ここで困ったことは、正解率、適合率、再現率、F1スコアなどはsklearnの評価指標として見つかるのですが特異度などは実装されていないということです。

実際に機械学習をする場面を想定してみましょう。

まずは最もシンプルにsklearnのワインデータセットロジステック回帰で分類してみます。

from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score, f1_score

wine = load_wine()
wine_target = wine["target"][:130]
wine_data = wine["data"][:130]

X_train, X_test, y_train, y_test = train_test_split(wine_data, wine_target, test_size=0.2, random_state=0)
lr = LogisticRegression()
lr.fit(X_train, y_train)
y_pred = lr.predict(X_test)

print('confusion matrix = \n', confusion_matrix(y_true=y_test, y_pred=y_pred))
print(f'Accuracy : {accuracy_score(y_true=y_test, y_pred=y_pred)}')
print(f'Precision : {precision_score(y_true=y_test, y_pred=y_pred)}')
print(f'Recall : {recall_score(y_true=y_test, y_pred=y_pred)}')
print(f'F1 score : {f1_score(y_true=y_test, y_pred=y_pred)}')
出力結果
confusion matrix = 
 [[14  1]
 [ 0 11]]
Accuracy : 0.9615384615384616
Precision : 0.9166666666666666
Recall : 1.0
F1 score : 0.9565217391304348

こんな感じで各評価指標を用いて、作成したモデルを評価するのでした。
混合行列はconfusion_matrix、正解率はaccuracy_score、適合率はprecision、再現率はrecall、F1スコアはF1-scoreでそれぞれ提供されています。

しかし特異度がない!!!




**うああああああああああ!!!!!!!**






特異度は以下のように実装します。 引数にy_true、y_predを受け取り、返値に評価値を渡します。 (confusion_matrixの返値の行列は冒頭の画像の順番と同じです。)
def specificity_score(y_true, y_pred):
    tn, fp, fn, tp = confusion_matrix(y_true, y_pred).flatten()
    return tn / (tn + fp)

print(f'specificity :{specificity_score(y_test,y_pred)}')
出力結果
specificity :0.9333333333333333

チョット面倒くさいね。

次はちょっと進んでクロスバリデーション(CV)を使って評価してみましょう。

cross_val_scoreが使えますね。

from sklearn.model_selection import cross_val_score

cross_val_score(lr, X_train, y_train, cv=3, scoring="accuracy")
出力結果
array([0.94285714, 0.94285714, 0.97058824])

このときscoringに、使用したい評価指標を文字列で入れます。'precision' 、'recall'、'f1'なども使えます。しかし特異度はやはり用意されていません。がーん。


ここで便利なのが、好きな評価指標を作成することができる、make_scorer
使い方はさっき作成したspecificity_scoreを、make_scorerでラップして入れるだけ。

from sklearn.metrics.scorer import make_scorer

cross_val_score(lr, X_train, y_train, cv=3, scoring=make_scorer(specificity_score))
出力結果
array([1.        , 0.93333333, 0.92857143])

簡単ですね!これでクロスバリデーションでも特異度(specificity)が使えるようになりました!


さらにcross_validate関数もあります。この関数は引数scoringに複数の評価指標を指定することができます。

score_funcs = {
    'accuracy': 'accuracy',
    'precision': 'precision',
    'recall' :'recall',
    'f1': 'f1',
    'specificity_score': make_scorer(specificity_score),
    }
    
cross_validate(lr, X_train, y_train, n_jobs=-1, cv=3, scoring=score_funcs)
出力結果
{'fit_time': array([0.05306554, 0.05142045, 0.05445862]),
 'score_time': array([0.01040649, 0.00802302, 0.00100088]),
 'test_accuracy': array([0.94285714, 0.94285714, 0.97058824]),
 'test_precision': array([1.        , 0.95      , 0.95238095]),
 'test_recall': array([0.9 , 0.95, 1.  ]),
 'test_f1': array([0.94736842, 0.95      , 0.97560976]),
 'test_specificity_score': array([1.        , 0.93333333, 0.92857143])}

辞書形式で出力されます。'test_hogehoge'というキーになるのですね。
うーん便利。

終わりに

近日加筆予定です。 ROC曲線とか、AUCとか、あとはクロスバリデーションとパラメータチュー二ングとの組み合わせ方も書きたいと思っています。

5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6