분류(classification)의 평가지표와 confusion matrix
1. confusion matrix 의 개념
분류의 평가지표를 살펴보려면 가장 먼저 알아둬야하는 것이 바로 confusion matrix 다. 이 것은 분류 모델이 제대로 예측을 진행했는지 파악하기 위한 것인데, 실제 값과 예측 값의 결과를 Positive와 Negative로 나누어 표로 나타낸다.
보통 왼쪽이 실제값, 위가 예측값으로 table 을 표현하고 그 순서는 각각 True False 순으로 나타내는데, 아닌 경우도 있어서 무작정 검색을 하다보면 헷갈리는 경우가 있다.
나는 sklearn metrics tool 에서 제공하는 표의 순서에 맞춰서 표현하려고 한다. 실제와 예측이 뒤바뀐 table 이더라도 어느 한 기준을 맞춰놓고 기억을 해둬야 다음에 쉽게 생각낼 수 있기 때문이다. (간단해보이지만 막상 필요할 때 생각하면 한 번에 안 떠오른다)
Confusion Matrix | 예측(Prediction) | ||
Positive | Negative | ||
실제 (Actual) |
Positive | TP | FN |
Negative | FP | TN |
뒤에 붙는 P, N 은 예측값의 Positive, Negative 를 나타내는 것이고 앞에 붙는 T, F 는 실제로 맞았냐 틀렸냐를 구분한다.
데이터가 TP 하고 TN 에 많이 몰려있고 FN, FP 에 적을 수록 정확한 예측 모델이다. 수식으로 조금 더 정확하게 평가하는 방법을 알아보자.
2. 분류의 평가 지표
1) Accuracy (정확도)
Confusion Matrix | 예측(Prediction) | ||
Positive | Negative | ||
실제 (Actual) |
Positive | TP | FN |
Negative | FP | TN |
정확도는 실제 True 와 실제 False 를 맞춘 비율이다. 즉, (TP + TN) / ALL 이다. 가장 직관적인 평가지표지만 정확도로만 분류 모델의 성능을 평가하기에는 함정이 있다.
전체 모수 100개 중 실제 True가 90개고 실제 False 가 10개인 경우, 100개 모두 True 라고 예측 했을 때의 정확도는 어떻게 될까? 90%다.
(TP(90개) + TN(0개)) / ALL(100개)
cofusion matrix 를 안 보고 정확도로만 평가하게 되면 이 분류 모델은 성능이 90% 되는 모델이라고 생각할 수 있지만 실제로는 모든 y값을 True 로만 예측하는, 학습이 제대로 되지 않은 모델인 것이다. 그래서 다른 평가지표들도 함께 살펴봐야 한다.
2) Precision (정밀도)
Confusion Matrix | 예측(Prediction) | ||
Positive | Negative | ||
실제 (Actual) |
Positive | TP | FN |
Negative | FP | TN |
Precision 은 예측한 값들 중에서 실제로 맞은 비율이다. TP / (TP+FP)
사실 한국어로 번역한다고 해서(정밀도) 이 개념이 바로 와닿지 않기 때문에 바로 Precision 이라고 알아두는 편이 더 편하다. 물론 뒤에 나오는 Recall, Specificity, Sensitivity 도 마찬가지다.
요즘에는 코로나때문에 confusion matrix를 이해하기가 조금 더 수월해진 것 같다.
예를 들어, 코로나 자가키트검사를 10명이 했는데 그중에 PCR 양성이 나온 사람이 7명이고 3명이 음성이라면 이 자가키트의 PCR 검사에 대한 정밀도는 70%라고 볼 수 있다.
Precision이 중요한 케이스는 '스팸메일' 이다. 중요하게 받아야하는 메일을 스팸메일로 잘못 분류해버리면 사용자 입장에서는 매우 화나는 일이 아닐 수 없다. 스팸메일이 정상메일에 조금 혼입되는 건 용납되더라도 정상메일이 스팸메일에 분류되는 건 치명적이다.
따라서 스팸메일이라고 분류한 것들 중에(예측 Positive) 정상메일(FP)의 비율이 적어야 한다. 그 비율을 나타내는 것이 바로 Precision이다.
3) Recall (재현율)
Confusion Matrix | 예측(Prediction) | ||
Positive | Negative | ||
실제 (Actual) |
Positive | TP | FN |
Negative | FP | TN |
Recall 은 실제 양성을 양성으로 예측한 비율이다. TP / (TP+FN)
Recall 이 중요한 예시 사례로 많이 나오는 건 암판정이다.
암환자는 최대한 많이 찾아내야하기 때문에 암환자로 예측된 사람들 중에 다수가 정상인이더라도 양성으로 예측을 많이 한다. 코로나 자가키트검사도 양성예측을 많이 하는 방향이라고 알고 있다. 즉, precision에서 중요하게 다룬 FP (양성 예측인데 실제로 아닌 케이스 = 암환자로 예측했는데 정상인인 케이스) 가 덜 중요한 사례다.
암환자 사례에서 중요한건 FN (실제 양성인데 음성으로 예측한 케이스 = 진짜 암환자인데 정상인으로 분류한 경우) 이고 이 FN이 작을 수록 성능이 좋은 모델이다. 이 것이 반영된 지표가 Recall 이다. TP / (TP+FN)
Sensitivity (민감도) 와 같은 지표다. 즉 이 수식의 이름이 두 개다. Recall 과 Sensitivity.
여기서 간단하게 실습을 해보자.
from sklearn.datasets import load_breast_cancer
import pandas as pd
data_func = load_breast_cancer()
df = pd.DataFrame(data = data_func.data, columns = data_func.feature_names)
df['target'] = data_func.target
X = df[[i for i in df.columns if i != 'target']]
y = df.target
print(df.shape)
# (569, 31)
train_test_split_num = int(df.shape[0] * 0.7)
X_train = X[:train_test_split_num]
X_test = X[train_test_split_num:]
y_train = y[:train_test_split_num]
y_test = y[train_test_split_num:]
from sklearn.preprocessing import MinMaxScaler
sc = MinMaxScaler()
X_train = sc.fit_transform(X_train)
X_test = sc.transform(X_test)
from sklearn.linear_model import LogisticRegression
lr = LogisticRegression()
lr.fit(X_train,y_train)
y_train_pred = lr.predict(X_train)
y_test_pred = lr.predict(X_test)
from sklearn.metrics import confusion_matrix, recall_score, precision_score, f1_score
confusion_matrix(y_train, y_train_pred)
# array([[165, 8],
# [ 2, 223]])
confusion_matrix(y_test, y_test_pred)
# array([[ 38, 1],
# [ 2, 130]])
print(precision_score(y_test, y_test_pred)) # 0.992 (130/131)
print(recall_score(y_test, y_test_pred)) # 0.985 (130/132)
print(precision_score(y_test, y_test_pred, average='weighted')) # 0.983
print(recall_score(y_test, y_test_pred, average='weighted')) # 0.982
맨 끝에 precision_score 에서 average='weighted' 라는 건, 양성/음성 모두 중요할 때 양성과 음성 각각에 가중치를 줘서 데이터를 산출하게 된다.
즉 0,1 로 구분된 데이터에서 1이 양성일 경우 precision score 를 계산하고, 다시 0이 양성일 경우 precision score 를 계산해서 가중치를 반영해 평균을 낸다는 의미다.
average 의 개념에 대해서 조금 더 살펴보자.
옵션으로는 micro, macro, weighted 가 있고 micro 가 imbalanced dataset 에서 적절하다.
precision score 에서 average micro 옵션은 sum(TP) / sum(TP + FP) 이다. 간단하다.
import numpy as np
a = np.array([ 0, 1, 1, 0, 1, 1])
b = np.array([ 0, 1, 0, 1, 1, 1])
confusion_matrix(a,b)
# array([[1, 1],
# [1, 3]])
precision_score(a,b) # 0.75
# 0,1 을 바꿔서 confusion matrix를 뽑아보자
a_1 = 1-a
b_1 = 1-b
confusion_matrix(a_1, b_1)
# array([[3, 1],
# [1, 1]])
precision_score(a_1,b_1) # 0.5
# 또는 labes 옵션으로 변경할 수 있다.
confusion_matrix(a,b, labels = [1,0])
# array([[3, 1],
# [1, 1]])
precision_score(a,b, labels = [1,0]) # 0.5
precision_score(a,b, average='micro') # 0.67
# (3+1)/(1+3+1+1)
# a,b confusion matrix 와 a_1,b_1 confusion matrix 의 TP 총합 / TP + FP 총합
# class imblance dataset 에서는 micro 옵션을 주는 것이 더 적절한 평가지표다.
precision_score(a,b, average='macro') # 0.625
# precision score 를 그냥 평균한 값
# ( precision_score(a,b) + precision_score(a_1,b_1) ) / 2
precision_score(a,b, average='weighted') # 0.67
# macro 방식에 가중치를 준 것
# precision_score(a,b)*4/6 + precision_score(a_1,b_1)*2/6
# 여기서 4와 2는 각각 confusion matrix에서 TP + FP 의 합
classification_report 모듈을 불러와서 precision, recall 을 한번에 볼 수도 있다.
역시 lables=[1,0] 옵션 주는 것도 가능하다.
support 는 각 label의 실제 개수다. 즉 라벨이 총 6개고 0은 2개고 1은 4개다. (y_true 기준이며 여기서는 a 기준이다)
from sklearn.metrics import classification_report
print(classification_report(a,b))
# precision recall f1-score support
#
# 0 0.50 0.50 0.50 2
# 1 0.75 0.75 0.75 4
#
# accuracy 0.67 6
# macro avg 0.62 0.62 0.62 6
#weighted avg 0.67 0.67 0.67 6
4) F1-score
Precision 과 Recall 의 조화평균이다.
spam 메일 사례나 암환자 사례처럼 특수한 경우를 제외하고는 성능이 Precision이나 Recall 한 쪽으로 치우친 모델은 일반적으로 좋은 모델이 아니다.
f1-score 는 분류모델을 평가할 때 precision과 recall 두가지 모두 고려할 수 있어서 자주 사용하는 평가지표다.
Confusion Matrix | 예측(Prediction) | ||
Positive | Negative | ||
실제 (Actual) |
Positive | TP | FN |
Negative | FP | TN |
sklearn.metrics 에서 f1_score 를 불러와서 f1_score(y_true, y_pred) 를 입력해서 계산할 수 있다.
precision recall 과 마찬가지로 average 옵션, labels 옵션 적용 가능하다.
5) Sensitivity (민감도)
위에 Recall 에서도 언급했듯이 Recall 과 Sensitivity 는 같다.
Confusion Matrix | 예측(Prediction) | ||
Positive | Negative | ||
실제 (Actual) |
Positive | TP | FN |
Negative | FP | TN |
6) Specificity (특이도)
fallout, 위양성률(1-specificity)이라고도 부른다.
실제 음성인 것들 중에 진짜 음성으로 예측한 비율이다. TN / (TN+FP)
precison 은 spam, recall 은 암환자 이렇게 대표적인 사례들이 있는데 specificity 는 딱히 없는 것 같다.
대표적 사례가 없으니 코로나 검사로 생각하면, PCR 음성인 사람들 중에서 자가키트도 음성으로 나온 비율이라고 보면 된다.
특이도는 FP 가 중요한 인자로, FP는 자가키트가 양성인데 PCR 에서 음성인 케이스(위양성)이다.
Confusion Matrix | 예측(Prediction) | ||
Positive | Negative | ||
실제 (Actual) |
Positive | TP | FN |
Negative | FP | TN |
sklearn 에서는 specificity 를 바로 구할 수 있는 모듈이 따로 제공되지 않는다.
하지만 recall 에서 label 만 바꾼거라고 볼 수 있으니 recall_score(y_true, y_pred, labels = [1,0]) 으로 계산할 수 있다.