ML/평가(Evaluation)

정확도(Accuracy)란?

야뤼송 2024. 2. 21. 14:04
반응형

 

 

1. 정확도란?

정확도는 전체 예측 데이터 중 예측 결과과 동일한 데이터가 포함되었는지에 대한 비율을 의미한다.

이를 공식으로 하면 다음과 같다.

 

2. 정확도의 문제점(1) - 타이타닉 성별 생존 확률

정확도는 직관적으로 모델 예측 성능을 나타내는 평가 지표이지만 데이터의 구성에 따라 머신러닝 모델의 성능을 왜곡할 수 있기 때문에 정확도 수치 하나만 가지고 성능을 평가해서는 안된다.

 

특히 정확도는 불균형한 레이블 값 분포에서 ML 모델의 성능을 판단할 경우, 적합한 평가 지표로 활용할 수  없다.

 

앞서 진행했던 타이타닉 생존자 예측에서 성별 생존확률을 살펴보자.(사이킷런을 이용한 타이타닉 생존자 예측

 

머신러닝 알고리즘을 적용한 예측결과에서 생존확률은 80%, 남성보다 여성이 생존확률이 높았다. 

여성의 생존확률이 더 높았기 때문에 단순하게 성별이 여자인 경우 생존, 남성인 경우 사망으로 예측을 해보면 비슷한 결과가 나오게 된다.

 

 실습

아래 예제를 통해 확인해보자.

 

DummyClassifer를 만들어서 남자면 사망, 여자면 생존으로 예측하는 Classifer를  생성한다.

참고로, 사이킷런에선  BaseEstimator를 상속받으면 Customizded된 Estimator를 생성할 수 있다.

import sklearn
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 결정값, 즉 예측한 값
        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

 

 

위에 Customized된 Classfier를  통해 생존자 예측을 해보면 다음과 같다.

앞서 진행된것 같이 Null처리, 불필요피처 제거, 레이블인코딩 수행은 동일하게 전처리해준다.

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

 

 

전처리된 데이터를 가지고 위의 Dummy로 생성한 Classifer를 적용하면 정확도는 약 78%로 결정트리를 적용한 머신러닝과 유사한 결과값이 나옴을 알 수 있다.

그렇기 때문에 정확도를 가지고 평가를 할 경우 신중해야한다.

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')
#print(titanic_df)

y_titanic_df = titanic_df['Survived']
#print(y_titanic_df)

X_titanic_df= titanic_df.drop('Survived', axis=1)
#print(X_titanic_df)

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)))

▶ Out

Dummy Classifier의 정확도는: 0.7877

 

 

3. 정확도의 문제점(2) - MNIST DATASET

다른 예시를 통해 정확도의 문제점을 확인해보자.

 

MINIST DATASET

  • 0~9까지의 손글씨로 작성한 숫자 이미지의 픽셀 정보를 가지고 있다.
  • 사이킷런에서는 load_digits() API를 통해 MNIST 정보를 제공하고 있다.

레이블 값인 7이면 True, 나머지값은 모두 False로 변환하여 불균형한 데이터 세트에서 예측 정확도가 어떻게 나오는지 확인해본다.

 

 실습

load_digits()를 통해 MNIST 데이터를 로딩해준다. 그리고 입력값으로 들어오는 X 데이터 셋의 크기만큼 모두 0값으로 만들어 변환하는 Fake Classifer를  생성한다.

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)

▶ Out

[[ 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,)

 

 

load_digits()에서 불러온 MNIST 데이터 셋을 불균형한 데이터 셋으로 변경해준다.

digits번호가 7번이면 True이고 이를 astype(int)로 1로 변환, 7번이 아니면 False이고 0으로 변환해준다.

 

총 450개의 데이터 중 0인 데이터는 전체의 90%, 1인 데이터는 전체인 10%이다.

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)
print('레이블 테스트 세트 크기 :', y_test.shape)
print('테스트 세트 레이블 0 과 1의 분포도')
print(pd.Series(y_test).value_counts())

▶ Out

[0 0 0 ... 0 0 0]
레이블 테스트 세트 크기 : (450,)
테스트 세트 레이블 0 과 1의 분포도
0    405
1     45
dtype: int64

 

 

위에서 생성한 FakeClassifier로 학습/예측/정확도 평가를 해보자.

predict()의  결과를 np.zero()로 모두 0 값으로 반환하였기 때문에 모든 예측 결과는 false가 된다.

그러나 y_test, 즉 테스트용 target의  450개의 데이터 세트에서 false가 결과가 전체의 90%이다보니 예측 정확도는 90%로 나오게 된다. 

이렇듯 불균형한 레이블 데이터 세트에서는 성능 수치로 사용하면 안돼는 것을 확인할 수 있다.

fakeclf = MyFakeClassifier()
fakeclf.fit(X_train , y_train)
fakepred = fakeclf.predict(X_test)
print('모든 예측을 0으로 하여도 정확도는:{:.3f}'.format(accuracy_score(y_test , fakepred)))

▶ Out

모든 예측을 0으로 하여도 정확도는:0.900

 

 

 

반응형