모델 평가하는 방법(혼동행렬, confusion matrix 사용방법, 특이도, 민감도, 재현율, 정확도, 정밀도)
혼동행렬(confusion matrix)과 모델을 평가하는 방법인 특이도, 민감도, 재현율, 정확도, 정밀도를 정리했습니다.
2021. 12. 9 최초작성
2022. 2. 3
2024. 3. 10 혼동행렬(confusion matrix) 내용 추가
2024. 3. 11
2024. 3. 13
2024. 3. 20
본 포스팅에서 다루는 모델 평가 방법을 계산하려면 우선 아래 표에 있는 TP, FP, TN, FN - 4가지에 해당되는 개수를 각각 구해야 합니다.
4가지 경우는 다음 두가지를 기준으로 합니다.
- Positive는 양성으로 판정을 의미, Negative는 음성으로 판정을 의미.
- True는 판정이 옳았음을 의미, False는 판정이 틀렸음을 의미.
혼동행렬에서 표시하는 다음 구조에 맞도록 표를 수정했습니다.
재현율(Recall) 또는 민감도(sensitivity) 또는 True Positive Rate (진짜 양성 비율)
실제 양성(positive)인 것을 모델이 올바르게 양성으로 예측한 비율을 나타냅니다.
재현율이 높다는 것은 모델이 실제 양성 케이스를 잘 식별한다는 의미입니다.
예를 들어 모델이 질병이 있는 사람을 얼마나 잘 감지하는지를 나타내는 지표입니다.
민감도 = TP / (TP + FN)
TP + FN : 전체 양성 개수
TP : 양성으로 판정했는데 실제로 양성인 경우
특이도(specificity) 또는 True Negative Rate (진짜 음성 비율)
실제 음성(negative)인 것을 모델이 올바르게 음성(negative)으로 예측한 비율을 나타냅니다.
특이도가 높다는 것은 모델이 실제 음성 케이스를 잘 식별한다는 의미입니다.
예를 들어 모델이 질병이 없는 사람을 얼마나 잘 식별하는지를 나타내는 지표입니다.
특이도 = TN/(TN+FP)
TN + FP : 전체 음성 개수
TN : 음성으로 판정했는데 실제로 음성인 경우
민감도와 특이도는 한 쪽이 증가하면 다른 한 쪽이 감소하는 경향을 보일 수 있습니다. 예를 들어 검사 항목을 모두 양성으로 판정하면 민감도는 1이 되지만 특이도는 0이 되며 반대로 모두 음성으로 판정하면 특이도는 1이 되지만 민감도는 0이 됩니다.
궁극적으로는 민감도와 특이도가 둘 다 높게 나오는 방법을 찾아야 합니다.
TN, FP, FN, TP 계산을 하기 위해 sklearn에서 제공하는 confusion_matrix 함수를 사용할 수 있습니다.
예측된 값 y_pred와 정답값 y_pred를 입력으로 사용하면 됩니다.
두 배열의 같은 인덱스에 있는 원소를 비교하여 결과를 계산하게 됩니다.
from sklearn.metrics import confusion_matrix # 1은 양성, 0은 음성을 의미합니다. y_true = [0, 0, 0, 1, 1, 1, 1, 1, 1, 1] y_pred = [0, 1, 1, 0, 0, 0, 1, 1, 1, 1] tn, fp, fn, tp = confusion_matrix(y_true, y_pred).ravel() print('tn, fp, fn, tp = ', tn, fp, fn, tp) sensitivity = tp/(tp + fn) # 민감도 또는 재현율 specificity = tn/(tn + fp) # 특이도 print('sensitivity, specificity = ', sensitivity, specificity) |
실행결과 다음과 같은 결과를 얻을 수 있습니다.
하나만 살펴보면 TN은 음성(0)으로 판정했는데 실제로 음성(0)인 경우로 y_pred에서 0이었는데 y_true에서도 0인 값을 찾으면 됩니다. 이 경우는 배열의 첫번째 원소 하나만 있으므로 confusion_matrix 함수 결과와 일치합니다. 아래 결과를 보면 TN은 1입니다. 나머지 경우도 따져보세요.
tn, fp, fn, tp = 1 2 3 4
sensitivity, specificity = 0.5714285714285714 0.3333333333333333
시험삼아 다음처럼 첫번째 원소만 다르고 나머지는 동일하게 바꾼후, 민감도와 특이도 값을 출력해봅니다.
y_true = [0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
y_pred = [1, 0, 0, 1, 1, 1, 1, 1, 1, 1]
실행결과입니다. 전체 양성(1) 중에 양성을 모두 찾았으므로 민감도는 1.0입니다. 전체 음성(0) 3개중에 2개를 찾았으므로 특이도는 0.66이 됩니다.
tn, fp, fn, tp = 2 1 0 7
sensitivity, specificity = 1.0 0.6666666666666666
그외 정확도(accuracy)와 오분류율(Error Rate), 정밀도(precision), F1 score가 있습니다.
정확도(Accuracy)
전체 데이터 중에서 양성과 음성을 맞춘 개수입니다. 민감도와 특이도를 평균낸 값으로, 모델이 양성과 음성 케이스를 얼마나 잘 구분하는지의 전체적인 척도입니다. 정확도는 전반적인 성능을 의미하지만, 양성과 음성 데이터 개수가 같지 않은 경우, 즉 클래스 불균형이 있는 경우에는 이 지표만으로 모델의 성능을 올바르게 평가하기 어렵습니다.
정확도 = (TP+TN) / (TP+FN+FP+TN)
오분류율(Error Rate)
모델이 전체 데이터에서 잘못 분류한 비율입니다. 정확도와 직접적으로 관련되어 있으며, 낮은 오분류율은 모델이 잘 예측하고 있음을 의미합니다.
(FP+FN)/(TP+TN+FP+FN)= 1 - accuracy
Precision(정밀도)
모델이 양성(positive)이라고 예측한 것 중에 실제 양성인 비율입니다.
높은 정밀도는 모델이 양성으로 예측할 때 그 예측의 신뢰도가 높음을 의미합니다.
정밀도 = (TP / TP + FP)
F1 score
재현율과 정밀도의 조화평균으로 재현율과 정밀도에 같은 가중치를 부여하여 평균을 구합니다. 두 지표의 균형을 나타냅니다. F1 점수가 높다는 것은 모델의 정밀도와 민감도가 모두 높으며, 이는 전반적으로 우수한 성능을 의미합니다.
2 x precision x Recall / (Precision + Recall)
혼동 행렬(confusion matrix)
예측한 값 y_pred와 실제 값 y_true를 confusion_matrix 함수의 입력으로 사용하여 혼동행렬을 계산합니다.
이렇게 계산된 혼동 행렬을 살펴봅니다.
from sklearn.metrics import confusion_matrix, classification_report import numpy as np import pandas as pd import matplotlib .pyplot as plt import seaborn as sn # 1은 양성, 0은 음성을 의미합니다. y_true = [0, 0, 0, 1, 1, 1, 1, 1, 1, 1] y_pred = [0, 1, 1, 0, 0, 0, 1, 1, 1, 1] # 2차원 혼동행렬을 생성합니다. conf_matrix = confusion_matrix(y_true, y_pred) # 1차원으로 변환하여 배열의 원소를 4개로 분리합니다. tn, fp, fn, tp = conf_matrix.ravel() print('tn, fp, fn, tp = ', tn, fp, fn, tp) print() print(conf_matrix) print() # 평가지표를 출력해줍니다. print(classification_report(y_true, y_pred,digits=4)) data = confusion_matrix(y_true, y_pred) df_cm = pd.DataFrame(data, columns=np.unique(y_true), index = np.unique(y_true)) df_cm.index.name = 'Actual' df_cm.columns.name = 'Predicted' plt.figure(figsize = (10,7)) sn.set(font_scale=1.4)#for label size sn.heatmap(df_cm, cmap="Blues", annot=True,annot_kws={"size": 16}, fmt='d')# font size plt.title("Confusion matrix") plt.show() # 주요 지표를 계산해봅니다. TN, FP, FN, TP = data = confusion_matrix(y_true, y_pred).ravel() accuracy = (TP+TN)/(TP+TN+FP+FN) error_rate = (FP+FN)/(TP+TN+FP+FN) recall = TP/(TP+FN) specificity = TN/(TN+FP) precision = TP/(TP+FP) F1_score = 2 * precision * recall / (precision + recall) print(f'정확도(Accuracy) {accuracy:.2f}') print(f'오분류율(Error Rate) {error_rate:.2f}') print(f'재현율(Recall) {recall:.2f}') print(f'특이도(specificity) {specificity:.2f}') print(f'정밀도(Precision) {precision:.2f}') print(f'F1 score {F1_score:.2f}') |
위 코드에서 다음 부분에서 혼동행렬이 계산됩니다. confusion_matrix 함수에 y_true와 y_pred를 적는 순서를 바꾸면 혼동행렬이 달라지니 주의하세요.
y_true = [0, 0, 0, 1, 1, 1, 1, 1, 1, 1] y_pred = [0, 1, 1, 0, 0, 0, 1, 1, 1, 1] # 2차원 혼동행렬을 생성합니다. conf_matrix = confusion_matrix(y_true, y_pred) # 1차원으로 변환하여 배열의 원소를 4개로 분리합니다. tn, fp, fn, tp = conf_matrix.ravel() |
실행 결과를 분석해보겠습니다.
생성된 혼동행렬 conf_matrix를 출력하면 다음처럼 2차원 행렬로 보이는데 각 위치의 의미는 오른쪽에 표시되어 있습니다.
[[1 2] [[tn fp]
[3 4]] [fn tp]]
2차원 혼동행렬을 1차원 배열로 바꾸어서 앞에서 살펴본 4개의 수치로 분리합니다.
tn, fp, fn, tp = 1 2 3 4
matplotlib로 출력되는 그래프상의 값의 의미는 다음과 같습니다.
classification_report 함수를 사용하여 출력되는 평가지표입니다.
여기서 살펴볼것은 혼동행렬의 수치와 관련된 것만 살펴봅니다.
추후 더 내용을 추가해봅겠습니다.
0 행에 있는 suport값이 3으로 TN + FP 한 값과 동일합니다. 즉 실제값이 0인 데이터 개수입니다.
1 행에 있는 support값은 7으로 FN + TP 한 값과 동일합니다. 즉 실제값이 1인 데이터 개수입니다.
precision recall f1-score support
0 0.2500 0.3333 0.2857 3
1 0.6667 0.5714 0.6154 7
accuracy 0.5000 10
macro avg 0.4583 0.4524 0.4505 10
weighted avg 0.5417 0.5000 0.5165 10
포스트에서 살펴본 지표를 출력해본 결과입니다. 테스트 삼아 임의의 데이터를 사용했기 때문에 수치가 높지는 않습니다.
정확도(Accuracy) 0.50
오분류율(Error Rate) 0.50
재현율(Recall) 0.57
특이도(specificity) 0.33
정밀도(Precision) 0.67
F1 score 0.62
참고
https://m.blog.naver.com/win0k/221599042773
https://www.waytoliah.com/1222
https://adnoctum.tistory.com/981