안 쓰던 블로그

[평가] kaggle - Pima 인디언 당뇨병 예측 본문

머신러닝/머신러닝

[평가] kaggle - Pima 인디언 당뇨병 예측

proqk 2021. 1. 25. 16:25
반응형

평가

1. 정확도(Accuracy)

2. 오차행렬(Confusion Matrix)

3. 정밀도(Precision)

4. 재현율(Recall)

5. F1 스코어: 정밀도와 재현율가 얼마나 균형 잡혀 있는가?

6. ROC AUC: 이진분류에서 많이 활용하는 성적 지표

 

1~4: foxtrotin.tistory.com/403

5~6: foxtrotin.tistory.com/440

실습: 현재글


www.kaggle.com/uciml/pima-indians-diabetes-database

 

Pima Indians Diabetes Database

Predict the onset of diabetes based on diagnostic measures

www.kaggle.com

캐글의 피마 인디언 당뇨병을 예측하는 평가 실습을 해 본다

 

피마 당뇨병 데이터 세트 구성

  • Pregnancies: 임신 횟수
  • Glucose: 포도당 부하 검사 수치
  • BloodPressure: 혈압(mm Hg)
  • SkinThickness: 팔 삼두근 뒤쪽의 피하지방 측정값(mm)
  • Insulin: 혈청 인슐린(mu U/ml)
  • BMI: 체질량지수(체중(kg)/(키(m))^2)
  • DiabetesPedigreeFunction: 당뇨 내력 가중치 값
  • Age: 나이
  • Outcome: 클래스 결정 값(0또는 1)

라이브러리 및 데이터 불러오기

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)

csv파일을 데이터 프레임으로 로딩한다

전체 768데이터 중 Outcome값은 0이 500개 1이 268이다. 당뇨병 양성(1)=Positive값이 268이다

 

diabetes_data.info()

NULL값은 존재하지 않으며 모두 int, float타입의 데이터이다.

NULL값과 문자열 처리를 위한 별도의 작업이 필요하지 않다

 

커스텀 함수 로딩

# 수정된 get_clf_eval() 함수 
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 = roc_auc_score(y_test, pred_proba)
    print('오차 행렬')
    print(confusion)
    # ROC-AUC print 추가
    print('정확도: {0:.4f}, 정밀도: {1:.4f}, 재현율: {2:.4f},\
    F1: {3:.4f}, AUC:{4:.4f}'.format(accuracy, precision, recall, f1, roc_auc))
def precision_recall_curve_plot(y_test=None, pred_proba_c1=None):
    # threshold ndarray와 이 threshold에 따른 정밀도, 재현율 ndarray 추출. 
    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], 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()

이전 글에서 커스텀했던 두 함수를 로딩한다

 

Logistic Regression(로지스틱 회귀)으로 학습 및 예측 수행

# 피처 데이터 세트 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) #예측
# roc_auc_score 수정에 따른 추가
pred_proba = lr_clf.predict_proba(X_test)[:, 1] #예측 확률 array

get_clf_eval(y_test , pred, pred_proba)

정밀도에 비해 재현율이 낮으므로 재현율을 높히는 쪽으로 방향을 잡으면 되겠다

 

precision recall 곡선 그림

pred_proba_c1 = lr_clf.predict_proba(X_test)[:, 1]
precision_recall_curve_plot(y_test, pred_proba_c1)

정밀도가 급격하게 떨어지는 부분이 있다

 

각 피처들의 값 4분위 분포 확인

diabetes_data.describe()

최소 값이 0이 나오면 안 되는데 0으로 되어 있는 값들이 많이 존재한다

Glucose(포도당 수치), BloodPressure(혈압), SkinThickness(피하지방), Insulin(인슐린), BMI(체질량 지수) 같은 값이 실제로 0일 수는 없다고 생각되므로 확인해 본다

 

'Glucose' 피처의 분포도

plt.hist(diabetes_data['Glucose'], bins=10)

포도당 수치를 대표로 hist로 분포도를 그려보면 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))

전체 데이터 건수는 아무 피처나 전체 개수를 세 주면 된다

피처들을 반복하면서 0값이 들어간 피처들만 카운트를 하고 출력한다

출력을 해 보면 SkinThickness와 Insulin쪽의 퍼센트가 꽤 높다

 

0값을 평균값으로 대체

# zero_features 리스트 내부에 저장된 개별 피처들에 대해서 0값을 평균 값으로 대체
diabetes_data[zero_features]=diabetes_data[zero_features].replace(0, diabetes_data[zero_features].mean())

 

StandardScaler 클래스를 이용해 피처 데이터 세트에 일괄적으로 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)
# roc_auc_score 수정에 따른 추가
pred_proba = lr_clf.predict_proba(X_test)[:, 1]
get_clf_eval(y_test , pred, pred_proba)

다시 확인을 해 보면, 재현율이 올라가서 앞선 예측보다 약간 개선이 되었다

 

분류결정 임곗값을 변경하면서 성능 측정

from sklearn.preprocessing import Binarizer

def get_eval_by_threshold(y_test , pred_proba_c1, thresholds):
    # thresholds 리스트 객체내의 값을 차례로 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)
        # roc_auc_score 관련 수정
        get_clf_eval(y_test , custom_predict, pred_proba_c1)

이 함수를 그대로 사용한다

 

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.3부터 0.5까지 임계값을 변경하면서 성능을 측정했다

정확도, 정밀도, 재현율, F1, AUC 등의 평가 지표를 보고 적절히 판단하여 임계값을 재설정하여 예측을 수행해야 한다

 

F1스코어가 가장 높은 0.6931의 경우 정밀도와 재현율이 너무 떨어지기 때문에, 적당히 타협을 본 0.6857 스코어의 임계값이 0.48을 선택해 본다

 

# 임곗값를 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)) 

# roc_auc_score 관련 수정
get_clf_eval(y_test , pred_th_048, pred_proba[:, 1])

최종 예측 결과는 다음과 같다

 

 

 

반응형

'머신러닝 > 머신러닝' 카테고리의 다른 글

결정 트리 과적합, 결정 트리 실습  (0) 2021.01.31
분류와 결정트리, 결정트리 시각화  (0) 2021.01.31
평가(Evaluation)_2  (0) 2021.01.24
평가(Evaluation)_1  (0) 2021.01.24
Linear Regression 선형 회귀  (0) 2021.01.21
Comments