캐글에 있는 데이터로 피마 인디언 당뇨병 데이터 세트를 이용해 당뇨병 여부를 판단하는 머신러닝 예측 모델을 수립하고, 평가 지표를 이용해서 성능을 측정하며 튜닝할 것이다. 데이터는 https://www.kaggle.com/uciml/pima-indians-diabetes-database에 들어가서 다운받을 수 있다.
데이터를 다운받은 다음 성능 지표들을 임포트하고, 정규화(StandardScaler) , 로지스틱 회귀 모델을 사용할 수 있는 LogisticRegression 클래스를 임포트한다. (Numpy, Pandas, Matplotlib.pyplot은 지난 포스팅과 동일하므로 생략)
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, precision_score, recall_score, roc_auc_score
from sklearn.metrics import f1_score, confusion_matrix, precision_recall_curve, roc_curve
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
diabetes_data = pd.read_csv('diabetes.csv')
print(diabetes_data['Outcome'].value_counts())
diabetes_data.head(3)
일단 예측해야 하는 레이블 값이 'Outcome' 이라는 칼럼에 들어있다. 이 칼럼을 .value_counts() 메서드를 통해서 각 레이블 값의 분포를 확인한다. 이후에 info()를 통해서 null값이 있는지, 각 칼럼은 어떤 데이터 타입으로 되어있는지 확인한다.
diabetes_data.info()
확인한 결과 전부 숫자형으로 되어있고, null 값은 딱히 없다는 것을 확인할 수 있다.
get_clf_eval이라는 분류기 성능을 측정하는 함수를 정의한다. 이 함수는 정확도, 재현율, 정밀도, f1 Score, ROC_AUC Score을 측정하여 출력하는 함수이다.
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)
print('오차 행렬')
print(confusion)
print('정확도 : {0:.4f}, 정밀도 : {1:.4f}, 재현율 : {2:.4f}, \
F1 : {3:.4f}, AUC : {4:.4f}'.format(accuracy, precision, recall, f1, roc_auc))
#피처 데이터 세트 X, 레이블 데이터 세트 y를 추출.
#맨 끝이 Outcome 칼럼으로 레이블 값임. 칼럼 위치 -1을 이용해 춫 ㄹ
X = diabetes_data.iloc[:, :-1]
y = diabetes_data.iloc[:, -1]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size =0.2, random_state = 156, stratify = y)
#로지스틱 회귀로 학습
lr_clf = LogisticRegression()
lr_clf.fit(X_train, y_train)
pred = lr_clf.predict(X_test)
pred_proba = lr_clf.predict_proba(X_test)
get_clf_eval(y_test, pred, pred_proba)
위의 성능 지표에 대해서 잘 모른다면,
https://dsbook.tistory.com/141?category=761052
(오차행렬, 정확도, 정밀도, 재현율)
https://dsbook.tistory.com/142?category=761052
(F1 Score, ROC&AUC)
여기를 확인하면 된다.
함수를 정의하고 실행한 결과 재현율의 성능이 약 60%로 다른 평가 지표보다 낮다. 이런 경우, 정밀도 재현율 곡선을 보고 임계값을 조절하여 지표를 조절할 수 있다. 그러기 위해서 정밀도 재현율 곡선을 시각화하는 함수(precision_recall_curve_plot)를 정의한다.
def precision_recall_curve_plot(y_test, pred_proba_c1):
precisions, recalls, thresholds = precision_recall_curve(y_test, pred_proba_c1)
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')
start, end = plt.xlim()
plt.xticks(np.round(np.arange(start, end , 0.1), 2))
plt.xlabel('Threshold value'); plt.ylabel('Precision and Recall value')
plt.legend(); plt.grid()
plt.show()
pred_proba_c1 = lr_clf.predict_proba(X_test)[:, 1]
precision_recall_curve_plot(y_test, pred_proba_c1)
함수를 정의한 후 시각화한 결과 임계값이 0.47인 부분에서 정밀도와 재현율이 비슷했다. 하지만, 두 수치 모두 0.7정도이다. 너무 낮은 수치이기 때문에, 이런 경우 데이터 값에 어떤 문제가 있는지 확인해 볼 필요가 있다. 데이터의 수치를 세부적으로 보기 위해 .describe() 메서드를 사용한다. Glucose 피처는 혈중 포도당 수치인데, 최소값(min)이 0으로 되어있는 것을 볼 수 있다. 이것은 말이 안되는 데이터이므로, 이와 비슷한 값들이 더 존재하는지 다른 값들도 확인한다.
diabetes_data.describe()
'BloodPressure', 'SkinThickness', 'Insuline' ,'BMI'가 Glucose 피처처럼 0이 되면 안되는 값들이 0으로 되어있는 것을 확인했다. 그럼 0값들이 각 피처마다 얼마나 있고, 그들의 비율이 얼마나 되는지 확인한다.
#0값을 검사할 피처 명 리스트
zero_features = ['Glucose', 'BloodPressure', 'SkinThickness','Insulin', 'BMI']
#전체 데이터 건수
total_count = diabetes_data['Glucose'].count()
#피처 별로 반복하면서 데이터 값이 0인 데이터 건수를 추출하고, 퍼센트 계산
for feature in zero_features:
zero_count = diabetes_data[diabetes_data[feature] == 0][feature].count()
print('{0} 0 건수는 {1}, 퍼센트는 {2:.2f} %'.format(feature, zero_count, 100*zero_count/total_count))
보면, SkinThickness와 Insuline을 제외한 피처에서 0의 비율은 5%를 넘지 않는 것에 비해, 나머지 두 피처는 29.56%, 48.70%를 차지하고 있다. 데이터가 많은 경우 칼럼을 삭제하는 방법도 있지만, 위의 데이터는 크지 않은 편이기 때문에, 평균값으로 0값들을 대체한다.
#zero_features 리스트 내부에 저장된 개별 피처들에 대해서 0값을 평균 값으로 대체
mean_zero_features = diabetes_data[zero_features].mean()
diabetes_data[zero_features] = diabetes_data[zero_features].replace(0, mean_zero_features)
이렇게 0값들을 대체한 이후 피처 스케일링을 적용하여 변환한다. 로지스틱 회귀의 경우, 피처마다 스케일이 다르면 성능저하의 우려가 있어 피처 스케일링을 해주는 것이 좋다.
X = diabetes_data.iloc[:, :-1]
y = diabetes_data.iloc[:, -1]
#StandardScaler 클래스를 이용해 피처 데이터 세트에 일괄적으로 스케일링 적용
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size = 0.2, random_state = 156, stratify = y)
#로지스틱 회귀로 학습
lr_clf = LogisticRegression()
lr_clf.fit(X_train, y_train)
pred = lr_clf.predict(X_test)
pred_proba = lr_clf.predict_proba(X_test)[: , 1]
get_clf_eval(y_test, pred, pred_proba)
다시 성능 측정을 한 결과 각 지표마다 전부 성능이 올라간 것을 확인할 수 있다.
임계값을 조금씩 옮기면서 각 지표들이 어디에서 가장 높은 수치를 나타내는지 확인하는 함수를 정의하고 실행한다. 임계값은 0.3부터 0.5까지 비슷한 간격으로 8개로 정했다.
from sklearn.preprocessing import Binarizer
def get_eval_by_threshold(y_test, pred_proba_c1, thresholds):
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)
thresholds = [0.3,0.33,0.36,0.39,0.42,0.45,0.48,0.50]
pred_proba = lr_clf.predict_proba(X_test)
get_eval_by_threshold(y_test, pred_proba[:, 1].reshape(-1, 1), thresholds)
가장 고르게 높은 수치들을 나타낸 임계값은 0.48인 것을 확인할 수 있다.
이제 임계값을 0.48로 해서 성능측정을 해야하는데, 사이킷런의 .predict() 메서드는 사용자가 임의로 임계값을 지정할수 없도록 만들어 놓았다. 따라서 Binarizer 클래스를 통해서 임계값을 0.48로 지정해주고, 로지스틱 회귀 모델로 학습하여 확률을 나타낸 pred_proba의, 데이터를 이용해서 분류를 수행한다. 지표들을 확인해보면, 정밀도는 조금 떨어졌지만, 그 이상으로 재현율이 올라간 것을 확인할 수 있다.
#임곗값을 0.48로 설정한 Binarizer 생성
binarizer = Binarizer(threshold = 0.48)
#위에서 구한 lr_clf의 predict_proba() 예측 확률 array에서 1에 해당하는 칼럼 값을 Binarizer 반환
pred_th_048 = binarizer.fit_transform(pred_proba[:, 1].reshape(-1, 1))
get_clf_eval(y_test, pred_th_048, pred_proba[:, 1])
'기계학습 > Kaggle' 카테고리의 다른 글
Kaggle (UCI) : Human Activity(파이썬 머신러닝 완벽 가이드) - 데이터 전처리, 결정트리 (2) | 2020.09.06 |
---|---|
Kaggle : Titanic [파이썬 머신러닝 완벽 가이드] - 2 (0) | 2020.08.27 |
Kaggle : Titanic [ 파이썬 머신러닝 완벽 가이드 ] - 1 (0) | 2020.08.26 |
최근댓글