• Home
  • About
    • SevillaBK's Blog photo

      SevillaBK's Blog

      Data Science Blog

    • Learn More
    • Github
  • Posts
    • All Posts
    • Python
    • ML & DL
    • All Tags

모델의 평가

02 Feb 2020

Reading time ~12 minutes

이번 포스팅에서는 학습한 모델을 어떻게 평가를 해야하는지 알아보겠습니다.
모델 평가를 위해 사용되는 지표는 여러 가지가 있지만 대표적으로 정확도(accuracy) 가 있고, 민감도(재현율)(recall), 특이도(specificity), 정밀도(precision), F1 score 등이 있습니다.

정확도(Accuracy)

정확도는 정답과 예측값이 얼마나 동일한지를 판단하는 지표입니다.
직관적으로 모델 예측 성능을 나타내는 평가지표이지만 데이터 레이블의 구성에 따라 모델 성능을 왜곡할 수 있습니다. 가령, 캐글의 타이타닉 예제에서도 생존자 중 여성의 비중이 높기 때문에, 특별한 알고리즘 없이 여성을 모두 생존자로 분류해도 정확도를 높게 나올 수 있습니다.

# 캐글 타이타닉 데이터셋에서 사망자 분포 확인
import pandas as pd
%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as sns

titanic_df = pd.read_csv('Titanic/input/train.csv')

sns.countplot(data = titanic_df, x = 'Survived', hue = 'Sex')
plt.title('Number of Survived with Sex')
plt.show()

Survived 가 1이 생존자를 나타내는데, 위의 그래프에서 보는 것 처럼 생존자 중 여성의 비중이 확연히 높습니다. 이런 경우에는 단순히 성별에 따라 생존자를 분류해도 정확도가 높게 나올 수 있습니다.

사이킷런의 BaseEstimator 를 활용하여, 단순히 성별에 따라 생존자 여부를 예측하는 분류기를 생성해서 정확도를 보겠습니다.

※ 사이킷런의 BaseEstimator 는 Customized된 Estimator를 생성할 수 있는 클래스입니다.

import numpy as np

# fit() 메서드는 아무 것도 수행하지 않고
# predict()는 Sex가 1이면 0, 그렇지 않으면 1로 예측하는 단순한 분류기 생성
from sklearn.base import BaseEstimator

class DummyClassifier(BaseEstimator):
  def fit(self, X, y = None):
    pass
  
  def predict(self, X):
    pred = np.zeros((X.shape[0],1))
    for i in range(X.shape[0]):
      if X['Sex'].iloc[i] == 1:
        pred[i] = 0
      else:
        pred[i] = 1
    return pred
# 생성된 DummyClassifier를 이용해 타이타닉 예제 생존자 예측 수행
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import LabelEncoder

# Null 처리 함수
def fillna(df):
  df['Age'].fillna(df['Age'].mean(), inplace = True)
  df['Cabin'].fillna('N', inplace = True)
  df['Embarked'].fillna('N', inplace = True)
  df['Fare'].fillna(0, inplace = True)
  return df

# 학습에 불필요한 피처 제거
def drop_feature(df):
  df.drop(['PassengerId', 'Name', 'Ticket'], axis = 1, inplace = True)
  return df

# LabelEncoder 수행
def format_feature(df):
  df['Cabin'] = df['Cabin'].str[:1]
  features = ['Cabin', 'Sex', 'Embarked']
  for feature in features:
    label_encoder = LabelEncoder()
    label_encoder.fit(df[feature])
    df[feature] = label_encoder.transform(df[feature])
  return df

# 위의 함수들을 한꺼번에 실행하는 함수
def transform_features(df):
  df = fillna(df)
  df = drop_feature(df)
  df = format_features(df)
  return df
# 타이타닉 데이터 로딩 및 학습 데이터 / 테스트 데이터 분할
y_titanic_df = titanic_df['Survived']
X_titanic_df = titanic_df.drop(['Survived'], axis = 1)
X_titanic_df = transform_features(X_titanic_df)

X_train, X_test, y_train, y_test = train_test_split(X_titanic_df, 
                                                    y_titanic_df,
                                                    test_size = 0.2,
                                                    random_state = 10)

# 위에서 생성한 Dummy Classifier를 활용해서 학습/예측/평가 수행
model = DummyClassifier()
model.fit(X_train, y_train)

predictions = model.predict(X_test)
print('Dummy Classifier의 정확도: {0: .4f}'.format(accuracy_score(y_test, predictions)))
# 출력:
Dummy Classifier의 정확도:  0.8212

위와 같이 단순한 알고리즘으로 예측을 하더라도, 데이터 레이블의 구성으로 인해 정확도는 82.12%로 매우 높은 수치가 나왔습니다. 때문에 정확도를 평가지표로 사용할 때는 신중해야 합니다.

정확도는 특히, 불균형한 레이블 값 분포에서 모델 성능을 판단할 경우에는 적합한 평가지표가 아닙니다.
가령 100개의 데이터 중 95개의 레이블이 0, 5개의 레이블이 1인 경우에 무조건 0을 반환하는 모델을 만들면 정확도가 95%가 됩니다. 하지만 이런 데이터셋에서는 대개 목표는 1을 찾아내는 것입니다. 때문에 정확도만으로는 목적에 맞는 모델평가를 수행할 수 없습니다.

이후에는 정확도 외에 또 어떤 지표를 활용하여 모델을 평가할 수 있는지 보겠습니다.

혼동행렬(Confusion Matrix)

혼동행렬(Confusion Matrix) 은 분류문제에서 예측 오류가 얼마나 되고, 어떤 유형의 오류가 발생하는지를 보여주는 행렬로 아래와 같은 배열을 가지고 있습니다.

그 후, 학습셋과 검증데이터셋 전체를 다시 학습하여 테스트셋에 대해 예측을 수행합니다. 이를 통해 학습하지 않은 데이터에 대해서도 모델의 성능이 잘 나오는지 확인합니다.

  • TN(True Negative) : 실제 값이 음성(Negative)인데 예측 값도 음성(Negative) → 맞게 예측
  • FP(False Positive) : 실제 값이 음성(Negative)인데 예측 값은 양성(Positive)
  • FN(False Negative) : 실제 값이 양성(Positive)인데 예측 값은 음성(Negative)
  • TP(True Positive) : 실제 값이 양성(Positive)인데 예측 값도 양성(Positive) → 맞게 예측

사이킷런은 혼동행렬을 구하기 위해 confusion_matrix( ) 함수를 제공합니다.
출력 결과의 배열은 위의 그림과 동일합니다.

앞에서 DummyClassifier로 예측한 결과를 이용해서 혼동행렬을 출력해보겠습니다.

# DummyClassifier를 이용하여 예측한 결과와 y_test를 비교하여 혼동행렬 출력하기
from sklearn.metrics import confusion_matrix

cm = confusion_matrix(y_test, predictions)
print(cm)
print('')
print('True Negative : ' , cm[0][0], ' → 사망자(0)를 사망자(0)로 예측')
print('False Positive : ', cm[0][1], '→ 사망자(0)인데 생존자(1)로 예측')
print('False Negative : ', cm[1][0], ' → 생존자(1)인데 사망자(0)로 예측')
print('True Positive: ', cm[1][1], '→ 생존자(1)를 생존자(1)로 예측')
# 출력:
[[104  13]
 [ 19  43]]
 
True Negative :  104  → 사망자(0)를 사망자(0)로 예측
False Positive :  13 → 사망자(0)인데 생존자(1)로 예측
False Negative :  19  → 생존자(1)인데 사망자(0)로 예측
True Positive:  43 → 생존자(1)를 생존자(1)로 예측

오차행렬을 통해 알 수 있는 지표들

(1) 정확도(Accuracy) = ( TN + TP ) / ( TN + FP + FN + TP )
  • 위에서 살펴보았던 정확도입니다. 전체 예측 중 양성을 양성이라 예측하고, 음성을 음성이라고 예측한 갯수의 비율입니다. 레이블의 분포에 따라 한쪽으로만 분류하더라도 결과가 높게 나올 수 있습니다.
(2) 정밀도(Precision) = TP / ( FP + TP )
  • 양성으로 예측한 것 중 실제로 양성인 비율입니다.
(3) 재현율(Recall) = TP / ( FN + TP )
  • 재현율은 전체 양성 데이터 중 양성으로 예측한 수의 비율입니다. 다른 말로 민감도라고도 하며 양성에 얼마나 민감하게 반응하느냐는 의미입니다.
  • 민감도(Sensitivity), 또는 TPR(True Positive Rate) 이라고도 합니다.
(4) 특이도(Specificity) = TN / ( TN + FP )
  • 특이도는 전체 음성 데이터 중 음성으로 예측한 수의 비율입니다. 음성을 얼마나 잘 골라내는지를 판정하는 지표입니다.
  • TNR(True Negative Rate) 이라고도 합니다.
※ FPR(False Positive Rate) = 1 - Specificity = FP / ( TN + FP )
  • 거짓양성비율(FalsePositiveRate)은 음성 중 양성으로 잘못 판정된 것의 비율입니다.

프로젝트의 목적에 따라 특정 지표가 유용하게 사용됩니다.
가령, 암 환자 판별의 경우에는 정상인을 암환자으로 분류하더라도, 암인 케이스를 반드시 판정해내야 합니다. 때문에 재현율이 중시됩니다.

사이킷런에서는 정확도, 재현율, 정밀도 계산을 위해accuracy_score( ), recall_score( ) 와 precision_score( ) 함수를 제공합니다.

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

# 호출한 지표들을 한꺼번에 계산하는 함수 정의
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)
    print('오차행렬')
    print(confusion)
    print('')
    print('정확도 : {:.4f}'.format(accuracy))
    print('정밀도 : {:.4f}'.format(precision))
    print('재현율 : {:.4f}'.format(recall))

로지스틱회귀 모델을 이용해 타이타닉 예제의 생존자를 다시 예측 후 평가를 수행해보겠습니다.

from sklearn.linear_model import LogisticRegression

titanic_df = pd.read_csv('Titanic/input/train.csv')
y_titanic_df = titanic_df['Survived']
X_titanic_df = titanic_df.drop(['Survived'], axis = 1)
X_titanic_df = transform_features(X_titanic_df)

X_train, X_test, y_train, y_test = train_test_split(X_titanic_df,
                                                    y_titanic_df,
                                                    test_size = 0.2,
                                                    random_state = 10)

clf = LogisticRegression()
clf.fit(X_train, y_train)
pred = clf.predict(X_test)
get_clf_eval(y_test, pred)
# 출력:
[[104  13]
 [ 17  45]]

정확도 : 0.8324
정밀도 : 0.7759
재현율 : 0.7258

정밀도와 재현율의 trade-off 관계

정밀도와 재현율은 상호보완적인 지표로 한 쪽을 높이려고 하면, 다른 한 쪽이 떨어지기 쉽습니다.

사이킷런의 분류 알고리즘은 예측 데이터가 특정 레이블에 속하는지 판단하기 위해 개별 레이블별로 확률을 구하고, 그 확률이 큰 레이블 값으로 예측합니다.

  • 이진 분류의 경우, 일반적으로는 임계값을 50%로 정하고 이보다 크면 양성(Positive), 작으면 음성(Negative)로 결정합니다.
  • predict_proba( ) 를 사용하여 개별 레이블별 예측확률을 반환할 수 있습니다.
# 타이타닉 생존자 데이터에서 predict() 결과 값과 predict_proba()결과값을 비교하기
pred_proba = clf.predict_proba(X_test)
pred = clf.predict(X_test)

print('pred_proba의 shape: {}'.format(pred_proba.shape))
print('pred_proba의 array의 앞 5개만 샘플로 추출:\n', pred_proba[:5])
# 예측확률 array와 예측결과값 array를 병합하여 예측확률과 결과값을 한 번에 확인하기
pred_proba_result = np.concatenate([pred_proba, pred.reshape(-1, 1)], axis=1)
print('두 개의 레이블 중 더 큰 확률을 클래스 값으로 예측: \n', pred_proba_result[:5])
# 출력:
pred_proba의 shape: (179, 2)
pred_proba의 array의 앞 5개만 샘플로 추출:
 [[0.88292372 0.11707628]
 [0.84599254 0.15400746]
 [0.86140126 0.13859874]
 [0.08019536 0.91980464]
 [0.13863631 0.86136369]]
두 개의 레이블 중 더 큰 확률을 클래스 값으로 예측: 
 [[0.88292372 0.11707628 0.        ]
 [0.84599254 0.15400746 0.        ]
 [0.86140126 0.13859874 0.        ]
 [0.08019536 0.91980464 1.        ]
 [0.13863631 0.86136369 1.        ]]

정밀도/재현율 trade-off 관계를 살펴보기

사이킷런의 Binarizer 클래스를 활용하여 임계치에 따른 정밀도/재현율의 변화를 살펴보겠습니다.

※ Binarizer : fit_transform( )을 이용하여 정해진 임계치보다 같거나 작으면 0, 크면 1보다 반환

from sklearn.preprocessing import Binarizer

# 예시
X = [[-1, -1, 2],
     [2, 0, 0], 
     [0, 1.1, 1.2]]

# X의 개별원소들이 threshold보다 크면 1, 작거나 같으면 0을 반환
binarizer = Binarizer(threshold=1.1)
print(binarizer.fit_transform(X))
# 출력:
[[0. 0. 1.]
 [1. 0. 0.]
 [0. 0. 1.]]

앞선 Logistic Regression 객체의 predict_proba()의 결과 값에 Binarizer클래스를 적용하여 최종 예측 값을 구하고, 최종 예측 값에 대해 평가해보겠습니다.

# 임계값
thresholds = [0.4, 0.45, 0.5, 0.55, 0.6]

# 임계치에 따른 평가지표를 조사하기 위한 함수 생성
def get_eval_by_threshold(y_test, pred_proba_c1, thresholds):
    for custom_threshold in thresholds:
        binarizer = Binarizer(threshold=custom_threshold)
        custom_predict = binarizer.fit_transform(pred_proba_c1)
        print('임계값: ', custom_threshold)
        get_clf_eval(y_test, custom_predict)
        print('---------------')

pred_proba = clf.predict_proba(X_test)
get_eval_by_threshold(y_test, pred_proba[:,1].reshape(-1,1), thresholds)
# 출력:
임계값:  0.4
오차행렬
[[98 19]
 [14 48]]

정확도 : 0.8156
정밀도 : 0.7164
재현율 : 0.7742
---------------
임계값:  0.45
오차행렬
[[101  16]
 [ 17  45]]

정확도 : 0.8156
정밀도 : 0.7377
재현율 : 0.7258
---------------
임계값:  0.5
오차행렬
[[104  13]
 [ 17  45]]

정확도 : 0.8324
정밀도 : 0.7759
재현율 : 0.7258
---------------
임계값:  0.55
오차행렬
[[105  12]
 [ 23  39]]

정확도 : 0.8045
정밀도 : 0.7647
재현율 : 0.6290
---------------
임계값:  0.6
오차행렬
[[107  10]
 [ 25  37]]

정확도 : 0.8045
정밀도 : 0.7872
재현율 : 0.5968
---------------

임계값이 증가할수록 정밀도 값은 높아지지만 재현율 값이 낮아짐을 볼 수 있습니다.
이를 precision_recall_curve( ) 함수와 matplitlib을 활용하여 시각화해보겠습니다.

※ precision_recall_curve : 실제클래스값과 예측 확률값을 입력인자로 받아 임계값에 따른 정밀도, 재현율, 임계값을 ndarray로 반환

import matplotlib.pyplot as plt
%matplotlib inline
from sklearn.metrics import precision_recall_curve

def precision_recall_curve_plot(y_test, predict_proba_c1):
    # 정밀도, 재현율, 임계값을 ndarray로 추출하기
    precisions, recalls, thresholds = precision_recall_curve(y_test, predict_proba_c1)
    
    # x축을 임계값, y축을 정밀도, 재현율로 그래프 그리기
    plt.figure(figsize = (8, 6))
    thresholds_boundary = thresholds.shape[0]
    # 정밀도와 재현율은 임계값보다 1개 적게 반환되기 때문에 슬라이싱을 해주어야 합니다.
    plt.plot(thresholds, precisions[:thresholds_boundary], 
             linestyle = '--', label='precision')
    plt.plot(thresholds, recalls[:thresholds_boundary], 
             linestyle = '-', label = 'recall')
    
    # x축의 scale을 0.1 단위로 변경
    plt.xticks(np.round(np.arange(0, 1, 0.1),2))
    plt.xlabel('thresholds')
    plt.ylabel('precision(recall)')
    
    plt.legend()
    plt.grid()
    plt.show()
    
precision_recall_curve_plot(y_test, clf.predict_proba(X_test)[:, 1])

위 그래프를 보면 임계값을 높임에 따라 정밀도는 개선되지만, 재현율은 나빠짐을 한눈에 알 수 있습니다.

F1 스코어

정밀도와 재현율을 결합한 지표로 정밀도와 재현율이 어느 한 족으로 치우지지 않을 때 상대적으로 높은 값을 가집니다.

예시 ) 모델 A의 정밀도 0.9, 재현율 0.1 / 모델 B의 정밀도 0.5, 재현율 0.5
→ 모델 A의 F1 = 0.18, 모델 B의 F1 = 0.5

사이킷런에서는 쉽게 f1을 계산할 수 있도록 f1_score( ) 함수를 제공하고 있습니다.

from sklearn.linear_model import LogisticRegresion

titanic_df = pd.read_csv('Titanic/input/train.csv')
y_titanic_df = titanic_df['Survived']
X_titanic_df = titanic_df.drop(['Survived'], axis = 1)
X_titanic_df = transform_features(X_titanic_df)

X_train, X_test, y_train, y_test = train_test_split(X_titanic_df,
                                                    y_titanic_df,
                                                    test_size = 0.2,
                                                    random_state = 10)

clf = LogisticRegression()
clf.fit(X_train, y_train)
pred = clf.predict(X_test)

# f1 score를 계산하기
from sklearn.metrics import precision_score, recall_score, f1_score
precision = precision_score(y_test, pred)
recall = recall_score(y_test, pred)
f1 = f1_score(y_test, pred)

print('정밀도(Precision) : {:.4f}'.format(precision))
print('재현율(Recall) : {:.4f}'.format(recall))
print('F1 : {:.4f}'.format(f1))
# 출력:
정밀도(Precision) : 0.7759
재현율(Recall) : 0.7258
F1 : 0.7500

타이타닉 생존자 예측에서 임계값을 변화시키며 F1, 정밀도, 재현율 구하기

# 예측결과에 따른 평가지표 계산
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_score(y_test, pred)
    print('오차행렬')
    print(confusion)
    # F1 score print 추가
    print('\n정확도: {:.4f}\n정밀도: {:.4f}\n재현율: {:.4f}\nF1: {:.4f}'.format(accuracy, precision, recall, f1))

# 임계값에 따른 평가지표 계산    
def get_eval_by_threshold(y_test, pred_proba_c1, thresholds):
    for custom_threshold in thresholds:
        binarizer = Binarizer(threshold=custom_threshold)
        custom_predict = binarizer.fit_transform(pred_proba_c1)
        print('임계값: ', custom_threshold)
        get_clf_eval(y_test, custom_predict)
        print('---------------')
    
thresholds = [0.4, 0.45, 0.5, 0.55, 0.6]
pred_proba = clf.predict_proba(X_test)
get_eval_by_threshold(y_test, pred_proba[:,1].reshape(-1, 1), thresholds)
# 출력:
임계값:  0.4
오차행렬
[[98 19]
 [14 48]]

정확도: 0.8156
정밀도: 0.7164
재현율: 0.7742
F1: 0.7442
---------------
임계값:  0.45
오차행렬
[[101  16]
 [ 17  45]]

정확도: 0.8156
정밀도: 0.7377
재현율: 0.7258
F1: 0.7317
---------------
임계값:  0.5
오차행렬
[[104  13]
 [ 17  45]]

정확도: 0.8324
정밀도: 0.7759
재현율: 0.7258
F1: 0.7500
---------------
임계값:  0.55
오차행렬
[[105  12]
 [ 23  39]]

정확도: 0.8045
정밀도: 0.7647
재현율: 0.6290
F1: 0.6903
---------------
임계값:  0.6
오차행렬
[[107  10]
 [ 25  37]]

정확도: 0.8045
정밀도: 0.7872
재현율: 0.5968
F1: 0.6789
---------------
import matplotlib.pyplot as plt
%matplotlib inline
from sklearn.metrics import precision_recall_curve

def precision_recall_curve_plot(y_test, predict_proba_c1):
    # 정밀도, 재현율, 임계값을 ndarray로 추출하기
    precisions, recalls, thresholds = precision_recall_curve(y_test, predict_proba_c1)
    f1 = 2 * (precisions * recalls) / (precisions + recalls)
    
    # x축을 임계값, y축을 정밀도, 재현율로 그래프 그리기
    plt.figure(figsize = (8, 6))
    thresholds_boundary = thresholds.shape[0]
    # 정밀도와 재현율은 임계값보다 1개 적게 반환되기 때문에 슬라이싱을 해주어야 합니다.
    plt.plot(thresholds, precisions[:thresholds_boundary], 
             linestyle = '--', label='precision')
    plt.plot(thresholds, recalls[:thresholds_boundary], 
             linestyle = '-', label = 'recall')
    plt.plot(thresholds, f1[:thresholds_boundary], 
             linestyle = ':',  label = 'f1')
    
    # x축의 scale을 0.1 단위로 변경
    plt.xticks(np.round(np.arange(0, 1, 0.1),2))
    plt.xlabel('thresholds')
    plt.ylabel('score')
    
    plt.legend()
    plt.grid()
    plt.show()
    
pred_proba = clf.predict_proba(X_test)
precision_recall_curve_plot(y_test, pred_proba[:, 1])

위의 그래프를 보면, f1 score 는 민감도와 재현율이 적절히 균형을 맞춘 지점에서 가장 높게 나타남을 알 수 있습니다.

ROC곡선과 AUC

ROC곡선은 False Positive Rate(FPR)이 변할 때 True Positive Rate(TPR)이 어떻게 변하는지를 나타내는 곡선입니다.

  • False Positive Rate : 음성 중 양성으로 잘못 판정한 것의 비율
  • True Positive Rate : 양성 중 양성으로 잘 판정한 것의 비율

위의 그림은 ROC 곡선의 예시로 가운데 대각선은 무작위로 분류를 하는 분류기의 ROC 곡선입니다. 곡선이 가운데 대각선에 가까울수록 성능이 떨어지며 멀어질수록 성능이 뛰어난 것입니다.

사이킷런의 roc_curve( ) 기능을 활용하면, FPR, TPR, 임계치을 반환합니다.

타이타닉 생존자 예측모델의 FPR, TPR, 임계치 구하기

from sklearn.metrics import roc_curve

# 레이블 값이 1일 때 예측 확률을 추출
pred_proba_class1 = clf.predict_proba(X_test)[:, 1]

fpr, tpr, thresholds = roc_curve(y_test, pred_proba_class1)

# 반환된 임계값 중 샘플로 10건만 추출하되 임계값을 5 step으로 추출
thr_index = np.arange(1, thresholds.shape[0], 5)
print('샘플 추출된 임계값 배열의 index 10개: ', thr_index)
print('샘플용 10개의 임계값: ', np.round(thresholds[thr_index], 2))

# 샘플로 추출된 임계값에 따른 FPR, TPR 값
print('샘플 임계값별 FPR: ', np.round(fpr[thr_index], 3))
print('샘플 임계값별 TPR: ', np.round(tpr[thr_index], 3))
# 출력: 
샘플 추출된 임계값 배열의 index 10개:  [ 1  6 11 16 21 26 31 36 41 46 51]
샘플용 10개의 임계값:  [0.95 0.69 0.66 0.51 0.38 0.28 0.23 0.15 0.13 0.13 0.04]
샘플 임계값별 FPR:  [0. 0.017 0.06  0.103 0.179 0.248 0.316 0.573 0.667 0.735 1. ]
샘플 임계값별 TPR:  [0.016 0.516 0.565 0.726 0.774 0.839 0.887 0.903 0.935 0.952 1. ]

roc_curve( )의 결과값을 보면 임계값이 1에 가까운 값에서 점점 작아질수록 FPR이 점점 커집니다. 그리고 FPR이 조금씩 커질 때 TPR은 가파르게 커집니다. 이것을 그래프로 시각화해보겠습니다.

# ROC 곡선의 시각화
def roc_curve_plot(y_test, pred_proba_c1):
    #임계값에 따른 FPR, TPR 값을반환 받음
    fprs, tprs, thresholds  = roc_curve(y_test, pred_proba_c1)
    # ROC곡선을 그래프로 그림
    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()
    
roc_curve_plot(y_test, pred_proba[:, 1])

일반적으로 ROC 곡선 자체는 FPR과 TPR의 변화 값을 보는데 이용하며 분류의 성능지표로는 ROC 면적에 기반한 AUC 값으로 결정합니다.

  • AUC(Area Under Curve) : 0.5와 1사이의 값을 가집니다. 곡선 밑의 면적 값으로 1에 가까울수록 좋은 수치입니다. ROC 곡선이 대각선 자체일 때는 0.5입니다.

    → roc_auc_score( ) 함수를 이용해서 AUC 값을 구할 수 있습니다.

from sklearn.metrics import roc_auc_score

pred = clf.predict(X_test)
roc_score = roc_auc_score(y_test, pred)
print('ROC AUC 값 : {:.4f}'.format(roc_score))
# 출력:
ROC AUC 값 : 0.8073

Reference
  • 파이썬 머신러닝 완벽가이드


사이킷런혼동행렬정확도재현율민감도정밀도F1ROCAUC Share Tweet +1