카테고리 없음

머신러닝 평가 방법(정확도, 오차행렬, 정밀도, 재현율)

강떡볶 2022. 5. 1. 16:30

1. 정확도(Accuracy)

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

 

  • But 이진 분류의 불균형한 레이블 데이터 세트의 경우 성능 왜곡 가능
    ⇒ 따라서, 여러 가지 분류 지표와 함께 적용해야 함

 

 

 

타이타닉 생존자 예측을 통한 정확도 확인

 

1. 사이킷런의 BestEstimator 클래스 상속받아 클래스 상속 받아 Customized 형태의 Estimator 생성

from sklearn.base import BaseEstimator
import numpy as np

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 == 1:
                pred[i] = 0
            else:
                pred[i] = 1
                
        return pred
  • MyDummyClassifier 클래스 생성
    ⇒ 예측을 수행하는 predict() 메서드는 성별에 따라 생존자 예측
    ⇒ 학습을 수행하는 fit() 메서드는 아무 것도 수행하지 않음

 

 

2. transform_features() 함수 정의

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

 

 

3. 원본 데이터 재로딩, 데이터 가공, 학습

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)

 

 

4. MyDummyClassifier 이용해 학습/예측/평가

# MyDummyClassifier 이용
myclf = MyDummyClassifier()

# 학습 수행
myclf.fit(X_train, y_train)

# 예측 수행
mypredictions = myclf.predict(X_test)

# 평가 수행
print('Dummy Classifier의 정확도는: {0:.4f}'.format(accuracy_score(y_test, mypredictions)))

 

 

Positive/Negative
  • 불균형한 이진 분류 데이터 세트에서는 중점적으로 찾아야 하는 적은 수의 결과값을 Positive로 설정해 1값 부여
  • 그렇지 않은 경우는 Negative로 설정해 0값 부여
    ex) 사기 행위 예측 모델
    사기 행위가 Positive(1), 정상 행위가 Negative(0)
  • ex) 암 검진 예측 모델
    암이 양성일 경우 Positive(1), 음성일 경우 Negative(0)
  • Positive 데이터 건수가 매우 적으면 ML 알고리즘은 Negative로 예측하는 경향이 있음
  • Positive에 대한 예측 정확도 판단 없이 Negative에 대한 예측 정확도만으로 분류의 정확도가 매우 높게 나타날 수 있음 ⇒ 정밀도/재현율 체크

 

 

 

2. 오차 행렬(Confusion Matrix)

이진 분류의 예측 오류가 얼마인지와 더불어 어떠한 유형의 예측 오류가 발생하고 있는지를 함게 나타내는 지표

 

  • True/False : 예측값과 실제값이 '같은가/다른가'
  • Negative/Positive: 예측 결과 값이 부정(0)/긍정(1)

 

 

 

 

오차행렬 실습

 

1. MyFakeClassifier 생성

class MyFakeClassifier(BaseEstimator):
    def fit(self, X, y):
        pass

# 입력값으로 들어오는 X 데이터 세트의 크기만큼 모두 0값으로 만들어서 변환
    def predict(self, X):
        return np.zeros((len(X), 1), dtype=bool)

# 사이킷런의 내장 데이터 세트인 load_digists()를 이용해 MNIST 데이터 로딩
digits = load_digits()

# digits 번호가 7번이면 True이고 이를 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)))

 

 

2) MyFakeClassifier의 예측 성능 지표를 오차 행렬로 표현

from sklearn.metrics import confusion_matrix

confusion_matrix(y_test, fakepred)

 

 

 

3. 정밀도와 재현율

Positive 데이터 세트의 예측 성능에 좀 더 초점을 맞춘 평가 지표

 

 

 

재현율이 중요 지표인 경우

: 실제 Positive인 데이터를 Negative로 잘못 판단하면 업무상 큰 영향이 발생하는 경우

 

ex) 암 판단 모델 : 실제 Positive인 암 환자를 Negative 음성으로 잘못 판단할 경우 심각함

ex) 금융 사기 적발 모델 : 실제 금융거래 사기인 Positive 건을 Negative로 잘못 판단할 경우 큰 손해

 

 

 

정밀도가 중요 지표인 경우

: 실제 Negative인 데이터를 Positive로 잘못 판단하면 업무상 큰 영향이 발생하는 경우

 

ex) 스팸메일 여부 판단 모델 : 실제 Negative인 일반 메일을 Positive인 스팸 메일로 분류할 경우 아예 메일을 못받음

 

 

 

정밀도/재현율 트레이드오프

 

분류의 결정 임계값(Threshold)조정 → 정밀도 또는 재현율 수치 조정

 

pred_proba = lr_clf.predict_proba(X_test)  # 개별 피처별 예측 확률을 반환
pred = lr_clf.predict(X_test)
print('preb_proba()결과 Shaep: {0}'.format(pred_proba.shape))
print('pred_proba array에서 앞 3개만 샘플로 추출 \n:', pred_proba[:3])

# 예측 확률 array와 예측 결과값 array를 concatenate 하여 예측 확률과 결과값을 한눈에 확인
pred_proba_result = np.concatenate([pred_proba, pred.reshape(-1,1)], axis=1)
print('두 개의 class 중에서 더 큰 확률을 클래스 값으로 예측 \n', pred_proba_result[:3])

  • ① Negative를 예측한 값, ② Positive를 예측한 값
  • 첫 번째 칼럼: 클래스 값 0에 대한 예측 확률첫 번째 칼럼 + 두 번째 칼럼 = 1
  • 두 번째 칼럼: 클래서 값 1에 대한 예측 확률
  • 첫 번째 칼럼 + 두 번째 칼럼 = 1
  • predict_proba() 메서드
    : 테스트 피처 데이터 세트 입력 → 테스트 피처 레코드의 개별 클래스 예측 확률 반환
  • predict() 메서드
    : 예측 결과 클래스 값 반환

  • 임계값=50%보다 크면 Positive, 작거나 같으면 Negative로 결정

 

 

1.  Binarizer 클래스 → 결정 임계값을 설정하고 이걸 기준으로 1, 0 결정

from sklearn.preprocessing import Binarizer

X = [[1, -1, -2],
    [2, 0, 0],
    [0, 1.1, 1.2]]

# threshold 기준값보다 같거나 작으면 0을, 크면 1을 반환
binarizer = Binarizer(threshold=1.1)
print(binarizer.fit_transform(X))

  • ndarray의 값이 지정된 threshold(1.1)보다 같거나 작으면 0, 크면 1로 변환

 

 

2. 분류 결정 임계값 0.5 기반에서 Binarizer를 이용하여 예측값 변환

from sklearn.preprocessing import Binarizer

# Binarizer의 threshold 설정값. 분류 결정 임곗값임.
custom_threshold = 0.5

# predict_proba() 반환값의 두 번째 컬럼, 즉 Positive 클래스 컬럼 하나만 추출하여 Binarizer를 적용
pred_proba_1 = pred_proba[:,1].reshape(-1,1)

binarizer = Binarizer(threshold=custom_threshold).fit(pred_proba_1)
custom_predict = binarizer.transform(pred_proba_1)

get_clf_eval(y_test, custom_predict)

⇒ predict()로 계산된 지표값과 같음. 즉, predict()는 predict_proba()에 기반

 

 

 

3. 분류 결정 임계값 0.4 기반에서 Binarizer를 이용하여 예측값 반환

# Binarizer의 threshold 설정값을 0.4로 설정. 즉 분류 결정 임곗값을 0.5에서 0.4로 낮춤

custom_threshold = 0.4
pred_proba_1 = pred_proba[:,1].reshape(-1,1)
binarizer = Binarizer(threshold=custom_threshold).fit(pred_proba_1)
custom_predict = binarizer.transform(pred_proba_1)

get_clf_eval(y_test, custom_predict)

  • 분류 결정 임계값은 Positive 예측값을 결정하는 확률의 기준
  • 임계값보다 크면 Positive, 작거나 같으면 Negative

 

 

4. 임계값을 0.4에서부터 0.6까지 0.05씩 증가(get_eval_by_threshold() 함수 정의)

# 테스트를 수행할 모든 임곗값을 리스트 객체로 저장
thresholds = [0.4, 0.45, 0.50, 0.55, 0.60]

def get_eval_by_threshold(y_test, pred_proba_c1, thresholds):
    # thresholds list 객체 내의 값을 차례로 iteration하면서 Evaluation 수행
    for custom_threshold in thresholds:
        binarizer = Binarizer(threshold=custom_threshold).fit(pred_proba_c1)
        custom_predict = binarizer.transform(pred_proba_c1)
        print('임곗값: ', custom_threshold)
        get_clf_eval(y_test, custom_predict)
        
get_eval_by_threshold(y_test, pred_proba[:,1].reshape(-1,1), thresholds)

 

 

 

정밀도와 재현율의 맹점

 

  • 정밀도가 100%가 되는 방법
    • 확실한 기준이 되는 경우만 Positive로 예측하고 나머지는 모두 Negative로 예측
      ex) 전체 환자 1000명 중 확실한 Positive 징후만 가진 환자는 단 1명이라고 하면 이 한 명만 Positive로 예측하고 나머지는 모두 Negative로 예측
    • 정밀도 = TP/(TP+FP) = 1/(1+0) = 100%
  • 재현율이 100%가 되는 방법
    • 모두 Positive로 예측
      ex) 실제 양성인 사람이 30명 정도라도 전체 환자 1000명을 다 Positive로 예측
    • 재현율 = TP/(TP+FN) = 30/(30+0) = 100%