# 22.01.12 머신러닝 스터디 3장. 평가
< 파이썬 머신러닝 완벽가이드 >¶
03. 평가 ( p147 ~ p182 )¶
1. 정확도¶
정확도 (Accuracy) = 예측 결과가 동일한 데이터 건수 / 전체 예측 데이터 건수
# BaseEstimator => 단순 Classifier
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
2장에서 사용한 코드
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
# 앞에서 설정한 전처리 함수 호출
def transform_features(df):
df = fillna(df)
df = drop_features(df)
df = format_features(df)
return df
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
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)
# 위에서 생성한 Dummy Classifier를 이용해 학습 / 예측 / 평가 수행
myclf = MyDummyClassifier()
myclf.fit(X_train, y_train)
mypredictions = myclf.predict(X_test)
print('Dummy Classifier의 정확도는 : ', accuracy_score(y_test, mypredictions))
Dummy Classifier의 정확도는 : 0.7877094972067039
- 타이타닉 데이터에서 Sex 피처가 남성인 사람은 모두 0으로 예측, 여성인 사람은 모두 1로 예측하는 위의 단순한 코드로도 높은 정확도가 나온다.
- MNIST데이터 셋에서 모두 0으로 예측하다라도 높은 정확도가 나온다.
-> 정확도 수치만 맹신하기에는 오류가 존재한다! ( MNIST 예시는 생략!! )
2. 오차 행렬¶
TN / FN / FP / TP
from sklearn.metrics import confusion_matrix
#confiusion_matrix(y 테스트값, 예측 y값)
# array형태로 출력된다
3. 정밀도, 재현율¶
정밀도 = TP / (FP + TP)
Positive로 예측한 대상 中 실제값이 Positive와 일치한 비율
정밀도가 중요할 경우 : 실제 Negative를 Positive로 잘못 판단하게 될 경우 업무상 큰 영향이 발생할 때
ex) 스팸메일 여부 판단
재현율 = TP / (FN + TP)
실제 Positive값 中 예측이 일치한 비율 ( = 민감도 )
재현율이 중요할 경우 : 실제 Positive를 Negative로 잘못 판단하게 될 경우 업무상 큰 영향이 발생할 때
ex) 암 판단 여부, 금융사기 적발
from sklearn.metrics import accuracy_score, precision_score, recall_score, roc_auc_score, confusion_matrix
def get_clf_eval(y_test, pred=None, pred_proba=None):
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))
from sklearn.linear_model import LogisticRegression
# 앞에서 사용한 titanic 데이터 가져와서 사용
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)
lr_clf = LogisticRegression(max_iter=200)
lr_clf.fit(X_train, y_train)
pred = lr_clf.predict(X_test)
get_clf_eval(y_test, pred)
오차행렬 [[92 18] [16 53]] 정확도: 0.8101, 정밀도: 0.7465, 재현율: 0.7681
python 오류 해결 : ConvergenceWarning: lbfgs failed to converge (status=1)
반복 횟수를 증가 or 데이터를 조정
LogisticRegression() -> LogisticRegression(max_iter=200)
정밀도와 재현율은 Trade-Off관계
predict_proba , predict
# Binarizer
from sklearn.preprocessing import Binarizer
X = [[ 1, -1, 2],
[ 2, 0, 0],
[ 0, 1.1, 1.2]]
# X의 개별 원소들이 threshold 값보다 같거나 작으면 0, 크면 1을 반환
binarizer = Binarizer(threshold = 1.1)
print(binarizer.fit_transform(X))
# 이후에 임계값을 변경해가며 확인 가능 - predict_proba의 Positive클래스를 추출해서 판단할거임!!
[[0. 0. 1.] [1. 0. 0.] [0. 0. 1.]]
만든 모델로 테스트를 predict_proba한 결과
pred_proba = 모델.predict_proba(X_test)
pred_proba[:, 1].reshape(-1,1) -> ( Positive 결과 ) 이 결과를 이후 Binarizer에 넣고 여러 임계값을 평가할 수 있다
# 오차행렬, 정밀도, 재현율을 구하는 함수 생성
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))
# 임계값을 움직여가며 평가를 해봅시다
# 테스트를 수행할 모든 임곗값을 리스트 객체로 저장
threshold = [0.4, 0.45, 0.5, 0.55, 0.6]
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)
# 임곗값별 정밀도와 재현율을 배열로 반환해주는 precision_recall_curve
from sklearn.metrics import precision_recall_curve
# 레이블 값이 1일 때의 예측 확률을 추측
pred_proba_class1 = lr_clf.predict_proba(X_test)[:,1]
# pred_proba_c1는 레이블 값이 1일 때의 예측확률을 의미한다
precisions, recalls, thresholds = precision_recall_curve(y_test, pred_proba_class1)
print('반환된 분류 결정 임곗값 배열의 Shape : ', thresholds.shape)
# 반환된 임계값 배열 로우의 개수를 확인하고 원하는 숫자와 step으로 임곗값을 추출
thr_index = np.arange(0, thresholds.shape[0], 15) # 15step
# 15 step으로 추출한 임계값에 따른 정밀도와 재현율 값
print('샘플 임계값별 정밀도 : ', np.round(precisions[thr_index], 3))
print('샘플 임계값별 재현율 : ', np.round(recalls[thr_index], 3))
반환된 분류 결정 임곗값 배열의 Shape : (166,) 샘플 임계값별 정밀도 : [0.399 0.418 0.464 0.52 0.594 0.659 0.724 0.803 0.826 1. 1. 1. ] 샘플 임계값별 재현율 : [1. 0.957 0.928 0.928 0.913 0.87 0.797 0.71 0.551 0.449 0.232 0.014]
# 이를 받았으면 시각화 해줄 수도 있다.
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
%matplotlib inline
from sklearn.metrics import precision_recall_curve
def precision_recall_curve_plot(y_test, pred_proba_c1):
precisions, recalls, thresholds = precision_recall_curve(y_test, pred_proba_c1)
# X축을 threshold값으로, Y축을 정밀도, 재현율 값으로 각각의 Plot, 정밀도는 점선으로!
plt.figure(figsize = (8,6))
threshold_boundary = thresholds.shape[0]
plt.plot(thresholds, precisions[0:threshold_boundary], ls = '--', label = 'precision')
plt.plot(thresholds, recalls[0:threshold_boundary], label = 'recall')
# threshold 값 X축의 Scale을 0.1 단위로 변경
start, end = plt.xlim() # 좋은 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값의 변화를 파악!!!
precision_recall_curve_plot( y_test, lr_clf.predict_proba(X_test)[:, 1])
4. f1 score¶
2 (precision recall) / (precision + recall)
from sklearn.metrics import f1_score
f1 = f1_score(y_test, pred)
print('F1스코어 : ', f1)
F1스코어 : 0.7571428571428571
# f1 스코어 추가
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)
f1 = f1_score(y_test, pred)
print('오차 행렬')
print(confusion)
print('정확도: {0:.4f}, 정밀도: {1:.4f}, 재현율: {2:.4f},\
f1_score: {3:.4f}'.format(accuracy, precision, recall, f1))
thresholds = [0.4, 0.45, 0.5, 0.55, 0.6]
pred_proba = lr_clf.predict_proba(X_test)
get_eval_by_threshold(y_test, pred_proba[:, 1].reshape(-1,1), thresholds)
임곗값: 0.4 오차 행렬 [[86 24] [13 56]] 정확도: 0.7933, 정밀도: 0.7000, 재현율: 0.8116, f1_score: 0.7517 임곗값: 0.45 오차 행렬 [[91 19] [14 55]] 정확도: 0.8156, 정밀도: 0.7432, 재현율: 0.7971, f1_score: 0.7692 임곗값: 0.5 오차 행렬 [[92 18] [16 53]] 정확도: 0.8101, 정밀도: 0.7465, 재현율: 0.7681, f1_score: 0.7571 임곗값: 0.55 오차 행렬 [[98 12] [18 51]] 정확도: 0.8324, 정밀도: 0.8095, 재현율: 0.7391, f1_score: 0.7727 임곗값: 0.6 오차 행렬 [[99 11] [25 44]] 정확도: 0.7989, 정밀도: 0.8000, 재현율: 0.6377, f1_score: 0.7097
5. ROC-AUC¶
이진분류의 예측 성능 측정에서 중요하게 사용되는 지표 ( Receiver Operation Characteristic Curve )
일반적으로 의학 분야에서 많이 사용하고 FPR이 변할 때 TPR이 어떻게 변하는지를 나타내는 곡선이다.
TPR = TP / (FN + TP) ( 재현율, y축 )
TNR = TN / (FP + TN)
FPR = FP / (FP + TB) ( 1 - TNR , x축 )
FPR을 0 -> 1로 변경하며 TPR의 변화값을 구한다. 임계값이 0일때는 모든 값을 Positive로 예측하기 때문에 TN이 0이 되고 따라서 FPR은 1이 된다. 임계값이 1일 때는 모든 값을 Negative로 예측하기 때문에 FP값이 0이 되어 FPR이 0이 된다. ( 임계값 초과여야 해당 값을 Positive로 예측하는듯 !! )
from sklearn.metrics import roc_curve
# 레이블 값이 1일때의 예측 확률을 추출
fprs, tprs, thresholds = roc_curve(y_test, pred_proba_class1)
# 반환된 임곗값 배열에서 샘플로 데이터를 추출하되, 임곗값을 5 Step으로 추출
# thresholds[0]은 max(예측확률)+1로 임의 설정 된다. 이를 제외하기 위해 arange를 1부터 시작
thr_index = np.arange(1, thresholds.shape[0], 5)
print('샘플 추출을 위한 임곗값 배열의 index : ', thr_index)
print('샘풀 index로 추출한 임곗값 : ', np.round(thresholds[thr_index], 2))
# 5 step 단위로 추출된 임곗값에 따른 FPR, TPR 값
print('샘플 임곗값별 FPR : ',np.round(fprs[thr_index], 3))
print('샘플 임곗값별 TPR : ',np.round(tprs[thr_index], 3))
샘플 추출을 위한 임곗값 배열의 index : [ 1 6 11 16 21 26 31 36 41 46 51] 샘풀 index로 추출한 임곗값 : [0.97 0.69 0.63 0.58 0.42 0.32 0.23 0.16 0.12 0.11 0.07] 샘플 임곗값별 FPR : [0. 0.045 0.082 0.1 0.209 0.245 0.355 0.464 0.682 0.782 0.927] 샘플 임곗값별 TPR : [0.014 0.536 0.565 0.71 0.797 0.855 0.884 0.928 0.928 0.942 0.971]
# 이를 시각화 해보자!
def roc_curve_plot(y_test, pred_proba_c1):
# 임곗값에 따른 FPR, TPR값을 반환받음
fprs, tprs, thresholds = roc_curve(y_test, pred_proba_c1)
# ROC 곡선을 그래프 곡선으로 그림
plt.plot(fprs, tprs, label='ROC')
# 대각선 그림
plt.plot([0,1],[0,1],'k--', label='Random')
# FPR X 축의 Scale을 0.1 단위로 변경, X, Y축 명 설정 등
start, end = plt.xlim()
plt.xticks(np.round(np.arange(start, end, 0.1),2))
plt.xlim(0,1)
plt.ylim(0,1)
plt.xlabel('FPR')
plt.ylabel('TPR')
plt.legend()
# roc_auc 수치를 얻는 법
from sklearn.metrics import roc_auc_score
pred_proba = lr_clf.predict_proba(X_test)[:,-1]
roc_score = roc_auc_score(y_test, pred_proba)
print('ROC-AUC 값 : {0:.4f}'.format(roc_score))
ROC-AUC 값 : 0.8704
# roc-auc값 추가
# 평가지표 출력하는 함수
def get_clf_eval(y_test, pred = None, pred_proba = None):
confusion = confusion_matrix(y_test, pred)
accuracy = accuracy_score(y_test, pred)
precision = precision_score(y_test, pred)
recall = recall_score(y_test, pred)
f1 = f1_score(y_test, pred)
roc_auc = roc_auc_score(y_test, pred_proba)
print('오차 행렬')
print(confusion)
print('정확도: {0:.4f}, 정밀도: {1:.4f}, 재현율: {2:.4f},f1_score: {3:.4f}, ROC-AUC : {4:.4f}'
.format(accuracy, precision, recall, f1, roc_auc))
피마 인디언 당뇨병 예측 예시는 생략!!
# 업로드용 창 맞추기
from IPython.core.display import display, HTML
display(HTML("<style>.container { width:90% !important; }</style>"))
'머신러닝(Machine Learning)' 카테고리의 다른 글
[파이썬 머신러닝 완벽 가이드] 5장. 회귀 - 1 (0) | 2022.01.23 |
---|---|
[파이썬 머신러닝 완벽 가이드] 4장. 분류 - 2 (0) | 2022.01.22 |
[파이썬 머신러닝 완벽 가이드] 4장. 분류 - 1 (0) | 2022.01.22 |
댓글