機械学習のメトリクス1【AAAMLP】

AAAMLP

今回は、機械学習のメトリクス、評価指標について学んだことを記録します。

AAAMLP (Approaching (Almost) Any Machine Learning Problem)という書籍で勉強した内容の整理です。

github.com

この書籍は多くの言語に翻訳されていて、2021年8月に「Kaggle Grandmasterに学ぶ 機械学習 実践アプローチ」として日本語版も発売されています。

www.amazon.co.jp

Motivtion

私は、近い将来に海外で働いてみたいと思っています。

そのためには、グローバルに通用する資格や実績が必要です。

AWSも業務で使うため考えましたが、会社で資格を取る機会があるかなと思うのでそのときに勉強しようと思います。 あんまり自習するのはおもしろくなさそうなのもあり。

とりあえずは、Kaggleを通して機械学習を勉強しようかなと思います。

Metrics

まずは、2クラス問題に関するメトリクスを見ていきます。 書いてみたら長すぎたので、マルチクラス問題、マルチラベルのメトリクスは分けて書きます。

Confusion Matrix

まず、予測結果の分類を紹介します。

Confusion Matrixとは、予測と真値の関係を表す行列です。 2クラスの場合は2×2の行列です。 各成分をTP, TN, FP, FNと呼びます。

これ、予測と真値どっちがTrue or FalseでどっちがPositive or Negativeなのかすぐ忘れるんですよね。。

f:id:harukary7518:20210918143656j:plain:w200

TP: True Positive

真値1に対し予測も1の場合、つまりPositiveな予測がTrueだった場合がTPです。

def true_positive(y_true, y_pred):
    tp = 0
    for yt, yp in zip(y_true, y_pred):
        if yt == 1 and yp == 1:
            tp += 1
    return tp

TN: True Negative

真値0に対し予測も0の場合、つまりNegativeな予測がTrueだった場合がTNです。

def true_negative(y_true, y_pred):
    tn = 0
    for yt, yp in zip(y_true, y_pred):
        if yt == 0 and yp == 0:
            tn += 1
    return tn

FP: False Positive

真値0に対し予測も1の場合、つまりPositiveな予測がFalseだった場合がFPです。

def false_positive(y_true, y_pred):
    fp = 0
    for yt, yp in zip(y_true, y_pred):
        if yt == 0 and yp == 1:
            fp += 1
    return fp

FN: False Negative

真値1に対し予測も0の場合、つまりNegativeな予測がFalseだった場合がFNです。

def false_negative(y_true, y_pred):
    fn = 0
    for yt, yp in zip(y_true, y_pred):
        if yt == 1 and yp == 0:
            fn += 1
    return fn

Accuracy

単純に y_t = y_p の割合です。 Confusion matrixの成分を使って書くと、以下のように書けます。

 Accuracy Score = (TP + TN) / (TP + TN + FP + FN)

f:id:harukary7518:20210918151907j:plain:w200

コードは、以下の通りです。 accuracy_v2では、Confusion matrixの成分を使っています。

def accuracy(y_true, y_pred):
    correct_counter = 0
    for yt, yp in zip(y_true, y_pred):
        if yt == yp:
            correct_counter += 1
    return correct_counter/len(y_true)
def accuracy_v2(y_true, y_pred):
    tp = true_positive(y_true, y_pred)
    fp = false_positive(y_true, y_pred)
    tn = true_negative(y_true, y_pred)
    fn = false_negative(y_true, y_pred)
    accuracy_score = (tp + tn) / (tp + tn + fp + fn)
    return accuracy_score

このAccuracyには問題があります。 それは、データセットに偏りがある場合に、モデルのよさを表現できないことです。

例えば、2クラス問題で90%の真値が1のデータがあるとします。 このとき、すべて1と予測する、つまり、何も考えていないような場合でも、Accuracyは0.9と高い値をとります。

このような場合には、Confusion Matrixの各成分を見る必要があります。

Precision

予測1の正解率を表します。

 Precision = TP / (TP + FP)

f:id:harukary7518:20210918120839j:plain:w200

コードは、以下の通りです。

def precision(y_true, y_pred):
    tp = true_positive(y_true, y_pred)
    fp = false_positive(y_true, y_pred)
    precision = tp / (tp + fp)
    return precision

Recall

真値1の正解率を表します。

 Recall = TP / (TP + FN)

f:id:harukary7518:20210918120827j:plain:w200

def recall(y_true, y_pred):
    tp = true_positive(y_true, y_pred)
    fn = false_negative(y_true, y_pred)
    recall = tp / (tp + fn)
    return recall

F1

PrecisionとRecallの両方が高い値をとるモデルがよいモデルといえます。

そこで、Precision とRecallを重み付き平均により合わせたメトリクスがF1です。

 F1 = 2PR / (P + R)

 F1 = 2TP / (2TP + FP + FN)

f:id:harukary7518:20210918120835j:plain:w200

def f1(y_true, y_pred):
    p = precision(y_true, y_pred)
    r = recall(y_true, y_pred)
    score = 2 * p * r / (p + r)
    return score

TPR: True Positive Rate

TPRはRecallと同じ定義で、sensitivityを表します。

 TPR = TP / (TP + FN)

def tpr(y_true, y_pred):
    return recall(y_true, y_pred)

FPR: False Positive Rate

1-FPRをTNR (True Negative Rate)と呼び、specificityを表します。

 FPR = FP / (TN + FP)

def fpr(y_true, y_pred):
    fp = false_positive(y_true, y_pred)
    tn = true_negative(y_true, y_pred)
    return fp / (tn + fp)

ROC curve: Receiver Operating Characteristic curve

2クラスで閾値を変化させた場合のTPR-FPRのグラフです。

f:id:harukary7518:20210918120830p:plain:w300

この曲線の積分値をAUC (Area Under Curve)と呼び、これも指標になる。

  • AUC=1は、完璧なモデルを意味する。しかし実際には、Validationでミスをしている可能性が高い。

  • AUC=0は、すべてを誤るモデルを意味する。反転することでAUC=1と等しくなるため、AUC=1と同じくValidationでミスをしている可能性が高い。

  • AUC=0.5は、完全にランダムなモデルを意味する。まったくあてにならないモデルであるといえる。

具体的に、PositiveサンプルとNegativeサンプルをそれぞれランダムに取り出したとき、PositiveサンプルがNegativeサンプルより高い予測がされる(つまり正しい予測)確率をAUCは表します。

問題によって、結果として確率が欲しい場合と、クラス(0 or 1)が欲しい場合があります。 もしクラスが欲しい場合は、適切な閾値を選ぶ必要があります。

この閾値の選択に、ROCカーブは有用です。 TPRが高くFPRが小さい、具体的には、グラフの左上の角の閾値がベストです。

Log Loss

2クラスの場合の定義を下に示します。

 Log Loss = - ( t \log(p) + (1 - t) \log(1 - p) )

ここで、 t, p はそれぞれ、真値、予測値を表しています。

Log Lossは、誤った予測や大きく異なる予測値に対し、大きなペナルティを課します。 たとえ正解しても、信頼度が低ければ値が小さくなります。

例えば、精度100%の予測をした場合でも、予測値が閾値付近で信頼度が高くない場合、Log Lossは大きくなります。

def log_loss(y_true, y_proba):
    epsilon = 1e-15
    loss = []
    for yt, yp in zip(y_true, y_proba)
        yp = np.clip(yp, epsilon, 1 - epsilon)
        temp_loss = - 1.0 * (yt * np.log(yp) + (1 - yt) * np.log(1 - yp))
        loss.append(temp_loss)
    return np.mean(loss)

Conclusion

一応勉強したことを記録しようとしたら、長すぎて3つに分けました。 それでもかなり長い。

技術ブログをやっている方々はどんなパイプラインでやっているのかすごく気になりますね。 一つ書くだけで心折れそうになる。

とりあえず2クラス問題を前提に、機械学習のメトリクスを整理できました。 次の多クラス問題と、マルチラベルのメトリクスは、今回出てきたメトリクスをもとにしてできているので、少しは楽になるかな。

Link

GitHub - abhishekkrthakur/approachingalmost: Approaching (Almost) Any Machine Learning Problem

機械学習のメトリクス2【AAAMLP】 - harukary.log

機械学習のメトリクス3【AAAMLP】 - harukary.log