본문 바로가기
Artificial Intelligence/60. Python

[PYTHON] 데이터 불균형 해결을 위한 Focal Loss와 Class Weight의 3가지 차이점과 적용 방법

by Papa Martino V 2026. 4. 28.
728x90

Focal Loss vs Class Weight
Focal Loss vs Class Weight

 

실질적인 머신러닝 프로젝트를 진행하다 보면 가장 빈번하게 마주치는 문제 중 하나가 바로 데이터 불균형(Imbalanced Data)입니다. 암 진단 데이터, 이상 거래 탐지(Fraud Detection), 시스템 장애 예측 등 대부분의 가치 있는 도메인에서 우리가 타겟으로 삼는 'Positive' 클래스는 전체의 1% 미만인 경우가 많습니다. 과거에는 오버샘플링 기법인 SMOTE(Synthetic Minority Over-sampling Technique)가 만능 열쇠처럼 여겨졌으나, 고차원 데이터에서의 노이즈 생성 문제와 연산 비용의 한계로 인해 최근에는 손실 함수(Loss Function) 자체를 최적화하는 Focal LossClass Weight 방식이 더 선호되고 있습니다. 본 포스팅에서는 이 두 가지 방식의 수학적 원리와 실무적 장단점을 심층 비교합니다.


1. 데이터 불균형 대응 전략의 핵심 개념

데이터 불균형을 해결하는 방법은 크게 세 가지 계층으로 나뉩니다. 데이터 레벨에서의 샘플링, 알고리즘 레벨에서의 비용 조절, 그리고 평가 지표의 변경입니다. 본 글에서 다루는 Focal Loss와 Class Weight는 알고리즘 레벨의 핵심 전략입니다.

  • Class Weight: 다수 클래스보다 소수 클래스의 오차에 더 큰 가중치를 부여하여 모델이 소수 데이터를 더 중요하게 학습하도록 유도합니다.
  • Focal Loss: 단순히 클래스 빈도뿐만 아니라, 모델이 '맞추기 쉬운 샘플(Easy Examples)'의 기여도를 대폭 낮추고 '어려운 샘플(Hard Examples)'에 집중하게 만듭니다.

2. Focal Loss vs Class Weight vs SMOTE 비교 분석

각 기법이 가진 고유의 특성을 실무 관점에서 비교하면 다음과 같습니다.

비교 항목 SMOTE (Sampling) Class Weight (Cost) Focal Loss (Dynamic)
주요 원리 데이터 복제 및 합성 클래스별 고정 가중치 부여 난이도 기반 동적 가중치
데이터 왜곡 발생 가능성 높음 (노이즈) 없음 없음
학습 속도 데이터 증강으로 느려짐 변화 없음 약간의 연산 추가
하이퍼파라미터 k-neighbors Weight ratio $\gamma$ (Focusing parameter)
최적 시나리오 데이터 자체가 너무 적을 때 일반적인 불균형 상황 Easy Negative가 압도적일 때

3. 수학적 배경: 왜 Focal Loss인가?

표준적인 Binary Cross Entropy(BCE)는 다음과 같습니다:

$$BCE(p_t) = -\log(p_t)$$

Focal Loss는 여기에 $(1-p_t)^\gamma$라는 변조 계수를 추가합니다:

$$FL(p_t) = -(1-p_t)^\gamma \log(p_t)$$

이 식의 마법은 $\gamma$(감마)에 있습니다. 모델이 샘플을 잘 맞출수록($p_t$가 높을수록) $(1-p_t)^\gamma$는 0에 가까워져 해당 샘플의 손실값 반영 비중을 낮춥니다. 반면, 틀린 샘플은 가중치를 유지하여 모델이 어려운 케이스를 해결하도록 강제합니다.


4. 실무 적용을 위한 Python 샘플 예제 (Example 7선)

다양한 환경에서 즉시 적용 가능한 코드 스니펫입니다.

Ex 1. Scikit-learn Random Forest에서 Class Weight 적용 방법

from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import make_classification

# 극심한 불균형 데이터 생성 (1:99)
X, y = make_classification(n_samples=1000, n_classes=2, weights=[0.99, 0.01], flip_y=0)

# class_weight='balanced' 설정 시 자동 계산 (n_samples / (n_classes * np.bincount(y)))
model = RandomForestClassifier(n_estimators=100, class_weight='balanced')
model.fit(X, y)

Ex 2. XGBoost에서 scale_pos_weight 해결 방법

from xgboost import XGBClassifier

# 계산법: (Negative 샘플 수) / (Positive 샘플 수)
ratio = 99 / 1 
xgb_model = XGBClassifier(scale_pos_weight=ratio, learning_rate=0.1)
xgb_model.fit(X, y)

Ex 3. LightGBM에서 고정 Class Weight 적용

import lightgbm as lgb

# class_weight 파라미터에 딕셔너리 형태로 직접 부여 가능
lgbm = lgb.LGBMClassifier(class_weight={0: 1, 1: 50})
lgbm.fit(X, y)

Ex 4. Keras/TensorFlow에서 손실 함수 커스터마이징 (Focal Loss)

import tensorflow as tf

def focal_loss(gamma=2., alpha=.25):
    def focal_loss_fixed(y_true, y_pred):
        pt_1 = tf.where(tf.equal(y_true, 1), y_pred, tf.ones_like(y_pred))
        pt_0 = tf.where(tf.equal(y_true, 0), y_pred, tf.zeros_like(y_pred))
        return -tf.reduce_sum(alpha * tf.pow(1. - pt_1, gamma) * tf.log(pt_1)) \
               -tf.reduce_sum((1 - alpha) * tf.pow(pt_0, gamma) * tf.log(1. - pt_0))
    return focal_loss_fixed

# 모델 컴파일 시 적용
# model.compile(optimizer='adam', loss=focal_loss(gamma=2.0))

Ex 5. PyTorch에서 WeightedRandomSampler 사용하기

import torch
from torch.utils.data import WeightedRandomSampler

# 클래스별 가중치 계산
weights = [1.0 / count for count in [990, 10]]
sample_weights = [weights[i] for i in y]
sampler = WeightedRandomSampler(sample_weights, len(sample_weights))

# DataLoader에 적용 (학습 시 데이터 레벨에서 균형을 맞춤)
# train_loader = DataLoader(dataset, batch_size=32, sampler=sampler)

Ex 6. CatBoost에서 auto_class_weights 활용

from catboost import CatBoostClassifier

# 'Balanced' 또는 'SqrtBalanced' 옵션 제공
cat_model = CatBoostClassifier(auto_class_weights='Balanced', iterations=500)
cat_model.fit(X, y)

Ex 7. Scikit-learn SVM에서 수동 가중치 조절

from sklearn.svm import SVC

# 소수 클래스(1)에 100배의 가중치를 부여하여 경계면 결정
svm_model = SVC(kernel='linear', class_weight={0: 1, 1: 100})
svm_model.fit(X, y)

5. 결론: 어떤 상황에서 무엇을 써야 하는가?

데이터 불균형 해결을 위한 최고의 전략은 단 하나가 아닙니다. 데이터의 특성에 따라 다음과 같은 가이드를 제안합니다.

  1. 소수 클래스의 절대량이 너무 적은 경우: SMOTE를 통해 데이터를 먼저 증강하는 것이 필요할 수 있습니다.
  2. 일반적인 정형 데이터 분류: Class Weight 조절(XGBoost의 scale_pos_weight 등)이 가장 빠르고 효율적인 해결책입니다.
  3. 객체 탐지(Object Detection)나 이미지 분류: 다수의 배경(Easy Negative)이 학습을 방해하므로 Focal Loss를 사용하는 것이 성능 향상에 큰 도움이 됩니다.

결국 Cross-Validation을 통해 F1-Score나 AUCPR(Precision-Recall Curve) 지표가 가장 높게 나오는 지점을 찾는 것이 중요합니다.


참고 문헌 (Sources)

  • Lin, T. Y., Goyal, P., Girshick, R., He, K., & Dollár, P. (2017). "Focal Loss for Dense Object Detection". Proceedings of the IEEE International Conference on Computer Vision (ICCV).
  • He, H., & Ma, Y. (2013). Imbalanced Learning: Foundations, Algorithms, and Applications. Wiley.
  • Scikit-learn Documentation: "API Reference for Classifier Class Weights".
728x90