본문으로 건너뛰기

023 다중 분류 평가 지표

키워드: 다중 분류, 평가 지표

개요

다중 분류에서는 이진 분류와 다른 방식으로 평가 지표를 계산합니다. Micro, Macro, Weighted 평균의 차이를 이해하는 것이 중요합니다.

실습 환경

  • Python 버전: 3.11 권장
  • 필요 패키지: flaml[automl], scikit-learn
pip install flaml[automl] scikit-learn

다중 분류 평가의 특징

이진 분류 vs 다중 분류

항목이진 분류다중 분류
Precision/Recall단일 값클래스별 계산 필요
F1 Score단일 값평균 방식 선택 필요
혼동 행렬2×2k×k

평균화 방식

다중 분류에서는 클래스별 지표를 어떻게 평균할지 결정해야 합니다:

  1. Micro: 전체 샘플 기준
  2. Macro: 클래스별 평균 (가중치 없음)
  3. 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현실적 성능 반영
단순한 전체 정확도MicroAccuracy와 동일

정리

  • Micro: 전체 집계, 다수 클래스 가중치 높음, = Accuracy
  • Macro: 클래스별 단순 평균, 소수 클래스 동등 취급
  • Weighted: 샘플 수 가중 평균, 현실적 성능 반영
  • 불균형 데이터에서는 Macro F1이 더 엄격한 평가
  • FLAML에서 metric="macro_f1" 또는 "micro_f1" 설정

다음 글 예고

다음 글에서는 불균형 데이터 처리 전략에 대해 알아보겠습니다. 클래스 불균형 문제를 해결하는 다양한 방법을 다룹니다.


FLAML AutoML 마스터 시리즈 #023