[ML] 평가 - 분류 성능 평가지표 개요와 정확도 (Acurracy)

강주형·2022년 7월 5일
0

지금부터는 분류에 대한 성능 평가지표를 소개한다.

분류의 성능 평가 지표 개요

  • 정확도 (Acurracy)
  • 오차행렬 (Confusion Matrix)
  • 정밀도 (Precision)
  • 재현율 (Recall)
  • F1 스코어
  • ROC AUC

정확도는 한계를 가지고 있어서 이진 분류에서 잘 사용하지 않고,
ROC AUC는 많이 사용하는 편이다.


정확도의 한계

정확도=예측결과가동일한데이터건수전체예측데이터건수정확도 = \frac{예측 결과가 동일한 데이터 건수}{전체 예측 데이터 건수}

위 식처럼 계산되기 때문에 불균형한 레이블 값 분포에서는 머신러닝 모델의 성능을 판단할 경우 적합한 평가 지표라고 하기 어려움!

아래 두 가지 예시로 확인해보자


타이타닉 예시

복잡한 머신러닝 모델을 사용하지 않고, 아주 간단한 Estimator를 새로 만들어서 예측해보자

  • Sex = 1 (남자) 이면 0 (사망)
  • Sex = 0 (여자) 이면 1 (생존)
    이렇게만 예측하는 모델을 만들어서 정확도를 측정해보자

MyDummyClassifier 라는 클래스를 새로 만든다.
DecisionTreeClassifier, XGBClassifier 같은 역할을 하는 Estimator를 새로 만든 것!
fit()은 아무 작업도 하지 않고, predict()는 단순히 남자면 사망, 여자면 생존 으로만 예측한다.

import numpy as np
from sklearn.base import BaseEstimator

class MyDummyClassifier(BaseEstimator):
    # fit( ) 메소드는 아무것도 학습하지 않음. 
    def fit(self, X , y=None):
        pass
    
    # predict( ) 메소드는 단순히 Sex feature가 1 이면 0 , 그렇지 않으면 1 로 예측함. 
    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

아래는 앞에 타이타닉 예시에서 했던 전처리와 동일하게 해주는 것

import pandas as pd
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_features(df):
    df.drop(['PassengerId','Name','Ticket'],axis=1,inplace=True)
    return df

# 레이블 인코딩 수행. 
def format_features(df):
    df['Cabin'] = df['Cabin'].str[:1]
    features = ['Cabin','Sex','Embarked']
    for feature in features:
        le = LabelEncoder()
        le = le.fit(df[feature])
        df[feature] = le.transform(df[feature])
    return df

# 앞에서 설정한 Data Preprocessing 함수 호출
def transform_features(df):
    df = fillna(df)
    df = drop_features(df)
    df = format_features(df)
    return df

정확도를 측정해보자!

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

# 원본 데이터를 재로딩, 데이터 가공, 학습데이터/테스트 데이터 분할
titanic_df = pd.read_csv('./titanic_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=0)

# 위에서 생성한 Dummy Classifier를 이용하여 학습/예측/평가 수행
myclf = MyDummyClassifier()
myclf.fit(X_train, y_train)

mypredictions = myclf.predict(X_test)
print('Dummy Classifier의 정확도는: {0:.4f}'.format(accuracy_score(y_test , mypredictions)))
Dummy Classifier의 정확도는: 0.7877

그냥 성별에 따라 사망 여부만 예측했는데도 정확도가 78.77%가 나옴
확실히 성능 지표로 사용하기에는 무리가 있어 보인다.


MNIST 예시

손글씨 데이터세트인 MNIST의 예시를 들어보자

불균형한 이진 분류로 만들기 위해 숫자 '7'만 True 나머지 숫자는 False로 설정하자

마찬가지로 fit은 아무것도 하지 않음
predict에서는 무조건 0을 반환함
모든 예측을 다 0으로 하는 것! (모두 7이 아닌 다른 숫자라고 예측)
당연히 모델로서 말이 안 되는데 이것의 정확도를 측정해보자

from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.base import BaseEstimator
from sklearn.metrics import accuracy_score
import numpy as np
import pandas as pd

class MyFakeClassifier(BaseEstimator):
    def fit(self,X,y):
        pass
    
    # 입력값으로 들어오는 X 데이터 셋의 크기만큼 모두 0값으로 만들어서 반환
    def predict(self,X):
        return np.zeros( (len(X), 1) , dtype=bool)

# 사이킷런의 내장 데이터 셋인 load_digits( )를 이용하여 MNIST 데이터 로딩
digits = load_digits()

print(digits.data)
print("### digits.data.shape:", digits.data.shape)
print(digits.target)
print("### digits.target.shape:", digits.target.shape)
[[ 0.  0.  5. ...  0.  0.  0.]
 [ 0.  0.  0. ... 10.  0.  0.]
 [ 0.  0.  0. ... 16.  9.  0.]
 ...
 [ 0.  0.  1. ...  6.  0.  0.]
 [ 0.  0.  2. ... 12.  0.  0.]
 [ 0.  0. 10. ... 12.  1.  0.]]
### digits.data.shape: (1797, 64)
[0 1 2 ... 8 9 8]
### digits.target.shape: (1797,)

이제 정확도를 예측해보자

# digits번호가 7번이면 True이고 이를 astype(int)로 1로 변환, 7번이 아니면 False이고 0으로 변환. 
y = (digits.target == 7).astype(int)
X_train, X_test, y_train, y_test = train_test_split( digits.data, y, random_state=11)

# 불균형한 레이블 데이터 분포도 확인. 
print('레이블 테스트 세트 크기 :', y_test.shape)
print('테스트 세트 레이블 0 과 1의 분포도')
print(pd.Series(y_test).value_counts())

# Dummy Classifier로 학습/예측/정확도 평가
fakeclf = MyFakeClassifier()
fakeclf.fit(X_train , y_train)
fakepred = fakeclf.predict(X_test)
print('모든 예측을 0으로 하여도 정확도는:{:.3f}'.format(accuracy_score(y_test , fakepred)))
레이블 테스트 세트 크기 : (450,)
테스트 세트 레이블 0 과 1의 분포도
0    405
1     45
dtype: int64
모든 예측을 0으로 하여도 정확도는:0.900

모든 예측을 0으로 했지만, 0 (7이 아닌 숫자)1 (숫자 7)보다 훨씬 많은 불균형 데이터였기 때문에, 정확도가 무려 90%가 나왔음

이렇게 두 가지 예제로 불균형 데이터에서 정확도의 성능 평가지표로서 한계를 보였다.

profile
Statistics & Data Science

0개의 댓글