머신러닝 완벽가이드 3강

shshin·2021년 7월 21일
0
post-thumbnail

평가

머신러닝은 데이터 가공/변환, 모델 학습/예측 그리고 평가의 프로세스로 구성된다. 앞 장의 타이타닉 생존자 예제에서는 모델 예측 성능의 평가를 위해 정확도(Accuracy)를 이용했다. 머신러닝 모델은 여러가지 방법으로 예측 성능을 평가할 수 있다.

성능 평가 지표는 일반적으로 모델이 분류냐 회귀냐에 따라 여러 종류로 나뉜다. 회귀의 경우 대부분 실제 값과 예측값의 오차 평균값에 기반한다.

ex) 오차에 절댓값을 씌운 뒤 평균 오차를 구하거나 오차의 제곱 값에 루트를 씌운 뒤 평균 오차를 구하는 방법과 같이 기본적으로 예측 오차를 가지고 정규화 수준을 재가공하는 방법이 회귀의 성능 평가 지표 유형이다. 회귀를 위한 평가는 그렇게 보잡하지 않으므로 5장의 회귀장에서 다시 상세하게 설명할 예정.

분류에 사용되는 성능 평가 지표에 대해 자세히 알아보자. 특히 0과 1이냐 혹은 긍정/부정을 판단하는 이진 분류같은 경우 정확도보다는 다른 성능 평가 지표가 더 중요시되는 경우가 많다.

분류의 성능 평가 지표

▫ 정확도(accuracy)
▫ 오차행렬 (Confusion Matrix)
▫ 정밀도(Precision)
▫ 재현율(Recall)
▫ F1 스코어
▫ ROC AUC

위 성능평가지표는 멀티 분류에도 적용되는 지표이지만, 이진 분류에서 더욱 중요하게 강조하는 지표이다.

1 정확도 (Accuracy)

정확도는 실제 데이터에서 예측 데이터가 얼마나 같은지를 판단하는 지표이다.

정확도(Accuracy) =
예측 결과가 동일한 데이터 건수 / 전체 예측 데이터 건수

#accuracy score 예시
from sklearn.metircs import accuracy_score

accuracy_score(y_test, pred)

그러나, 불균형한 레이블 데이터 세트에서는 성능 수리초 사용돼서는 안된다. 정확도가 가지는 분류 평가 지표로서 이러한 한계점을 극복하기 위해 여러가지 분류 지표와 함꼐 적용해야 한다. 먼저 True/False, Positive/Negative의 4분면으로 구성되는 오차 행렬(Confusion Matrix)에 대해 알아보자.

2 오차 행렬

이진분류에서 성능 지표로 잘 활용되는 오차행렬은 학습된 분류 모델이 예측을 수행하면서 얼마나 헷갈리고 있는지도 함께 보여주는 지표이다. 즉, 이진 분류의 예측 오류가 얼마인지와 더불어 어떠한 유형의 예측 오류가 발생하고 있는지를 함께 나타내는 지표이다.

▫ TN은 예측값을 (-) 값 0으로 예측했고 실제 값 역시 (-) 값 0
▫ FP는 예측값을 (+) 값 1로 예측했는데 실제 값은 (-) 값 0
▫ FN은 예측값을 (-) 값 0으로 예측했는데 실제 값은 (+) 값 1
▫ TP는 예측값을 (+) 값 1 로 예측했는데 실제 값 역시 (+) 1

사이킷런은 오차행렬을 구하기 위해 confusion_matrix() API를 제공한다.

이 값들을 조합해 Classifier의 성능을 측정할 수 있는 주요 지표인 정확도 (Accuracy), 정밀도(Precision), 재현율(Recall) 값을 알 수 있다.

앞에서도 소개한 정확도는 예측값과 실제 값이 얼마나 동일한가에 대한 비율만으로 결정된다. 즉, TN과 TP에 좌우된다.

불균형한 데이터 세트에서 정확도보다 더 선호되는 평가 지표인 정밀도(Precision)와 재현율(Recall)에 대해 알아보자

3 정밀도와 재현율

정밀도와 재현율은 Positive 데이터 세트의 예측 성능에 좀 더 초점을 맞춘 지표이다.

공식은 위와 같다.

정밀도는 예측을 Positive으로 한 대상 중 예측과 실제값이 Positive로 일치한 데이터의 비율을 뜻한다. Positive 예측 성능을 더욱 정밀하게 측정하기 위한 평가 지표로 양성 예측도라고 불린다.

재현율은 실제값이 Positive인 대상 중에 예측과 실제 값이 Positive로 일치한 데이터의 비율을 뜻한다. 민감도(sensitivity) 또는 TPR(True Positive Rate)이라고도 불린다.

  • 재현율이 상대적으로 더 중요한 지표인 경우는 실제 Positive 양성인 데이터 예측을 Negative로 잘못 판단하게 되면 업무상 큰 영향이 발생하는 경우

예) 암 판단 모델 : 실제 Positive인 암 환자를 Positive 양성이 아닌 Negative 음성으로 잘못 판단했을 경우 오류의 대가가 생명을 앗아갈 정도로 심각하기 때문

  • 정밀도가 상대적으로 더 중요한 지표인 경우는 실제 Negative 음성인 데이터 예측을 Positive 양성으로 잘못 판단하게 되면 업무상 큰 영향이 발생하는 경우

예) 스팸 메일 여부 : 실제 Positive인 스팸 메일을 Negative인 일반 메일로 분류하더라도 사용자가 불편함을 느끼는 정도지만, 실제 Negative인 일반 메일을 Positive인 스팸 메일로 분류할 경우에는 큰 영향을 끼침

따라서, 재현율과 정밀도 둘다 TP를 높이는데 동일하게 초점을 맞추지만, 재현율은 FN을 낮추는데, 정밀도는 FP를 낮추는 데 초점을 맞춘다.

정밀도는 precision_score(y_test, pred)
재현율은 recall_score(y_test, pred)

3-1 정밀도 재현율 트레이드오프

분류하려는 업무의 특성상 정밀도 또는 재현율이 특별히 강조돼야 할 경우 분류의 결정 임계값(Threshold)을 조정해 정밀도 또는 재현율의 수치를 높일 수 있다. 하지만 정밀도와 재현율은 상호 보완적인 평가 지표이기 때문에 어느 한쪽을 강제로 높이면 다른 하나의 수치는 떨어지기 쉽다. 즉, 정밀도나 재현율 중 하나를 강제로 높이면 하나는 낮아지는 것을 trade-off이라고 한다.

임계값을 낮추면 재현율 값이 올라가고 정밀도가 떨어진다. 분류 결정 임계값은 Positive 예측값을 결정하는 확률의 기준이 된다. 확률이 0.5가 아닌 0.4부터 Positive로 예측을 더 너그럽게 하기 때문에 임계값 값을 낮출수록 True값이 많아지게 된다.

4 F1 스코어

F1 스코어(Score)는 정밀도와 재현율을 결합한 지표이다. F1 스코어는 정밀도와 재현율이 어느 한 쪽으로 치우치지 않은 수치를 나타낼 때 상대적으로 높은 값을 가진다.

5 ROC 곡선과 AUC

ROC곡선과 이에 기반한 AUC 스코어는 이진 분류의 예측 성능 측정에서 중요하게 사용되는 지표이다. ROC 곡선(Receiver Operation Characteristic Curve)은 우리말로 수신자 판단 곡선으로 불린다.

머신러닝의 이진 분류 모델의 예측 성능 판단하는 중요한 평가 지표이다. ROC곡선은 FPR(False Positive Rate)이 변할 때 TPR(True Positive Rate)이 어떻게 변하는지를 나타내느 곡선이다. FPR를 X축으로, TPR를 Y축으로 잡으면 FPR의 변화에 따른 TPR의 변화가 곡선 형태로 나타난다.

TPR은 True Positive Rate의 약자로, 재현율을 나타낸다. 따라서 TPR은 TP/(FN + TP)이다. TPR 즉 재현율은 민감도로도 불린다. 그리고 민감도에 대응하는 지표로 TNR(True Negative Rate)이라고 불리는 특이성(Specificity)이 있다.

  • 민감도(TPR)는 실제값 Positive이 정확히 예측돼야 하는 수준을 나타낸다
  • 특이성(TNR)은 실제값 Negative가 정확히 예측돼야 하는 수준을 나타낸다.

TNR 인 특이성은 다음과 같이 구할 수 있다. TNR = TN / (FP + TN) 그리고 ROC 곡선의 X축 기준인 FPR(False Positive Rate)은 FP / (FP + TN) 이므로 1-TNR 또는 1-특이성으로 표현된다.

임계값을 변경함으로써 FPR를 0부터 1까지 변경한다. (FPR이 0이 되려면 임계값을 1로 설정, FPR이 1이 되려면 임계값을 0으로 설정)

따라서 임계값을 1부터 0까지 변화시키면서 FPR을 구하고, FPR변화에 따른 TPR값을 구하는 것이 ROC 곡선이다.

일반적으로 ROC곡선 자체는 FPR과 TPR의 변화 값을 보는 데 이용하며 분류의 성능 지표로 사용되는 것은 ROC곡선 면적에 기반한 AUC값으로 결정한다. AUC값은 ROC 곡선의 밑의 면적을 구한 것으로 일반저긍로 1에 가까울수록 좋은 수치이다. AUC수치가 커지려면 FPR이 작은 상태에서 얼마나 큰 TPR을 얻을 수 있느냐가 관건이다.

code 예시>

roc_auc = roc_auc_score(y_test, pred_proba)

6 피마 인디언 당뇨 예측

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('C:/Users/사용자/Desktop/data/diabetes.csv')
print(diabetes_data['Outcome'].value_counts())
diabetes_data.head()

>>>

	Pregnancies	Glucose	BloodPressure	SkinThickness	Insulin	BMI	DiabetesPedigreeFunction	Age	Outcome
0	6		148	72		35		0	33.6	0.627				50	1
1	1		85	66		29		0	26.6	0.351				31	0
2	8		183	64		0		0	23.3	0.672				32	1
3	1		89	66		23		94	28.1	0.167				21	0
4	0		137	40		35		168	43.1	2.288				33	1
diabetes_data.info()

>>>
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 768 entries, 0 to 767
Data columns (total 9 columns):
 #   Column                    Non-Null Count  Dtype  
---  ------                    --------------  -----  
 0   Pregnancies               768 non-null    int64  
 1   Glucose                   768 non-null    int64  
 2   BloodPressure             768 non-null    int64  
 3   SkinThickness             768 non-null    int64  
 4   Insulin                   768 non-null    int64  
 5   BMI                       768 non-null    float64
 6   DiabetesPedigreeFunction  768 non-null    float64
 7   Age                       768 non-null    int64  
 8   Outcome                   768 non-null    int64  
dtypes: float64(2), int64(7)
memory usage: 54.1 KB
#평가 함수를 담은 def

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('정확도 : %.4f, 정밀도 : %.4f, 재현율 : %.4f, F1 : %.4f, AUC : %.4f' %(accuracy, precision, recall, f1, roc_auc))
   
   
#피처 데이터세트 X, 레이블 데이터 세트 y 추출
#맨 끝이 Outcome 칼럼으로 레이블(target)값임.

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)[:,1]

get_clf_eval(y_test, pred, pred_proba)   

>>>

오차 행렬
[[88 12]
 [23 31]]
정확도 : 0.7727, 정밀도 : 0.7209, 재현율 : 0.5741, F1 : 0.6392, AUC : 0.7919
diabetes_data.describe()


#min 이 0 값인 곳이 굉장히 많이 존재
#0 (null) 처리를 해야함

zero_features = ['Glucose','BloodPressure','SkinThickness','Insulin','BMI']

mean_zero_features = diabetes_data[zero_features].mean()
diabetes_data[zero_features]=diabetes_data[zero_features].replace(0,mean_zero_features)



#로지스틱 회귀의 경우 일반적으로 숫자 데이터에 스케일링을 적용하는 것이 좋음
#StandardScaler 클래스 이용

X = diabetes_data.iloc[:,:-1]
y = diabetes_data.iloc[:,-1]

sc = StandardScaler()
X_scaled = sc.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
profile
안녕하세요, 머신러닝/딥러닝 입문자입니다!

0개의 댓글