[BDA 데이터 분석 모델링반 (ML 1) 3회차] ML 평가지표
머신러닝 모델의 평가지표를 알아보고, 교차검증 평가지표를 코드로 구현해본다.
평가지표란?
모델에 의해 예측된 값이 실제 값과 비교하였을 때의 오차를 구하는 과정이다.
머신러닝 모델의 성능을 평가하는 지표는 크게 회귀 문제와 분류 문제로 나뉠 수 있다.
문제 종류 | 목표 변수 | 모델 | 평가지표 |
회귀(Prediction) | 연속형 | 선형 회귀 | MSE, MAE, MAPE 등 |
분류(Classification) | 범주형 | 로지스틱, SVM | 정확도, 정밀도, 재현율, F1 점수 |
회귀 문제의 평가지표
Mean Squared Error(MSE, 평균 제곱 오차)
실제 값과 예측 값의 차이를 제곱하여 평균을 낸 지표로, 값이 작을수록 모델의 성능이 좋다고 판단된다.
Mean Absolute Error(MAE, 평균 절대 오차)
실제 값과 예측 값의 차이의 절댓값을 평균한 지표로, MSE와 마찬가지로 값이 작을수록 모델의 성능이 좋다고 판단된다.
R-squared(Coefficient of Determination, 결정계수)
독립 변수에서 예측할 수 있는 종속 변수의 분산 비율로, 예측 값이 실제 값을 얼마나 잘 설명할 수 있는지 나타내며, 1에 가까울수록 좋은 모델이라 평가된다.
분류 문제의 평가지표
Confusion Matrix
Confusion matrix는 모델의 예측 결과와 실제 결과를 비교하여 만든 행렬로, 이진 분류 문제를 해결하는데 활용합니다.
해당 행렬은 다음 네 가지 항목으로 구성된다.
True Negative (TN) : 실제값이 Negative인데, 모델이 예측한 값이 Negative인 경우
False Negative (FN) : 실제값이 Positive인데, 모델이 예측한 값이 Negative인 경우
True Positive (TP) : 실제값이 Positive인데, 모델이 예측한 값이 Positive인 경우
False Positive (FP) : 실제값이 Negative인데, 모델이 예측한 값이 Positive인 경우
Accuracy(정확도)
전체 예측 중 올바르게 분류된 비율을 나타내는 지표로, 클래스 불균형이 심하지 않을 때 유용하다.
Accuracy = (TP + TN) / (TP + TN + FP + FN)
Precision(정밀도)
양성으로 예측한 것 중 실제 양성인 비율을 나타내는 지표로, 거짓 양성(FP)를 줄이는데 활용한다.
Precision = TP / (TP + FP)
Recall(재현율)
실제 양성 중 모델이 양성으로 올바르게 예측한 비율을 나타내는 지표로, 거짓 음성 (FN)을 줄이는데 활용한다.
Recall = TP / (TP + FN)
F1 Score(F1 점수)
정밀도와 재현율의 조화 평균으로, 두 지표를 모두 고려해야하는 상황에 활용된다.
클래스 불균형에 적합한 지표로 활용될 수 있다.
F1 score = 2 * (Precision * Recall) / (Precision + Recall) = 2 * TP / (2 * TP + FP + FN)
다중 분류에서 F1 Score의 계산
TP | FP | FN | Precision | Recall | f1-score | |
A | 1 | 2 | 1 | 0.33 | 0.50 | 0.40 |
B | 1 | 2 | 2 | 0.33 | 0.33 | 0.33 |
C | 3 | 1 | 2 | 0.75 | 0.60 | 0.67 |
계 | 5 | 5 | 5 |
Micro Average
모든 클래스의 결과를 합하여 평가하며, 전체 데이터에 대한 TP, FN, FP를 합산하여 계산한다. 다중 분류에서 정확도와 같은 값을 제공하며, 클래스 불균형이 있을 때 유용하다.
precision = 5 / (5+5) = 0.5
recall = 5 / (5+5) = 0.5
f1-score (micro) = 2 * (0.5) * (0.5) / (0.5+0.5) = 0.5
Macro Average
각 클래스에 대한 평가지표를 개별적으로 계산한 후, 이를 단순 평균하여 계산한다. 모든 클래스를 동일하게 취급하여 소수 클래스의 성능이 모델 평가에 큰 영향을 미쳐 클래스 간 성능 차이를 파악할 때 유용하다.
f1-score (macro) = (0.4 + 0.33 + 0.67) / 3 = 0.47
Weighted Average
각 클래스의 샘플 수를 고려하여 가중 평균을 계산한다. 클래스 불균형을 고려할 때 유용하여 각 클래스의 중요도, 샘플 수에 따라 평가 지표를 조정한다.
f1-score (weighted) = (0.4) * (0.2) + (0.33) * (0.3) + (0.67) * (0.5) = 0.51
ROC curve, AUC
ROC curve는 분류 모델의 성능을 판단할 때 사용한다. 좌상단으로 가장 많이 치우친 그래프를 갖는 모델이 가장 높은 성능을 보인다.
AUC(Area Under the ROC Curve)는 ROC curve의 밑면적으로, 1에 가까울수록 좋은 모델이다.
교차검증 평가지표 코드 구현
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score, cross_validate
from sklearn.datasets import load_iris
import numpy as np
cross_val_score : 교차검증을 간단하게 수행할 수 있는 함수
cross_validate : 교차검증을 실행할 수 있다.
테스트 훈련시간, 테스트 시간, 정밀도, 재현율, 다른 평가지표를 볼 수 있음.
iris= load_iris()
X,y = iris.data, iris.target
clf = RandomForestClassifier(random_state=111)
scores = cross_val_score(clf, X,y, cv=5, scoring='accuracy')
scoring=['accuracy','precision','recall','f1']
results= cross_validate(clf, X,y, cv=5, scoring=scoring, return_train_score=True)
results
'''
{'fit_time': array([0.11419272, 0.11044788, 0.11291409, 0.11139798, 0.11540914]),
'score_time': array([0.01901007, 0.01136398, 0.01375604, 0.01259017, 0.01200366]),
'test_accuracy': array([0.96666667, 0.96666667, 0.93333333, 0.93333333, 1. ]),
'train_accuracy': array([1., 1., 1., 1., 1.]),
'test_precision': array([nan, nan, nan, nan, nan]),
'train_precision': array([nan, nan, nan, nan, nan]),
'test_recall': array([nan, nan, nan, nan, nan]),
'train_recall': array([nan, nan, nan, nan, nan]),
'test_f1': array([nan, nan, nan, nan, nan]),
'train_f1': array([nan, nan, nan, nan, nan])}
'''
precision, recall, f1값이 nan값이 나왔다.
iris는 다중분류 문제로, 이진분류로 생각하는 평가지표에서 nan값이 출력된다.
scoring=['accuracy','precision_macro','recall_macro','f1_macro']
results_mc= cross_validate(clf, X,y, cv=5, scoring=scoring, return_train_score=True)
results_mc
'''
{'fit_time': array([0.11103892, 0.11026311, 0.12451887, 0.1116271 , 0.10880995]),
'score_time': array([0.01640201, 0.01425886, 0.01555991, 0.01432681, 0.0139792 ]),
'test_accuracy': array([0.96666667, 0.96666667, 0.93333333, 0.93333333, 1. ]),
'train_accuracy': array([1., 1., 1., 1., 1.]),
'test_precision_macro': array([0.96969697, 0.96969697, 0.94444444, 0.93333333, 1. ]),
'train_precision_macro': array([1., 1., 1., 1., 1.]),
'test_recall_macro': array([0.96666667, 0.96666667, 0.93333333, 0.93333333, 1. ]),
'train_recall_macro': array([1., 1., 1., 1., 1.]),
'test_f1_macro': array([0.96658312, 0.96658312, 0.93265993, 0.93333333, 1. ]),
'train_f1_macro': array([1., 1., 1., 1., 1.])}
'''
scoring=['accuracy','precision_micro','recall_micro','f1_micro']
results_mic= cross_validate(clf, X,y, cv=5, scoring=scoring, return_train_score=True)
results_mic
'''
{'fit_time': array([0.11259913, 0.11184406, 0.10997415, 0.10895491, 0.11297464]),
'score_time': array([0.01431084, 0.01452708, 0.01375699, 0.01471996, 0.01375318]),
'test_accuracy': array([0.96666667, 0.96666667, 0.93333333, 0.93333333, 1. ]),
'train_accuracy': array([1., 1., 1., 1., 1.]),
'test_precision_micro': array([0.96666667, 0.96666667, 0.93333333, 0.93333333, 1. ]),
'train_precision_micro': array([1., 1., 1., 1., 1.]),
'test_recall_micro': array([0.96666667, 0.96666667, 0.93333333, 0.93333333, 1. ]),
'train_recall_micro': array([1., 1., 1., 1., 1.]),
'test_f1_micro': array([0.96666667, 0.96666667, 0.93333333, 0.93333333, 1. ]),
'train_f1_micro': array([1., 1., 1., 1., 1.])}
'''
titanic 데이터를 활용하여 이진 분류 예시를 확인한다.
import seaborn as sns
df = sns.load_dataset('titanic')
df_tt=df[['survived','pclass','fare','age']]
df_tt=df_tt.dropna()
df_tt_x= df_tt[['pclass','fare','age']]
df_tt_y= df_tt['survived']
cross_val_score(clf, df_tt_x,df_tt_y, cv=5, scoring='accuracy')
scoring=['accuracy','precision','recall','f1']
results= cross_validate(clf, df_tt_x,df_tt_y, cv=5, scoring=scoring, return_train_score=True)
results
'''
{'fit_time': array([0.15940094, 0.14485717, 0.14535308, 0.14746022, 0.16975808]),
'score_time': array([0.01802397, 0.01742101, 0.01955986, 0.01792002, 0.02737784]),
'test_accuracy': array([0.58741259, 0.68531469, 0.63636364, 0.66433566, 0.6971831 ]),
'train_accuracy': array([0.97022767, 0.98073555, 0.97197898, 0.9737303 , 0.97202797]),
'test_precision': array([0.49152542, 0.61016949, 0.55555556, 0.6 , 0.64150943]),
'train_precision': array([0.98206278, 0.97835498, 0.96956522, 0.97379913, 0.97787611]),
'test_recall': array([0.5 , 0.62068966, 0.51724138, 0.51724138, 0.5862069 ]),
'train_recall': array([0.94396552, 0.97413793, 0.9612069 , 0.9612069 , 0.95258621]),
'test_f1': array([0.4957265 , 0.61538462, 0.53571429, 0.55555556, 0.61261261]),
'train_f1': array([0.96263736, 0.9762419 , 0.96536797, 0.96746204, 0.9650655 ])}
'''
DT, RF, LR 알고리즘을 활용하여 교차검증 평가지표 계산하기
model=[DecisionTreeClassifier(),RandomForestClassifier(),LogisticRegression()]
name = ['DT','RF','LR']
for model, name in zip(model,name):
print("### 사용할 알고리즘",name,'###')
for score in ['accuracy','precision','recall','f1']:
print(score)
print('-----')
print(cross_val_score(model,df_tt_x, df_tt_y, scoring=score, cv=5))
'''
### 사용할 알고리즘 DT ###
accuracy
-----
[0.61538462 0.67832168 0.6013986 0.65734266 0.62676056]
precision
-----
[0.5 0.60344828 0.51785714 0.56603774 0.53846154]
recall
-----
[0.44827586 0.60344828 0.51724138 0.51724138 0.60344828]
f1
-----
[0.47787611 0.5862069 0.50877193 0.53571429 0.55284553]
### 사용할 알고리즘 RF ###
accuracy
-----
[0.60839161 0.69230769 0.65034965 0.68531469 0.69014085]
precision
-----
[0.50877193 0.59677419 0.52631579 0.66037736 0.63636364]
recall
-----
[0.5 0.63793103 0.51724138 0.5862069 0.60344828]
f1
-----
[0.48695652 0.63793103 0.55855856 0.60550459 0.61682243]
### 사용할 알고리즘 LR ###
accuracy
-----
[0.60839161 0.70629371 0.70629371 0.79020979 0.68309859]
precision
-----
[0.525 0.66 0.7 0.85 0.64444444]
recall
-----
[0.36206897 0.56896552 0.48275862 0.5862069 0.5 ]
f1
-----
[0.42857143 0.61111111 0.57142857 0.69387755 0.5631068 ]
'''