정확도
클래스의 값을 평가하는 여러가지 유형 중 대표적인 방식에는 정확도가 있다.
정확도 = 예측 결과가 동일한 데이터 건수/ 전체 예측 데이터 건수
이를 바탕으로 타이타닉 생존자 데이터에 남자면 사망, 여자면 생존하는 분류 모델을 작성해보고 이 결과를 정확도를 측정해보자
import sklearn
from sklearn.base import BaseEstimator
import numpy as np
분류 모델 작성
#성별이 남자(1) 이면 사망, 여자(0) 이면 생존하는 분류 모델
class MyDummyClassifier(BaseEstimator):
def fit(self, X, y=None):
pass
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
필요한 함수들
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
정확도 측정
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
titanic_df = pd.read_csv('./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
정확도 평가 방법의 단점
정확도가 불균형한 데이터 분포에서는 적합한 평가 지표가 될 수 없다!!
예를 들어 0,1...,9 중에서 7을 가려내 정확도를 평가한다고 가정하자
이때 데이터셋들은 0~9까지의 값들이 대체로 동일하게 배정하게 되면 데이터셋을 7/ not 7로 구분할 때 not 7 이 앞도적으로 많게 된다.
이때 모든 값들을 0으로 설정하는 모델을 작성한다고 할 때 결과값에 대한 정확도를 평가하면 'not 7'의 비율이 앞도적으로 높기 때문에 정확도가 상당히 높게 나온다.
" 즉, 아무런 분류도 진행하지 않았음에도 데이터셋이 불균형하게 분포되어있기 때문에 정확도가 높게 나올 수 있는 것이다. "
다음은 이와 관련된 소스코드이다.
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
def predict(self, X):
return np.zeros((len(X), 1), dtype=bool)
digits = load_digits()
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())
fakeclf = MyFakeClassifier()
fakeclf.fit(X_train, y_train)
fakepred = fakeclf.predict(X_test)
print('모든 예측을 0으로 하여도 정확도는:{:.3f}'.format(accuracy_score(y_test, fakepred)))
오차 행렬
오차 행렬은 이진 분류를 사용하는 데이터셋을 평가하는 방식이다.
예측값과 실제 결과값에 따라서 분류가 달라진다.
예측 클래스 | |||
실제 클래스 | negative(0) | positive(0) | |
negative(0) | TN | FP | |
positive(1) | FN | TP |
정밀도와 재현율
정밀도 = TP / (FP + TP)
재현율 = TP / (FN + TP)
앞서 작성했던 7을 찾아내는 모델 같은 경우에 다음과 같은 평가 방식을 사용하면 TP가 0이기 때문에 정밀도, 재현율은 0이 된다.
1. 정밀도
nagetive 데이터를 positive로 잘못 판단하면 문제가 생기는 경우에 중요시된다.
ex) 스팸메일 여부를 판단할 때 일반 메일을 스팸 메일로 판단해버리면 문제가 생기기 때문에 정밀도가 중요시된다.
2. 재현율
positive 데이터를 negative로 잘못 판단하면 문제가 생기는 경우에 중요시된다.
ex) 암 발병을 판단하는 경우에 암 환자를 음성으로 잘못 판단하는 경우 대가가 크기 때문에 재현율이 매우 중요하다.
따라서 정밀도는 FP를 낮추는 데에, 재현율은 FN를 낮추는 데에 초점을 둔다.
앞서 사용했던 타이타닉 생존자 예측 모델을 이용해 정확도, 정밀도, 재현율을 평가해보자
1) sklearn.metrics를 이용하여 오차행렬, 정확도, 정밀도, 재현율을 계산하는 함수를 생성한다.
from sklearn.metrics import accuracy_score, precision_score, recall_score, confusion_matrix
def get_clf_eval(y_test, pred):
confusion = confusion_matrix(y_test, pred)
accuracy = accuracy_score(y_test, pred)
precision = precision_score(y_test, pred)
recall = recall_score(y_test, pred)
print('오차 행렬')
print(confusion)
print('정확도: {0:.4f}, 정밀도: {1:.4f}, 재현율: {2:.4f}'.format(accuracy, precision, recall))
2) 데이터셋을 불러와서 logisticregression 모델을 이용해 생존자를 예측하고 평가라는 모델을 생성한다.
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
titanic_df = pd.read_csv('./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.20, random_state=11)
lr_clf = LogisticRegression()
lr_clf.fit(X_train, y_train)
pred = lr_clf.predict(X_test)
get_clf_eval(y_test, pred)
3) 다음과 같은 결과가 나온다.
오차 행렬
[[104 14]
[ 13 48]]
정확도: 0.8492, 정밀도: 0.7742, 재현율: 0.7869
정밀도와 재현율의 관계
정밀도와 재현율의 지표는 상호보완적인 관계이기 때문에 TP의 값을 늘리는 게 아니라 다른 F 지표를 강제로 낮추려고 할 경우 다른 지표의 수치가 감소하게 된다.
이때 분류 결정 임곗값을 조절해 정밀도와 재현율의 수치를 조정할 수 있다.
분류 결정 임곗값에 대해 알아보기 위해 predict_proba() 라는 함수에 대해 알아보도록 하자.
소스코드는 다음과 같이 작성할 수 있다.
pred_proba = lr_clf.predict_proba(X_test)
pred = lr_clf.predict(X_test)
print('pred_proba()결과 Shape : {0}'.format(pred_proba.shape))
print('pred_proba array 에서 앞 3개만 샘플로 추출 \n:', pred_proba[:3])
pred_proba_result = np.concatenate([pred_proba, pred.reshape(-1, 1)], axis=1)
print('두 개의 클래스 중에서 더 큰 확률을 클래스 값으로 예측 \n', pred_proba_result[:3])
이때 원래 사용했던 predict() 함수 같은 경우에는 예측을 해서 0,1 의 값으로 반환을 했지만 pred_proba_result() 는 다음과 같이 각 숫자에 대한 확률을 반환한다.
pred_proba()결과 Shape : (179, 2)
pred_proba array 에서 앞 3개만 샘플로 추출
: [[0.46196457 0.53803543]
[0.87861802 0.12138198]
[0.8771453 0.1228547 ]]
두 개의 클래스 중에서 더 큰 확률을 클래스 값으로 예측
[[0.46196457 0.53803543 1. ]
[0.87861802 0.12138198 0. ]
[0.8771453 0.1228547 0. ]]
따라서 현재는 0,1 에 대한 구분이 0.5로 설정되어있지만 분류 결정 임곗값을 높이거나 낮춰서 정밀도와 재현율의 값을 조정할 수 있다.
다음과 같이 분류 결정 임곗값을 원하는대로 설정해줄 수 있다.
from sklearn.preprocessing import Binarizer
#Binarizer의 threshold 설정값, 분류 결정 임곗값 0.5로 설정
custom_threshold = 0.5
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)
임곗값을 0.5로 설정하게 되면 다음과 같이 앞서 봤던 결과를 얻을 수 있다.
오차 행렬
[[104 14]
[ 13 48]]
정확도: 0.8492, 정밀도: 0.7742, 재현율: 0.7869
임곗값을 0.4로 설정해보면 다음과 같은 결과를 얻을 수 있다.
from sklearn.preprocessing import Binarizer
#Binarizer의 threshold 설정값, 분류 결정 임곗값임
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)
다음과 같이 재현율을 높아지고 정밀도가 낮아진 결과를 얻을 수 있다.
오차 행렬
[[98 20]
[10 51]]
정확도: 0.8324, 정밀도: 0.7183, 재현율: 0.8361
이렇게 분류 결정 임곗값을 높이면 0을 판단하는 양이 많아지고 이에 따라 FN 값이 증가하게 되어 정밀도 지표의 수치가 상승한다.
다음으로 임곗값을 0.4부터 0.6까지 다양하게 지정해놓고 증가함에 따라 어떤식으로 변하게 되는지 확인해보자.
threshold = [0.4, 0.45, 0.50, 0.55, 0.60]
def get_eval_by_threshold(y_test, pred_proba_c1, thresholds):
for custom_threshold in threshold:
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), threshold)
임곗값: 0.4
오차 행렬
[[98 20]
[10 51]]
정확도: 0.8324, 정밀도: 0.7183, 재현율: 0.8361
임곗값: 0.45
오차 행렬
[[103 15]
[ 12 49]]
정확도: 0.8492, 정밀도: 0.7656, 재현율: 0.8033
임곗값: 0.5
오차 행렬
[[104 14]
[ 13 48]]
정확도: 0.8492, 정밀도: 0.7742, 재현율: 0.7869
임곗값: 0.55
오차 행렬
[[109 9]
[ 15 46]]
정확도: 0.8659, 정밀도: 0.8364, 재현율: 0.7541
임곗값: 0.6
오차 행렬
[[112 6]
[ 16 45]]
정확도: 0.8771, 정밀도: 0.8824, 재현율: 0.7377
from sklearn.metrics import precision_recall_curve
#레비릉 값이 1일 때의 예측 확률을 추출
pred_proba_class1 = lr_clf.predict_proba(X_test)[:,1]
#실제값 데이터 세트와 레이블 값이 1일 때의 예측 확률을 precision_recall_curve 인자로 입력
precisions, recalls, thresholds = precision_recall_curve(y_test, pred_proba_class1)
print('반환된 분류 결정 임곗값 배열의 Shape:', thresholds.shape)
#반환된 임계값 배열 로우가 147건이므로 샘플로 10건만 추출하되, 임곗값을 15step으로 추출
thr_index = np.arange(0, thresholds.shape[0], 15)
print('샘플 추출을 위한 임계값 배열의 index 10개:', thr_index)
print('샘플용 10개의 임곗값: ', np.round(thresholds[thr_index],2))
#15 step 단위로 추출된 임계값에 따른 정밀도와 재현율 값
print('샘플 임계값별 정밀도: ', np.round(precisions[thr_index], 3))
print('샘플 임계값별 재현율: ', np.round(recalls[thr_index], 3))
정밀도와 재현율의 시각화
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
%matplotlib inline
def precision_recall_curve_plot(y_test, pred_proba_c1):
#threshold ndarray와 이 threshold에 따른 정밀도, 재현율 ndarray 추출
precisions, recalls, thresholds = precision_recall_curve(y_test, pred_proba_c1)
#X축을 thresholds, y축 정밀도, 재현율 값으로 각각 plot 수행, 정밀도는 점선으로 표시
plt.figure(figsize=(8,6))
threshold_boundary = thresholds.shape[0]
plt.plot(thresholds, precisions[0:threshold_boundary], linestyle='-', label='precision')
plt.plot(thresholds, recalls[0:threshold_boundary], label='recall')
#threshold 값 X축의 Scale을 0.1 단위로 변경
start, end = plt.xlim()
plt.xticks(np.round(np.arange(start, end, 0.1), 2))
#x축, y축 label과 legend, 그리고 grid 설정
plt.xlabel('Threshold value');plt.ylabel('Precision and Recall value')
plt.legend();plt.grid()
plt.show()
precision_recall_curve_plot(y_test, lr_clf.predict_proba(X_test)[:,1])
대표사진 삭제
사진 설명을 입력하세요.
'인공지능 > 데이터 분석' 카테고리의 다른 글
[사이킷런] 데이터 전처리 2. 피처스케일링 (0) | 2021.08.16 |
---|---|
[사이킷런] 데이터 전처리 1. 데이터 인코딩 (0) | 2021.08.16 |
[사이킷런] GridSearchCV : 교차 검증 + 하이퍼 파라미터 튜닝 (0) | 2021.08.16 |
[사이킷런] 과적합의 문제와 교차 검증 모델(KFold, StratifiedKFold, cross_val_score) (0) | 2021.08.16 |
[사이킷런] train_test_split, DecisionTreeClassifier 체험 (0) | 2021.08.16 |