본문 바로가기

Lecture ML

머신러닝 강좌 #8] 머신러닝 모델 성능 평가 - 정밀도(Precision), 재현율(Recall)

반응형

정밀도와 재현율은 Positive 데이터 세트의 예측 성능에 좀 더 초점을 맞춘 평가 지표입니다.  본 장을 진행하기 전 간략하게 내용을 보려면 페이지(https://nicola-ml.tistory.com/88?category=861724) 하단을 다시 한번 복습해주세요.

 

정밀도 = TP / (FP+TP)

재현율 = TP / (FN+TP)

 

정밀도는 예측을 Positive로 한 대상 중에 예측과 실제 값이 Positive로 일치한 데이터의 비율을 뜻합니다. 공식의 분모인 FP+TP는 예측을 Positive로 한 모든 데이터 건수이며, 분자인 TP는 예측과 실제 값이 Positive로 일치한 데이터 건수입니다. Positive예측 성능을 더욱 정밀하게 측정하기 위한 평가 지표로 양성 예측도라고도 불립니다.

 

재현율은 실제 값이 Positive인 대상 중에 예측과 실제 값이 Positive로 일치한 데이터의 비율을 뜻합니다. 공식의 분모인 FN+TP는 실제 값이 Positive인 모든 데이터 건수이며 분자인 TP는 예측과 실제 값이 Positive로 일치한 데이터 건수입니다. 이를 민감도(Sensitivity) 또는 TPR(Tru Positive Rate)라고도 불립니다.

 

정밀도와 재현율 지표의 중요성은 비즈니스의 특성에 따라 달라질 수 있습니다. 재현율이 중요 지표인 경우는 실제 Positive 양성 데이터를 Negative로 잘못 판단하게 되면 업무상 큰 영향이 발생하는 경우입니다.  암환자 예측이나 보험사기와 같은 금융 사기 적발 모델도 재현율이 중요합니다. 정밀도도 중요하나 업무적 특성상 재현율이 더 중요하다는 의미입니다. 보통은 재현율이 정밀도보다 상대적으로 중요한 업무가 많지만, 스팸메일 여부 적발 모델의 경우 정밀도가 더 중요합니다. 

 

  • 재현율이 상대적으로 더 중요한 지표인 경우는 실제 Positive양성인 데이터 예측을 Negative로 잘못 판단하게 되면 업무상 큰 영향이 발생하는 경우
  • 정밀도가 상대적으로 더 중요한 지표인 경우는 실제 Negative음성인 데이터 예측을 Positive 양성으로 잘못 판단하게 되면 업무상 큰 영향이 발생하는 경우

 

다시 한번 재현율과 정밀도의 공식을 살펴보면, 재현율=TP/(FN+TP), 정밀도=TP/(FP+TP)입니다. 재현율과 정밀도 모두 TP를 높이는 데 동일하게 초점을 맞추지만, 재현율은 FN을 낮추는데 정밀도는 FP를 낮추는 데 초점을 맞춥니다. 이 같은 특성 때문에 재현율과 정밀도는 서로 보완적인 지표로 분류의 성능을 평가하는 데 적용됩니다. 가장 좋은 것은 정밀도와 재현율 모두 높은 수치를 얻는 것입니다. 반면에 둘 중 어느 한 평가 지표만 매우 높고, 다른 수치는 매우 낮은 결과를 나타내는 경우는 바람직하지 않습니다.

 

사이킷런은 정밀도, 재현율, 정확도, Confusion Matrix를 구하는 API가 존재합니다.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.metrics import accuracy_score, precision_score, recall_score, confusion_matrix
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression

def fillna(df):
    df['Age'].fillna(df['Age'].mean(), inplace=True)
    df['Cabin'].fillna('N', inplace=True)
    df['Embarked'].fillna('N', inplace=True)
    df['Fare'].fillna(0, inplace=True)
    return df

def drop_features(df):
    df.drop(['PassengerId', 'Name', 'Ticket'], axis=1, inplace=True)
    return df

def format_features(df):
    df['Cabin'] = df['Cabin'].str[:1]
    features = ['Cabin', 'Sex', 'Embarked']
    for feature in features:
        le = LabelEncoder()
        le = le.fit(df[feature])
        df[feature] = le.transform(df[feature])
    return df

def transform_features(df):
    df = fillna(df)
    df = drop_features(df)
    df = format_features(df)

    return df

def get_clf_eval(y_test, pred):
    confusion = confusion_matrix(y_test, pred)
    accuracy = accuracy_score(y_test, pred)
    precision = precision_score(y_test, pred)
    recall = recall_score(y_test, pred)

    print('confusion matric :', confusion)
    print('accuracy :', accuracy)
    print('precision :', precision)
    print('recall :', recall)

titanic_df = pd.read_csv('./titanic_train.csv')
y_titanic_df = titanic_df['Survived']
X_titanic_df = titanic_df.drop('Survived', axis=1)
X_titanic_df = transform_features(X_titanic_df)
X_train, X_test, y_train, y_test = train_test_split(X_titanic_df, y_titanic_df, test_size=0.20, random_state=0)

lr_clf = LogisticRegression(solver='liblinear')
lr_clf.fit(X_train, y_train)
pred = lr_clf.predict(X_test)
get_clf_eval(y_test, pred)

 

 


정밀도/재현율 트레이드오프

 

분류하려는 업무의 특성상 정밀도 또는 재현율이 특별히 강조돼야 할 경우 분류의 결정 임곗값(Threshold)을 조정해 정밀도 또는 재현율의 수치를 높일 수 있습니다. 하지만 정밀도와 재현율은 상호 보완적인 평가 지표이기 때문에 어느 한쪽을 강제로 높이면 다른 하나의 수치는 떨어지기 쉽습니다. 이를 정밀도/재현율의 트레이드오프(Trade-Off)라고 부릅니다.

 

사이킷런의 분류 알고리즘은 예측 데이터가 특정 레이블(Label, 결정 클래스 값)에 속하는지를 계산하기 위해 먼저 개별 레이블별로 결정 확률을 구합니다. 그리고 예측 확률이 큰 레이블 값으로 예측하게 됩니다. 가령 이진 분류 모델에서 특정 데이터가 0이 될 확률이 10%, 1이 될 확률이 90%로 예측됐다면 최종 예측은 더 큰 확률을 가진, 즉 90% 확률을 가진 1로 예측합니다. 일반적으로 이진 분류에서는 이 임곗값을 0.5, 즉 50%로 정하고 이 기준 값보다 확률이 크면 Positive, 작으면 Negative로 결정합니다.

 

사이킷런은 개별 데이터별로 예측 확률을 반환하는 메서드인 predict_proba()를 제공합니다. predict_proba()메서드는 학습이 완료된 사이킷런 Classsifier객체에서 호출이 가능하며 테스트 피처 데이터 세트를 파라미터로 입력해주면 테스트 피처 레코드의 개별 클래스 예측 확률을 반환합니다. predict()메소드와 유사하지만 단지 반환 결과가 예측 결과 클래스 값이 아닌 예측 확률 결과입니다.

 

predict_proba()를 수행해 반환되는 ndarray는 첫 번째 칼럼이 클래스 값 0에 대한 예측 확률, 두 번째 칼럼이 클래스 1에 대한 예측확률입니다.

 

titanic 예제 하단에 입력하여 실행.

반환 결과인 ndarray는 0과 1에 대한 확률을 나타내므로 첫번째와 두 번째 칼럼을 더하면 1이 됩니다. 그리고 맨 마지막 줄의 predict() 메서드의 결과 비교에서도 나타나듯이, 두 개의 칼럼 중에서 더 큰 확률 값으로 predict() 메서드가 최종 예측하고 있습니다.

 

 

정밀도와 재현율의 맹점

Positive예측의 임곗값을 변경함에 따라 정밀도와 재현율의 수치가 변경됩니다. 임곗값의 이러한 변경은 업무 환경에 맞게 두 개의 수치를 상호 보완할 수 있는 수준에서 적용돼야 합니다. 그렇지 않고 단순히 하나의 성능 지표 수치를 높이기 위한 수단으로 사용돼서는 안 됩니다. 다음은 정밀도 또는 재현율 평가 지표 수치 중 하나를 극단적으로 높이는 방법이지만 숫자 놀엠에 불과한 방법입니다.

 

정밀도가 100%가 되는 방법

확실한 기준이 되는 경우만 Positive로 예측하고 나머지는 모두 Negatove로 예측합니다. 예를 들어 환자가 80세 이상이고 비만이며 이전에 암 진단을 받았고 암 세포의 크기가 상위 0.1%이상이면 무조건 Positive, 다른 경우는 Negative로 예측하는 겁니다.

정밀도 = TP / (TP+FP)입니다. 전체 환자 1000명 중 확실한 Positive징후만 가진 환자는 단 1명이라고 하면 이 한 명만 Positive로 예측하고 나머지는 모두 Negative로 예측하더라도 FP는 0, TP는 1이 되므로 정밀도는 1/(1+0)으로 100%가 됩니다.

 

재현율이 100%가 되는 방법

모든 환자를 Positive로 예측하면 됩니다. 재현율=TP/(TP+FN)이므로 전체 환자 1000명을 다 Positive로 예측하는 겁니다. 이 중 실제 양성인 사람이 30명 정도라도 TN이 수치에 포함되지 않고 FN은 아예 0이므로 1/(1+0)으로 100%가 됩니다.

 

이처럼 정밀도와 재현율 성능 수치도 어느 한쪽만 참조하면 극단적인 수치 조작이 가능합니다. 따라서 정밀도 또는 재현율 중 하나만 스코어가 좋고 다른 하나는 스코어가 나쁜 분류는 성능이 좋지 않은 분류로 간주할 수 있습니다. 

 

이런 부분을 보완하기 위해 만든것이 정밀도와 재현율의 수치가 적절하게 조합돼 분류의 종합적인 성능 평가에 사용될 수 있는 평가 지표가 필요합니다. 바로 F1 스코어입니다.

반응형