본문 바로가기
Machine Learning/scikit-learn

[이진 분류_성능 평가 지표] 정밀도 / 재현율

by ISLA! 2023. 8. 20.

 

정밀도와 재현율은 Positive 데이터 세트의 예측 성능에 좀 더 초점을 맞춘 평가 지표

 

 

 # 정밀도

  • Positive 예측 성능을 더욱 정밀하게 측정하기 위한 평가 지표로, 양성 예측도라고도 불림
  • 예측을 Positive로 한 대상 중실제 값도 Positive로 일치한 데이터 비율
  • 사이킷런의 API는 다음과 같다
from sklearn.metrics import precision_score

 

# 재현율

  • 실제 값이 Positive인 대상 중에 예측도 Positive로 일치한 데이터 비율
  • 민감도 또는 TPR(True Positive Rate)라고도 불림
  • 사이킷런의 API는 다음과 같다
from sklearn.metrics import recall_score

 

 

🔍 정밀도와 재현율은 언제 중요할까?

  • 재현율이 중요 지표인 경우는 실제 양성인 데이터를 음성으로 잘못 판단하게 되면 업무상 큰 지장이 생기는 경우이다
    • 예시 1) 암 판단 모델에서 암 양성 환자를 음성으로 잘못 판단하게 되면 큰일이 난다! 🤕
    • 예시 2) 금융 사기 적발 모델도, 금융사기가 맞는데 아닌 것으로 잘못 판단하면 큰 손실이 발생! 🤑
  • 정밀도가 중요한 경우는 실제 음성인 데이터를 양성으로 잘못 판단하게 되면 업무상 큰 영향이 생기는 경우이다
    • 예 : 스팸메일 여부(스팸으로 예측했는데, 실제 스팸이 아닌 경우 메일을 받을 수 없음)
  • 보통은 재현율이 정밀도보다 상대적으로 중요한 업무가 많다

 

🔍 정밀도와 재현율, 둘 중 하나만 써야 하나? 👉 No!

  • 두 경우 모두, TP를 높이는 데 동일하게 초점을 맞춤
    • 재현율은 FN(실제 양성, 예측 음성)을 낮추는데 초점
    • 정밀도는 FP(실제 음성, 예측 양성)을 낮추는데 초점
    • 따라서 재현율과 정밀도는 서로 보완적인 지표가 되어, 분류 성능 평가에 함께 사용됨
  • 가장 좋은 성능 평가 결과 ▶︎ 정밀도와 재현율 모두 높은 수치를 얻는 것
  • 둘 중 한 쪽만 매우 높게 나타난다면 바람직하지 않음 

🧑🏻‍💻 예제

  • 분류 평가 지표를 모두 호출하는 함수 작성
from sklearn.metrics import accuracy_score, precision_score, recall_score, confusion_matrix

# 📍 여러가지 평가지표를 한번에 호출하는 함수 
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('오차 행렬')
    print(confusion)
    print('정확도:{0:.4f}, 정밀도:{1:.4f}, 재현율:{2:.4f}'.format(accuracy, precision, recall))

 

  • 타이타닉 데이터로 예측 및 평가 수행
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression

titanic_df = pd.read_csv('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.2, random_state=11)
#solver의 기본값은 lbfgs(데이터 크고, 다중분류일때 적합), liblinear(작은데이터셋의 이진분류에 적합)
lr_clf = LogisticRegression(solver = 'liblinear')
lr_clf.fit(X_train, y_train)
pred = lr_clf.predict(X_test)

get_clf_eval(y_test, pred)

 

결과

 

🔍 정밀도와 재현율의 트레이드오프(Trade-off)

  • 정밀도와 재현율이 특별히 강조돼야 할 경우 분류의 결정 임곗값을 조정해 정밀도/재현율 수치를 높일 수 있다
  • 하지만, 두 지표는 상호 보완적이라 한 쪽을 강제로 높이면, 한쪽이 떨어지기 쉽다 
  • 일반적으로 이진분류에서는 임곗값을 0.5(50%)로 정하고 이 값보다 확률이 크면 Positive, 작으면 Negative로 분류한다

 

🧑🏻‍💻 개별 데이터 별로 예측 확률을 반환하는 predict_proba( ) 

  • 학습이 완료된 사이킷런 Classifier 객체에서 호출 가능
  • 테스트 피처 데이터 세트를 피라미터로 입력하주면, 테스트 피처 레코드의 개별 클래서 예측 확률을 반환
  • 이진 분류에서 predict_proba()를 수행해 반환되는 ndarray는,
    • 첫 번째 칼럼이 클래스 값 0에 대한 예측 확률
    • 두 번째 칼럼이 클래스 값 1에 대한 예측 확률

 

pred_proba = lr_clf.predict_proba(X_test)
pred = lr_clf.predict(X_test)

print('pred_proba() 결과 shape: {0}'.format(pred_proba.shape))
print('\npred_proba array에서 앞 3개만 샘플로 추출: \n', pred_proba[:3])

# 예측 확률 array와 예측 결괏값 array를 병합해 예측 확률과 결괏값을 한번에 확인
pred_proba_result = np.concatenate([pred_proba, pred.reshape(-1, 1)], axis = 1)
print('\n두 개의 class 중, 더 큰 확률을 클래스 값으로 예측 \n', pred_proba_result[:3])

 

 

 

🧑🏻‍💻 정밀도와 재현율 관계를 보여주는 precision_recall_curve( ) 

입력 파라미터 y_true : 실제 클래스값 배열 (배열크기 = 데이터 건수)
probas_pred : positive 칼럼의 예측 확률 배열 (배열크기 = 데이터 건수)
반환 값 정밀도 : 임곗값 별 정밀도 값을 배열로 반환
재현율 : 임곗값 별 재현율 값을 배열로 반환
임곗값

 

  • 활용 예시 및 결과 : 임곗값이 낮을수록, 많은 양성 예측으로 인해 재현율 값이 극도로 높아지고, 정밀도 값이 극도로 낮아진다
  • 그래프에서 두 선이 만나는 정도의 구간이 임곗값으로 적합하다 (두 개 수치의 상호 보완!)
from sklearn.metrics import precision_recall_curve
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker

def precision_recall_curve_plot(y_test, pred_proba_c1):
    # threshold ndarray와 이 threshold에 따른 정밀도, 재현율 ndarray 추출.
    precisions, recalls, thresholds = precision_recall_curve( y_test, pred_proba_c1)

    # X축을 threshold값으로, Y축은 정밀도, 재현율 값으로 각각 Plot 수행. 정밀도는 점선으로 표시
    plt.figure(figsize=(8,6))
    threshold_boundary = thresholds.shape[0]
    plt.plot(thresholds, precisions[0:threshold_boundary], linestyle='--', label='precision')
    plt.plot(thresholds, recalls[0:threshold_boundary],label='recall')

    # threshold 값 X 축의 Scale을 0.1 단위로 변경
    start, end = plt.xlim()
    plt.xticks(np.round(np.arange(start, end, 0.1),2))

    # x축, y축 label과 legend, 그리고 grid 설정
    plt.xlabel('Threshold value')
    plt.ylabel('Precision and Recall value')
    plt.legend()
    plt.grid()
    plt.show()
    
   
# 함수 적용
precision_recall_curve_plot(y_test, lr_clf.predict_proba(X_test)[:,1])

 

 

👉 결국, 임곗값에 따라 정밀도와 재현율의 수치가 달라지는데,
      이 둘을 적절하게 조합하여 분류의 종합적인 성능평가에 사용될 수 있는 지표가 필요해진다.

 

정답은?
F1 스코어로, 다음 포스팅에 이어진다
728x90