文章参考:Mean Average Precision (mAP) Explained | Paperspace Blog
目次
2. 適合率-再現率曲線、平均適合率 PR 曲線、平均適合率
まず最も単純な二項分類問題を考えてみましょう。
1. 混同メトリクスの混同マトリックス
(画像ソースについては透かしを参照、予測クラスの予測分類、実際のクラスの実際の分類を参照)
実際、混乱メトリクス オントロジーは単なる 2 x 2 テーブルですが、ここでより重要なのは、タイプ I エラー (最初のタイプのエラー) とタイプ II エラー (2 番目のタイプのエラー) を理解することです。対応するものは、Accuracy (精度)、Precision (精度)、Recall/Sensitivity (再現率) です。
(1)精度: すべての肯定的な予測 (つまり、予測が 1) のうち、正しい予測の割合。
(2) Recall/Sensitivity : 実際に 1 の場合、Positive によって予測される (つまり 1 として予測され、正しく予測される) 割合はどれくらいですか。つまり、今は真ん中の 1 ですが、モデルでは 0 と予測されます。このとき、上図のタイプ II エラーが発生します。
*病気の検出にモデルを使用する場合、再現率/感度は重要な参照指標であることに言及する価値があります。なぜなら、私たちが得たい結果は、病気の可能性のある患者を放っておくよりもむしろ間違った予測をすることだからです。
(3)精度: 前の 2 つの計算方法、列合計の比率または行合計の比率とは異なり、この指標は全世界を俯瞰した比率と見なすことができ、予測が 1、現実が 1 となります。予測は 1 0 で、実際の比率は 0 です。分母はすべてのケース全体の数です。
(4) F1-score : (1) Precision (精度) と (2) Recall (再現率) の両方を考慮した指標。
下の写真で医師が言ったことは、モデルの陽性/陰性の予測として理解できます。
2. 適合率-再現率曲線、平均適合率 PR 曲線、平均適合率
予測結果の観点から見ると、精度は、バイナリ分類器によって予測された正の例が実際の正の例であるかどうか、つまり、バイナリ分類器によって予測された正の例がどれだけ正確であるかを示します。再現率は、バイナリ分類器によって予測された正の例がどのくらい正確であるかを示します。実際の結果の観点から見た正確さ バイナリ分類子によってテスト セット内の実際の陽性例がいくつ選択されるか、つまり、バイナリ分類子によって呼び出される実際の陽性例がいくつあるか。
精度と再現率は通常、相反するパフォーマンス指標のペアです。一般に、精度が高くなるほど再現率は低くなります。その理由は、精度を高めたい場合、つまり 2 つの分類器によって予測される正の例が可能な限り本物である場合、正の例を予測するために 2 つの分類器のしきい値を増やす必要があるためです。これは正の例であるため、バイナリ分類器によって選択された正の例が実際の正の例である可能性がより高くなるように、これを正の例としてマークする前に確率を 0.7 以上に増やす必要があります。 ; そして、この目標はまさに再現率の向上に関係しています。逆に、再現率を向上させたい場合、つまり 2 つの分類子が実際の正の例をできるだけ多く抽出できるようにしたい場合は、2 つの分類子のしきい値を下げる必要があります。正の例を予測するための分類子。実際の正の例としてマークするだけなので、バイナリ分類器ができるだけ多くの値を選択できるように、値を 0.3 以上に減らして正の例としてマークします。可能な限り実際のポジティブな例。
それでは、精度と再現率の観点から 2 つの分類器の総合的なパフォーマンスを特徴付ける指標はあるのでしょうか? 答えは「はい」です。上で述べたように、陽性例を予測するための 2 つの分類器の異なるしきい値に従って、精度と再現率の複数のセットを取得できます。多くの場合、バイナリ分類器の予測結果を並べ替えることができます。一番上のものは、バイナリ分類器が正の例である可能性が最も高いと考えるサンプルであり、最後のものは、バイナリ分類器が最もポジティブな例であると考えるサンプルです。良い例となる可能性があります。サンプル。この順序で、バイナリ分類器の正例を予測するためのしきい値が 1 つずつ徐々に低くなり、そのたびに現在の適合率と再現率が計算されます。再現率を横軸に、適合率を縦軸にすると、PR 図と呼ばれる適合率-再現率曲線図を得ることができます。
PR グラフは、バイナリ分類器の精度と再現率を視覚的に表示できます。比較する場合、バイナリ分類器の PR 曲線が別のバイナリ分類器の PR 曲線に完全に囲まれている場合、後者のパフォーマンスが優れていると主張できます。前者、たとえば、上図のバイナリ分類器 A のパフォーマンスは C のパフォーマンスよりも優れています。2 つのバイナリ分類器の PR グラフが交差する場合、どちらが優れているかを言うのは困難です。上図の分類子 A と B。
ただし、多くの場合、依然として 2 つの分類器 A と B のパフォーマンスを比較したいと考えます。現時点では、より合理的な指標は PR 曲線の下の領域のサイズであり、分類器のパフォーマンスをある程度特徴づけます。 Precision の 2 つの分類器 これら 2 つの側面の総合的なパフォーマンス。これは AP (Average Precision) の平均精度であり、単に PR 曲線上の Precision 値を平均することを意味します。PR 曲線の場合、積分を使用して次の計算を行います。
実際には、2 つの分類問題に限定されず、多くの分類問題が存在します。一般的に、AP は 1 つのカテゴリの下にあり、mAP (平均平均精度) はすべてのカテゴリの下の AP 値の平均値です。
また、実際の演算処理ではPRカーブを直接計算するのではなく、PRカーブを平滑化します。つまり、PR 曲線上の各点について、Precision の値は、その点の右側にある最大の Precision の値になります。
数式で説明してください。この式を使用して平滑化し、AP の値を計算します。たとえば、Interploated AP (Pascal Voc 2008 の AP 計算方法)。平滑化された PR カーブ上で、横軸 0 ~ 1 上の 10 個の等しい点 (ブレークポイントを含む 11 点) の Precision 値を取り、その平均値を最終的な AP 値として計算します。
もちろん、直接統合して処理することもできます。
3. サンプルとコードの実装
バイナリ分類問題を考慮すると、プロセス全体には次の手順があります。
(1) 予測スコアからクラスラベルまで
ポジティブとネガティブの 2 つのカテゴリがあると仮定すると、y_true として示される 10 個のサンプルの実際のラベルは次のとおりです。
y_true = ["positive", "negative", "negative", "positive", "positive", "positive", "negative", "positive", "negative", "positive"]
これらのサンプルがモデルに入力されると、次の予測スコア pred_scores が返されます。
pred_scores = [0.7, 0.3, 0.5, 0.6, 0.55, 0.9, 0.4, 0.2, 0.4, 0.3]
これらのスコアに基づいて、サンプルを分類します (つまり、各サンプルにクラス ラベルを割り当てます)。まず、予測スコアの閾値を人為的に設定し、スコアが閾値以上の場合にサンプルを1つのクラス(通常は陽性クラス1)に分類します。それ以外の場合は、他のもの (通常は負の 0) として分類されます。ここでは、モデルによって予測されたラベル y_pred を取得するために、しきい値を 0.5 に設定します。
import numpy
pred_scores = [0.7, 0.3, 0.5, 0.6, 0.55, 0.9, 0.4, 0.2, 0.4, 0.3]
y_true = ["positive", "negative", "negative", "positive", "positive", "positive", "negative", "positive", "negative", "positive"]
threshold = 0.5
y_pred = ["positive" if score >= threshold else "negative" for score in pred_scores]
print(y_pred)
出力:
['positive', 'negative', 'positive', 'positive', 'positive', 'positive', 'negative', 'negative', 'negative', 'negative']
true ラベルと予測ラベルの両方がy_true変数 と y_pred変数 で使用できるようになりました 。これらのラベルに基づいて、混同行列、適合率、および再現率を前の定義から計算できます。
r = numpy.flip(sklearn.metrics.confusion_matrix(y_true, y_pred))
print(r)
precision = sklearn.metrics.precision_score(y_true=y_true, y_pred=y_pred, pos_label="positive")
print(precision)
recall = sklearn.metrics.recall_score(y_true=y_true, y_pred=y_pred, pos_label="positive")
print(recall)
結果:
# Confusion Matrix (From Left to Right & Top to Bottom: True Positive, False Negative, False Positive, True Negative)
[[4 2]
[1 3]]
# Precision = 4/(4+1)
0.8
# Recall = 4/(4+2)
0.6666666666666666
(2) 適合率-再現率曲線 (適合率-再現率曲線)
適合率と再現率は重要であるため、適合率-再現率曲線は、さまざまなしきい値に対する適合率と再現率の値の間のトレードオフを示すことができます。この曲線は、両方のメトリクスを最大化するための最適なしきい値を選択するのに役立ちます。
適合率と再現率の曲線を作成するには、いくつかの入力が必要です。
1. 本物のラベル。2. サンプルの予測スコア。3. 予測スコアをクラスラベルに変換するためのしきい値。
このスニペットは、 真のラベル を保持するy_trueリスト、予測スコア のpred_scoresリスト、そして最後に、さまざまな しきい値 (ここでは 0.2 から 0.7 まで 0.05 刻み)のしきい値リストを作成します。
import numpy
y_true = ["positive", "negative", "negative", "positive", "positive", "positive", "negative", "positive", "negative", "positive", "positive", "positive", "positive", "negative", "negative", "negative"]
pred_scores = [0.7, 0.3, 0.5, 0.6, 0.55, 0.9, 0.4, 0.2, 0.4, 0.3, 0.7, 0.5, 0.8, 0.2, 0.3, 0.35]
thresholds = numpy.arange(start=0.2, stop=0.7, step=0.05)
閾値には10個の閾値があるので、10個の適合率と再現率の値が作成されます。precision_recall_curve() と呼ばれる次の関数は、真のラベル、予測スコア、およびしきい値を受け取ります。精度と再現率の値を表す 2 つの同じ長さのリストを返します。
import sklearn.metrics
def precision_recall_curve(y_true, pred_scores, thresholds):
precisions = []
recalls = []
for threshold in thresholds:
y_pred = ["positive" if score >= threshold else "negative" for score in pred_scores]
precision = sklearn.metrics.precision_score(y_true=y_true, y_pred=y_pred, pos_label="positive")
recall = sklearn.metrics.recall_score(y_true=y_true, y_pred=y_pred, pos_label="positive")
precisions.append(precision)
recalls.append(recall)
return precisions, recalls
以前のデータを呼び出して以下を取得します。
precisions, recalls = precision_recall_curve(y_true=y_true, pred_scores=pred_scores,thresholds=thresholds)
出力:
# Precision
[0.5625,
0.5714285714285714,
0.5714285714285714,
0.6363636363636364,
0.7,
0.875,
0.875,
1.0,
1.0,
1.0]
# Recall
[1.0,
0.8888888888888888,
0.8888888888888888,
0.7777777777777778,
0.7777777777777778,
0.7777777777777778,
0.7777777777777778,
0.6666666666666666,
0.5555555555555556,
0.4444444444444444]
同じ長さの 2 つのリストがある場合、それらの値は次のように 2 次元プロットにプロットできます。
import matplotlib.pyplot
matplotlib.pyplot.plot(recalls, precisions, linewidth=4, color="red")
matplotlib.pyplot.xlabel("Recall", fontsize=12, fontweight='bold')
matplotlib.pyplot.ylabel("Precision", fontsize=12, fontweight='bold')
matplotlib.pyplot.title("Precision-Recall Curve", fontsize=15, fontweight="bold")
matplotlib.pyplot.show()
再現率が増加すると、精度が低下することがわかります。その理由は、陽性サンプルの数が増加すると (再現率が高く)、各サンプルを正しく分類する精度が低下する (精度が低い)ためです。サンプルが多数ある場合、モデルが失敗する可能性が高くなるため、これは想定内のことです。
適合率と再現率の曲線を使用すると、適合率と再現率の両方が高いポイントを簡単に特定できます。上の図によると、最良の点は (再現率、精度)=(0.778, 0.875) です。より良い方法は、F1 スコア インジケーターを使用することです (上記の式を参照)。
f1 = 2 * ((numpy.array(precisions) * numpy.array(recalls)) / (numpy.array(precisions) + numpy.array(recalls)))
F1 リストの値に基づくと、最高スコアは 0.82352941 です。これはリストの 6 番目の要素 (つまり、インデックス 5) です。再現率リストと適合率リストの 6 番目の要素は、それぞれ 0.778 と 0.875 です。対応するしきい値は 0.45 です。
# F1-score
[0.72,
0.69565217,
0.69565217,
0.7,
0.73684211,
0.82352941,
0.82352941,
0.8,
0.71428571,
0.61538462]
下の画像は、再現率と精度の最適なバランスに対応するポイントの位置を青色で示しています。要約すると、適合率と再現率のバランスをとるための最適なしきい値は 0.45 で、適合率は 0.875、再現率は 0.778 です。
matplotlib.pyplot.plot(recalls, precisions, linewidth=4, color="red", zorder=0)
matplotlib.pyplot.scatter(recalls[5], precisions[5], zorder=1, linewidth=6)
matplotlib.pyplot.xlabel("Recall", fontsize=12, fontweight='bold')
matplotlib.pyplot.ylabel("Precision", fontsize=12, fontweight='bold')
matplotlib.pyplot.title("Precision-Recall Curve", fontsize=15, fontweight="bold")
matplotlib.pyplot.show()
(3) 平均精度 AP (平均精度)
AP は次の式に従って計算されます。ループを使用してすべての精度の精度/リコールのリコールを調べ、現在のリコールと次のリコールの差を計算し、現在の精度を乗算します。言い換えれば、平均精度は各しきい値の精度の重み付き合計であり、重みは再現率の差です。これは実際には、微積分における「除算と合計」のプロセスです。
その中には、再現率 (n) = 0、精度 (n) = 1、n は選択されたしきい値の数です。つまり、リコールリストのリコールと正確なリストの精度には、それぞれ 0 と 1 が追加されます。
AP = numpy.sum((recalls[:-1] - recalls[1:]) * precisions[:-1])
AP を計算するための完全なコードは次のとおりです。
import numpy
import sklearn.metrics
def precision_recall_curve(y_true, pred_scores, thresholds):
precisions = []
recalls = []
for threshold in thresholds:
y_pred = ["positive" if score >= threshold else "negative" for score in pred_scores]
precision = sklearn.metrics.precision_score(y_true=y_true, y_pred=y_pred, pos_label="positive")
recall = sklearn.metrics.recall_score(y_true=y_true, y_pred=y_pred, pos_label="positive")
precisions.append(precision)
recalls.append(recall**
return precisions, recalls
y_true = ["positive", "negative", "negative", "positive", "positive", "positive", "negative", "positive", "negative", "positive", "positive", "positive", "positive", "negative", "negative", "negative"]
pred_scores = [0.7, 0.3, 0.5, 0.6, 0.55, 0.9, 0.4, 0.2, 0.4, 0.3, 0.7, 0.5, 0.8, 0.2, 0.3, 0.35]
thresholds=numpy.arange(start=0.2, stop=0.7, step=0.05)
precisions, recalls = precision_recall_curve(y_true=y_true,
pred_scores=pred_scores,
thresholds=thresholds)
precisions.append(1)
recalls.append(0)
precisions = numpy.array(precisions)
recalls = numpy.array(recalls)
AP = numpy.sum((recalls[:-1] - recalls[1:]) * precisions[:-1])
print(AP)