1 3. 평가(Evaluation)
학습 데이터로 분류 모델을 만들고, 이를 테스트 데이터에 넣어서 좋은 모델인지 성능을 평가하고자 한다.
이 때 사용되는 분류 성능평가 지표들과 함수들에 대해 알아보자
- 오차행렬
- 정확도
- 정밀도,재현율
- 정밀도와 재현율의 (trade-off) 관계
- F1 Score
- ROC AUC
import pandas as pd
import numpy as np
# 경고 메시지 무시
import warnings
warnings.filterwarnings(action='ignore')
2 3.1 정확도(Accuracy)
이진 분류 시 정확도는 그닥 좋은 평가 지표는 아니다. 예시를 들어서 살펴보면,
2.0.1 (1) 타이타닉 데이터 - 성별로만 생존, 사망 예측하는 DummyClassifier 만들어서 학습 및 예측 정확도 구해보기
titanic_df = pd.read_csv("C:/Users/JIN SEONG EUN/OneDrive/바탕 화면/빅데이터 분석가 과정/머신러닝/실강/CH03/titanic_train.csv")
titanic_df.head()
titanic_df.shape
>>> (891, 12)
pred = np.zeros( (titanic_df.shape[0], 1) )
print(len(pred))
pred[:10]
>>>
891
array([[0.],
[0.],
[0.],
[0.],
[0.],
[0.],
[0.],
[0.],
[0.],
[0.]])
import numpy as np
from sklearn.base import BaseEstimator
# 성별로만 생존, 사망을 예측하는 클래스
class MyDummyClassifier(BaseEstimator):
# fit 메소드는 아무것도 학습하지 않음.
def fit(self, X , y=None):
pass
# 데이터프레임(X)의 성별(Sex)이 여자면(0) 생존(1), 남자(1)면 사망(0)으로 하는 pred를 반환함.
def predict(self, X):
# pred는 성별을 기준으로 생존(1), 사망(0)을 예측하는 것
pred = np.zeros( ( X.shape[0], 1 ))
for i in range (X.shape[0]) :
if X['Sex'].iloc[i] == 1:
pred[i] = 0
else :
pred[i] = 1
return pred
# 데이터 전처리 함수들
import pandas as pd
from sklearn.preprocessing import LabelEncoder
# Null 처리 함수
def fillna(df):
df['Age'].fillna(df['Age'].mean(),inplace=True)
df['Cabin'].fillna('N',inplace=True)
df['Embarked'].fillna('N',inplace=True)
df['Fare'].fillna(0,inplace=True)
return df
# 머신러닝 알고리즘에 불필요한 속성 제거
def drop_features(df):
df.drop(['PassengerId','Name','Ticket'],axis=1,inplace=True)
return df
# 레이블 인코딩 수행.
def format_features(df):
df['Cabin'] = df['Cabin'].str[:1]
features = ['Cabin','Sex','Embarked']
for feature in features:
le = LabelEncoder()
le = le.fit(df[feature])
df[feature] = le.transform(df[feature])
return df
# 앞에서 설정한 Data Preprocessing 함수 호출
def transform_features(df):
df = fillna(df)
df = drop_features(df)
df = format_features(df)
return df
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# 타이타닉 데이터 로드 및 feature(X), target(y) 데이터 분리
titanic_df = pd.read_csv("C:/Users/JIN SEONG EUN/OneDrive/바탕 화면/빅데이터 분석가 과정/머신러닝/실강/CH03/titanic_train.csv")
y_titanic_df = titanic_df['Survived']
X_titanic_df= titanic_df.drop('Survived', axis=1)
# 데이터 전처리
X_titanic_df = transform_features(X_titanic_df)
# train, test 데이터 분리
X_train, X_test, y_train, y_test=train_test_split(X_titanic_df, y_titanic_df, \
test_size=0.2, random_state=0)
# MyDummyClassifier 객체 생성
myclf = MyDummyClassifier()
# MyDummyClassifier 학습
myclf.fit(X_train, y_train)
# 테스트 데이터 예측
mypredictions = myclf.predict(X_test)
print('MyDummyClassifier의 정확도는: {0:.4f}'.format(accuracy_score(y_test , mypredictions)))
>>> MyDummyClassifier의 정확도는: 0.7877
-> 여성이면 생존, 남성이면 사망이라는 단순한 모델로 예측해도 정확도가 79%가 나온다.
2.0.2 (2) mnist 예측 - 7이면 False, 다른 숫자는 True인 모델 만들어서 학습 및 예측 정확도 구해보기
mnist 데이터 : 손글씨 0~9까지 적혀있는 데이터
궁금증. 숫자 10개 중 1개만 7이므로, "모두 7이 아니다"라고 예측하면 정확도가 90%가 나오지 않을까?
from sklearn.datasets import load_digits # mnist 데이터셋 로드
from sklearn.model_selection import train_test_split
from sklearn.base import BaseEstimator
from sklearn.metrics import accuracy_score
import numpy as np
import pandas as pd
# 모든 데이터를 0으로 만드는 클래스
class MyFakeClassifier(BaseEstimator):
def fit(self, X, y):
pass
# 입력값으로 들어오는 X 데이터 셋의 크기만큼 모두 0값으로 만들어서 반환
def predict(self, X):
return np.zeros( (len(X), 1) , dtype=bool)
# 사이킷런의 내장 데이터 셋인 load_digits( )를 이용하여 MNIST 데이터 로딩
digits = load_digits()
digits
{'data': array([[ 0., 0., 5., ..., 0., 0., 0.],
[ 0., 0., 0., ..., 10., 0., 0.],
[ 0., 0., 0., ..., 16., 9., 0.],
...,
[ 0., 0., 1., ..., 6., 0., 0.],
[ 0., 0., 2., ..., 12., 0., 0.],
[ 0., 0., 10., ..., 12., 1., 0.]]),
'target': array([0, 1, 2, ..., 8, 9, 8]),
'frame': None,
'feature_names': ['pixel_0_0',
'pixel_0_1',
'pixel_0_2',
'pixel_0_3',
'pixel_0_4',
'pixel_0_5',
'pixel_0_6',
'pixel_0_7',
'pixel_1_0',
'pixel_1_1',
'pixel_1_2',
'pixel_1_3',
'pixel_1_4',
'pixel_1_5',
'pixel_1_6',
'pixel_1_7',
'pixel_2_0',
'pixel_2_1',
'pixel_2_2',
'pixel_2_3',
'pixel_2_4',
'pixel_2_5',
'pixel_2_6',
'pixel_2_7',
'pixel_3_0',
'pixel_3_1',
'pixel_3_2',
'pixel_3_3',
'pixel_3_4',
'pixel_3_5',
'pixel_3_6',
'pixel_3_7',
'pixel_4_0',
'pixel_4_1',
'pixel_4_2',
'pixel_4_3',
'pixel_4_4',
'pixel_4_5',
'pixel_4_6',
'pixel_4_7',
'pixel_5_0',
'pixel_5_1',
'pixel_5_2',
'pixel_5_3',
'pixel_5_4',
'pixel_5_5',
'pixel_5_6',
'pixel_5_7',
'pixel_6_0',
'pixel_6_1',
'pixel_6_2',
'pixel_6_3',
'pixel_6_4',
'pixel_6_5',
'pixel_6_6',
'pixel_6_7',
'pixel_7_0',
'pixel_7_1',
'pixel_7_2',
'pixel_7_3',
'pixel_7_4',
'pixel_7_5',
'pixel_7_6',
'pixel_7_7'],
'target_names': array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
'images': array([[[ 0., 0., 5., ..., 1., 0., 0.],
[ 0., 0., 13., ..., 15., 5., 0.],
[ 0., 3., 15., ..., 11., 8., 0.],
...,
[ 0., 4., 11., ..., 12., 7., 0.],
[ 0., 2., 14., ..., 12., 0., 0.],
[ 0., 0., 6., ..., 0., 0., 0.]],
[[ 0., 0., 0., ..., 5., 0., 0.],
[ 0., 0., 0., ..., 9., 0., 0.],
[ 0., 0., 3., ..., 6., 0., 0.],
...,
[ 0., 0., 1., ..., 6., 0., 0.],
[ 0., 0., 1., ..., 6., 0., 0.],
[ 0., 0., 0., ..., 10., 0., 0.]],
[[ 0., 0., 0., ..., 12., 0., 0.],
[ 0., 0., 3., ..., 14., 0., 0.],
[ 0., 0., 8., ..., 16., 0., 0.],
...,
[ 0., 9., 16., ..., 0., 0., 0.],
[ 0., 3., 13., ..., 11., 5., 0.],
[ 0., 0., 0., ..., 16., 9., 0.]],
...,
[[ 0., 0., 1., ..., 1., 0., 0.],
[ 0., 0., 13., ..., 2., 1., 0.],
[ 0., 0., 16., ..., 16., 5., 0.],
...,
[ 0., 0., 16., ..., 15., 0., 0.],
[ 0., 0., 15., ..., 16., 0., 0.],
[ 0., 0., 2., ..., 6., 0., 0.]],
[[ 0., 0., 2., ..., 0., 0., 0.],
[ 0., 0., 14., ..., 15., 1., 0.],
[ 0., 4., 16., ..., 16., 7., 0.],
...,
[ 0., 0., 0., ..., 16., 2., 0.],
[ 0., 0., 4., ..., 16., 2., 0.],
[ 0., 0., 5., ..., 12., 0., 0.]],
[[ 0., 0., 10., ..., 1., 0., 0.],
[ 0., 2., 16., ..., 1., 0., 0.],
[ 0., 0., 15., ..., 15., 0., 0.],
...,
[ 0., 4., 16., ..., 16., 6., 0.],
[ 0., 8., 16., ..., 16., 8., 0.],
[ 0., 1., 8., ..., 12., 1., 0.]]]),
'DESCR': ".. _digits_dataset:\n\nOptical recognition of handwritten digits dataset\n--------------------------------------------------\n\n**Data Set Characteristics:**\n\n :Number of Instances: 1797\n :Number of Attributes: 64\n :Attribute Information: 8x8 image of integer pixels in the range 0..16.\n :Missing Attribute Values: None\n :Creator: E. Alpaydin (alpaydin '@' boun.edu.tr)\n :Date: July; 1998\n\nThis is a copy of the test set of the UCI ML hand-written digits datasets\nhttps://archive.ics.uci.edu/ml/datasets/Optical+Recognition+of+Handwritten+Digits\n\nThe data set contains images of hand-written digits: 10 classes where\neach class refers to a digit.\n\nPreprocessing programs made available by NIST were used to extract\nnormalized bitmaps of handwritten digits from a preprinted form. From a\ntotal of 43 people, 30 contributed to the training set and different 13\nto the test set. 32x32 bitmaps are divided into nonoverlapping blocks of\n4x4 and the number of on pixels are counted in each block. This generates\nan input matrix of 8x8 where each element is an integer in the range\n0..16. This reduces dimensionality and gives invariance to small\ndistortions.\n\nFor info on NIST preprocessing routines, see M. D. Garris, J. L. Blue, G.\nT. Candela, D. L. Dimmick, J. Geist, P. J. Grother, S. A. Janet, and C.\nL. Wilson, NIST Form-Based Handprint Recognition System, NISTIR 5469,\n1994.\n\n.. topic:: References\n\n - C. Kaynak (1995) Methods of Combining Multiple Classifiers and Their\n Applications to Handwritten Digit Recognition, MSc Thesis, Institute of\n Graduate Studies in Science and Engineering, Bogazici University.\n - E. Alpaydin, C. Kaynak (1998) Cascading Classifiers, Kybernetika.\n - Ken Tang and Ponnuthurai N. Suganthan and Xi Yao and A. Kai Qin.\n Linear dimensionalityreduction using relevance weighted LDA. School of\n Electrical and Electronic Engineering Nanyang Technological University.\n 2005.\n - Claudio Gentile. A New Approximate Maximal Margin Classification\n Algorithm. NIPS. 2000.\n"}
# mnist의 feature 데이터
print(digits.data.shape)
digits.data
(1797, 64)
array([[ 0., 0., 5., ..., 0., 0., 0.],
[ 0., 0., 0., ..., 10., 0., 0.],
[ 0., 0., 0., ..., 16., 9., 0.],
...,
[ 0., 0., 1., ..., 6., 0., 0.],
[ 0., 0., 2., ..., 12., 0., 0.],
[ 0., 0., 10., ..., 12., 1., 0.]])
.
# mnist의 target 데이터
print(digits.target.shape)
digits.target
(1797,)
array([0, 1, 2, ..., 8, 9, 8])
# 7번이면 True이고 1로 변환, 7번이 아니면 False이고 0으로 변환.
y = (digits.target == 7).astype(int)
y
>>> array([0, 0, 0, ..., 0, 0, 0])
# train, test 데이터 분리
X_train, X_test, y_train, y_test = train_test_split(digits.data, y, random_state=11)
# InteractiveShell : print 안 써도 쉘의 모든 결과를 출력해주는 라이브러리
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
X_train.shape
y_train.shape
>>>
(1347, 64)
(1347,)
X_test.shape
y_test.shape
>>>
(450, 64)
(450,)
# 불균형한 레이블 데이터 분포도 확인.
print('레이블 테스트 세트 크기 :', y_test.shape, '\n')
print('테스트 세트 레이블 0(7이 아닌 숫자)과 1(숫자 7)의 분포도')
print(pd.Series(y_test).value_counts())
>>>
레이블 테스트 세트 크기 : (450,)
테스트 세트 레이블 0(7이 아닌 숫자)과 1(숫자 7)의 분포도
0 405
1 45
dtype: int64
# MyFakeClassifier(모든 숫자 예측을 0으로 하는 모델)로 학습 및 예측
fakeclf = MyFakeClassifier()
fakeclf.fit(X_train , y_train)
# 정확도 평가
fakepred = fakeclf.predict(X_test)
print('모든 예측을 0으로 하여도 정확도는:{:.3f}'.format(accuracy_score(y_test , fakepred)))
>>> 모든 예측을 0으로 하여도 정확도는:0.900
-> 이진 분류 시 정확도는 그닥 좋은 평가 지표는 아니다. 이진분류 시 사용할 수 있는 다른 평가지표들을 살펴보자.
2.1 3.2 오차 행렬(Confusion Matrix)과 정밀도(precision), 재현율(recall) 소개
2.2 오차 행렬(Confusion matrix)
from sklearn.metrics import confusion_matrix
# 틀린것까지 고려해서 계산하는 것이 오차 행렬
# 예측 결과 fakepred와 실제 결과 y_test의 Confusion Matrix출력
confusion_matrix(y_test, fakepred)
>>>
array([[405, 0],
[ 45, 0]], dtype=int64)
정확도 : 예측 결과와 실제 값이 동일한 건수 / 전체 데이터
= (TN + TP) / (TN + TP + FN + FP)
여기서는 405 / 450 = 90%
2.3 정밀도(Precision)와 재현율(Recall)
2.3.1 MyFakeClassifier의 예측 결과로 정밀도와 재현율 측정
from sklearn.metrics import accuracy_score, precision_score , recall_score
print("정밀도:", precision_score(y_test, fakepred))
print("재현율:", recall_score(y_test, fakepred))
>>>
정밀도: 0.0
재현율: 0.0
-> 정밀도, 재현율이 0인 것을 보니, MyFakeClassifier가 이상한 모델임이 드러남
2.3.2 오차행렬, 정확도, 정밀도, 재현율을 한꺼번에 계산하는 함수 생성
from sklearn.metrics import accuracy_score, precision_score , recall_score , confusion_matrix
# confusion matrix, accuracy, precision, recall을 한꺼번에 계산하는 함수
def get_clf_eval(y_test , pred):
confusion = confusion_matrix( y_test, pred)
accuracy = accuracy_score(y_test , pred)
precision = precision_score(y_test , pred)
recall = recall_score(y_test , pred)
print('오차 행렬')
print(confusion)
print('정확도: {0:.4f}, 정밀도: {1:.4f}, 재현율: {2:.4f}'.format(accuracy , precision ,recall), '\n')
2.3.3 타이타닉 데이터에 로지스틱 회귀 모델로 이진 분류한 후에 오차행렬, 정확도, 정밀도, 재현율 구해보기
- 로지스틱 회귀 분석 : 이진 분류를 회귀 분석할 때 사용
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
# 타이타닉 데이터 로드
titanic_df = pd.read_csv('./titanic_train.csv')
# feature(X), target(y) 데이터 분리
y_titanic_df = titanic_df['Survived']
X_titanic_df= titanic_df.drop('Survived', axis=1)
# 데이터 전처리
X_titanic_df = transform_features(X_titanic_df)
# train, test 데이터 분리
X_train, X_test, y_train, y_test = train_test_split(X_titanic_df, y_titanic_df, \
test_size=0.20, random_state=11)
# 로지스틱 회귀(분류 모델) 모델 정의
lr_clf = LogisticRegression()
lr_clf
# 학습
lr_clf.fit(X_train , y_train)
# 예측
pred = lr_clf.predict(X_test)
get_clf_eval(y_test , pred)
>>>
LogisticRegression()
오차 행렬
[[104 14]
[ 13 48]]
정확도: 0.8492, 정밀도: 0.7742, 재현율: 0.7869
3 3.3 정밀도와 재현율의 (trade-off) 관계
먼저 결론부터 보면 :
적절한 분류 임계값을 설정하면 원하는 정밀도, 재현율 각각의 값을 구할 수 있다..
3.0.1 predict_proba 함수 : 분류 결정 예측 확률을 반환하는 함수
# 테스트 데이터(타이타닉)의 분류 결정 예측 확률을 반환해준다.
pred_proba = lr_clf.predict_proba(X_test)
pred_proba.shape
pred_proba[:10]
# [Negatve(0)가 될 확률, Positive(1)가 될 확률]
>>>
(179, 2)
array([[0.46200316, 0.53799684],
[0.87869995, 0.12130005],
[0.87717144, 0.12282856],
[0.88258546, 0.11741454],
[0.85519892, 0.14480108],
[0.88221403, 0.11778597],
[0.88844438, 0.11155562],
[0.20873676, 0.79126324],
[0.78278355, 0.21721645],
[0.36933486, 0.63066514]])
# 테스트 데이터 predict 결과
pred = lr_clf.predict(X_test)
pred
>>>
array([1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1,
1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0,
1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0,
0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0,
1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0,
1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0,
0, 1, 1], dtype=int64)
3.0.2 예측 확률(pred_proba)과 예측 결과값(pred)을 결합해서 비교해보자
# 예측 결과값은 1차원 이므로 2차원으로 reshape 한 후에 예측 확률과 oncatenate 함
pred_proba_result = np.concatenate([pred_proba , pred.reshape(-1, 1)],axis=1)
# 두개의 class 중에서 더 큰 확률을 클래스 값으로 예측
pred_proba_result[:10]
>>>
array([[0.46200316, 0.53799684, 1. ],
[0.87869995, 0.12130005, 0. ],
[0.87717144, 0.12282856, 0. ],
[0.88258546, 0.11741454, 0. ],
[0.85519892, 0.14480108, 0. ],
[0.88221403, 0.11778597, 0. ],
[0.88844438, 0.11155562, 0. ],
[0.20873676, 0.79126324, 1. ],
[0.78278355, 0.21721645, 0. ],
[0.36933486, 0.63066514, 1. ]])
3.0.3 Binarizer : 요소들이 기준값보다 큰지 작은지를 알려주는 함수
<Binarizer>
요소가 기준값(threshold)과 비교해서,
- 같거나 작으면 0을 반환
- 크면 1을 반환
X = [[ 1, -1, 2],
[ 2, 0, 0],
[ 0, 1.1, 1.2]]
from sklearn.preprocessing import Binarizer
# Binarizer의 threshold를 1.1로 세팅.
binarizer = Binarizer(threshold=1.1)
# array X의 값들이 1.1보다 작거나 같으면 0, 크면 1을 반환한다.
binarizer.fit_transform(X)
>>>
array([[0., 0., 1.],
[1., 0., 0.],
[0., 0., 1.]])
3.0.4 분류 임계값 0.5로 예측값 변환(Binarizer 이용)
from sklearn.preprocessing import Binarizer
# predict_proba 반환값의 두번째 컬럼, 즉 Positive 컬럼을 추출한 후
# (1차원 이므로 2차원으로 reshape으로 해준다.)
pred_proba_1 = pred_proba[:, 1].reshape(-1, 1)
pred_proba_1
array([[0.53799684],
[0.12130005],
[0.12282856],
[0.11741454],
[0.14480108],
[0.11778597],
[0.11155562],
[0.79126324],
[0.21721645],
[0.63066514],
[0.10021365],
[0.12498883],
[0.12283455],
[0.11160156],
[0.56330906],
[0.14099857],
[0.0962784 ],
[0.26666115],
[0.27528024],
[0.82836242],
[0.24647437],
[0.38099516],
[0.14536163],
[0.1853228 ],
[0.11197256],
[0.2345612 ],
[0.14036747],
[0.0741542 ],
[0.28049438],
[0.30455448],
[0.94724273],
[0.81723538],
[0.12692588],
[0.82618937],
[0.39954133],
[0.2345612 ],
[0.07238686],
[0.6109852 ],
[0.05292897],
[0.10389371],
[0.3510202 ],
[0.08336636],
[0.82170747],
[0.7078496 ],
[0.63041403],
[0.63043034],
[0.91886337],
[0.35878463],
[0.94889806],
[0.11200713],
[0.59263761],
[0.11160156],
[0.13277036],
[0.72545921],
[0.30943249],
[0.19698736],
[0.22630875],
[0.1228296 ],
[0.1541851 ],
[0.43258842],
[0.28020218],
[0.10074847],
[0.54540416],
[0.5144293 ],
[0.4442517 ],
[0.09457555],
[0.66670558],
[0.59400948],
[0.95181746],
[0.14821211],
[0.12888207],
[0.16843078],
[0.10389596],
[0.94800485],
[0.19868547],
[0.11160156],
[0.34816081],
[0.1837076 ],
[0.83563826],
[0.1228296 ],
[0.79476857],
[0.64545498],
[0.93112838],
[0.13317889],
[0.94893467],
[0.95034719],
[0.15355938],
[0.12547576],
[0.87451093],
[0.11160156],
[0.11160156],
[0.2345612 ],
[0.23235924],
[0.11160156],
[0.63043034],
[0.07569608],
[0.92886897],
[0.10069322],
[0.50524968],
[0.9650872 ],
[0.50167009],
[0.09456011],
[0.94791236],
[0.09751939],
[0.52993279],
[0.1283557 ],
[0.14104564],
[0.1482118 ],
[0.44948524],
[0.1078898 ],
[0.11706552],
[0.10886732],
[0.40332981],
[0.65404327],
[0.11197256],
[0.07101904],
[0.12439228],
[0.19845499],
[0.92594835],
[0.06864323],
[0.1115927 ],
[0.13083364],
[0.06366075],
[0.32172155],
[0.01162504],
[0.1115927 ],
[0.1162293 ],
[0.31673149],
[0.67763077],
[0.32157296],
[0.9650872 ],
[0.45384476],
[0.73540252],
[0.44271678],
[0.56980225],
[0.35080161],
[0.74843463],
[0.18610214],
[0.10392113],
[0.80346863],
[0.90893162],
[0.1482118 ],
[0.11803347],
[0.10127439],
[0.09160985],
[0.66797721],
[0.075651 ],
[0.23389456],
[0.91809015],
[0.16824154],
[0.42890583],
[0.63132242],
[0.63656891],
[0.12277396],
[0.77778659],
[0.88084234],
[0.48737851],
[0.13293387],
[0.75175744],
[0.69041575],
[0.14975655],
[0.79296357],
[0.09124913],
[0.66663799],
[0.38022651],
[0.65120193],
[0.88419584],
[0.30926709],
[0.09162914],
[0.89303326],
[0.11155562],
[0.85425927],
[0.25087817],
[0.24028367],
[0.40073614],
[0.0622901 ],
[0.14105266],
[0.54483675],
[0.62699391]])
# Binarizer를 적용 - threshold는 0.5로 세팅(0.5 : 분류 결정 임계값)
custom_threshold = 0.5
binarizer = Binarizer(threshold=custom_threshold).fit(pred_proba_1)
binarizer
>>> Binarizer(threshold=0.5)
# Positive 컬럼에 Binarizer를 적용하면 1 or 0을 예측한다.
custom_predict = binarizer.transform(pred_proba_1)
# 예측 결과와 실제 값 간의 오차행렬을 구해보면
get_clf_eval(y_test, custom_predict)
>>>
오차 행렬
[[104 14]
[ 13 48]]
정확도: 0.8492, 정밀도: 0.7742, 재현율: 0.7869
-> 3.2의 결과(정확도: 0.8492, 정밀도: 0.7742, 재현율: 0.7869)와 동일하다.
3.0.5 이번에는 분류 임계값을 0.4로 낮춰서 예측값을 보면 재현율이 높아지는지 확인해보자
# Binarizer의 threshold 설정값을 0.4로 설정. 즉 분류 결정 임곗값을 0.5에서 0.4로 낮춤
custom_threshold = 0.4
pred_proba_1 = pred_proba[:,1].reshape(-1,1)
binarizer = Binarizer(threshold=custom_threshold).fit(pred_proba_1)
custom_predict = binarizer.transform(pred_proba_1)
get_clf_eval(y_test , custom_predict)
>>>
오차 행렬
[[98 20]
[10 51]]
정확도: 0.8324, 정밀도: 0.7183, 재현율: 0.8361
-> 분류 결정 임계값이 낮아지니 재현율이 높아졌다(0.7869 -> 0.8361).
3.0.6 분류 임값을 증가시키면서 예측값 변환(Binarizer 이용)
# 임곗값(threshold)을 점차 높여보면 재현율이 감소할까?
thresholds = [0.4, 0.45, 0.50, 0.55, 0.60]
def get_eval_by_threshold(y_test , pred_proba_c1, thresholds):
# 임계값을 차례로 돌면서 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)
get_eval_by_threshold(y_test, pred_proba[:,1].reshape(-1,1), thresholds )
임곗값: 0.4
오차 행렬
[[98 20]
[10 51]]
정확도: 0.8324, 정밀도: 0.7183, 재현율: 0.8361
임곗값: 0.45
오차 행렬
[[103 15]
[ 12 49]]
정확도: 0.8492, 정밀도: 0.7656, 재현율: 0.8033
임곗값: 0.5
오차 행렬
[[104 14]
[ 13 48]]
정확도: 0.8492, 정밀도: 0.7742, 재현율: 0.7869
임곗값: 0.55
오차 행렬
[[109 9]
[ 15 46]]
정확도: 0.8659, 정밀도: 0.8364, 재현율: 0.7541
임곗값: 0.6
오차 행렬
[[112 6]
[ 16 45]]
정확도: 0.8771, 정밀도: 0.8824, 재현율: 0.7377
-> 분류 임계값을 점차 높이니 재현율은 점차 감소하고, 정밀도는 점차 증가한다.
결론 : 적절한 분류 임계값을 설정하면 원하는 정밀도, 재현율 각각의 값을 구할 수 있다.
3.1 precision_recall_curve( ) 를 이용하여 임곗값에 따른 정밀도-재현율 값 추출
- 코드가 복잡하니 결과만 보고 정리하자.
-> 분류 임계값이 증가하면 재현율(recall)이 감소하고, 정밀도(precision)가 증가한다.
-> 분류 임계값이 감소하면 재현율(recall)이 증가하고, 정밀도(precision)가 감소한다.
4 3.4 F1 Score
from sklearn.metrics import f1_score
# f1_score 클래스를 이용해서 f1 score 계산
f1 = f1_score(y_test, pred) # 실제값, 예측값
print('F1 스코어: {0:.4f}'.format(f1))
def get_clf_eval(y_test , pred):
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 = f1_score(y_test,pred)
print('오차 행렬')
print(confusion)
# f1 score print 추가
print('정확도: {0:.4f}, 정밀도: {1:.4f}, 재현율: {2:.4f}, F1:{3:.4f}'.format(accuracy, precision, recall, f1), '\n')
thresholds = [0.4 , 0.45 , 0.50 , 0.55 , 0.60]
pred_proba = lr_clf.predict_proba(X_test)
# 분류 임계값 변경을 하면서 f1 score를 포함한 평가지표 확인
get_eval_by_threshold(y_test, pred_proba[:,1].reshape(-1,1), thresholds)
임곗값: 0.4
오차 행렬
[[98 20]
[10 51]]
정확도: 0.8324, 정밀도: 0.7183, 재현율: 0.8361, F1:0.7727
임곗값: 0.45
오차 행렬
[[103 15]
[ 12 49]]
정확도: 0.8492, 정밀도: 0.7656, 재현율: 0.8033, F1:0.7840
임곗값: 0.5
오차 행렬
[[104 14]
[ 13 48]]
정확도: 0.8492, 정밀도: 0.7742, 재현율: 0.7869, F1:0.7805
임곗값: 0.55
오차 행렬
[[109 9]
[ 15 46]]
정확도: 0.8659, 정밀도: 0.8364, 재현율: 0.7541, F1:0.7931
임곗값: 0.6
오차 행렬
[[112 6]
[ 16 45]]
정확도: 0.8771, 정밀도: 0.8824, 재현율: 0.7377, F1:0.8036
-> 임계값이 0.6일 때 f1 score가 가장 높다.
그런데 재현율이 너무 낮기 때문에 여러가지 요소를 고려해야 한다.
5 3-5 ROC Curve와 AUC
from sklearn.metrics import roc_curve
# 레이블 값이 1일때의 예측 확률을 추출
pred_proba_class1 = lr_clf.predict_proba(X_test)[:, 1]
print(len(pred_proba_class1))
pred_proba_class1[:20]
>>>
179
array([0.53799684, 0.12130005, 0.12282856, 0.11741454, 0.14480108,
0.11778597, 0.11155562, 0.79126324, 0.21721645, 0.63066514,
0.10021365, 0.12498883, 0.12283455, 0.11160156, 0.56330906,
0.14099857, 0.0962784 , 0.26666115, 0.27528024, 0.82836242])
# fpr, tps, thresholds
fprs , tprs , thresholds = roc_curve(y_test, pred_proba_class1)
print('분류 임곗값 Shape :', thresholds.shape, '\n')
>>> 분류 임곗값 Shape : (55,)
# 반환된 임곗값 배열 로우가 55건이므로 샘플로 10건만 추출하되, 임곗값을 5 Step으로 추출.
thr_index = np.arange(0, thresholds.shape[0], 5)
print('샘플 추출을 위한 임곗값 배열의 index 10개:', thr_index)
print('샘플용 10개의 임곗값: ', np.round(thresholds[thr_index], 2))
>>>
샘플 추출을 위한 임곗값 배열의 index 10개: [ 0 5 10 15 20 25 30 35 40 45 50]
샘플용 10개의 임곗값: [1.97 0.75 0.63 0.59 0.49 0.4 0.35 0.23 0.13 0.12 0.11]
# 5 step 단위로 추출된 임계값에 따른 FPR, TPR 값
print('샘플 임곗값별 FPR: ', np.round(fprs[thr_index], 3))
print('샘플 임곗값별 TPR: ', np.round(tprs[thr_index], 3))
>>>
샘플 임곗값별 FPR: [0. 0.017 0.034 0.051 0.127 0.161 0.203 0.331 0.585 0.636 0.797]
샘플 임곗값별 TPR: [0. 0.475 0.689 0.754 0.787 0.836 0.869 0.902 0.918 0.967 0.967]
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')
# 가운데 대각선 직선(random 값일 때)을 그림.
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 - Sensitivity )'); plt.ylabel('TPR( Recall )')
plt.legend()
plt.show()
roc_curve_plot(y_test, lr_clf.predict_proba(X_test)[:, 1] )
from sklearn.metrics import roc_auc_score
### 아래는 roc_auc_score()의 인자를 잘못 입력한 것으로, 책에서 수정이 필요한 부분입니다.
### 책에서는 roc_auc_score(y_test, pred)로 예측 타겟값을 입력하였으나
### roc_auc_score(y_test, y_score)로 y_score는 predict_proba()로 호출된 예측 확률 ndarray중 Positive 열에 해당하는 ndarray입니다.
# pred = lr_clf.predict(X_test)
# roc_score = roc_auc_score(y_test, pred)
# 수정된 내용
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.9024
# ROC-AUC가 추가된 get_clf_eval 함수
# : 모델의 평가지표들(오차 행렬, 정확도, 정밀도, 재현율, f1 score, ROC AUC)을 보여준다.
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), '\n')
'Machine Learning > 머신러닝 완벽가이드 for Python' 카테고리의 다른 글
ch03 요약 (0) | 2022.10.06 |
---|---|
ch.3.6 실습 파마 인디언 당뇨병 예측(실습) (0) | 2022.10.06 |
ch.3.5 ROC Curve와 AUC (0) | 2022.10.06 |
ch.3.4 F1 Score (0) | 2022.10.06 |
ch.3.3 정밀도와 재현율의 관계 (1) | 2022.10.06 |