はじめに
機械学習モデルの作成などにおいて、二値分類の精度を測る際に評価指標としてよく使用される「再現率」と「適合率」についてまとめました。
※以下、コード部分ではPythonを使用しています
目次
二値分類の評価指標
混同行列
再現率と適合率を理解するには、混同行列(confusion matrix)を使うとわかりやすいです。
混同行列の図は以下になります。
ここで、TP・FP・FN・TNの意味は以下になります。
用語 | 説明 | 例 |
---|---|---|
TP(True Positive、真陽性) | 予測値を正例として、その予測が正しい場合 | 陽性の患者を、正しく陽性と診断 |
TN(True Negative、真陰性) | 予測値を負例として、その予測が正しい場合 | 陰性の患者を、正しく陰性と診断 |
FP(False Positive、偽陽性) | 予測値を正例として、その予測が誤りの場合 | 陰性の患者を、誤って陽性と診断 |
FN(False Negative、偽陰性) | 予測値を負例として、その予測が誤りの場合 | 陽性の患者を、誤って陰性と診断 |
正例・負例とは、「陽性か、陰性か」「成年か、未成年か」といった分類のことを指します(注釈)。
True/Falseは予測が正しいか誤っているか、Positive/Negativeは正解が正例か負例かに、それぞれ対応します。
上図のような混同行列を考えます。
numpyを用いた混同行列の計算は以下のようになります。
# 正例を1、負例を0とする
y_true = [1, 0, 1, 1, 0, 1, 1, 0] # 正解
y_pred = [0, 0, 1, 1, 0, 0, 1, 1] # 予測
import numpy as np
tp = np.sum((np.array(y_true)==1)&(np.array(y_pred)==1))
tn = np.sum((np.array(y_true)==0)&(np.array(y_pred)==0))
fp = np.sum((np.array(y_true)==0)&(np.array(y_pred)==1))
fn = np.sum((np.array(y_true)==1)&(np.array(y_pred)==0))
confusion_matrix1 = np.array([[tp, fp],
[fn, tn]])
print(confusion_matrix1)
# [[3 1]
# [2 2]]
結果が図と一致していることがわかります。
混同行列は、scikit-learnのconfusion_matrixを使用しても計算できます。ただし、表示順が図と異なるので注意が必要です。
from sklearn.metrics import confusion_matrix
confusion_matrix2 = confusion_matrix(y_true, y_pred)
print(confusion_matrix2)
# [[2 1]
# [2 3]]
正答率と誤答率
混同行列の要素を用いることで、正答率と誤答率を計算することができます。
正答率と誤答率の意味は、それぞれ以下になります。
- 正答率(accuracy):予測がどれだけ正しいか
- 誤答率(error rate):予測がどれだけ誤っているか
それぞれ式で表すと以下になります。
\begin{align}
{\rm accuracy} &= \frac{TP+TN}{TP+TN+FP+FN}\\
{\rm error \:rate} &= 1 - {\rm accuracy}
\end{align}
正答率と誤答率は、scikit-learnのaccuracy_scoreを使って計算することができます。
from sklearn.metrics import accuracy_score
accuracy_score = accuracy_score(y_true, y_pred)
error_rate = 1 - accuracy_score
print(accuracy_score)
print(error_rate)
# 0.625
# 0.375
再現率と適合率
再現率と適合率の意味は、それぞれ以下になります。
- 再現率(recall):正解が正例のもののうち、どれだけ正例と予測できたか
- 適合率(precision):正例と予測したもののうち、どれだけ正解だったか
先程の混同行列の図で表すと、以下のようになります。
また、それぞれ式で表すと以下になります。
\begin{align}
{\rm recall} &= \frac{TP}{TP+FN}\\
{\rm precision} &= \frac{TP}{TP+FP}
\end{align}
これらは、1に近いほどスコアは高く、0に近いほどスコアは低いものになります。
再現率と適合率は、scikit-learnのprecision_score, recall_scoreを使ってそれぞれ計算することができます。
from sklearn.metrics import precision_score, recall_score
recall_score = recall_score(y_true, y_pred)
precision_score = precision_score(y_true, y_pred)
print(recall_score)
print(precision_score)
# 0.6
# 0.75
また、例として、1000人に対して陽性か陰性かを診断することを考えます。一般に陽性の方が少ないことを考え、陽性が10人、陰性が990人という設定にします。
- 【ケース1】陽性と診断したのが5人であり、そのうち5人全員が陽性だった
- 再現率:$5/10=0.5$
- 適合率:$5/5=1$
- 【ケース2】陽性と診断したのが50人であり、そのうち10人が陽性だった
- 再現率:$10/10=1$
- 適合率:$10/50=0.2$
ケース1では適合率が、ケース2では再現率が高くなっています。また、以下のことがわかります。
- ケース1では、誤検出が0人と少ない一方で、取りこぼしが5人と多くなっている
- ケース2では、誤検出が40人と多い一方で、取りこぼしが0人と少なくなっている
このことから、適合率と再現率は互いにトレードオフ、つまり一方の値を高くするともう一方の値が低くなることがわかります。
- 誤検出を少なくしたい場合には適合率
- 取りこぼしを少なくしたい場合には再現率
を評価指標として採用するとよいでしょう。
例えば上記の診断の例のように、
「陽性患者の取りこぼしが多いと、命に関わるような重大な問題に繋がる」
といったような場合には、再現率を評価指標に使用するのが適切かと思われます。
F1-score(F値)とFbeta-score
再現率と適合率は、互いにトレードオフの関係にあることがわかりました。
それらのトレードオフを考慮した、F1-scoreとF$\beta$-scoreという評価指標があります。F1スコアは、F値と呼ばれることも多いです。
F1-scoreとF$\beta$-scoreは、それぞれ以下の式で表されます(注釈)。
\begin{align}
{\rm F}_1 &= \frac{2\cdot {\rm recall}\cdot {\rm precision}}{{\rm recall}+{\rm precision}}\\
{\rm F}_\beta &= \frac{(1+\beta)\cdot {\rm recall}\cdot {\rm precision}}{{\rm recall}+{\rm \beta^2precision}}
\end{align}
F1-scoreは、再現率と適合率の調和平均をとったもの、F$\beta$-scoreは再現率と適合率に重み付けして平均をとったものになります。
両者の間には以下の関係が成り立っています。
{\rm F}_\beta\xrightarrow[\beta\rightarrow 1]{} {\rm F}_1
F1-scoreとF$\beta$-scoreは、scikit-learnのf1_score, fbeta_scoreを使ってそれぞれ計算することができます。
F$\beta$-scoreでは、$\beta$の値(以下は例として0.1)を与える必要があります。
from sklearn.metrics import f1_score, fbeta_score
f1_score = f1_score(y_true, y_pred)
fbeta_score = fbeta_score(y_true, y_pred, beta=0.1)
print(f1_score)
print(fbeta_score)
# 0.6666666666666665
# 0.7481481481481482
注釈
- より厳密には、ある事例を正クラスと負クラスの2クラスに分けたとき、正クラスである事例を正例、負クラスである事例を負例と呼びます。
- F1-scoreは、混同行列の要素を使用すると以下のように表すことができます。
{\rm F}_1 = \frac{2TP}{2TP+FP+FN}
ここで、分子には$TP$のみで$FP$がないことから、正例と負例について対称な式となっていません。そのため、どちらを正例・負例とするかによって結果が変わってくることに注意する必要があります。