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
'ML > 평가(Evaluation)' 카테고리의 다른 글
피마 인디언 당뇨병 데이터로 머신러닝 평가하기 (0) | 2024.03.25 |
---|---|
F1 Score와 Roc, 그리고 AUC (0) | 2024.03.12 |
오차행렬(Confusion Matrix), 정밀도(Precision), 재현율(Recall) (2) | 2024.03.11 |