분류 모델의 평가 지표

sy_healing·2022년 5월 9일
1

머신러닝

목록 보기
13/16

머신러닝 모델은 일반적으로 모델 회귀냐 분류냐에 따라 서로 다른 평가 지표를 사용해야 한다.
지난 번에 회귀 모델의 평가지표로 MAE, MSE, RMSE, R2를 정리했다. 오늘은 분류 모델의 평가지표에 대해 알아보고자 한다.


분류 모델의 평가지표를 알기 전에 우리는 Confusion Matrix에 대해 알아야 한다.

💡 혼동 행렬(Confusion Matrix)

혼동 행렬은 예측 값과 실제 값에 따라 TP, TN, FP, FN을 판단하기 위해 사용하는 표이다.
이를 통해 모델의 예측 오류가 얼마인지, 어떤 유형의 예측 오류가 발생하고 있는지를 확인할 수 있다.

Confusion Matrix에 대한 개념을 바탕으로 정확도, 정밀도, 재현율 등의 평가지표를 계산하게 된다/

True Positive: 실제 Positive인 정답을 Positive라고 예측 (True)
True Negative: 실제 Negative인 정답을 Negative라고 예측 (True)
False Positive: 실제 Negative인 정답을 Positive라고 예측 (False) – Type I error
False Negative: 실제 Positive인 정답을 Negative라고 예측 (False) – Type II error

📖 Python

# 사이킷런에서 confusion_matrix()를 통해 혼동 행렬을 구할 수 있다.
# ndarray 형태로 출력되며, [0,0]=TN, [0,1]=FP, [1,0]=FN, [1,1]=TP이다.
TN | FP
--------
FN | TP

from sklearn.metricx import confusion_matrix

confusion_matrix(y_true, y_pred)

---------------------------------------------------------------------
# 파이프라인을 통해 랜덤포레스트 모델을 구성함

from category_encoders import OrdinalEncoder
from sklearn.impute import SimpleImputer
from sklearn.ensemble import RandomForestClassifier
from sklearn.pipeline import make_pipeline
from sklearn.metrics import plot_confusion_matrix

pipe = make_pipeline(OrdinalEncoder(),
  				     SimpleImputer(),
  				     RandomForestClassifier(n_estimators = 100, n_jobs = -1,
    									random_state = 10, oob_score = True))

pipe.fit(X_train, y_train)

plot_confusion_matrix(pipe, X_val, y_val, cmap=plt.cm.Blues)

confusion_matrix 공식문서
plot_confusion_matrix 공식문서


  • Accuracy=TP+TNTP+TN+FP+FNAccuracy = \frac{TP + TN}{TP + TN + FP + FN}
  • Precision=TPTP+FPPrecision = \frac{TP}{TP + FP}
  • Recall=TPTP+FNRecall= \frac{TP}{TP + FN}
  • F1Score=2PrecisionRecallPrecision+RecallF1 Score = 2*\frac {Precision * Recall}{Precision + Recall}

💡 정확도(Accuracy)

  • Accuracy=TP+TNTP+TN+FP+FNAccuracy = \frac{TP + TN}{TP + TN + FP + FN}

정확도는 전체 예측 건수에서 정답을 맞친 건수의 비율이다.

정확도는 최적의 평가 지표일까?

정확도를 사용할 때 주의해야 할 점이 있다. 이진 분류인 데이터는 머신러닝 모델의 성능을 정확하게 판별하지 못하기 때문에 정확도 수치 하나만 가지고 성능을 평가하지 않는다는 것이다.

단편적으로 Accuracy만 봤을 때 좋은 값이 도출되지만, 실제로 부정인 것은 예측을 잘 못하기 때문에 좋은 모델이라고 하기 어렵다. 이러한 결과는 데이터의 불균형으로 인해 발생한다.

예를 들어 9명의 건강한 사람과 1명의 질병을 가진 사람이 있다면, Accuracy는 90%가 된다. 그럼 정확도가 높으니 좋은 모델인가? 아니다.
이 경우의 머신러닝 모델은 모든 사람을 건강한 사람으로 예측할 경우에도 Accuracy는 90%가 된다. 결국 정확도는 90%이지만, 이 모델은 질병을 가진 사람을 건강한 사람으로 분류되기도 하기 때문에 좋은 모델이 아니다.

결론
불균형한 레이블 데이터 셋에서는 평가 지표로 단독 사용해서는 안된다. 정확도의 한계점을 극복하기 위해 여러 가지 분류 지표와 함께 사용해야 한다.


💡 정밀도(Precision) & 재현율(Recall)

  • Precision=TPTP+FPPrecision = \frac{TP}{TP + FP}

  • Recall=TPTP+FNRecall = \frac{TP}{TP + FN}

정밀도와 재현율은 Positive 데이터에 조금 더 집중한 평가 지표이다.

Precision는 Positive로 예측한 것 중 실제로 맞춘 비율로 Positive 예측 성능을 더욱 정밀하게 측정하기 위한 평가 지표이다. ⇒ FP를 낮추는 것에 집중

Recall실제 Positive를 얼마나 잘 예측했는지를 나타내는 지표이다.
(=민감도(Sensitivity) or TPR(True Positive Rate)) ⇒ FN를 낮추는 것에 집중

두 지표 모두 1이 가장 이상적인 값이다. (즉, 잘못 예측하는 FP, FN이 0이 되는 상태)

그렇다면 언제 정밀도를 사용하고, 또 언제 재현율을 사용하는가?
이것은 주어진 문제에서 FN과 FP중 어떤 경우가 더 크리티컬(critical) 한가를 생각해보면 된다.

예를 들어보자.

FP스팸메일이 아닌데 스팸메일이라고 판단
FN스팸메일인데 스팸메일이 아니라고 판단

위와 같은 경우는 FP가 더욱 크리티컬 한 상황이다. 중요한 메일인데 스팸으로 간주되었다고 생각해보면 알 수 있다.

FP암이 아닌 환자에게 암 진단을 하는 경우
FN암 환자에게 암이 아니라고 진단하는 경우

이러한 경우는 FN이 훨씬 크리티컬하다. 암 환자에게 암이 아니라고 할 경우, 생명에 치명적일 수 있다.

이렇게 상황에 따라 FP와 FN을 잘 생각해보고, 적절한 평가지표를 사용해야한다.
FP가 크리티컬 한 경우는 정밀도를, FN이 더 크리티컬하면 재현율을 사용하는 것이 바람직하다.

가장 좋은 평가는 재현율과 정밀도 모두 높은 값을 얻는 것이다. 만약 둘 중 어느 한 평가 지표만 매우 높고, 다른 수치는 매우 낮으면 바람직 하지 않은 결과이다.

📖 Python

# 위의 pipeline을 그대로 사용한다고 가정!

from sklearn.metrixs import classification_report
# (import precision_score, recall_score, f1_score, fbeta_score)

pred = pipe.predict(X_val)

print(classification_report(y_val, pred))

'''
아래와 같은 결과 값이 나왔다고 하면,
(1)positive 기준으로 정밀도 0.75, 재현율 0.70, f1-score 0.72 임을 알 수 있다.
'''


classificaion_report 공식문서

정밀도 & 재현율 Trade-off

머신러닝에서 Bias와 Variance를 둘 다 동시에 낮출 순 없는 것처럼, precision과 recall 도 둘 다 모두 최댓값이 될 수는 없다. precision이 극도로 높아진다면 recall은 낮아질 수 밖에 없고, recall이 극도로 높아진다면 precision 값이 낮아 질 수 밖에 없다.

일반적으로 이진 분류에서는 임계값을 0.5 (50%)로 정하고 임곗값보다 확률이 크면 Positive, 작으면 Negative로 결정한다. 이 임계값을 조정해서 정밀도 또는 재현율을 강조할 수 있다.

임계값을 낮춘다는 것은 모델이 Positive라고 예측하는 횟수가 많아진다는 뜻이다.
그렇다면 FP가 높아지고 FN은 낮아진다는 것인데, 이는 즉 정밀도가 낮아지고 재현율이 커진다는 것이다.
반대로 임계값을 높이면 FP는 낮아지고 FN은 높아져서, 정밀도는 높아지고 재현율은 낮아진다.

이러한 임계치 변경을 Scikit-learn을 통해서 구현할 수 있다.

  • 임계값⬇️ = FP⬆️, FN⬇️ = precision⬇️, recall⬆️
  • 임계값⬆️ = FP⬇️, FN⬆️ = precision⬆️, recall⬇️

📖 Python

# predict() : 예측 결과 클래스값
# predict_probba() : 예측 확률 결과(0 또는 1 일때 확률)

pred = pipe.predict(X_val)
pred_proda = pipe.predict_proba(X_val) #[:, 1] = 1로 판단할 proba만 추출

# DataFrame으로 표현해서 한 눈에 보기쉽게!
import numpy as np
result = np.concatenate([pred_proba, pred.reshape(-1,1)], axis=1)
data = pd.DataFrame(result)
data.rename(columns={0:'probability of Negative(0)',
					 1: 'probability of Positive(1)',
                     2:'predict'}, inplace=True)


'''
임계치 default = 0.5를 기준으로 positive(1)의 값이
0.5 이상이면 1, 0.5 미만이면 0으로 예측한다. 이는 임계값을 조절해서 조정 가능하다.
'''

#임계값(threshold)의 변화로 recall, precision 변화 보기

def convert(x, threshold):
  if x > threshold :
    return 1
  else :
    return 0

# array에 함수를 적용시키기 위해 vectorize
convert = np.vectorize(convert) 

val_pred1 = convert(val_pred_proba, 0.5)
val_pred2 = convert(val_pred_proba, 0.6)
val_pred3 = convert(val_pred_proba, 0.7)

print(classification_report(y_val, val_pred1))
print(classification_report(y_val, val_pred2))
print(classification_report(y_val, val_pred3))


# 임계값이 커짐에 따라 precision이 커지고, recall은 작아짐을 확인할 수 있다.


💡 F1 Score & Fbeta Score

F1 score는 Recall과 Precision을 이용하여 조화평균을 구한 지표이다.
불균형 분류 문제에서 평가 지표로 주로 사용되며, 정밀도와 재현율 어느 한쪽으로 치우치지 않는 수치를 나타낼 때 F1 score는 상대적으로 높은 값을 가진다.

Fbeta score는 Recall과 Precision 중 어떤 지표에 가중치를 주어 계산할 것인가를 위한 평가 지표이다. 만약 bate 값이 1보다 크면 recall에, 1보다 작으면 precision에 가중치를 준다는 의미이다.
예를 들어 recall에 2배의 가중치를 준다면 beta=2를 넣으면 된다. beta=1이면 F1score이다.


💡 ROC Curve, AUC Score

AUC score는 ROC curve과 이에 기반한 값으로, 이진 분류의 예측 성능 측정에서 중요하게 사용되는 지표이다.

ROC curve를 이해하려면 TPR와 FPR를 알아야 한다.

  • TPR(True Positive Rate) = Recall = TP / TP+FN
  • FPR(False Positive Rate) = FP / FP+TN

TPR은 실제 positive 중 positive라고 예측, FPR은 실제 negative 중 positive라고 잘못 예측한 것이다.

😶‍🌫️ROC curve

ROC curve는 임계값에 대한 TPR, FPR의 변화를 곡선으로 나타낸 것이다.
X축에 FPR, Y축에 TPR을 두어 최적의 임계값을 찾는 것이다.

TPR이 클수록, FPR이 작을수록 성능이 좋으며 이것은 ROC Curve가 좌측 상단측을 향할 때 모델의 성능이 좋다고 판단할 수 있다. (좌측 상단은 TPR =1, FPR = 0이니까)

또한 그레프를 통해서 좌측 상단에서의 임계값일 때 성능이 가장 좋다는 것도 판단할 수 있다.
즉, 직선일 때 가장 성능이 안좋다고 보면 된다.

😶‍🌫️AUC(Area Under the Curve) Score

AUC score는 말 그대로 커브 아래 공간을 말하는 것인데, 이 공간의 넓이가 넓을수록 성능이 좋은 모델이라는 뜻이다.

이는 sklearn에서 roc_curve, roc_auc_score를 통해 확인 할 수 있다.

📖 Python

# Decision Tree와 Random Forest의 ROC curve를 비교해보자.
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier

pipe_dt = make_pipeline(OrdinalEncoder(),
					SimpleImputer(),
                    DecisionTreeClassifier(random_state=42, max_depth=20))

pipe_rf = make_pipeline(OrdinalEncoder(),
					SimpleImputer(),
                    RandomForestClassifier(random_state=42, 
                    					   max_depth=20, n_estimators=100))

pipe_dt.fit(X_train, y_train)
pipe_rf.fit(X_train, y_train)

----------------------------------------------------------------------------
from sklearn.metrics import roc_curve, roc_auc_curve

pred_dt = pipe_dt.predict_proba(X_val)[:, 1]
pred_rf = pipe_rf.predict_proba(X_val)[:, 1]

def make_roc(y_val, y_pred) :
  fpr, tpr, thresholds = roc_curve(y_val, y_pred)
  sns.scatterplot(fpr, tpr)
  plt.xlabel('FPR')
  plt.ylabel('TPR')

make_roc(y_val, val_pred_dt)
make_roc(y_val, val_pred_rf)

'''
파란색이 의사결정 트리, 주황색이 랜덤포레스트이다
랜덤 포레스트의 roc curve가 더 좌측에 있으므로, 성능이 더 좋음을 알 수 있다.
'''

# ⭐최적의 임계값을 확인하는 방법

fpr, tpr, thresholds = roc_curve(y_val, val_pred_rf)

optimal_threshold = thresholds[np.argmax(tpr - fpr)]

roc_curve 공식문서
roc_auc_score 공식문서

📖 Python

from sklearn.metrics import roc_auc_score

print('Decision Tree AUC Score :', roc_auc_score(y_val, val_pred_dt))
print('Random Forest AUC Score :', roc_auc_score(y_val, val_pred_rf))

'''
Random Forest의 AUC score가 더 높고, 성능이 좋음을 알 수 있다.
'''

0개의 댓글