머신러닝 분류 모델 성능평가 지표(ROC곡선과 AUC)
ROC 곡선과 이에 기반한 AUC 스코어는 이진 분류의 예측 성능 측정에서 중요하게 사용되는 지표이다.
ROC 곡선 : Reciver Operation Characteristic Curve로 FPR(False Positive Rate)이 변할 때 TPR(True Positive Rate)이 어떻게 변하는지를 나타내는 곡선이다.
AUC(Area Under Curve) : 이름에서도 알 수 있듯이 ROC곡선 밑의 면적을 구한 것으로서 일반적으로 1에 가까울수록 좋은 수치이다.
FPR을 X축으로, TPR을 Y 축으로 잡으면 FPR의 변화에 따른 TPR의 변화가 곡선 형태로 나타난다.
TPR은 재현율을 나타낸다. 또한 재현율은 민감도로도 불린다.
그리고 민감도에 대응하는 지표로 TNR이라고 불리는 특이성 이 있다.
- 민감도(TPR)은 실제값 Positive(양성)가 정확히 예측돼야 하는 수준을 나타낸다.
- 특이성(TNR)은 실제값 Negative(음성)가 정확이 예측돼야 하는 수준을 나타낸다.
TNR(True Negative Rate)는 TNR = TN / ( FP + TN)이며,
ROC곡선의 X축 기준인 FPR은 FPR = FP / (FP + TN)이므로 1-TNR 또는 1 - 특이성 으로 표현한다
위에 그림은 ROC 곡선의 예이다. 가운데 직선은 ROC 곡선의 최저 값이다.
다른 말로 하면 랜덤 수준의 이진 분류 예를 들면 동전을 무작위로 던져 앞/뒤를 맞추는 랜덤 수준의 이진 분류 ROC 직선이다.(AUC는 0.5이다)
ROC 곡선이 가운데 직선에 가까울수록 성능이 떨어지는 것이며, 멀어질수록 성능이 뛰어난 것이다.
ROC 곡선은 FPR을 0부터 1까지 변경하면서 TPR의 변화 값을 구한다. 그럼 어떻게 FPR을 0부터 1까지 변경 할 수 있을까?
바로 분류 결정 임계값을 변화시키면 된다.
분류 결정 임계값은 Positive 예측값을 결정하는 확률의 기준이므로 분류 결정 임계값이 1에 가까워진다는 것은 Positive 예측을 하는 기준이 매우 높아지기 때문에 Positive로 예측을 잘 하지 않게 된다.
FPR을 0으로 만들려면 어떻게 해야 할까? : 분류 결정 임계값을 1로 만들면 된다.
- 분류 결정 임계값이 1에 가까워진다는 것은 Positive 예측을 하는 기준이 매우 높아지기 때문에 Positive로 예측을 하지 않게 된다.
- 그러면 어떠한 값이 들어와도 Positive로 예측을 하지 않으므로(Positive를 예측하는 기준인 분류 결정 임계값이 1이므로 분류기(Classifier)가 임계값보다 높은 확률을 가진 데이터를 Positive로 예측을 할 수 없다.) FPR이 저절로 0이 되게 된다.
- 공식을 봐도 FPR = FP / (FP + TN)인데 FP가 0이 되기에 자연스럽게 0이된다.
FPR을 1으로 만들려면 어떻게 해야 할까? : 분류 결정 임계값을 0로 만들면 된다.
- 분류 결정 임계값이 0이 되면 Positive 예측을 하는 기준이 너무 낮아져서 모든 예측을 Positive로 하게 된다.
- 그러면 TN이 0이 되고 FP가 1이 되기에 공식으로 확인해도 FPR이 1이 된다.
사이킷런은 ROC 곡선을 구하기 위해 roc_curve( ) API를 제공한다.
이는 정밀도-재현율 커브(precision_recall_curve( ))와 유사하다.
이제 roc_curve( )의 파라미터와 반환값을 알아보자!!
입력 매개변수
- y_true : 실제 클래스 값 array(배열)
- y_score : predict_proba( )의 반환 값 array에서 Positive 칼럼의 예측 확률이 보통 사용됨
- predict_proda( )의 return값은 Class의 Negative와 Positive의 예측 확률로 값을 주는데 이것은 리스트 안에 [Negative 예측확률, Positive 예측확률] 형태로 쭉 n차원의 배열로 주게 된다. 이때 Positive 칼럼의 예측 확률만을 y_score에 들고 오게 된다.
반환 값
- fpr : FPR 값을 array로 반환
- tpr : TPR 값을 array로 반환
- thresholds : 분류 결정 임계값 array
코드로 예시를 보자!!!
from sklearn.metrics import roc_curve
# 레이블 값이 1일때의 예측 확률을 추출
pred_proba_class1 = lr_clf.predict_proba(X_test)[:, 1]
fprs , tprs , thresholds = roc_curve(y_test, pred_proba_class1)
# 반환된 임곗값 배열에서 샘플로 데이터를 추출하되, 임곗값을 5 Step으로 추출.
# thresholds[0]은 max(예측확률)+1로 임의 설정됨. 이를 제외하기 위해 np.arange는 1부터 시작
thr_index = np.arange(1, thresholds.shape[0], 5)
print('샘플 추출을 위한 임곗값 배열의 index:', thr_index)
print('샘플 index로 추출한 임곗값: ', np.round(thresholds[thr_index], 2))
# 5 step 단위로 추출된 임계값에 따른 FPR, TPR 값
print('샘플 임곗값별 FPR: ', np.round(fprs[thr_index], 3))
print('샘플 임곗값별 TPR: ', np.round(tprs[thr_index], 3))
결과값을 보자!!
우리가 roc_curve( )를 사용하고 난 결과값을 보도록 하자.
우리가 생각했던 대로 분류 결정 임계값이 1에 가까운 값에서 점점 0에 가까운 값에 갈 수록 FPR은 점점 커지는 것을 볼 수 있다.
TPR을 보면 FPR이 점점 커질수록 TPR은 가파르게 커지는 것을 볼 수 있다.
이러한 수치로는 한눈에 보기 힘들기 때문에 시각화를 해보자 결과값 또한 배열이기에 시각화를 하기 편할 것이다.
def roc_curve_plot(y_test , pred_proba_c1):
# 임곗값에 따른 FPR, TPR 값을 반환 받음.
fprs , tprs , thresholds = roc_curve(y_test ,pred_proba_c1)
# ROC Curve를 plot 곡선으로 그림.
plt.plot(fprs , tprs, label='ROC')
# 가운데 대각선 직선을 그림.
plt.plot([0, 1], [0, 1], 'k--', label='Random')
# FPR X 축의 Scale을 0.1 단위로 변경, X,Y 축명 설정등
start, end = plt.xlim()
plt.xticks(np.round(np.arange(start, end, 0.1),2))
plt.xlim(0,1); plt.ylim(0,1)
plt.xlabel('FPR( 1 - Specificity )'); plt.ylabel('TPR( Recall )')
plt.legend()
plt.show()
roc_curve_plot(y_test, lr_clf.predict_proba(X_test)[:, 1] )
결과값으로
일반적으로 ROC 곡선 자체는 FPR과 TPR의 변화 값을 보는데 이용되며, 분류의 성능 지표로 사용되는 것은 ROC 곡선 면적에 기반한 AUC 값으로 결정한다. ==> AUC의 정의는 위에다 해놓았다.
AUC 수치가 커지려면 FPR이 작은 상태에서 얼마나 큰 TPR을 얻을 수 있느냐가 관건이다.
가운데 직선에서 멀어지고 왼쪽 상단 모서리 쪽으로 가파르게 곡선이 이동 할 수록 AUC는 1에 가까운 값으로 가게 될 것이다.
그것이 좋은 ROC AUC성능 수치를 얻게 되는 것이다.
우리가 랜덤 수준으로 설정한 직선의 밑에 넓이 AUC를 0.5로 설정했다.
이제 ROC AUC 값을 구하는 코드를 살펴보자.
사이킷런에서 roc_auc_score( )라는 메서드를 제공한다. 이것을 사용하면 된다.
매개변수
- y_test : 학습한 모델을 확인할 모델
- pred_proba : Class의 Positive를 예측한 예측 확률
from sklearn.metrics import roc_auc_score
pred_proba = lr_clf.predict_proba(X_test)[:, 1]
roc_score = roc_auc_score(y_test, pred_proba)
print('ROC AUC 값: {0:.4f}'.format(roc_score))
결과값으로 AUC의 값이 나오게 된다!!!