[Tree Based Model] 분류모델 평가지표(Evaluation Metrics for Classification)

Confusion Matrix

  • 분류모델의 성능평가지표를 보여준다.
  • sklearn.metrics.plot_confusion_matrix
  • 데이터는 전 포스팅의 H1N1을 그대로 사용한다.
1
2
3
4
5
6
7
8
9
from sklearn.metrics import plot_confusion_matrix
import matplotlib.pyplot as plt

fig, ax = plt.subplots()
pcm = plot_confusion_matrix(pipe, X_val, y_val,
                            cmap=plt.cm.Blues,
                            ax=ax);
plt.title(f'Confusion matrix, n = {len(y_val)}', fontsize=15)
plt.show()

스크린샷 2021-08-19 10 21 57

  • 분류성능을 쉽게 확인할 수 있다.
  • 가로축은 예측범주, 세로축은 실제범주이다.
    (Table 마다 TP, TN, FP, FN의 위치가 다르기 때문에 축을 잘 봐야한다.)

Accuracy(정확도) 계산

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
cm = pcm.confusion_matrix
cm
'''
array([[6165, 1515],
       [1930, 4442]])
'''
# TN FP
# FN TP

# TP : 백신을 맞은 사람을 잘 예측한 경우
cm[1][1]
'''
4442
'''

# TP + TN : 정확하게 맞춘 예측
correct_predictions = np.diag(cm).sum()
correct_predictions
'''
10607
'''

# Total
total_predictions = cm.sum()
total_predictions
'''
14052
'''

# 분류 정확도(classification accuracy)
correct_predictions/total_predictions
'''
0.7548391688015941
'''

print('검증 정확도: ', accuracy_score(y_val, y_pred))
'''
검증 정확도:  0.7548391688015941

같은 값이 나오는 걸 볼 수 있다.
'''

그 외 평가지표

  • 분류기의 정확한 성능을 판단하기 위해 정확도 외의 다른 평가지표를 같이 사용해야 한다.
  • 정확도(Accuracy) : 전체 범주를 모두 바르게 맞춘 경우를 전체 수로 나눈 값
\[\large \frac{TP + TN}{Total}\]
  • 정밀도(Precision) : Positive로 예측한 경우 중 올바르게 Positive를 맞춘 비율 (타입 1 에러 최소화)
\[\large \frac{TP}{TP + FP}\]
  • 재현율(Recall, Sensitivity) : 실제 Positive인 것 중 올바르게 Positive를 맞춘 것의 비율 (타입 2 에러 최소화)
\[\large \frac{TP}{TP + FN}\]
  • F1점수(F1 score) : 정밀도와 재현율의 조화평균(harmonic mean)
\[2\cdot\large\frac{precision\cdot recall}{precision + recall}\]

스크린샷 2021-08-19 10 46 29

  • 다루는 문제에 따라 정밀도와 재현율 중 어느 평가지표를 우선시 해야하는지 판단해야 한다.
    • 초기 암진단 : Recall
    • 넷플릭스 영화추천 : Precision

Sklearn 사용 정밀도, 재현율 확인

1
2
3
4
5
6
7
8
9
10
11
12
13
from sklearn.metrics import classification_report
print(classification_report(y_val, y_pred))
'''
              precision    recall  f1-score   support

           0       0.76      0.80      0.78      7680
           1       0.75      0.70      0.72      6372

    accuracy                           0.75     14052
   macro avg       0.75      0.75      0.75     14052
weighted avg       0.75      0.75      0.75     14052

'''

직접 계산

스크린샷 2021-08-19 10 21 57

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 백신접종을 Positive라 할 때
tp = 4442
tn = 6165
fp = 1515
fn = 1930
total = tp + tn + fp + fn

# 백신접종을 옳게 예측한 경우
tp
'''
4442
'''

# 백신접종을 잘못 예측한경우
fp
'''
1515
'''

# 백신접종에 대한 정밀도(Precision)
positives = tp + fp
tp/positives
'''
0.75
'''

# 실제 백신 접종한 경우
real_positives = tp + fn
real_positives
'''
6372
'''

# 재현율(Recall)
tp/real_positives
'''
0.7
'''
  • Precision과 Recall은 상황에 따라 어느것을 중요하게 볼 지가 다르다.
  • 암환자 진단 -> Recall이 중요하다(신경써야한다) -> Recall 값을 높여야한다 -> FN(암이 있는데 의사가 없다고 예측한 경우) 값을 최소화해야한다.
  • 스팸메일 -> Precision이 중요하다 -> FP(좋은 메일인데 스팸메일이라고 예측한 경우) 값을 최소화해야한다.

정밀도, 재현율, 임계값(thresholds), 예측확률간 관계

시나리오

  • 전체 인구의 70%가 코로나 항체를 갖고 있으면 ‘집단 면역’이 형성돼 코로나 유행을 멈출 수 있다.
  • 양지면에서 지역주민들에게 계절독감에 대한 무료 접종을 준비하고 있다.
  • 무료 접종의 설계를 잘 해 집단 면역을 생성하고자 한다.
  • 양지면 인구는 14052명이다.
  • 모두 항체가 없다고 가정했을 때 이들 중 70%가 백신을 접종한다면 집단 면역이 형성된다.
1
2
3
4
5
6
7
8
9
10
11
12
# 양지면에서 잡단 면역을 위한 백신 접종 수
herdImmunity = total * 0.7
herdImmunity
'''
9836.4
'''

# 추가 접종이 필요한 인원
herdImmunity - real_positives
'''
3464.3999999999996
'''
  • 양지면에서는 무료백신 예산이 4500명에 한정되어 있다.
  • 어떻게 하면 개인적으로 백신을 접종하지 않을 사람들 중 4500명을 예측하여 최대한 집단면역을 실현할 수 있을까?
  • 우선 확보한 무료 백신을 앞으로 백신을 받을 가능성이 없어 보이는 사람들에게 우선 접종하고, 어차피 기존에 백신을 맞을 사람들은 본인이 접종하도록 유도해야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 무작위로 무료백신 접종
true_ratio = y_train.value_counts(normalize=True)[1]
true_ratio
'''
0.4534552700875383
'''

public_vacc = 4500
overlap = public_vacc * true_ratio
overlap
'''
public_vacc = 4500
overlap = public_vacc * true_ratio
overlap
'''

lack = herdImmunity - (public_vacc + real_positives - overlap)
lack
'''
1004.9487153939226

만약 무작위로 주사한다면 백신을 맞을 수 있는 사람도 무료로 맞추게 되어 집단면역에 이르기까지 1000여 명이 부족하게 된다.
'''
  • 최대한 백신을 맞지 않을 사람을 골라 백신을 맞추어 주기 위해 임계값을 조정한다.
  • 각 예측 범주에 대한 확률값을 제공한다. 즉 예측 범주와 함께 범주의 확률값을 함께 제공한다
  • 주의사항 : 접종한다고 예측 결과가 나와도 다 같은 예측이 아니다. 접종에 대한 확률이 51% 이거나 99%이거나 모두 접종으로 예측하지만 51%인 경우는 틀릴 확률도 49%나 된다. 이 확률값을 사용해서 더 정밀하게 백신접종이 필요한 사람들을 타켓팅 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
pipe.classes_
'''
array([0, 1])
'''

pipe.predict(X_val)
'''
array([1, 0, 0, ..., 1, 1, 0])
'''

pipe.predict_proba(X_val)
'''
array([[0.46      , 0.54      ],
       [0.85      , 0.15      ],
       [0.78      , 0.22      ],
       ...,
       [0.14      , 0.86      ],
       [0.38459524, 0.61540476],
       [0.92      , 0.08      ]])
'''

# 백신접종 확률
y_pred_proba = pipe.predict_proba(X_val)[:, 1]
y_pred_proba
'''
array([0.54      , 0.15      , 0.22      , ..., 0.86      , 0.61540476,
       0.08      ])
'''

# 시각화하고 임계값을 히스토그램에 표시 Randomforestclassifier 기본 임계값은 0.5입니다.
threshold = 0.5
y_pred_proba = pipe.predict_proba(X_val)[:, 1]
y_pred = y_pred_proba > threshold

ax = sns.histplot(y_pred_proba)
ax.axvline(threshold, color='red')

pd.Series(y_pred).value_counts()
'''
False    8095
True     5957
dtype: int64
'''

스크린샷 2021-08-19 13 45 52

  • 임계값을 바꾸어가며 정밀도와 재현율의 변화 관찰한다.
  • 임계값을 낮추어 백신을 접종하지 않을 확률이 높은 사람들을 더 정확하게 구하는 것이 도움이 된다.
  • 임계값을 낮추면 Precision은 올라가지만 Recall은 떨어진다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from ipywidgets import interact, fixed

def explore_threshold(y_true, y_pred_proba, threshold=0.5):
    y_pred = y_pred_proba >= threshold
    vc = pd.Series(y_pred).value_counts()
    ax = sns.histplot(y_pred_proba, kde=True)
    ax.axvline(threshold, color='red')
    ax.set_title(f'# of target, 1={vc[1]}, 0={vc[0]}')
    plt.show()
    print(classification_report(y_true, y_pred))

    
interact(explore_threshold, 
    y_true=fixed(y_val), 
    y_pred_proba=fixed(y_pred_proba), 
    threshold=(0, 1, 0.01));

스크린샷 2021-08-19 14 11 40

스크린샷 2021-08-19 14 12 00

1
2
3
# 백신 접종 확률이 낮은 순서
top = pred_proba.sort_values(by='pred_proba', ascending=True)[:4500]
top

스크린샷 2021-08-19 14 22 45

  • 일부 주민들은 접종 확률이 낮음에도 접종한 사람들이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
vc = top['y_val'].value_counts()
vc
'''
0    3850
1     650
Name: y_val, dtype: int64
'''

# 접종을 하지 않았을 3850명에게 접종할 수 있게 되어 집단 면역이 가능한 항체 보유 비율을 맞출 수 있게 된다.
final = real_positives + vc[0]
final
'''
10222
'''
  • 예측확률과 임계값을 잘 활용하면 필요한 범주의 정밀도나 재현율을 조정하여 최소한의 백신접종으로 최대한의 결과를 거둘 수 있다.
  • 분류 문제에서 임계값을 잘 설정한다면 보다 효율적으로 문제를 해결할 수 있는데, 모든 임계값을 한눈에 보고 모델을 평가하는 방법은 ROC curve이다.

ROC, AUC (Receiver Operating Characteristic, Area Under the Curve)

  • ROC curve와 AUC를 사용하면 분류문제에서 여러 임계값 설정에 대한 모델의 성능을 구할 수 있게 된다.
  • ROC curve는 여러 임계값에 대해 TPR(True Positive Rate, recall)과 FPR(False Positive Rate) 그래프를 보여준다.
  • Receiver operating characteristic
  • Recall(재현율)
\[Sensitivity = {\displaystyle \mathrm {TPR} ={\frac {\mathrm {TP} }{\mathrm {P} }}={\frac {\mathrm {TP} }{\mathrm {TP} +\mathrm {FN} }}=1-\mathrm {FNR} }\]
  • Fall-out(위양성률)
\[{\displaystyle \mathrm {FPR} ={\frac {\mathrm {FP} }{\mathrm {N} }}={\frac {\mathrm {FP} }{\mathrm {FP} +\mathrm {TN} }}=1-\mathrm {TNR(Specificity)} }\]
  • 재현율을 높이기 위해서는 Positive로 판단하는 임계값을 계속 낮추어 모두 Positive로 판단하게 만들면 된다. 하지만 이렇게 하면 동시에 Nagative이지만 Positive로 판단하는 위양성률도 같이 높아진다.
  • 재현율은 최대화하고 위양성률은 최소화하는 임계값이 최적의 임계값이다.
  • AUC는 ROC curve의 아래 면적을 말한다.
  • 아래 면적은 학습이 잘될수록 1에 가깝게 된다. 안될수록 0.5에 가깝게 된다.
  • 사이킷런 roc_curve는 임계값에 따른 TPR, FPR 수치를 자동으로 계산해준다.
1
2
3
4
5
6
7
8
9
10
11
from sklearn.metrics import roc_curve

# roc_curve(타겟값, prob of 1)
fpr, tpr, thresholds = roc_curve(y_val, y_pred_proba)

roc = pd.DataFrame({
    'FPR(Fall-out)': fpr, 
    'TPRate(Recall)': tpr, 
    'Threshold': thresholds
})
roc

스크린샷 2021-08-19 14 32 24

1
2
3
4
5
# ROC curve
plt.scatter(fpr, tpr)
plt.title('ROC curve')
plt.xlabel('FPR(Fall-out)')
plt.ylabel('TPR(Recall)');

스크린샷 2021-08-19 14 34 55

1
2
3
4
5
6
7
8
9
10
# threshold 최대값의 인덱스, np.argmax() 최적의 threshold
optimal_idx = np.argmax(tpr - fpr)
optimal_threshold = thresholds[optimal_idx]

print('idx:', optimal_idx, ', threshold:', optimal_threshold)
'''
idx: 256 , threshold: 0.4633333333333334
'''

plt.plot(tpr-fpr);

스크린샷 2021-08-19 14 36 57

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
y_pred_optimal = y_pred_proba >= optimal_threshold
print(classification_report(y_val, y_pred_optimal))
'''
              precision    recall  f1-score   support

           0       0.78      0.77      0.77      7680
           1       0.73      0.74      0.73      6372

    accuracy                           0.75     14052
   macro avg       0.75      0.75      0.75     14052
weighted avg       0.75      0.75      0.75     14052
'''

# threshold 0.5 와 비교
y_pred_05 = y_pred_proba >= 0.5
print(classification_report(y_val, y_pred_05))
'''
              precision    recall  f1-score   support

           0       0.77      0.80      0.78      7680
           1       0.74      0.71      0.72      6372

    accuracy                           0.76     14052
   macro avg       0.75      0.75      0.75     14052
weighted avg       0.75      0.76      0.75     14052
'''

# AUC점수 계산
from sklearn.metrics import roc_auc_score
auc_score = roc_auc_score(y_val, y_pred_proba)
auc_score
'''
0.8187195526653066
'''
  • ROC curve는 이진분류문제에서 사용할 수 있다.
  • 다중분류문제에서는 각 클래스를 이진클래스 분류문제로 변환(One vs ALL)하여 구할 수 있다.
    • 3-class(A, B, C) 문제 -> A vs (B,C), B vs (A,C), C vs (A,B) 로 나누어 수행
  • 분류문제에서 모델을 올바르게 평가하기 위해서는 정확도 외에도 정밀도, 재현율을 잘 이해하고 사용해야 한다.
  • 특히 각 범주를 예측하는 기준이되는 임계값의 위치에 따라 정밀도나 재현율이 달라지기 때문에 문제의 상황에 따라 적절한 임계값을 선택할 필요성이 있다.
  • 이진 분류문제에서는 ROC curve와 AUC 점수를 잘 활용하면 좋은 결과를 만들 수 있다.
0%