[파이썬 머신러닝 완벽 가이드 (권철민)]
2장. 사이킷런으로 시작하는 머신러닝 - Chapter 03 평가
[머신러닝 프로세스]
데이터 가공/변환 ▶ 모델 학습/예측 ▶ 평가
성능평가 지표(Evaluation Metric)
일반적으로 모델이 분류냐 회귀냐에 따라 여러종류로 나뉘어진다.
회귀 모델 : 대부분 실제값과 오차값의 오차 평균값에 기반. 예측오차를 자기고 정규화 수준을 재가공하는 방식
분류 모델 : 일반적을 ㅗ실제 결과 데이터와 예측 결과 데이터가 얼마나 정확하고 오류가 적은가에 기반.
이진분류에서는 정확도 보다 다른 지표가 더 중요시되는 경우가 많다.
[분류의 성능평가 지표]
• 정확도(accuracy)
• 오차 행렬(confusion matrix)
• 정밀도(precision)
• 재현율(recall)
• F1 스코어
• ROC AUC
분류는 결정 클래스 값 종류 유형에 따라 나뉠 수 있다.
- 이진 분류(0이냐 1이냐, 긍/부정 같은 2개의 결괏값만을 가지는 분류)
- 멀티 분류(여러 개의 결정 클래스 값을 가지는 분류)
위에서 언급한 분류의 성능 지표는 이진/멀티 분류 모두에 적용되는 지표이지만, 특히 이진 분류에서 더욱 중요하게 강조되는 지표이다.
1. 정확도(Accuracy)
정확도 = (예측 결과가 동일한 데이터 건수)/ (전체 예측 데이터 건수)
실제 데이터에서 예측 데이터가 얼마나 같은지를 판단하는 지표입니다.
(직관적 모델 예측 성능을 나타내는 평가 지표입니다.)
하지만 이진 분류의 경우 데이터 구성에 따라 ML 모델 성능 왜곡 가능성 있음
2장에서 진행한 타이타닉 예제 수행 결과를 보면 ML알고리즘을 적용한 후 예측 정확도의 결과가 보통 80%대였지만 탑승객이 남자인 경우보다 여자인 경우 생존 확률이 높았기 떄문에 별다른 알고리즘 적용 없이 무조건 성별이 여자인 경우 생존으로, 남자인 경우 사망으로 예측결과를 예측해도 이와 비슷한 수치가 나올 수 있습니다. 단지 성별 조건 하나만 가지고 결정하는 별거 아닌 알고리즘도 높은 정확도를 나타내는 상황이 발생하는 것이다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
import numpy as np
from sklearn.base import BaseEstimator
class MyDummyClassifier(BaseEstimator):
# fit( ) 메소드는 아무것도 학습하지 않음.
def fit(self, X , y=None):
pass
# predict( ) 메소드는 단순히 Sex feature가 1 이면 0 , 그렇지 않으면 1 로 예측함.
def predict(self, X):
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
# 원본 데이터를 재로딩, 데이터 가공, 학습데이터/테스트 데이터 분할.
titanic_df = pd.read_csv('./drive/MyDrive/data/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)
X_train, X_test, y_train, y_test=train_test_split(X_titanic_df, y_titanic_df, test_size=0.2, random_state=0)
# 위에서 생성한 Dummy Classifier를 이용하여 학습/예측/평가 수행.
myclf = MyDummyClassifier()
myclf.fit(X_train ,y_train)
mypredictions = myclf.predict(X_test)
print('Dummy Classifier의 정확도는: {0:.4f}'.format(accuracy_score(y_test , mypredictions)))
|
cs |
output : Dummy Classifier의 정확도는: 0.7877
사이킷런의 BaseEstimator 클래스를 이용하여 학습을 하지 않고 성별만으로 생존자를 예측하는 Classifier를 생성했다.
사이킷런은 BaseEstimator를 상속받으면 Customized 형태의 Estimator를 개발자가 직접 생성할 수 있다.
생성할 MyDummyClassifier 클래스는 fit() method에선 아무 학습도 진행하않으며 예측을 수행하는 , predict() 매서드에서 Sex 피쳐가 1이면 0으로, 그렇지 않으면 1로 예측하는 매우 단순한 Classfier이다.
이렇게 단순하게 알고리즘을 만들어도 데이터 구성에 따라 정확도가 78.77%로 높은 수치가 나오는 것을 확인할 수 있다. 따라서 정확도를 평가 지표로 사용할 때는 매우 신중해야하며, 특히 불균형한(imbalanced) 레이블 분포에서 ML 모델의 성능을 판단할 경우에는 적합한 평가 지표로 사용될 수 없다.
즉 레이블 값 분포가 불균형할 경우 ML 모델 성능 판단에서 왜곡 가능성 높음
여러가지 분류 지표와 함께 모델 성능을 평가해야한다.
2. 오차 행렬(Confusion matrix, 혼동행렬)
오차 행렬(Confusion matrix, 혼동행렬) 은 학습된 분류 모델이 예측을 수행하며 얼마나 헷갈리고 (confused) 있는지 보여주는 지표이다. 즉 이진 분류의 예측 오류가 얼마인지와 더불어 어떠한 유형의 예측 오류가 발생하고 있는지를 함꼐 나타내는 지표입니다.
4분면 행렬에서 실제&예측 레이블 클래스 값을 매핑시키는지 나타냅니다.
• 사분면 위/아래 : 실제 클래스 값 기준으로 Negative, Positive
• 사분면 왼/오른쪽 : 예측 클래스 값 기준으로 Negative, Positive
TN, FP, FN, TP 형태로 4분면을 채움
1. TN은 예측값을 Negative 값인 0으로 예측했고 실제값 또한 Negative 값인 0일 때
2. FP은 예측값을 Positive 값인 1으로 예측했는데 실제값은 Negative 값인 0일 때
3. FN은 예측값을 Negative 값인 0으로 예측했는데 실제값은 Positive 값인 1일 때
4. TP은 예측값을 Positive 값인 1으로 예측했고 실제값 또한 Positive 값인 1일 때
왼쪽은 맞았냐 틀렸냐 (TRUE/ FALSE) / 오른쪽은 무엇으로 예측했나?(POSIIVE / NEGATIVE) 로 생각
정확도 = (예측 결과가 동일한 데이터 건수)/ (전체 예측 데이터 건수) = (TN + TP) / (TN + FP + FN + TP)
$$ accuracy = {TN + TP \over TN+TP+FN+FP }
$$
$$
precision = {TP \over TP+FP}
$$
precision : 1로 예측한 것중 1로 맞춘 비율, 최대값 조건
$$
precision : FP \downarrow
$$
$$
recall = {TP \over TP+FN}
$$
recall : 실제값 1중 1로 맞춘 비율, 최대값 조건
$$
reacll : FN \downarrow
$$
▶ 일반적으로 이러한 불균형 레이블 클래스를 가지는 이진 분류 모델에서는 많은 데이터 중에서 중점적으로 찾고자 하는 매우 적은 양의 결과값에 Positive를 설정하고, 그렇지 않은 경우는 Negative로 부여하는 경우가 많다.
▶ Positive 데이터 수가 매우 작은 불균형한 이진 분류 dataset이기 때문에 데이터에 기반하는 ML 알고리즘은 Positive보다는 Negative로 예측 정확도를 높아지게 하는 경향이 있다.
▶ TN은 매우 커지고 TP는 매우 작아지게 될 것이다. 또한 Negative로 예측할 때 정확도가 높기 때문에 FN이 매우 작고, Positive로 예측하는 경우가 애초에 매우 작아서 FP 또한 매우 작아진다.
▶ 결과적으로 비대칭한 데이터셋에서는 정확한 판단이 아님에도 불구하고 정확도가 매우 높게 나타나는 수치적인 판단 오류를 발생시킨다.
정확도는 분류(CLassfication) 모델의 성능을 측정할 수 이는 한 가지 요소일 뿐입니다.
불균형한 데이터 세트에서 정확도만으로는 모델 신뢰도가 떨어질 수 있다. 다음으로 불균형 데이터셋에서 정확도보다 더 선호되는 평가 지표인 정밀도(Precision)와 재현율(Recall)를 알아보자.
3. 정밀도와 재현율
정밀도와 재현율은 Positive dataset의 예측 성능에 좀 더 초점을 맞춘 평가 지표이다.
정밀도 =
재현율 =
정밀도는 예측을 Positive로 한 대상중에 예측값과 실제값이 Positive로 일치한 데이터의 비율을 뜻한다.
Positive 예측 성능을 더욱 정밀하게 측정하기 위한 평가 지표로 양성 예측도라고도 불린다.
재현율은 실제값이 Positive인 대상중에 예측값과 실제값이 Positive로 일치한 데이터의 비율을 뜻한다.
민감도(Sensitivity) 또는 TPR(True Positive Rate)라고도 불린다.
재현율이 중요 지표인 경우는 암 판단 모델이나 금융 사기 적발 모델과 같이 실제 Positive 양성 데이터를 Negative로 잘못 판단하게 되면 업무상 큰 영향이 발생하는 경우이다.
예로들어 암 판단 모델은 재현율이 훨씬 중요한 지표입니다. 왜냐하면 Positivr 양성이 아닌 Negative 음성을 잘못 판단 했을 경우 오류의 대가가 생명을 앗아갈 정도로 심각하기 떄문이다. 반대로 실제 Negative인 건강한 환자를 암 환자인 Positive로 예측한 경우면 다시 한번 재검사를 하는 수준의 비용이 소모 될 것이다.
따라서 보통 재현율이 정밀도보다 상대적으로 중요한 업무가 많지만, 스팸메일 여부를 판단하는 모델과 같은 경우는 정밀도가 더 중요한 지표이다.
- 재현율(Recall)이 상대적으로 더 중요한 지표인 경우는 실제 Positive 양성인 데이터 예측을 Negative 음성으로 잘못 판단하게 되면 업무상 큰 영향이 발생하는 경우
- 정밀도(Precesion)가 상대적으로 더 중요한 지표인 경우는 실제 Negative 음성인 데이터 예측을 Positive 양성으로 잘못 판단하게 되면 업무상 큰 영향이 발생하는 경우
재현율과 정밀도는 둘 다 TP를 높이는데 초점을 맞추고 있는 점이 같지만, 재현율은 FN(실제 Positive + 예측 Negative)을 낮추는 데, 정밀도는 FP(실제 Negative + 예측 Positive)를 낮추는 데 초점을 맞춘다는 점이 다르다.
이 특성때문에 재현율과 정밀도는 서로 보완적인 지표로 분류 모델의 성능을 평가하는데 적용되며, 둘 다 높은 수치를 얻는 것이 가장 좋은 성능을 의미한다. 반면 둘 중 어느 한 평가 지표만 매우 높고, 다른 하나는 매우 낮은 결과를 보이는 것은 바람직하지않다.
사이킷런은
• 정밀도 계산을 위해 precision_score(),
• 재현율 계산을 위해 recall_score()
를 API로 제공한다.
평가를 간편하게 적용하기 위해 confusion matrix, accuracy, precision, recall 등의 평가를 한번에 호출하는 get_clf_eval() 함수를 만들었다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, precision_score , recall_score , confusion_matrix
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(f'TN {confusion[0][0]}\t/ FP {confusion[0][1]}')
print(f'FN {confusion[1][0]}\t/ TP {confusion[1][1]}')
print('정확도: {0:.4f}, 정밀도: {1:.4f}, 재현율: {2:.4f}'.format(accuracy , precision ,recall))
# 원본 데이터를 재로딩, 데이터 가공, 학습데이터/테스트 데이터 분할.
titanic_df = pd.read_csv('./drive/MyDrive/data/titanic_train.csv')
y_titanic_df = titanic_df['Survived']
X_titanic_df= titanic_df.drop('Survived', axis=1)
# transform_features 함수 호출
X_titanic_df = transform_features(X_titanic_df)
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.fit(X_train , y_train)
pred = lr_clf.predict(X_test)
get_clf_eval(y_test , pred)
|
cs |
3-2. 정밀도/재현율 트레이드오프 Trade-off
분류 모델이 사용될 업무의 특성을 고려하여 정밀도와 재현율 중 더 강조돼야 할 부분을 결정하기 위한 임계값(Threshold)를 조정해 정밀도나 재현율의 수치를 높일 수 있습니다. 하지만 정밀도와 재현율은 상호 보완적인 평가 지표이기 때문에 이 두 지표에는 트레이드오프(Trade-off)가 존재하며, 이를 정밀도/재현율의 트레이드오프라고 부릅니다.
사이킷런의 분류 알고리즘은 예측 데이터가 특정 레이블(label, 결정 클래스 값)에 속하는지를 계산하기 위해 먼저 개별 레이블별로 결정 확률을 구합니다. 그리고 예측 확률이 큰 레이블 값.( 더 높은 확률의 label)로 예측한다.
일반적으로 이진 분류에서는 결정 확률의 임계값이 0.5로 정해지고 확률이 크면 Positive, 낮으면 Negative로 결정한다.
사이킷런은 개별 데이터 별 예측 확률을 반환하는 메서드로 predict_proba()를 제공한다.
precision과 recall 값은 확률의 임계값으로 조절 할 수 있다. / 확률 = 어느 한 클래스에 속하게 될 확률
학습이 완료된 Classifier 객체에서 호출이 가능하며 테스트 피처 데이터 세트를 파라미터로 입력해주면 테스트 피처 레코드의 개별 클래스 예측 확률을 반환합니다.
predict() 매서드와 유사하지만 단지 반환 결과가 예측 결과 클래스값이 아닌 예측 확률의 결과입니다.
입력 파라미터 | predict() method와 동일하게 보통 test feature dataset을 입력 |
반환 값 | 개별 class의 예측 확률을 ndarray m x n (m:입력 값의 레코드 수, n:class 값 유형) 형태로 반환 입력 test dataset의 표본 개수가 100개이고, 예측 class 값 유형이 2개(이진 분류)라면 반환값은 100 x 2 ndarray 각 row는 개별 class의 예측 확률이고, 이진 분류에서 첫번째 column은 0 Negative의 확률, 두번째 column은 1 Positive의 확률이다. |
predict_proba()에 대한 설명
predict_proba() 메서드는 분류(classification) 문제에서 각 클래스(범주)에 속할 확률을 예측할 때 사용됩니다. 이 메서드는 학습된 모델을 사용하여 각 입력 샘플이 각 클래스에 속할 예측 확률을 나타내는 배열을 반환합니다.
예를 들어, 이진 분류 문제에서 predict_proba()는 각 샘플에 대해 두 개의 확률을 반환합니다: 첫 번째는 0 클래스에 속할 확률이고, 두 번째는 1 클래스에 속할 확률입니다. 다중 클래스 분류에서는 각 클래스에 대한 확률을 반환합니다.
여기서 주의할 점은 predict() 메서드가 최종적으로 선택된 클래스만을 반환하는 반면, predict_proba() 메서드는 모든 클래스에 대한 예측 확률을 제공한다는 것입니다. 이 정보를 사용하여 특정 임계값을 설정하고 그에 따라 최종 클래스를 결정할 수 있습니다.
정밀도/재현율 트레이드오프에 대한 설명
정밀도(Precision)와 재현율(Recall)은 분류 문제의 성능 평가 지표 중 두 가지입니다.
- 정밀도는 예측한 긍정(positive) 예측 중 실제 긍정인 예측의 비율입니다. 공식으로는 TP / (TP + FP)로 계산됩니다. 여기서 TP는 진짜 양성(True Positive), FP는 거짓 양성(False Positive)입니다.
- 재현율은 실제 긍정 중 모델이 긍정으로 예측한 비율입니다. 공식으로는 TP / (TP + FN)로 계산됩니다. 여기서 FN은 거짓 음성(False Negative)입니다.
이 두 지표는 서로 트레이드오프 관계에 있습니다. 일반적으로 정밀도를 높이면 재현율이 낮아지고, 재현율을 높이면 정밀도가 낮아집니다. 이 트레이드오프는 분류 결정 임계값(threshold)에 의해 영향을 받습니다. 예를 들어, 모델이 클래스 1에 속한다고 예측할 확률을 높여야만 양성으로 분류한다면, 보다 확신 있는 예측만 양성으로 분류하게 되어 정밀도는 높아지지만, 일부 실제 양성 케이스를 놓치게 되어 재현율은 낮아집니다.
이러한 이유로, 특정 임계값을 조절하여 predict_proba()의 결과를 기반으로 하여 정밀도와 재현율의 균형을 맞출 수 있습니다. 예를 들어, 의료 진단 같이 재현율이 더 중요한 상황에서는 임계값을 낮추어 실제로 양성인 케이스를 더 많이 포착할 수 있도록 설정할 수 있습니다.
효과적인 임계값 설정은 ROC 곡선(Receiver Operating Characteristic curve)과 PR 곡선(Precision-Recall curve)을 분석함으로써 찾을 수 있으며, 이를 통해 문제에 가장 적합한 정밀도와 재현율의 균형을 찾을 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
pred_proba = lr_clf.predict_proba(X_test)
pred = lr_clf.predict(X_test)
print('pred_proba() 결과 Shape : {0}'.format(pred_proba.shape))
print('pred_proba array에서 앞 3개만 샘플로 추출 :')
print('Negative (0)\t\tPositive (1)')
print('-'*60)
for i in range(3):
print(f'{pred_proba[i][0]}\t{pred_proba[i][1]}')
# 예측 확률 array 와 예측 결과값 array 를 concatenate 하여 예측 확률과 결과값을 한눈에 확인
pred_proba_result = np.concatenate([pred_proba , pred.reshape(-1,1)],axis=1)
print('두개의 class 중에서 더 큰 확률을 클래스 값으로 예측 :')
print('Negative (0)\t\tPositive (1)\t\tPrediction')
print('-'*60)
for i in range(3):
print(f'{pred_proba_result[i][0]}\t{pred_proba_result[i][1]}\t{pred_proba_result[i][2]}')
print('='*60)
|
cs |
지금부터 이러한 로직을 구현을 위해 Binarizer클래스를 이용하겠습니다.
Binarizer 클래스
사이킷런의 predict()는 predict_proba() method가 반환하는 확률값을 가진 ndarray에서 정해진 임계값(default=0.5)을 만족하는 column 위치를 최종 예측 class로 결정한다. 이러한 로직을 구현하기 위해 Binarizer 클래스를 이용하였다.
특정 threshold 값을 변수로 설정하여 Binarizer 클래스를 객체로 생성하고 fit_transform method를 사용하여 threshold 보다 크면 1, 작으면 0으로 반환한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
from sklearn.preprocessing import Binarizer
x = [[1, -1, -2], [2, 0, 0], [0, 1.1, 1.5]]
print(x, type(x))
print('=' * 15)
# threshold 1.1 설정 / 1.1 같거나 작으면 0 / 1.1보다 크면 1 변환
binar = Binarizer(threshold=1.1)
binar.fit_transform(x)
# 앞서 구한 pred_proba 변환
binar = Binarizer(threshold=0.5)
y_binar = binar.fit_transform(pred_proba)
print(y_binar[:, 1])
print(y_test.shape, y_binar[:, 1].shape)
print('=' * 20)
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(f'TN {confusion[0][0]}\t/ FP {confusion[0][1]}')
print(f'FN {confusion[1][0]}\t/ TP {confusion[1][1]}')
print('정확도: {0:.4f}, 정밀도: {1:.4f}, 재현율: {2:.4f}'.format(accuracy , precision ,recall))
print('-' * 20)
#matrix, accuracy, precision, recall 평가 한번에 호출하는 함수
get_clf_eval(y_test, y_binar[:, 1])
def get_eval_binar(thresholds, y_tests, pred_probas):
binar = Binarizer(threshold=thresholds)
y_binar = binar.fit_transform(pred_probas)
print('====', 'thresholds', ' : ', thresholds)
get_clf_eval(y_tests, y_binar[:, 1] )
thresholds = [0.40, 0.45, 0.50, 0.55, 0.60]
for i in thresholds:
get_eval_binar(i, y_test, pred_proba)
|
cs |
위의 결과에 따르면 threshold가 0.45인 경우가 default(threshold=0.5)인 경우와 비교해서 정확도는 동일하고, 정밀도는 약간 떨어졌으며 재현율이 올랐다. 만약 재현율을 향상시키면서 다른 수치를 약간 감소시켜야 하는 경우라면 threshold 값은 0.45가 가장 적당할 것이다.
precision_recall_curve
사이킷런은 threshold 변화에 따른 정밀도와 재현율을 구할 수 있게 해주는 precision_recall_curve() API를 제공한다.
- precision, recall, threshold 출력해주는 sklearn 클래스
입력 파라미터 | • y_true : 실제 class 값 배열 (배열 크기 = [데이터 건수]) • probas_pred : Positive column의 예측 확률 배열 (배열 크기=[데이터 건수]) |
반환 값 | • 정밀도 : 임계값별 정밀도 값을 배열로 반환 • 재현율 : 임계값별 재현율 값을 배열로 반환 • threshold : 0.11 ~ 0.95 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
from sklearn.metrics import precision_recall_curve
import matplotlib.pyplot as plt
# 레이블 값이 1일때의 예측 확률을 추출
pred_proba_class1 = lr_clf.predict_proba(X_test)[:, 1]
# 실제값 데이터 셋과 레이블 값이 1일 때의 예측 확률을 precision_recall_curve 인자로 입력
precisions, recalls, thresholds = precision_recall_curve(y_test, pred_proba_class1 )
def pre_recall_plot(precisions, recalls, thresholds):
fig, axes = plt.subplots()
axes.plot(thresholds, precisions, 'k--',label = 'precision' )
axes.plot(thresholds, recalls, 'g-', label='recall')
axes.legend()
axes.set_xlabel('Threshold')
axes.set_ylabel('Precision & Recall')
axes.grid()
pre_recall_plot(precisions[:thresholds.shape[0]], recalls[:thresholds.shape[0]], thresholds[:thresholds.shape[0]])
print('반환된 분류 결정 임곗값 배열의 Shape:', thresholds.shape)
print('반환된 precisions 배열의 Shape:', precisions.shape)
print('반환된 recalls 배열의 Shape:', recalls.shape)
print('-' * 30)
print("thresholds 5 sample:", thresholds[:5])
print("precisions 5 sample:", precisions[:5])
print("recalls 5 sample:", recalls[:5])
print('-' * 30)
# 반환된 임계값 배열 로우가 147건이므로 샘플로 10건만 추출하되, 임곗값을 15 Step으로 추출.
thr_index = np.arange(0, thresholds.shape[0], 15)
print('샘플 추출을 위한 임계값 배열의 index 10개:', thr_index)
print('샘플용 10개의 임곗값: ', np.round(thresholds[thr_index], 2))
print('-' * 30)
# 15 step 단위로 추출된 임계값에 따른 정밀도와 재현율 값
print('샘플 임계값별 정밀도: ', np.round(precisions[thr_index], 3))
print('샘플 임계값별 재현율: ', np.round(recalls[thr_index], 3))
print('-' * 30)
|
cs |
- 책에서는 반환된 임곗값이 147건이라고 했으나 출력된 값에 166을 확인
- precesion_recall_curve() 함수를 익히는데 중점으로 추가적으로 체크 안함.
- 중요한 것은 임계값의 구체적인 개수가 아니라, 해당 임계값들과 그에 대응하는 정밀도와 재현율의 관계를 이해하고 적절한 임계값을 설정하는 것
3-3 정밀도와 재현율의 맹점
threshold의 변경은 업무 환경에 맞게 2개의 수치를 상호 보완할 수 있는 수준에서 적용돼야 한다.
단순히 하나의 성능 지표 수치를 높이기 위한 수단으로 사용돼서는 안된다.
다음 방법들은 2개의 수치 중 하나를 극단적으로 높이는 방법이지만 사실 단순한 숫자 놀음에 불과하다.
- 정밀도가 100%가 되는 방법
• threshold = 1 이면, 모두 N 으로 예측
• TP = 1 / 모두 정답
• FP = 0
${TP \over TP + FP} = 1$
- 재현율이 100%가 되는 방법
• threshold = 0 이면, 모두 P 으로 예측
• TP 모두 정답
• FN = 0
${TP \over TP + FN} = 1$
위 방법들과 같이 정밀도와 재현율은 어느 한쪽만을 극단적으로 높이는 수치 조작이 가능하기 때문에 하나의 수치만 높고 다른 하나의 수치는 낮은 경우는 성능이 좋지 않은 분류로 평가해야한다.