성능 평가 지표 Evaluation Metric¶
사이킷런의 BaseEstimator를 활용하여 단순히 성별에 따라 생존자를 예측하는 classifier 생성¶
In [28]:
import numpy as np
from sklearn.base import BaseEstimator
class MyDummyClassifier(BaseEstimator):
# fit 메서드는 아무것도 학습하지 않음
def fit(self, X, y=None):
pass
#predict 메서드는 단순히 Sex 피처가 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 [29]:
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
#앞에서 정의된 함수들 호출, 전처리 수행하는 함수 생성
def transform_features(df):
df = fillna(df)
df = drop_features(df)
df = format_features(df)
return df
In [30]:
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)
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
- 이렇게 단순한 알고리즘으로도 정확도가 꽤 높게 나오기 때문에 정확도를 평가 지표로 사용할 때는 매우 신중해야 함
- 특히 정확도는 불균형한 레이블 값 분포에서는 적합하지 않음
MNIST 데이터셋을 multi classification에서 binary classification으로 변경하여 불균형한 데이터셋으로 만든 뒤
어떤 문제가 발생하는지 살펴보기
¶
- MNIST 데이터셋은 0부터 9까지의 숫자 이미지의 픽셀 정보를 가짐, 이를 기반으로 숫자 Digit을 예측하는데 사용
- 사이킷런은 load_digits을 통해 MNIST 데이터셋을 제공
- 레이블 값이 7인 것만 True, 나머지는 모두 False로 변환해 이진 분류 문제로 바꾸기
- 즉, 전체 데이터의 10%만 True, 90%은 False인 불균형한 데이터셋으로 변형하는 것
In [1]:
#불균형한 데이터셋과 Dummy Classifier를 생성하기
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를 이용해 데이터 로당
digits = load_digits()
#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 [2]:
#불균형한 레이블 데이터 분포도 확인
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으로 하여도 정확도는: {0:.3f}',format(accuracy_score(y_test, fakepred)))
레이블 세트 크기: (450,)
테스트 세트 레이블 0과 1의 분포도
0 405
1 45
dtype: int64
모든 예측을 0으로 하여도 정확도는: {0:.3f} 0.9
- 이처럼 정확도 평가 지표는 불균형한 레이블 데이터셋에서는 성능 수치로 사용해선 안됌
- 이런 한계점을 보완하기 위해 여러 가지 분류 지표와 함께 적용해야 함 >> 오차행렬
다음 글에서 계속!!
'Data Science > 파이썬 머신러닝 완벽 가이드' 카테고리의 다른 글
[sklearn] (12) - 정밀도와 재현율 Precision and Recall (trade-off, predict_proba(), Binarizer, threshold...) (2) | 2023.05.10 |
---|---|
[sklearn] (11) - 오차 행렬 confusion_matrix() (0) | 2023.05.09 |
[sklearn] (9) - 데이터 스케일링 Data Scaling (0) | 2023.05.05 |
[kaggle] 타이타닉 생존률 예측하기 (2) - 모델링 (0) | 2023.05.04 |
[sklearn] (8) - 데이터 전처리(LabelEncoder, OneHotEncoder, get_dummies, StandardScaler, MinMaxScaler) (0) | 2023.05.02 |