023 다중 분류 평가 지표
키워드: 다중 분류, 평가 지표
개요
다중 분류에서는 이진 분류와 다른 방식으로 평가 지표를 계산합니다. Micro, Macro, Weighted 평균의 차이를 이해하는 것이 중요합니다.
실습 환경
- Python 버전: 3.11 권장
- 필요 패키지:
flaml[automl], scikit-learn
pip install flaml[automl] scikit-learn
다중 분류 평가의 특징
이진 분류 vs 다중 분류
| 항목 | 이진 분류 | 다중 분류 |
|---|---|---|
| Precision/Recall | 단일 값 | 클래스별 계산 필요 |
| F1 Score | 단일 값 | 평균 방식 선택 필요 |
| 혼동 행렬 | 2×2 | k×k |
평균화 방식
다중 분류에서는 클래스별 지표를 어떻게 평균할지 결정해야 합니다:
- Micro: 전체 샘플 기준
- Macro: 클래스별 평균 (가중치 없음)
- Weighted: 클래스별 샘플 수로 가중 평균
예제 데이터 준비
import numpy as np
from sklearn.metrics import (
precision_score, recall_score, f1_score,
classification_report, confusion_matrix
)
# 023 예제: 3클래스 분류 결과
# 023 클래스 0: 10개, 클래스 1: 30개, 클래스 2: 60개 (불균형)
y_true = np.array([0]*10 + [1]*30 + [2]*60)
y_pred = np.array([0]*8 + [1]*2 + # 클래스 0: 8개 맞춤, 2개 틀림
[1]*25 + [2]*5 + # 클래스 1: 25개 맞춤, 5개 틀림
[1]*5 + [2]*55) # 클래스 2: 55개 맞춤, 5개 틀림
print(f"실제 분포: 0={sum(y_true==0)}, 1={sum(y_true==1)}, 2={sum(y_true==2)}")
print(f"예측 분포: 0={sum(y_pred==0)}, 1={sum(y_pred==1)}, 2={sum(y_pred==2)}")
혼동 행렬
cm = confusion_matrix(y_true, y_pred)
print("\n혼동 행렬:")
print(cm)
print("\n해석:")
print(f" 클래스 0: {cm[0,0]}개 정확, {cm[0,1]+cm[0,2]}개 오분류")
print(f" 클래스 1: {cm[1,1]}개 정확, {cm[1,0]+cm[1,2]}개 오분류")
print(f" 클래스 2: {cm[2,2]}개 정확, {cm[2,0]+cm[2,1]}개 오분류")
실행 결과
혼동 행렬:
[[ 8 2 0]
[ 0 25 5]
[ 0 5 55]]
해석:
클래스 0: 8개 정확, 2개 오분류
클래스 1: 25개 정확, 5개 오분류
클래스 2: 55개 정확, 5개 오분류
클래스별 지표
# 023 클래스별 Precision, Recall, F1
for cls in [0, 1, 2]:
# 해당 클래스를 양성으로 보는 이진 문제로 변환
y_true_binary = (y_true == cls).astype(int)
y_pred_binary = (y_pred == cls).astype(int)
prec = precision_score(y_true_binary, y_pred_binary)
rec = recall_score(y_true_binary, y_pred_binary)
f1 = f1_score(y_true_binary, y_pred_binary)
print(f"\n클래스 {cls}:")
print(f" Precision: {prec:.4f}")
print(f" Recall: {rec:.4f}")
print(f" F1: {f1:.4f}")
Micro 평균
개념
전체 TP, FP, FN을 합산하여 계산합니다.
Micro Precision = 전체 TP / (전체 TP + 전체 FP)
Micro Recall = 전체 TP / (전체 TP + 전체 FN)
계산
# 023 Micro 평균
micro_prec = precision_score(y_true, y_pred, average='micro')
micro_rec = recall_score(y_true, y_pred, average='micro')
micro_f1 = f1_score(y_true, y_pred, average='micro')
print("Micro 평균:")
print(f" Precision: {micro_prec:.4f}")
print(f" Recall: {micro_rec:.4f}")
print(f" F1: {micro_f1:.4f}")
# 023 수동 계산으로 검증
total_tp = cm[0,0] + cm[1,1] + cm[2,2] # 대각선 합
total_samples = len(y_true)
print(f"\n검증: {total_tp}/{total_samples} = {total_tp/total_samples:.4f}")
특징
- Micro Precision = Micro Recall = Micro F1 = Accuracy
- 다수 클래스에 가중치가 높음
- 전체적인 성능 파악에 유용
Macro 평균
개념
클래스별 지표의 단순 평균입니다.
Macro F1 = (F1_class0 + F1_class1 + F1_class2) / 3
계산
# 023 Macro 평균
macro_prec = precision_score(y_true, y_pred, average='macro')
macro_rec = recall_score(y_true, y_pred, average='macro')
macro_f1 = f1_score(y_true, y_pred, average='macro')
print("Macro 평균:")
print(f" Precision: {macro_prec:.4f}")
print(f" Recall: {macro_rec:.4f}")
print(f" F1: {macro_f1:.4f}")
특징
- 모든 클래스를 동등하게 취급
- 소수 클래스의 성능이 중요할 때 사용
- 불균형 데이터에서 Micro보다 엄격
Weighted 평균
개념
클래스별 샘플 수로 가중 평균합니다.
Weighted F1 = Σ(샘플수_i × F1_i) / 전체샘플수
계산
# 023 Weighted 평균
weighted_prec = precision_score(y_true, y_pred, average='weighted')
weighted_rec = recall_score(y_true, y_pred, average='weighted')
weighted_f1 = f1_score(y_true, y_pred, average='weighted')
print("Weighted 평균:")
print(f" Precision: {weighted_prec:.4f}")
print(f" Recall: {weighted_rec:.4f}")
print(f" F1: {weighted_f1:.4f}")
특징
- 샘플 수를 고려한 평균
- 불균형 데이터에서 현실적인 성능 파악
- Micro와 Macro의 중간 정도
평균 방식 비교
print("\n" + "="*50)
print("평균 방식 비교")
print("="*50)
averages = ['micro', 'macro', 'weighted']
results = {}
for avg in averages:
results[avg] = {
'Precision': precision_score(y_true, y_pred, average=avg),
'Recall': recall_score(y_true, y_pred, average=avg),
'F1': f1_score(y_true, y_pred, average=avg)
}
# 023 표 형식 출력
print(f"\n{'Average':<12} {'Precision':<12} {'Recall':<12} {'F1':<12}")
print("-" * 48)
for avg in averages:
r = results[avg]
print(f"{avg:<12} {r['Precision']:<12.4f} {r['Recall']:<12.4f} {r['F1']:<12.4f}")
실행 결과
Average Precision Recall F1
------------------------------------------------
micro 0.8800 0.8800 0.8800
macro 0.8926 0.8533 0.8716
weighted 0.8876 0.8800 0.8829
classification_report 활용
print("\n분류 리포트:")
print(classification_report(y_true, y_pred,
target_names=['Class 0', 'Class 1', 'Class 2']))
실행 결과
precision recall f1-score support
Class 0 1.00 0.80 0.89 10
Class 1 0.78 0.83 0.81 30
Class 2 0.92 0.92 0.92 60
accuracy 0.88 100
macro avg 0.90 0.85 0.87 100
weighted avg 0.89 0.88 0.88 100
FLAML에서 다중 분류 지표
from flaml import AutoML
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
# 023 데이터
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 023 Macro F1 최적화
automl_macro = AutoML()
automl_macro.fit(
X_train, y_train,
task="classification",
time_budget=30,
metric="macro_f1", # Macro F1 최적화
verbose=0
)
# 023 Micro F1 최적화
automl_micro = AutoML()
automl_micro.fit(
X_train, y_train,
task="classification",
time_budget=30,
metric="micro_f1", # Micro F1 최적화
verbose=0
)
print("Macro F1 최적화 모델:")
print(f" 검증 점수: {1 - automl_macro.best_loss:.4f}")
print("\nMicro F1 최적화 모델:")
print(f" 검증 점수: {1 - automl_micro.best_loss:.4f}")
선택 가이드
| 상황 | 권장 평균 | 이유 |
|---|---|---|
| 균형 데이터 | 모두 비슷 | 차이 적음 |
| 불균형 + 소수 클래스 중요 | Macro | 모든 클래스 동등 취급 |
| 불균형 + 전체 성능 중요 | Weighted | 현실적 성능 반영 |
| 단순한 전체 정확도 | Micro | Accuracy와 동일 |
정리
- Micro: 전체 집계, 다수 클래스 가중치 높음, = Accuracy
- Macro: 클래스별 단순 평균, 소수 클래스 동등 취급
- Weighted: 샘플 수 가중 평균, 현실적 성능 반영
- 불균형 데이터에서는 Macro F1이 더 엄격한 평가
- FLAML에서
metric="macro_f1"또는"micro_f1"설정
다음 글 예고
다음 글에서는 불균형 데이터 처리 전략에 대해 알아보겠습니다. 클래스 불균형 문제를 해결하는 다양한 방법을 다룹니다.
FLAML AutoML 마스터 시리즈 #023