Data Analysis & ML/Machine Learning

[Machine Learning][머신러닝] Classification(분류) 평가지표

YSY^ 2020. 8. 28. 13:43

Classification(분류) 평가지표

이번 포스팅에서는 분류모델에 대한 평가지표에 대해 알아봅니다.

분류와 회귀의 평가방법

분류 평가 지표

  1. 정확도 (Accuracy)
  2. 정밀도 (Precision)
  3. 재현률 (Recall)
  4. F1점수 (F1 Score)
  5. AUC

회귀 평가방법

  1. MSE (Mean Squareed Error)
  2. RMSE (Root Mean Squared Error)
  3. R^2 (결정계수)

sckit-learn 평가함수

  • sklearn.metrics 모듈을 통해 제공

 

분류(Classification) 평가 기준

  • 이진 분류에서의 양성과 음성 의미
    • 양성: 예측하려는(찾으려는) 대상
    • 음성: 예측하려는 대상이 아닌 것
      • 암환자 분류 : 양성 - 암 환자, 음성 - 정상인
      • 스팸메일 분류 : 양성 - 스팸메일, 음성 - 정상메일
      • 금융사기 모델: 양성 - 사기거래, 음성 - 정상거래

정확도 (Accuracy)

  • 전체 예측 한 것중 맞게 예측한 비율로 평가한다.
  • accuracy_score(모델예측값, 정답)

Accuracy 평가지표의 문제

  • 불균형 데이터의 경우 정확한 평가지표가 될 수 없다.
    • 예: 양성과 음성의 비율이 1:9 인 경우 모두 음성이라고 하면 정확도는 90%가 된다.

Classification 모델링 Accuracy Score 평가 코드 예시

from sklearn.datasets import load_digits
import numpy as np
import matplotlib.pyplot as plt

digit = load_digits()
X = digit.data
y = digit.target
X.shape, y.shape
#==> ((1797, 64), (1797,))

np.unique(y, return_counts=True)
#==> (array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
#     array([178, 182, 177, 183, 181, 182, 181, 179, 174, 180], dtype=int64))

num1 = X[0].reshape(8,8)
num1.shape

print(y[0]) #0
plt.imshow(num1, cmap='Greys')
plt.xticks([])
plt.yticks([])
plt.show()

print(y[50]) #2
plt.imshow(X[50].reshape(8,8), cmap='Greys')
plt.xticks([])
plt.yticks([])
plt.show()

불균형 데이터셋으로 만들기

  • y를 9와 나머지로 변경한다.
  • Positive(양성 - 1): 9
  • Negative(음성 - 0): 0 ~ 8
y = np.where(y==9, 1, 0)
y_cnt = np.unique(y, return_counts=True)
#==> (array([0, 1]), array([1617,  180], dtype=int64))
y_cnt[1]/y.shape
#==> array([0.89983306, 0.10016694])

훈련, 테스트 데이터셋 분할

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y)
X_train, X_test = X_train/255, X_test/255   #이미지 feature scaling   0 - 1

모델 생성 및 학습

  • Dummy Model 정의
    • Target Label중 무조건 최빈값으로 예측하는 모델을 정의한다.
from sklearn.base import BaseEstimator

class MyClassifier(BaseEstimator):
    def fit(self, X, y):
        # y중 최빈값을 instance변수로 저장
        cnt = np.unique(y, return_counts=True)#(array([0, 1]), array([1617,  180])
        max_index = np.argmax(cnt[1])
        self.pred = cnt[0][max_index]

    def predict(self, X):
#         X의 행수와 같은 열벡터에 최빈값을 넣어 반환.
        return np.full(shape=(X.shape[0], 1), fill_value=self.pred)
model = MyClassifier()
model.fit(X_train, y_train)
pred_train_dummy = model.predict(X_train)
pred_test_dummy = model.predict(X_test)

np.unique(pred_test_dummy), pred_test_dummy.shape
# ==> (array([0]), (450, 1))

모델 평가 (Accuracy_score)

from sklearn.metrics import accuracy_score
print(accuracy_score(y_train, pred_train_dummy)) # 0.8997772828507795
print(accuracy_score(y_test, pred_test_dummy)) # 0.9

 

혼동 행렬(Confusion Marix)

  • 분류의 평가지표의 기준으로 사용된다.
  • 혼동행렬을 이용해 다양한 평가지표(정확도, 재현률, 정밀도, F1 점수, AUC 점수)를 계산할 수 있다.
  • 함수: confusion_matrix(정답, 모델예측값)
  • 결과의 0번축: 실제 class, 1번 축: 예측 class

  • TP(True Positive) - 양성으로 예측했는데 맞은 개수
  • TN(True Negative) - 음성으로 예측했는데 맞은 개수
  • FP(False Positive) - 양성으로 예측했는데 틀린 개수 (음성을 양성으로 예측)
  • FN(False Negative) - 음성으로 예측했는데 틀린 개수 (양성을 음성으로 예측)

 

이진 분류 평가점수

  • Accuracy (정확도)
    • 전체 데이터 중에 맞게 예측한 것의 비율
  • Recall/Sensitivity(재현율/민감도)
    • 실제 Positive(양성)인 것 중에 Positive(양성)로 예측 한 것의 비율
    • TPR(True Positive Rate) 이라고도 한다.
    • ex) 스팸 메일 중 스팸메일로 예측한 비율. 금융사기 데이터 중 사기로 예측한 비율
  • Precision(정밀도)
    • Positive(양성)으로 예측 한 것 중 실제 Positive(양성)인 비율
    • PPV(Positive Predictive Value) 라고도 한다.
    • ex) 스팸메일로 예측한 것 중 스팸메일의 비율. 금융 사기로 예측한 것 중 금융사기인 것의 비율
  • F1 점수
    • 정밀도와 재현율의 조화평균 점수
  • Specificity(특이도)
    • 실제 Negative(음성)인 것들 중 Negative(음성)으로 맞게 예측 한 것의 비율
    • TNR(True Negative Rate) 라고도 한다.
  • Fall out(위양성률)
    • 실제 Negative(음성)인 것들 중 Negative(양성)으로 잘못 예측한 것의 비율. 1 - 특이도
    • FPR (False Positive Rate) 라고도 한다.

 

각 평가 지표 계산 함수

  • sklearn.metrics 모듈
  • confusion_matrix(y 실제값, y 예측값)
    • 혼돈 행렬 반환
  • recall_score(y 실제값, y 예측값)
    • Recall(재현율) 점수 반환 (Positive 중 Positive로 예측한 비율 (TPR))
  • precision_score(y 실제값, y 예측값)
    • Precision(정밀도) 점수 반환 (Positive로 예측한 것 중 Positive인 것의 비율 (PPV))
  • f1_score(y 실제값, y 예측값)
    • F1 점수 반환 (recall과 precision의 조화 평균값)
  • classification_report(y 실제값, y 예측값)
    • 클래스 별로 recall, precision, f1 점수와 accuracy를 종합해서 보여준다.

Dummy 모델 혼동행렬

from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y_test, pred_test_dummy)
print(cm)
# [[405   0]
# [ 45   0]]

import seaborn as sns
import matplotlib as mpl
import matplotlib.pyplot as plt

mpl.rcParams['font.size']=20
plt.figure(figsize=(7,6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', cbar=False)  
plt.title('dummy 모델 confusion matrix')
plt.ylabel('실제')
plt.xlabel('예측')
plt.xticks([0.5, 1.5], ['Neg', 'Pos'])
plt.yticks([0.5, 1.5], ['Neg', 'Pos'])
plt.show()

dummy 모델 Accuracy, Recall, Precision, F1-Score

from sklearn.metrics import accuracy_score, recall_score, precision_score, f1_score
print('정확도:', accuracy_score(y_test, pred_test_dummy)) 
print('recall(재현율):', recall_score(y_test, pred_test_dummy))
print('precision(정밀도):', precision_score(y_test, pred_test_dummy))
print('F1점수:', f1_score(y_test, pred_test_dummy))

# 정확도: 0.9
# recall(재현율): 0.0
# precision(정밀도): 0.0
# F1점수: 0.0

머신러닝 모델을 이용해 학습

from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression

# 모델들 생성
dt = DecisionTreeClassifier(max_depth=3)
rfc = RandomForestClassifier(n_estimators=100) #tree 100개 만들어라.
lr = LogisticRegression(max_iter=500)

# 모델들 학습
dt.fit(X_train, y_train)
rfc.fit(X_train, y_train)
lr.fit(X_train, y_train)

# 예측 및 평가
pred_test_dt = dt.predict(X_test)
pred_test_rfc = rfc.predict(X_test)
pred_test_lr = lr.predict(X_test)

# confusion matrix
print('결정트리 결과')
confusion_matrix(y_test, pred_test_dt)
# [[400,   5],
# [ 13,  32]]

print('랜덤포레스트 결과')
confusion_matrix(y_test, pred_test_rfc)
# [[405,   0],
# [  9,  36]]

print('로지스틱회귀 결과')
confusion_matrix(y_test, pred_test_lr)
# [[405,   0],
# [ 45,   0]]

from sklearn.metrics import plot_confusion_matrix

plot_confusion_matrix(rfc, X_test, y_test, values_format='d', cmap='Blues')  #모델, X, y
plt.show()

모델 평가

def print_metrics(y, pred_y, title=None):
    print(title)
    print("정확도:", accuracy_score(y, pred_y))
    print('재현율(recall):', recall_score(y, pred_y))
    print('정밀도(precision):', precision_score(y, pred_y))
    print('F1 score:', f1_score(y, pred_y))


print_metrics(y_test, pred_test_dt, '결정트리 결과')
# 결정트리 결과
- 정확도: 0.96
- 재현율(recall): 0.7111111111111111
- 정밀도(precision): 0.8648648648648649
- F1 score: 0.7804878048780488'

print_metrics(y_test, pred_test_rfc,'랜덤포레스트 결과')
# 랜덤포레스트 결과
- 정확도: 0.98
- 재현율(recall): 0.8
- 정밀도(precision): 1.0
- F1 score: 0.888888888888889

classification_report()

from sklearn.metrics import classification_report
print(classification_report(y_test, pred_test_rfc))

 

 

재현율과 정밀도의 관계

이진 분류의 경우 Precision(정밀도)가 중요한 경우와 Recall(재현율)이 중요한 경우가 있다.

재현율이 더 중요한 경우

  • 실제 Positive 데이터를 Negative 로 잘못 판단하면 업무상 큰 영향이 있는 경우.
  • FN(False Negative)를 낮추는데 촛점을 맞춘다.
  • 암환자 판정 모델, 보험사기적발 모델

정밀도가 더 중요한 경우

  • 실제 Negative 데이터를 Positive 로 잘못 판단하면 업무상 큰 영향이 있는 경우.
  • FP(False Positive)를 낮추는데 초점을 맞춘다.
  • 스팸메일 판정

임계값(Threshold) 변경을 통한 재현율, 정밀도 변환

  • 임계값 : 모델이 분류의 답을 결정할 때 기준값
  • 정밀도나 재현율을 특히 강조해야 하는 상황일 경우 임계값 변경을 통해 평가 수치를 올릴 수있다.
  • 단 극단적으로 임계점을 올리나가 낮춰서 한쪽의 점수를 높이면 안된다. (ex: 암환자 예측시 재현율을 너무 높이면 정밀도가 낮아져 걸핏하면 정상인을 암환자로 예측하게 된다.)

임계값 변경에 따른 정밀도와 재현율 변화관계

  • 임계값을 높이면 양성으로 예측하는 기준을 높여서(엄격히 해서) 음성으로 예측되는 샘플이 많아 진다. 그래서 정밀도는 높아지고 재현율은 낮아진다.
  • 임계값을 낮추면 양성으로 예측하는 기준이 낮아져서 양성으로 예측되는 샘플이 많아 진다. 그래서 재현율은 높아지고 정밀도는 낮아진다.
  • 임계값을 변화시켰을때 재현율과 정밀도는 음의 상관관계를 가진다.
  • 임계값을 변화시켰을때 재현율과 위양성율(Fall-Out/FPR)은 양의 상관관계를 가진다.

Binarizer - 임계값 변경

  • Transformer로 양성 여부를 선택하는 임계값을 변경할 수 있다.
from sklearn.preprocessing import Binarizer
exam = [[0.3, 0.7, 0.6, 0.4,0.5]]
b = Binarizer(threshold=0.2)
b.fit_transform(exam)

# 양성 판단의 기준 임계점을 높이면 재현율은 낮아지고 정밀도가 높아진다.
# 양성 판단의 기준 임계점을 낮추면 재현율은 높아지고 정밀도가 낮아진다.
import numpy as np

binarizer = Binarizer(threshold=0.1) #양성판단의 임계점을 0.1로 변경.
pred_proba = dt.predict_proba(X_test)
# pred_proba[:5]
  • predict_proba(X_test)
    • predict_proba의 출력은 각 클래스에 대한 확률
    • 각 행의 첫 번째 원소는 첫 번째 클래스의 예측 확률이고 두 번째 원소는 두 번째 클래스의 예측 확률입니다.
    • 확률이기 때문에 predict_proba의 출력은 항상 0과 1 사이의 값이며 두 클래스에 대한 확률의 합은 항상 1입니다.
      • 확률 합은 1이므로 두 클래스 중 하나는 50% 이상의 확률이며 바로 그 클래스가 예측값이 됩니다.
pred_test_dt2 = binarizer.fit_transform(pred_proba)[:, 1]

print_metrics(y_test, pred_test_dt, "----임계값 0.5-----")
----임계값 0.5-----
정확도: 0.96
재현율(recall): 0.7111111111111111
정밀도(precision): 0.8648648648648649
F1 score: 0.7804878048780488

print_metrics(y_test, pred_test_dt2, "----임계값 0.1-----")
----임계값 0.1-----
정확도: 0.9488888888888889
재현율(recall): 0.8666666666666667
정밀도(precision): 0.6964285714285714
F1 score: 0.7722772277227722

 

ROC 곡선과 AUC 점수

이진 분류 모델 성능 측정에서 중요하게 사용되는 지표.

  • FPR(False Positive Rate)
    • 위양성율 (fall-out)
    • 1-특이도(TNR)
    • 실제 음성중 양성으로 잘못 예측 한 비율
  • TPR(True Positive Rate)
    • 재현율(recall)
    • 실제 양성중 양성으로 맞게 예측한 비율
  • ROC 곡선
    • FPR을 X축, TPR을 Y축으로 놓고 임계값을 변경해서 FPR이 변할 때 TPR이 어떻게 변하는지 나타내는 곡선.
  • AUC
    • ROC 곡선 아래쪽 면적
    • 0 ~ 1 사이 실수로 나오며 클수록 좋다.
      • AUC 점수기준
        • 1.0 ~ 0.9 : 아주 좋음
        • 0.9 ~ 0.8 : 좋음
        • 0.8 ~ 0.7 : 괜찮은 모델
        • 0.7 ~ 0.6 : 의미는 있으나 좋은 모델은 아님
        • 0.6 ~ 0.5 : 좋지 않은 모델

ROC, AUC 점수 확인

  • roc_curve(y값, 예측확률) : FPR, TPR, Thresholds (임계치)
  • roc_auc_score(y값, 예측확률) : AUC 점수 반환
dt_pos = dt.predict_proba(X_test)[:, 1]
rfc_pos= rfc.predict_proba(X_test)[:, 1]

from sklearn.metrics import roc_curve, roc_auc_score

fprs1, tprs1, thresholds1 = roc_curve(y_test, dt_pos)
fprs2, tprs2, thresholds2 = roc_curve(y_test, rfc_pos)

plt.figure(figsize=(7,7))
plt.plot(fprs1, tprs1, label='Tree-Roc곡선')
plt.plot(fprs2, tprs2, label='RFC-Roc곡선')
plt.legend()
plt.grid(True)
plt.title('ROC Curve')
plt.show()

print("결정트리-AUC 점수")
roc_auc_score(y_test, dt_pos)

결정트리-AUC 점수
0.9217283950617283

print("랜덤포레스트-AUC점수")
roc_auc_score(y_test, rfc_pos)

랜덤포레스트-AUC점수
0.9993964334705076
728x90
반응형