F1 Score는 재현율과 정밀도에 밀접한 관련이 있음
관련 내용을 먼저 살펴보자
정밀도를 100%로 만드는 법
확실한 기준이 되는 경우만 Positive로 예측하고 나머지는 모두 Negative로 예측하기
1000명 중 확실한 Positive 징후만 가진 환자는 단 1명이라고 하면
이 한 명만 Positive로 예측하고, 나머지는 모두 Negative로 예측하더라도
FP = 0, TP = 1 되므로, 정밀도는 1 / (1+0) 으로 100%가 됨
재현율을 100%로 만드는 법
모든 환자를 Positive로 예측하면 됨
전체 환자 1000명을 다 Positive로 예측했을 때 이 중 실제 양성인 사람이 30명이라도
TN이 수치에 포함되지 않고 FN은 아예 0이므로 30/(30+0)으로 100%가 됨
이렇게 정밀도와 재현율 역시 완벽한 평가지표가 아님!
어느 한쪽으로 치우치지 않고, 정밀도와 재현율을 결합한 지표
정밀도와 재현율의 조화평균
앞에 식은 복잡하니까 뒤에꺼만 잘 알아두자 2PR / (P+R)
scikit-learn에서 F1 Score를 구해보자
코드는 앞에 하던 거에서 이어서 한다.
pred가 타이타닉 데이터 LogisitcRegression으로 fit() 후 predict() 한 것임
from sklearn.metrics import f1_score
f1 = f1_score(y_test , pred)
print('F1 스코어: {0:.4f}'.format(f1))
F1 스코어: 0.7805
이렇게 간단하게 F1 Score를 구해줄 수 있음
앞에서 만들었던 평가지표들을 출력하는
get_clf_eval()
에 F1 Score를 추가해서 돌려보자
get_eval_by_threshold() 함수는 임곗값에 따라 반복해서 평가지표를 업데이트하는 함수로, 이전 글에 코드 있음
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)
# F1 스코어 추가
f1 = f1_score(y_test,pred)
print('오차 행렬')
print(confusion)
# f1 score print 추가
print('정확도: {0:.4f}, 정밀도: {1:.4f}, 재현율: {2:.4f}, F1:{3:.4f}'.format(accuracy, precision, recall, f1))
thresholds = [0.4 , 0.45 , 0.50 , 0.55 , 0.60]
pred_proba = lr_clf.predict_proba(X_test)
get_eval_by_threshold(y_test, pred_proba[:,1].reshape(-1,1), thresholds)
임곗값: 0.4
오차 행렬
[[99 19]
[10 51]]
정확도: 0.8380, 정밀도: 0.7286, 재현율: 0.8361, F1:0.7786
임곗값: 0.45
오차 행렬
[[103 15]
[ 12 49]]
정확도: 0.8492, 정밀도: 0.7656, 재현율: 0.8033, F1:0.7840
임곗값: 0.5
오차 행렬
[[104 14]
[ 13 48]]
정확도: 0.8492, 정밀도: 0.7742, 재현율: 0.7869, F1:0.7805
임곗값: 0.55
오차 행렬
[[109 9]
[ 15 46]]
정확도: 0.8659, 정밀도: 0.8364, 재현율: 0.7541, F1:0.7931
임곗값: 0.6
오차 행렬
[[112 6]
[ 16 45]]
정확도: 0.8771, 정밀도: 0.8824, 재현율: 0.7377, F1:0.8036
임곗값이 0.6일 때 F1 Score가 가장 큰 것을 확인하자
ROC 곡선과 이에 기반한 AUC 스코어는 이진 분류의 예측 성능 측정에서 중요하게 사용되는 지표임
ROC Curve는 Receiver Operating Characteristic Curve로 "수신자 판단 곡선" 이라는 뜻임
2차 대전 때 통신 장비 성능 평가를 위해 고안된 수치라 이렇게 이상한 이름이라고 함
알 필요 없음
일반적으로 의학 분야에서 많이 사용되지만,
머신러닝 이진 분류 모델의 예측 성능을 판단하는 중요한 평가지표이다.
ROC Curve
FPR(False Postive Rate)
변할 때TPR (True Positive Rate)
어떻게 변하는지 나타내는 곡선
FPR은1 - TNR (True Negative Rate, Specificity, 특이도)
이기도 함
여기서 TPR이 우리가 아는재현율 (Recall)
(= 민감도 (Sensitivity))
식으로 정리해보자
어휴 드럽게 복잡하다.
아래 그래프 보면서 다시 이해해보자
앞에서 임곗값에 대해서 알아봤음. 그 임곗값의 변화에 따라서 FPR이 변화할 것이다.
ROC Curve는 그 FPR의 변화에 따른 TPR의 변화를 그래프로 나타낸 것이다.
아래 예시를 보자
scikit-learn에서는 roc_curve() 함수로 Roc Curve 값을 구함
아래 예시를 천천히 살펴보자
predict_proba()[:,1]
로 1 (Positive)로 예측할 확률을 뽑음roc_curve()
로 FPR, TPR, 그에 해당하는 임곗값(Threshold)를 뽑음 (ndarray)- 임곗값을 5 Step으로 추출해서
thr_index
에 저장
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))
샘플 추출을 위한 임곗값 배열의 index: [ 1 6 11 16 21 26 31 36 41 46 51]
샘플 index로 추출한 임곗값: [0.97 0.65 0.63 0.57 0.45 0.38 0.31 0.13 0.12 0.11 0.1 ]
샘플 임곗값별 FPR: [0. 0.017 0.034 0.076 0.127 0.186 0.237 0.576 0.619 0.754 0.814]
샘플 임곗값별 TPR: [0.033 0.639 0.705 0.754 0.803 0.852 0.902 0.902 0.951 0.967 1. ]
숫자로만 보면 경향성을 보기가 힘드니까 시각화해보자
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 - Sensitivity )'); plt.ylabel('TPR( Recall )')
plt.legend()
plt.show()
roc_curve_plot(y_test, lr_clf.predict_proba(X_test)[:, 1] )
그럼 AUC는 뭘까?
AUC: Area Under Curve로 ROC 곡선 아래의 면적을 말함
- 일반적으로 1에 가까울수록 좋은 수치
- AUC 수치가 커지려면 FPR이 작은 상태에서 얼마나 큰 TPR을 얻을 수 있느냐가 관건!
- 위에 시각화 그래프를 보면 가운데에 선 그려져 있는데 그 아래 면적이
0.5
임- 이렇게 동전 던지기 같은 랜덤 수준의 이진 분류의 AUC 값이
0.5
- 그니까 보통의 분류는 0.5 이상의 AUC 값이 나오겠지? Random보단 높아야 의미가 있으니까!
scikit-learn에서는 roc_auc_score() 함수로 ROC AUC 스코어를 구할 수 있음
ROC AUC Score를 구하려면predict_proba()
(0 또는 1을 예측할 확률) 꼭 필요함!
아래 코드를 확인하자
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))
ROC AUC 값: 0.9024
마지막으로 성능지표를 구하는 get_clf_eval() 함수에 ROC AUC Score까지 추가해서 출력해보자
def get_clf_eval(y_test, pred=None, pred_proba=None):
confusion = confusion_matrix( y_test, pred)
accuracy = accuracy_score(y_test , pred)
precision = precision_score(y_test , pred)
recall = recall_score(y_test , pred)
f1 = f1_score(y_test,pred)
# ROC-AUC 추가
roc_auc = roc_auc_score(y_test, pred_proba)
print('오차 행렬')
print(confusion)
# ROC-AUC print 추가
print('정확도: {0:.4f}, 정밀도: {1:.4f}, 재현율: {2:.4f}, F1: {3:.4f}, AUC:{4:.4f}'.format(accuracy, precision, recall, f1, roc_auc))
get_clf_eval(y_test, pred, pred_proba)
오차 행렬
[[104 14]
[ 13 48]]
정확도: 0.8492, 정밀도: 0.7742, 재현율: 0.7869, F1: 0.7805, AUC:0.9024