4. F1 스코어
F1 스코어
- 정밀도와 재현율을 결합한 지표
- 정밀도와 재현율이 어느 한 쪽으로 치우치지 않을때 상대적으로 높은 값을 가짐
- f1_score() 사용

5. ROC 곡선과 AUC
ROC 곡선(Receiver Operation Characteristic Curve)
- 이진 분류의 예측 성능 측정에서 중요하게 사용되는 지표
- FPR(False Positive Rate)이 변할 때 TPR(재현율 또는 민감도, True Positive Rate)이 어떻게 변화하는지 나타내는 곡선
- FPR : X축, TPR : Y축 -> FPR에 따른 TPR의 변화가 곡선 형태로 나타남
- 민감도 : 실제값 Positive가 정확히 예측되야 하는 수준
- 특이성(Specificity, 민감도에 대응하는 지표) : 실제값 Negative가 정확히 예측돼야 하는 수준
- FPR을 0부터 1까지 변경하면서 TPR의 변화값을 구함
* FPR = FP / (FP+TN) = 1 - TNR = 1 - 특이성
- roc_curve() 사용

from sklearn.metrics import roc_curve
# 레이블 값이 1일때의 예측 확률을 추출
pred_proba_class1 = lr_clf.predict_proba(X_test)[:, 1]
fprs , tprs , thresholds = roc_curve(y_test, pred_proba_class1)
# 반환된 임곗값 배열에서 샘플로 데이터를 추출하되, 임곗값을 5 Step으로 추출.
# thresholds[0]은 max(예측확률)+1로 임의 설정됨. 이를 제외하기 위해 np.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]
샘플 index로 추출한 임곗값: [0.94 0.73 0.62 0.52 0.44 0.28 0.15 0.14 0.13 0.12]
샘플 임곗값별 FPR: [0. 0.008 0.025 0.076 0.127 0.254 0.576 0.61 0.746 0.847]
샘플 임곗값별 TPR: [0.016 0.492 0.705 0.738 0.803 0.885 0.902 0.951 0.967 1. ]
# 시각화
def roc_curve_plot(y_test , pred_proba_c1):
# 임곗값에 따른 FPR, TPR 값을 반환 받음.
fprs , tprs , thresholds = roc_curve(y_test ,pred_proba_c1)
# ROC Curve를 plot 곡선으로 그림.
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( 1 - Specificity )'); plt.ylabel('TPR( Recall )')
plt.legend()
plt.show()
roc_curve_plot(y_test, lr_clf.predict_proba(X_test)[:, 1] )

- AUC (Area Under Curve) 값은 ROC 곡선 밑의 면적으로, 1에 가까울 수록 좋다
(= 면적이 클수록 좋다, FPR이 작은 상태에서 얼마나 큰 TPR을 얻을 수 있는지)
- 가운데 대각선 직선은 동전 던지기 수준의 이진 분류 AUC 값으로 0.5이며 보통의 분류는 0.5 이상의 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.8987
앞서 만든 get_clf_eval 함수에 ROC AUC 값을 추정하는 로직 추가
#ROC AUC는 예측확률값을 기반으로 계산되므로 이를 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))
6. 피마 인디언 당뇨병 예측
# Outcome 클래스 결정값의 분포와 데이터 확인
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)
# feature 타입과 Null 개수 확인
diabetes_data.info( )
# Null 값은 없고 피처 타입은 모두 숫자

# 로지스틱 회귀를 이용하여 예측 모델 생성
# 데이터셋을 피처 데이터셋과 클래스 데이터셋으로 나누고 학습 데이터셋과 테스트 데이터셋으로 분리
# 앞서 사용했던 함수를 사용하여 성능 평가 지표 출력
# 피처 데이터 세트 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(solver='liblinear')
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)
오차 행렬
[[87 13]
[22 32]]
정확도: 0.7727, 정밀도: 0.7111, 재현율: 0.5926, F1: 0.6465, AUC:0.8083
# 재현율 곡선 시각화
pred_proba_c1 = lr_clf.predict_proba(X_test)[:, 1]
precision_recall_curve_plot(y_test, pred_proba_c1)

임곗값을 0.42 정도일때 낮추면 정밀도와 재현율의 균형이 맞지만, 두 지표 모두 0.7 이하임
# 임곗값 조절 전, 데이터 점검
diabetes_data.describe()

min 값이 0인 피처가 많음
실제값이 0일수 없는 피처가 존재함
# Glucose 피처의 히스토그램을 통해 0의 개수 확인
plt.hist(diabetes_data['Glucose'], bins=100)
plt.show()

0값이 5개 있음을 확인
# 각 피처의 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))
Glucose 0 건수는 5, 퍼센트는 0.65 %
BloodPressure 0 건수는 35, 퍼센트는 4.56 %
SkinThickness 0 건수는 227, 퍼센트는 29.56 %
Insulin 0 건수는 374, 퍼센트는 48.70 %
BMI 0 건수는 11, 퍼센트는 1.43 %
전체 데이터 수가 많지 않기 때문에, 0값을 모두 삭제할 경우 학습을 효과적으로 수행하기 어려움
# 따라서, 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)
오차 행렬
[[90 10]
[21 33]]
정확도: 0.7987, 정밀도: 0.7674, 재현율: 0.6111, F1: 0.6804, AUC:0.8433
데이터 변환과 스케일링을 통해 정확도, 정밀도 개선
재현율은 여전히 개선 필요 -> 분류 결정 임곗값을 변화시키면서(0.3~0.5까지 0.03씩) 확인
# 앞서 사용한 get_eval_by_threshold() 이용
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)
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.48일때만 발췌)
임곗값: 0.48
오차 행렬
[[88 12]
[19 35]]
정확도: 0.7987, 정밀도: 0.7447, 재현율: 0.6481, F1: 0.6931, AUC:0.8433
# 앞서 학습된 로지스틱 회귀 모델을 이용하여 임곗값을 0.48로 낮춰 다시 예측
# 임곗값를 0.48로 설정한 Binarizer 생성
binarizer = Binarizer(threshold=0.48)
# 위에서 구한 lr_clf의 predict_proba() 예측 확률 array에서 1에 해당하는 컬럼값을 Binarizer변환.
# 0.48 보다 크면 positive, 작으면 negative로 분류
pred_th_048 = binarizer.fit_transform(pred_proba[:, 1].reshape(-1,1))
get_clf_eval(y_test , pred_th_048, pred_proba[:, 1])
오차 행렬
[[88 12]
[19 35]]
정확도: 0.7987, 정밀도: 0.7447, 재현율: 0.6481, F1: 0.6931, AUC:0.8433
'머신러닝' 카테고리의 다른 글
[4장] 파이썬 머신러닝 완벽 가이드_분류_2 (1) | 2023.05.24 |
---|---|
[4장] 파이썬 머신러닝 완벽 가이드_분류_1 (0) | 2023.05.20 |
[3장] 파이썬 머신러닝 완벽가이드_평가_1 (1) | 2023.05.05 |
[2장] 파이썬 머신러닝 완벽가이드_사이킷런_2 (0) | 2023.04.28 |
[2장] 파이썬 머신러닝 완벽 가이드_사이킷런_1 (0) | 2023.04.21 |