- 목표: 피마 인디언 당뇨병 데이트 세트를 이용하여 당뇨병 여부를 판단하는 머신러닝 예측모델을 수립하고, 여러 평가 지표를 적용
피마 당뇨병 데이터 세트 구성¶
- Pregnancies : 임신횟수
- Glucose : 포도당 부하 검사 수치
- BloodPressure : 혈압
- SkinThickness : 팔 삼두근 뒤쪽의 피하지방 측정값
- Insulin : 혈청 인슐린
- BMI : 체질량 지수
- DiabetesPedigreeFunction : 당뇨 내력 가중치 값
- Age : 나이
- Outcome : 당뇨여부(0 또는 1)
라이브러리 불러오기¶
In [2]:
import pandas as pd
import numpy as np
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, Binarizer
from sklearn.linear_model import LogisticRegression
import warnings
warnings.filterwarnings('ignore')
데이터 불러오기¶
In [3]:
diabetes_data = pd.read_csv('pima indian/diabetes.csv')
print(diabetes_data['Outcome'].value_counts())
diabetes_data.head(3)
Out[3]:
전체 768개의 데이터 중 Positive 값 (1)이 268개, Negative(0) 값이 500개로 구성되어 있음
In [4]:
# diabetes 데이터 갼략히 보기(feature type 및 Null 값 개수 보기)
diabetes_data.info()
Null 값은 존재하지 않으며 모두 int, float의 숫자형 데이터
--> Null 값과 문자열 처리를 위한 별도의 작업은 필요하지 않음
로지스틱 회귀를 이용한 예측모델 생성¶
In [5]:
# 평가지표 출력하는 함수 설정
def get_clf_eval(y_test, y_pred):
confusion = confusion_matrix(y_test, y_pred)
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
F1 = f1_score(y_test, y_pred)
AUC = roc_auc_score(y_test, y_pred)
print('오차행렬:\n', confusion)
print('\n정확도: {:.4f}'.format(accuracy))
print('정밀도: {:.4f}'.format(precision))
print('재현율: {:.4f}'.format(recall))
print('F1: {:.4f}'.format(F1))
print('AUC: {:.4f}'.format(AUC))
In [6]:
# Precision-Recall Curve Plot 그리기
def precision_recall_curve_plot(y_test, pred_proba):
# threshold ndarray와 이 threshold에 따른 정밀도, 재현율 ndarray 추출
precisions, recalls, thresholds = precision_recall_curve(y_test, pred_proba)
# x축을 threshold, y축을 정밀도, 재현율로 그래프 그리기
plt.figure(figsize=(8, 6))
thresholds_boundary = thresholds.shape[0]
plt.plot(thresholds, precisions[:thresholds_boundary], linestyle='--', label='precision')
plt.plot(thresholds, recalls[:thresholds_boundary], linestyle=':', label='recall')
# threshold의 값 X축의 scale을 0.1 단위로 변경
stard, end = plt.xlim()
plt.xticks(np.round(np.arange(stard, end, 0.1), 2))
plt.xlim()
plt.xlabel('thresholds')
plt.ylabel('precision & recall value')
plt.legend()
plt.grid()
In [7]:
# 피쳐 데이터 세트 X, 레이블 데이터 세트 y 를 추출
X = diabetes_data.iloc[:,:-1]
y = diabetes_data['Outcome']
# 데이터를 훈련과 테스트 데이터 셋으로 분리
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)
get_clf_eval(y_test, pred)
In [8]:
# 임계값별로 정밀도-재현율 출력
pred_proba = lr_clf.predict_proba(X_test)[:, 1]
precision_recall_curve_plot(y_test, pred_proba)
위의 정밀도-재현율 그래프를 보면 임계값을 약 0.42 정도로 맞추면 정밀도와 재현율이 균형을 이룰 것으로 보임
--> 이 때의 정밀도, 재현율은 0.7이 조금 되지 않는 수준으로 그렇게 높은 것은 아님
--> 데이터를 먼저 다시 확인해서 개선할 부분이 있는지 확인
In [9]:
# 데이터의 기초 통계값들
diabetes_data.describe()
Out[9]:
최소 값이 0으로 되어 있는 값들이 많이 존재함
- Glucose(당 수치), BloodPressure(혈압), SkinThickness(피하지방), Insulin(인슐린), BMI(체질량 지수) 같은 값이 실제로 0일 수는 없다고 생각되므로 확인이 필요
In [10]:
feature_list = ['Glucose', 'BloodPressure', 'SkinThickness', 'Insulin', 'BMI']
def hist_plot(df):
for col in feature_list:
df[col].plot(kind='hist', bins=20).set_title('Histogram of '+col)
plt.show()
hist_plot(diabetes_data)
In [11]:
# 위 컬럼들에 대한 0 값의 비율 확인
zero_count = []
zero_percent = []
for col in feature_list:
zero_num = diabetes_data[diabetes_data[col]==0].shape[0]
zero_count.append(zero_num)
zero_percent.append(np.round(zero_num/diabetes_data.shape[0]*100,2))
zero = pd.DataFrame([zero_count, zero_percent], columns=feature_list, index=['count', 'percent']).T
zero
Out[11]:
SkinThickness와 Insulin의 경우 0 값의 비율이 각각 29.56%, 48.70%로 상당히 높음
이들 데이터를 삭제하기에는 너무 많으므로 피처 0 값을 평균 값으로 대체
In [12]:
# 0 값들을 우선 NaN 값으로 대체
diabetes_data[feature_list] = diabetes_data[feature_list].replace(0, np.nan)
# 위 5개 feature 에 대해 0값을 평균 값으로 대체
mean_features = diabetes_data[feature_list].mean()
diabetes_data[feature_list] = diabetes_data[feature_list].replace(np.nan, mean_features)
In [13]:
# 데이터 세트에 대해 피처 스케일링을 적용하여 변환하기(로지스틱 회귀의 경우, 숫자 데이터에 스케일링을 적용하는 것이 일반적으로 성능이 좋음)
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)
get_clf_eval(y_test, pred)
0 값에 대한 처리와 스케일링을 통해 앞선 예측보다는 소폭 개선되었음
In [14]:
# 평가지표를 조사하기 위한 새로운 함수 생성
def get_eval_by_threshold(y_test, pred_proba_c1, thresholds):
#thresholds list 객체 내의 값을 iteration 하면서 평가 수행
for custom_threshold in thresholds:
binarizer = Binarizer(threshold=custom_threshold).fit(pred_proba_c1)
custom_predict = binarizer.transform(pred_proba_c1)
print('\n임계값: ', custom_threshold)
get_clf_eval(y_test, custom_predict)
임계값 변화에 따른 예측 성능 확인
In [15]:
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)
정확도, 정밀도, 재현율, F1, AUC 등의 평가 지표를 보고 적절히 판단하여 임계값을 재 설정하여 예측을 수행할 수 있음
In [16]:
# 임계값을 0.48로 설정하여 예측 수행
binarizer = Binarizer(threshold=0.48)
# 위에서 구한 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)
'Machine Learning > 파이썬 머신러닝 완벽가이드 학습' 카테고리의 다른 글
[Chapter 4. 분류] 앙상블 학습 (0) | 2019.10.14 |
---|---|
[Chapter 4. 분류] Decision Tree Classifier (0) | 2019.10.03 |
[Chapter 3. 평가] 머신러닝 성능 평가에 활용되는 지표들 (0) | 2019.10.02 |
[Chapter 2. 사이킷런을 이용한 머신러닝] 타이타닉 경진대회 실습 (3) | 2019.10.02 |
[Chapter 2. 사이킷런을 이용한 머신러닝] 데이터 전처리 (0) | 2019.10.02 |