본문 바로가기

AI Study/Machine Learning

머신러닝 성능 평가 지표 (1) - 정확도(accuracy_score)

Machine Learning Evaluation

  • Evaluation Metric (성능 평가 지표) : 모델의 타입(분류 / 회귀)에 따라 나뉨

    • 회귀 : 대부분 실제값과 예측값의 오차 평균값에 기반함
    • 분류 : 실제 결과 데이터와 예측 결과 데이터의 차이만으로 판단하지는 않음.
      • 특히, 이진 분류(0 or 1로 판단)에서는 accuracy score보다는 다른 성능 평가 지표를 함께 사용하는 것이 선호됨.
      • 분류의 성능 평가 지표
        • 정확도(Accuracy) : 불균형한 label data set에서 사용 X
        • 오차행렬(confusion Matrix) : 오차의 정도, 종류를 알 수 있음
        • 정밀도(Precision) : (선호됨)
        • 재현율(Recall) : (선호됨)
        • F1 스코어
        • ROC AUC
      • 분류는 label class 값 종류에 따라 이진분류(0 or 1) or 멀티 분류로 구분됨
        • 위의 6가지는 이진분류에서 중요하게 다루는 방법들임.

Accuracy

  • 정확도 : 실제 데이터에서 예측 데이터가 얼마나 같은지를 판단하는 지표

    정확도(Accuray) = 예측 결과가 동일한 데이터 건수 / 전체 예측 데이터 건수
    • 직관적으로 모델 예측 성능을 나타내는 평가 지표
    • 불균형한 label data set에서는 사용하면 안된다.
    • 이진 분류의 경우, 데이터 구성에 따라 모델 성능을 왜곡할 수 있기에 정확도는 여러개를 사용하는 것이 좋음.
    • titanic에 대해 sklearn으로 보는 '정확도 지표가 모델 성능을 왜곡하는 예제
    • 불균형한 label 값 분포에서 ML 모델의 성능을 판단할 경우, 적합하지 않음.
 
In [30]:
# 단순히 Sex feature가 1(남자)이면 0(사망), 그렇지 않으면(여자면) 1(생존)로 예측하는 단순한 Classifier

import pandas as pd
from sklearn.base import BaseEstimator

class MyDummyClassifier(BaseEstimator):
    # fit() method는 아무것도 학습하지 않음.
    def fit(self, X, y=None):
        pass
    # predict() method는 단순히 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
 
In [31]:
# 데이터 전처리 함수
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
 
In [32]:
# 생성된 MyDummyClassifier를 이용해 titanic 생존자 예측을 수행. 
# transform_features는 2장에서 진행한 데이터 처리함수

from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import numpy as np

# 원본 데이터를 재로딩, 데이터 가공, 학습 데이터/테스트 데이터 분할.
titanic = pd.read_csv('data/titanic_train.csv')
titanic_y = titanic['Survived']
titanic_X = titanic.drop('Survived', axis=1)

X_titanic = transform_features(titanic_X)
X_train, X_test, y_train, y_test = train_test_split(titanic_X, titanic_y, 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)))
 

MNIST DATASET로 해보는 accuracy_score 실습

: MNIST dataset를 변환해 불균형한 dataset에 대한 정확도의 오류를 확인해보는 코드

 

MNIST dataset

  • 0-9까지의 숫자 이미지의 픽셀 정보를 가지고 있음
  • 숫자 Digit을 예측하는데 사용함
  • load_digits() API를 통해 MNIST dataset를 제공함
  • 원래 MNIST에서는 값이 0-9까지 multi label임.
    • 이진분류 문제로의 변형을 위해 7에 대해 True, 나머지는 False로 바꾸고 시작.
    • 모든 데이터를 False, 즉 0ㅡ로 예측하는 Classifier를 이용해 정확도를 측정 -> 0.9예상
      • 데이터 불균형 정도가 심할수록 이 Classifier의 성능이 높은 수치를 기록하게 됨

 

In [33]:
# 불균형한 dataset의 DummyClassifier를 생성함

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,)
 
 
In [34]:
digits.target == 7
 
Out[34]:
array([False, False, False, ..., False, False, False])
 
 
In [35]:
# 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)
 
 
In [36]:
# 불균형한 레이블 데이터 분포도 확인. 
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