데이터로그😎

1. 분류의 평가지표 본문

머신러닝/지도 학습

1. 분류의 평가지표

지연v'_'v 2023. 9. 4. 10:23
<분류(classification)의 성능평가 지표>

1. 정확도(accurcay)
2. 오차행렬(confusion matrix)
3. 정밀도(precision)
4. 재현율(recall)
5. F1 score
6. ROC AUC

0. 데이터 준비

  • 암 데이터 받아오기
from sklearn.datasets import load_breast_cancer
import pandas as pd
import numpy as np

cancer = load_breast_cancer()

data = cancer.data
target = cancer.target
feature_names = cancer.feature_names

df = pd.DataFrame(data= data, columns = feature_names)
df['target'] = target
df

df

 

  • target 변경
#cancer.target_names를 확인해보면 0: 암, 1: 종양이다.
# 모델 생성 목적을 암을 예측하기 위해서 만들 것이라서 종양을 0으로, 암을 1로 바꿔주기

tumor = df[df['target']==1].copy()
cancer = df[df['target']==0].copy()

tumor['target'] = 0
cancer['target'] = 1

sample = pd.concat([tumor, cancer[:30]], sort=True)
# 암 비율이 훨씬 적어야 하니까 cancer데이터는 30개까지만 합쳐주기

sample['target'].value_counts()

 

  • 데이터 분할 & 모델정의 & 학습
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
# LogisticRegression은 회귀라는 이름이 붙어있지만 사실은 분류이다.

X_train, X_test, y_train, y_test = train_test_split(
        sample.drop('target',axis=1),
        sample['target'],
        random_state = 42
)


model = LogisticRegression(max_iter = 10000) #max_iter = 최대반복횟수
model.fit(X_train, y_train)

lr_pred = model.predict(X_test)

 

1. 정확도(accuracy)

from sklearn.metrics import accuracy_score

accuracy_score(y_test, lr_pred)

>>>결과: 0.9690721649484536

그러나..정확도로 모델을 평가하면 아래와 같은 상황이 발생할 수도 있다.

  • 돌팔이 曰 "암인 사람도 어차피 많이 없는데 그냥 모든 암검사 한 사람한테 결과가 종양이라고 해볼까?”
dolpal_pred = np.zeros(shape=y_test.shape)

>>>

array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
accuracy_score(y_test, dolpal_pred)

>>0.9072164948453608

엉터리 데이터인데도 정확도가 90%가 넘는다니…!

따라서 정확도만 보고 성능을 판단하는 것은 위험하다. 더 세밀한 지표가 필요!!

→ 오차행렬 & 정밀도 & 정확도

 

 

2. 오차행렬( 혼돈행렬, confusion matrix)

  Predicted Negative Predicted Positive
Actual Negative True Negative (TN) False Positive (FP)
Actual Positive False Negative (FN) True Positive (TP)

 

from sklearn.metrics import confusion_matrix

# LogisticRegression의 혼동행렬
lr_conf_matrix = confusion_matrix(y_test, lr_pred)
print('모델의 오차행렬: \n{}'.format(lr_conf_matrix))

>>결과:
모델의 오차행렬: 
[[87  1]
 [ 2  7]]
  • 해석
    • TN(실제로 암 X 진단: 암 X)=87
    • FN (실제로 암 O 진단: 암 X) =2
    • FP (실제 암 X 진단: 암O) = 1
    • TP(실제 암 O 진단 암O) = 7
    • FN(2)을 낮추는 방식으로 성능을 올려야 함

 

  • 여기서.. 돌팔이의 오차행렬은 어떨까?
    • 돌팔이의 오차행렬은 1로 예측(암이라고 예측)한 것이 하나도 없음 (FP=0, TP=0)
    • FN이 9나 되어버림. (암인데 암이 아니라고 한 것)
# 돌팔이의 혼동 행렬
dolpal_conf_matrix = confusion_matrix(y_test, dolpal_pred)
print('돌팔이의 오차행렬: \n{}'.format(dolpal_conf_matrix))

>>결과:
돌팔이의 오차행렬: 
[[88  0]
 [ 9  0]]

 

  • 오차행렬 heatmap
import matplotlib.pyplot as plt
import seaborn as sns

plt.figure(figsize=(12, 5))

plt.subplot(121)
ax = sns.heatmap(
    lr_conf_matrix,
    annot=True,
    cmap="Reds"
)
ax.set_title("model's confusion matrix")

plt.subplot(122)
ax = sns.heatmap(
    dolpal_conf_matrix,
    annot=True,
    cmap="Reds",
)

ax.set_title("dolpal's confusion matrix")

plt.xlabel("Predict")
plt.ylabel("Actual")
plt.show()

오차행렬 heatmap

 

3. 정밀도(precision) & 재현율(recall)

3-1. 정밀도, 재현율이란?

  • 업무 특성상 정밀도 or 재현율을 강조해야 하는경우 , threshold를 조정해 수치를 조정할 수 있음
  • 두 지표 모두 높게 얻는게 best!!
  • 그러나 정밀도/재현율은 trade-off관계라서 하나가 올라가면 하나가 떨어지기 쉬움. 조심.

정밀도재현율 (민감도, TPR)공식의미어떤 경우 사용?예시초점

  정밀도 재현율(민감도, TPR)
공식 TP/(FP+TP) TP/(FN+TP)
의미 - 예측을 Positive로 한 값 중에서 예측값과 실제값이 일치한 데이터 비율
- 예측을 Positive로 한 값(TP+FP) 중 실제 값이 Positive인 경우(TP)
- Positive 예측 성능을 더욱 정밀하게 측정하는 지표
실제로 Positive인 값 중에 예측값이 Positive였던 값 = 모델이 실제 양성인 샘플을 얼마나 잘 찾아내는지.
when to use? FP가 중요한 경우 (실제 Positive인데 Negative로 잘못예측한 경우) FN이 중요한 경우 (실제 Positive인데 Negative음성으로 잘못 판단한 경우)
예시 스팸메일이 아닌데 스팸메일이라고 분류한 경우(업무차질) 암인데 암아니라고 판단
초점 FP 낮추기 FN 낮추기
  • 정밀도, 재현율 구하기
from sklearn.metrics import precision_score, recall_score

print('정밀도:{:.3f}'.format(precision_score(y_test, lr_pred))
print('재현율:{:.3f}'.format(recall_score(y_test, lr_pred))


>>정밀도: 0.875
재현율: 0.778
# 재현율이 더 낮음. 따라서 재현율 높여야함.

 

3-2. 사이킷런의 predict_proba()

  • predict_proba: 각 클래스에 대한 예측 확률을 확인.
  • predict()가 수행될 때 predict_proba가 먼저 수행되고 담에 예측값이 결정된다고 보면 됨.
  • sklearn의 분류 알고리즘이 확률을 구하는 과정
    1. 개별 레이블로 결정 확률 구함
    2. 레이블 중 예측 확률이 큰 레이블값으로 예측을 함
    3. 이러한 개별 레이블의 예측 확률을 반환하는 메소드가 predict_proba임
lr_pred_proba = model.predict_proba(X_test) #예측확률
lr_pred = model.predcit(X_test) # 예측결과

print(lr_pred_proba[:3])
print()
print(lr_pred[:3])

>>[[9.99277955e-01 7.22045316e-04] 
 [9.99317729e-01 6.82270968e-04]
 [9.99999986e-01 1.35283319e-08]]

[0 0 0]


#해석
[9.99277955e-01 7.22045316e-04] 둘 중에 target이 0인 수가 높음. 따라서 
[0 0 0]에서 첫번째 데이터가 0인 것임

 

3-3. Binarizer클래스를 통해 threshold 조절하기

  • Binarizer: 임계값(threshold)를 기준으로 입력 데이터를 0 또는 1의 이진(binary) 데이터로 변환하는 변환기(transformer).
  • 이진 분류문제에서 모델의 출력을 이진화할 때 주로 사용.
  • default threshold = 0.5
  • threshold ↓ : P로 예측될 확률(FP ↑), N으로 예측될 확률(FN↓ ) ⇒ 재현율이 높아짐
  • threshold ↑ : P로 예측될 확률(FP ↓), N으로 ㅇ예측될 확률(FN↑)) ⇒ 재현율 낮아짐

threshold

 

from sklearn.preprocessing import Binarizer

#임계값=0.5일때 오차행렬
# 사실 따로 binarizer로 임계값 설정하지 않아도 됨.why?기본이 0.5니까
binarizer = Binarizer(threshold=0.5)

# predict_proba() 반환값의 두번째 칼럼, 즉 Positive 클래스 칼럼만 추출해 Binarizer적용
lr_pred_proba_1 = model.predict_proba(X_test)[:,1].reshape(-1,1)
custom_predict = binarizer.fit_transform(lr_pred_proba_1)

 

  • model.predict_proba(X_test)결과
    • 인덱스[0][0],[1][0],[2][0]...은 target이 0일 때를 의미 = tumor
    • 인덱스[0][1],[1][1],[2][1]…은 target이 1일때를 의미 = cancer
array([[9.99277955e-01, 7.22045316e-04],
       [9.99317729e-01, 6.82270968e-04],
       [9.99999986e-01, 1.35283319e-08],
       [9.99971619e-01, 2.83811234e-05],
       [9.99999999e-01, 1.33687847e-09],
       [9.99972012e-01, 2.79882348e-05],
.........
	[1.00000000e+00, 2.78828346e-11],
       [9.99973593e-01, 2.64071385e-05],
       [9.99999671e-01, 3.29157646e-07]])

 

  • custom_predict 결과
    • fit_trasform()메소드는 입력된 narray의 값을 지정된 threshold(여기서는 0.5)보다 같거나 작으면 0, 크면 1값으로 반환. 즉 1로 나온건 암이라는 것
array([[0.],
       [0.],
       [0.],
       [0.],
       [0.],
       [0.],
       [0.],
       [0.],
       [0.],
       [0.],
       [0.],
       [0.],
       [0.],
       [0.],
       [1.],
       [0.],
   .....

 

  • 결과를 히트맵으로 그려보기
import matplotlib.pyplot as plt
import seaborn as sns

from sklearn.metrics import accuracy_score, precision_score , recall_score , confusion_matrix

def get_clf_eval(y_test, pred, ax=None):
    confusion = confusion_matrix( y_test, pred)
    precision = precision_score(y_test , pred)
    recall = recall_score(y_test , pred)
    accuracy = accuracy_score(y_test, pred)
    hmap_ax = sns.heatmap(
        confusion,
        annot=True,
        cmap="Reds",
        ax=ax
  )
    hmap_ax.set_title('A: {0:.4f}, P: {1:.4f}, R: {2:.4f}'.format(accuracy , precision ,recall))
get_clf_eval(y_test, custom_predict)

 

*정밀도:0.88%, 재현율: 0.78% → 재현율을 높여야 함  →  threshold낮춰야

 

  • Threshold 변화 시 정확도, 재현율, 정밀도 어케 변하는가?
# threshold = 0.1일 때
binarizer = Binarizer(threshold=0.1)
custom_predict = binarizer.fit_transform(lr_pred_proba_1)

get_clf_eval(y_test, custom_predict)

# threshold = 0.8일 때
binarizer = Binarizer(threshold=0.8)
custom_predict = binarizer.fit_transform(lr_pred_proba_1)

get_clf_eval(y_test, custom_predict)
# threshold=0.1,0.5일때보다 재현율(R) 낮아짐.

좌: thre 0.1, 우: thre 0.8

 

3-4. precision_recall_curve

The precision_recall_curve function returns three arrays:

precision_recall_curve는 precision, recall, thresholds 행렬을 각각 반환함. (1차원)/ threshold는 자동으로 설정됨.(가능한 모든 threshold에서 정밀도,재현율 계산)

precision.shape = (97,0), recall.shape=(97,0), thresholds.shape=(96,0)

  • precision: An array of precision values, corresponding to the thresholds at which the precision was calculated.
  • recall: An array of recall values, corresponding to the thresholds at which the recall was calculated.
  • thresholds: An array of probability thresholds used to calculate precision and recall.
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from sklearn.metrics import precision_recall_curve

def precision_recall_curve_plot(y_test , pred_proba_c1):
    # threshold ndarray와 이 threshold에 따른 정밀도, 재현율 ndarray 추출. 
    precisions, recalls, thresholds = precision_recall_curve( y_test, pred_proba_c1)
    
    # X축을 threshold값으로, Y축은 정밀도, 재현율 값으로 각각 Plot 수행. 정밀도는 점선으로 표시
    plt.figure(figsize=(8,6))
    threshold_boundary = thresholds.shape[0]
    plt.plot(thresholds, precisions[0:threshold_boundary], linestyle='--', label='precision')
    plt.plot(thresholds, recalls[0:threshold_boundary],label='recall')

    # x축, y축 label과 legend, 그리고 grid 설정
    plt.xlabel('Threshold value')
    plt.ylabel('Precision and Recall value')
    plt.legend()
    plt.grid()
    plt.show()
    
precision_recall_curve_plot( y_test, model.predict_proba(X_test)[:, 1] )

precision_recall_curve

4. F1 Score

  • 정밀도와 재현율을 결합한 지표
from sklearn.metrics import f1_score

f1 = f1_score(y_test, lr_pred)
f1

 

5. ROC 곡선

  • 머신러닝 이진 분류 모델의 예측 성능을 판단하는 중요한 평가 지표
  • FPR을 0~1로 변경하면서 TPR(재현율)변화값 구함
    • 어떻게 FPR변화? 임곗값을 변화시키면 됨
    • FPR(False Positive Rate)가 변할 때 TPR(True Positive Rate)가 어떻게 변하는지를 나타내는 곡선이다.
    • TPR = 재현율 = TP/(TP+FN)=민감도= 실제값 positive가 정확히 예측되어야 하는 수준
    • TNR = 특이성 = TN/(TN+FP) = True Negative Rate = 실제값 negative가 정확히 예측돼야 하는 수준
    • FPR = 1-TNR = FP/(FP+TN) = 실제로 음성인 데이터 중에서 음성을 양성으로 잘못 예측한 비율
  • FPR을 0으로 만드는 법?
    • 임계값을 1로 지정하면됨
      • 임계값이 1 → 아예 Positive로 예측하지 않음 → FP가 0이 됨 → FPR 0
  • FPR을 1로 만드는 법?
    • 임계값을 0으로 지정하면 됨
      • 임계값 0 → 아예 Negative로 예측 x → FPR = FP/FP = 1
  • roc_curve()
    • 입력 파라미터:
      • y_true: 실제 클래스 값 (y_test)
      • y_score: predict_proba()반환값중 Positive 칼럼
  • roc_curve는 FPR, TPR, threshold 반환함.
    • FPR: 1 - Specificity, 거짓 양성 비율, (FP / (FP + TN))
    • TPR: Recall, 진짜 양성 비율, (TP / (TP + FN))
    • thresholds: 임계값, 분류 결정 임계값을 ndarray로 반환합니다.
from sklearn.metrics import roc_curve

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, model.predict_proba(X_test)[:, 1] )

roc_curve

from sklearn.metrics import roc_auc_score
# roc auc 성능 수치 반환
pred_proba = model.predict_proba(X_test)[:, 1]
roc_score = roc_auc_score(y_test, pred_proba)
print('ROC AUC 값: {0:.4f}'.format(roc_score))

'머신러닝 > 지도 학습' 카테고리의 다른 글

4-1. Voting  (0) 2023.09.04
4. Ensemble (앙상블)  (0) 2023.09.04
3. Logistic Regression  (0) 2023.09.04
2. Decision Tree  (1) 2023.09.04
0. 분류(Classification)  (0) 2023.09.04