본문 바로가기

Study doc./Summary

모델 평가하기 - 정확도만 높으면 좋은 모델?

* 카테고리 별로 읽기보다, 글 순서대로 읽는걸 추천드려요.

 

순서

  1. 정확도의 문제
  2. 재현율, 정밀도
  3. F score
  4. 임계값 조절
  5. PR curve vs ROC curve
  6. Confusion Matrix

1. 정확도의 문제

모델 성능을 비교할 때 그리고 그리드 서치를 이용하여 최적은 매개변수를 구할 때,

어떤 값을 기준으로 모델을 선택하고 매개변수를 선택하시나요?

 

이 주제에 대한 고민을 안해보셨다면 대부분 정확도를 기준으로 선택하셨을텐데,

정확도만을 가지고 성능을 비교하면 어떤 문제점이 있는지 알려드릴게요!

 

가장 유명하게 알려진 예시로 암 환자의 진단을 들어보겠습니다.

어떤 회사에서 두 종류의 암 진단 키트를 발명했다고 가정해봅시다 !!

A 키트의 정확도는 99% 

B 키트의 정확도는 95%

라고 발표했을 때, 우리는 A 키트가 최고! 라고 할 수 있을까요?

 

정답은 모른다! 입니다. 

100명의 환자 중 1명이 암 환자인 집단에서 '100명 모두 암에 걸리지 않았다'고 한다면

99명에 대한 진단은 맞춘 것이기 때문에 정확도가 99% 일테니깐요.

(가장 중요한 1명의 암 환자에게도 정상이라고 진단해버렸습니다.)

 

암 진단과 같은 경우에는 암에 걸리지 않은 환자를 암에 걸린 환자라고 잘못 진단하는 한이 있더라도 

암 환자를 정확하게 구별해야 합니다. 

다시 말해 100명 중 1명이 암 환자인 집단에서 5명이 암 환자라고 진단하게 되더라도(정확도 95%)

1명의 암 환자는 꼭 파악해야 한다는 것입니다.

즉 B 키트가 A 키트보다 좋다고 할 수 있는데, 정확하게 말하기 위해서는 다른 정보가 필요합니다. 

 


2. 재현율, 정밀도

그래서 있는게 '재현율''정밀도'의 개념입니다.

재현율은 실제 암에 걸린 사람 중(1명) 해당 키트가 암에 걸렸다고 진단(A키트 : 0명/ B키트 : 1명)하는 확률입니다.

B 키트가 5명이 아닌 1명인 이유 - '실제 암에 걸린 사람 중'에서 키트가 암에 걸렸다고 진단한 경우의 수이기 때문

A 키트의 재현율 = 0 / 1 = 0%

B 키트의 재현율 = 1 / 1 = 100%

이렇게 보니 B 키트가 A 키트보다 좋다고 할 수 있겠네요.

 

하지만 B 키트는 4명에 대해서 잘못 판단했습니다. 

극단적으로 얘기해서 100명 중 1명이 암 환자인데 50명을 암 환자라고 판단해버리면 이것 역시 좋은 키트라고 판단 할 수 없겠죠? (그래도 재현율은 100%로 동일함)

따라서 재현율과는 또 다른 지표가 필요한데, 그게 바로 '정밀도' 입니다.

 

정밀도는 해당 키트가 암이라고 진단한 경우(A : 0명/ B : 5명) 중 실제 암 환자인 경우(A : 0명/ B : 1명) 입니다.

A 키트의 정밀도 = 0 / 0 

B 키트의 정밀도 = 1 / 5 = 20%

 

이제 키트를 사용하는 사람의 기준에 따라 둘 중 하나를 선택하거나 아무것도 선택하지 않을 것입니다.

 

 


3. F Score

키트 개발자는 B 키트의 재현율은 100%이니, 정밀도만 높이기 위해 임계값을 조절했습니다.

그리고 마침내 5명이라고 진단하던 키트를 1명으로 줄이는 데에 성공했습니다.

이제 정밀도가 100%이니 다시 성능을 발표하려는 찰나, 재현율이 0%로 떨어진 것을 확인했습니다. 

정밀도가 높아지니 재현율이 말썽입니다.

 

사실 정밀도와 재현율은 반비례하는 관계이기 때문인데요,

직관적으로 생각해보면 5명을 양성이라고 진단하던 키트가 1명을 양성으로 진단하려다 보니,

그 사람이 실제 양성인 확률이 줄어드는 당연한 이치임을 알 수 있습니다.

그럼 서로 반비례하는 관계에서 둘 다 적당히 좋은 값을 찾는 방법이 없을까? 하고 봤더니!!

'F score' 라는 지표가 있더랍니다

 

F score는 재현율과 정밀도의 조화평균으로 재현율과 정밀도가 비슷해질 수록 높은 F score 는 높아지게 됩니다.

높은 F score 를 기준으로 판단하겠다는 의미는 재현율과 정밀도가 둘 다 좋은(한 쪽으로 치우쳐 지지 않은) 값을 기준으로 판단하겠다고 해석할 수 있습니다.

 

재현율과 정밀도가 동등하게 중요한 상황에서는 F1 Score를 사용하면 되지만, 

재현율이 정밀도보다 더 중요하다고 여겨지는 상황에서는 1 이상의 값을 사용하면 됩니다.

반대로 정밀도가 재현율보다 더 중요하다고 여겨지는 상황에서는 0과 1사이의 값을 사용하면 되겠죠?

정확히 얼마의 값을 사용할지는 사용자의 판단에 달려있습니다.

재현율이 2배 중요하다 3배 중요하다는 상황에 따라 다르니까요!!

 

참고로 F Score를 설정하는 코드를 공유해드릴게요.

# F score 성능 평가
from sklearn.metrics import precision_score, recall_score

def f_score(beta,y_real, y_pred):
    beta = beta
    pr = precision_score(y_real, y_pred) #정밀도
    re = recall_score(y_real, y_pred) #재현율
    score = (1+beta**2)*(pr*re)/((beta**2*pr)+re)
    return score

print('F1 score : {:.6f}'.format(f_score(1, y_real, y_pred)))
print('F1.5 score : {:.6f}'.format(f_score(1.5,y_real, y_pred)))
print('F2 score : {:.6f}'.format(f_score(2,y_real, y_pred)))


# GridSearch에서의 F score 활용
from sklearn.metrics import fbeta_score
from sklearn.metrics import make_scorer

f_scorer = make_scorer(fbeta_score, beta = 1.5)
GridSearchCV(estimator = LogisticRegression(), param_grid=param_grid, scoring=f_scorer, verbose=1, n_jobs=-1, cv=5)

 

 


4. 임계값(Thresholds)

아까 정밀도를 높이기 위해 임계값을 조절했다고 했죠?

이제 이 주제로 넘어와 보겠습니다.

로지스틱 회귀를 예로 들면, 이 알고리즘은 '시그모이드 함수'를 기반으로 작동합니다.

계산한 확률값이 0.5보다 크면 1, 0.5보다 작으면 0으로 분류하는데, 이 0.5를 '임계값' 이라고 합니다.

 

그렇다면 0.9 보다 크면 1(양성, 암 환자), 0.9보다 작으면 0 으로 분류한다면 어떤 변화가 있을까요?

'양성으로 판단하는 기준이 까다로워 졌다' 라고 해석할 수 있습니다.

0.5만 넘으면 양성이라고 하던 키트가 이제는 0.9를 넘어야 양성이라고 진단하게 된 것입니다.

 

즉 5명을 양성으로 판단하던 기준이 까다로워져서 1명(정밀도의 분모)으로 줄어든 것입니다.

다시 말해 '정밀도가 높아졌다' 고 할 수 있네요.

반면 키트가 양성으로 예측하는 경우(재현율의 분자)가 줄어 '재현율은 낮아졌다' 라고 할 수 있습니다.

 

 

임계값에 따른 재현율, 정밀도 값 (예시)

 

결국 임계값을 조절하며 정밀도와 재현율을 조절할 수 있다는 말과 같은데요,

얼만큼 조절하는게 가장 좋냐구요?

아까 말씀 드렸듯 그건 상황에 따라 사용자가 결정하는 것입니다.

간단하게 생각해봤을때, 재현율에 대한 오류값과 정밀도에 대한 오류값 사이 손익분기점으로 임계값을 조절할 수 있을것 같은데, 손익분기점 관련해서 자세한 내용은 '[Kaggle] Telecom Customer Churn Prediction - Part 3' 에서 다루겠습니다.  

 

자 이제 그럼 임계값을 어떻게 변화시키는지 알아야겠죠?

Binarizer 클래스를 활용하여 임계값을 조절하는 코드를 구현해보겠습니다.

from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import Binarizer

# 적용해볼 임계값 설정
custom_threshold = 0.5

# 임계값과 비교할 예측 확률값 준비
lr_clf = LogisticRegression()
lr_clf.fit(X_train, y_train)
pred = lr_clf.predict(X_test)
pred_prob = lr_clf.predict_prob(X_test)
pred_prob_1 = pred_prob[:,1].reshape(-1,1) # pred_prob의 데이터프레임중 두번째 열(양성 클래스에 대한 확률값)만 사용

# Binarizier 클래스 사용
binarizer = Binarizer(threshold = custom_thresdhold).fit(pred_prob_1)
custom_predict = binarizer.transform(pred_prob_1)

# 점수 출력
print(precision_score(y_real, custom_predict))

 


5. PR curve vs ROC curve

똑똑한 사람들은 여기서 또 다른 생각을 했습니다.

임계값에 따라 변하는 재현율과 정밀도를 가지고 하나의 지표(값)으로 나타낼 수는 없을까?

그래서 나타난게 PR curve AUC ROC curve AUC 입니다.

 

PR curve(Precision Recall curve)는 말 그대로 '정밀도-재현율 곡선' 인데요,

0부터 1사이의 모든 임계값에 따라 x축을 재현율, y축을 정밀도로 그린 그림입니다.

정밀도와 재현율 모두 높을수록(그래프의 오른쪽 위) 좋은 모델이라고 할 수 있습니다.

 

PR curve (예시)

from sklearn.metrics import precision_recall_curve

# 임계값의 변화에 따른 정밀도와 재현율의 값 보기
# 확률값 추출
pred_prob_1 = lr_clf.predict_prob(X_test)[:,1]

# 확률값을 활용하여 정밀도, 재현율, 임계값 리스트를 구함
precisions, recalls, thresholds = precision_recall_curve(y_real, pred_prob_1)

# 임계값 인덱스 지정
thr_index = np.arange(0, thresholds.shape[0], 15)

# 인덱스에 해당하는 정밀도, 재현율, 임계값 구하기
print('thresholds : ', np.round(thresholds[thr_index],2))
print('precisions : ', np.round(precisions[thr_index],3))
print('recalls : ', np.round(recalls[thr_index],3))



# 그래프 그리기
def precision_recall_curve_plot(y_real, pred_prob_1):
    precisions, recalls, thresholds = precision_recall_curve(y_real, pred_prob_1)
    
    plt.figure(figsize=(8,6))
    threshold_boundary = threshold.shape[0] # 마지막은 포함시키지 않겠다
    plt.plot(thresholds, precisions[0:threshold_boundary], '--', label='precision')
    plt.plot(thresholds, recalls[0:threshold_boundary], label='recalls')
    
    start, end = plt.xlim()
    plt.xticks(np.round(np.arange(start, end, 0.1),2)) # scale 0.1 단위

 

 

ROC curve(Receiver operating characteristic)는 모든 임계값에 따라

x 축을 (1-특이도)로 y 축을 민감도로 그린 그림입니다.

1-특이도의 의미 : 실제 음성인 것 중 양성으로 잘못 예측한 것의 비율
민감도의 의미 : 실제 양성인 것 중 양성으로 잘 예측한 것의 비율

즉 실제 음성인데 양성으로 잘못 예측한 경우가 적고(1-특이도가 작고),

실제 양성을 양성으로 잘 예측한 경우가 많으면(민감도가 높을수록) 

좋은 모델(그래프의 왼쪽 위)이라고 할 수 있습니다.

 

ROC curve (예시)

from sklearn.metrics import roc_curve

# 임계값의 변화에 따른 FPR과 TPR의 값 보기
# 확률값 추출
pred_prob_1 = lr_clf.predict_prob(X_test)[:,1]

# 확률값을 활용하여 FPR, TPR, 임계값 리스트를 구함
fprs, tprs, thresholds = roc_curve(y_real, pred_prob_1)

# 임계값 인덱스 지정, thresholds의 첫번째 인자는 max(확률값)+1로 임의 설정되어 빼준다(모든 값을 음성으로 예측하기 위해)!!
thr_index = np.arange(1, thresholds.shape[0], 5)

# 인덱스에 해당하는 정밀도, 재현율, 임계값 구하기
print('thresholds : ', np.round(thresholds[thr_index],2))
print('precisions : ', np.round(fprs[thr_index],3))
print('recalls : ', np.round(tprs[thr_index],3))



# 그래프 그리기
def roc_curve_plot(y_real, pred_prob_1):
    fprs, tprs, thresholds = roc_curve(y_real, pred_prob_1)
    
    plt.plot(fprs, tprs, label='ROC')
    plt.plot([0,1],[0,1], 'k--',label = 'Random')
    
    start, end = plt.xlim()
    plt.xticks(np.round(np.arange(start, end, 0.1),2)) # scale 0.1 단위

 

 

아하! 그럼 PR curve는 오른쪽 위로, ROC curve는 왼쪽 위로 갈수록 좋은 모델이구나.

그럼 매번 이렇게 그려봐야해? 그리고 눈으로 구별하기 힘든 경우에는?

그래서 나온 개념이 AUC(Area Under the Curve) 입니다.

 

위의 파란 곡선 아래 면적의 넓이를 AUC 라고 하는데, 

이 값을 알면 그래프를 그릴 필요도, 눈으로 크기를 비교해 볼 필요도 없어졌습니다.

 

참고로 AUC 구하는 코드 공유해드릴게요.

from sklearn.metrics import roc_auc_score, average_precision_score

print('PR curve_auc score : {:.6f}'.format(average_precision_score(y_real, y_prob)))
print('ROC curve_auc score : {:.6f}'.format(roc_auc_score(y_real, y_prob)))
PR curve의 auc가 정밀도의 평균으로 구하는 이유는 적분의 개념으로 접근하면 쉽습니다.
모든 x축(재현율)에 따른 y축(정밀도)의 평균값, 즉 면적과 비슷해지게 되는 것입니다.

 

 

그럼 언제 PR curve AUC를 확인하고, 언제 ROC curve AUC를 확인하냐?!

결론부터 말씀드리고 근거를 들어보겠습니다.

 

- PR curve AUC를 사용하는 경우

  • 데이터가 불균형 할때 
  • 양성 클래스를 탐지하는 중요도가 음성 클래스를 탐지하는 중요도보다 높을때 (ex. 암 환자 진단)

 

- ROC curve AUC를 사용하는 경우

  • 데이터가 균형일때
  • 양성 클래스 탐지와 음성 클래스 탐지의 중요도가 비슷할 때 (ex. 개와 고양이 분류)

 

- 근거

  • PR curve의 정밀도는 데이터의 불균형에 영향을 많이 받는 것에 반해, ROC curve의 TPR과 FPR은 영향을 받지 않습니다(실제 양성 중 양성 예측 비율, 실제 음성 중 음성 예측 비율이기 때문에).
  • PR curve에서는 TN(실제 음성을 음성으로 잘 판단한 경우)이 관여하지 않습니다. 즉 음성인 것을 판단하는 비중이 적다는 뜻이며, 반대로 양성을 판단하는 것에 더 집중한다는 뜻입니다.

 

 


6. Confusion matrix

처음부터 TP, FN, FP, TN 의 개념으로 출발하면 지루할 수 있을것 같아서 마지막에 순서를 두었습니다.

지금까지 다뤘던 모든 개념은 사실 이것으로부터 출발하는데, 쉽게 정리 한번 해보겠습니다.

 

- True / False : 판단 결과(잘 판단/ 잘못 판단)

- Positive(양성)(1) / Negative(음성)(0) : 나의 예측

 

  • TP : 양성으로 잘 판단했다
  • FN : 음성으로 잘못 판단했다 -> 실제로는 양성이다
  • FP : 양성으로 잘못 판단했다 -> 실제로는 음성이다
  • TN : 음성으로 잘 판단했다

 

  • 정밀도 : TP / TP+FP(양성으로 예측한것) : 양성으로 예측한 것 중에 실제 양성인것
  • 재현율, 민감도 (TPR) : TP / TP+FN(실제 양성인 것) : 실제 양성인 것 중에 양성으로 예측한 것
  • 특이도 : TN / FP+TN(실제 음성인 것) : 실제 음성인 것 중에 음성으로 예측한 것
  • 1-특이도 (FPR) : FP / FP+TN : 실제 음성인 것 중에 양성으로 예측한 것

 

from sklearn.metrics import confusion_matrix

# label을 설정해주지 않으면 순서가 바뀜
print('Confusion Matrix : {}'.format(confusion_matrix(y_real, y_pred, labels = [1,0])))
  예측 양성 예측 음성
실제 양성 TP FN
실제 음성 FP TN

 

confusion matrix (예시)