특성을 잘 만들어도 데이터 자체가 한쪽으로 치우쳐 있으면 모델은 그 편향을 그대로 학습한다. 대출 심사 모델이 특정 성별에 불리하게 판단하거나, 사기 탐지 모델이 0.1%뿐인 사기를 전부 놓치는 일이 대표적이다.
SageMaker Clarify는 두 가지 일을 한다.
시점
무엇을 분석하나
대표 산출물
학습 전 (pre-training)
데이터 자체의 그룹 간 불균형
CI, DPL 등 편향 지표
학습 후 (post-training)
모델 예측의 그룹 간 차이, 특성 기여도
예측 편향 지표, SHAP 값
핵심 개념은 민감 속성(facet)이다. 성별·인종·나이처럼 공정성을 따져야 하는 컬럼을 지정하면, Clarify가 그 그룹 간 데이터·예측이 균형 잡혔는지 지표로 보여준다.
from sagemaker import clarifyclarify_processor = clarify.SageMakerClarifyProcessor( role=role, instance_count=1, instance_type="ml.m5.xlarge", sagemaker_session=session)bias_config = clarify.BiasConfig( label_values_or_threshold=[1], # 긍정 결과 (예: 대출 승인) facet_name="gender", # 민감 속성 facet_values_or_threshold=[0], # 보호 그룹)clarify_processor.run_pre_training_bias( # 학습 전 데이터 편향 data_config=data_config, data_bias_config=bias_config,)
대표적 편향 지표는 둘이다.
지표
무엇을 재나
높으면
Class Imbalance (CI)
그룹 간 표본 수가 얼마나 치우쳤나
한 그룹의 데이터가 훨씬 적음
Difference in Positive Proportions in Labels (DPL)
그룹 간 긍정 레이블 비율 차이
한 그룹만 유리한 결과를 많이 받았음
머신러닝 편향은 데이터에서 시작한다. "garbage in, garbage out"의 공정성 버전이다. 과거 데이터에 사회적 편향(예: 특정 그룹의 낮은 승인율)이 담겨 있으면, 모델은 그것을 "패턴"으로 학습해 편향을 자동화·증폭한다. 학습 전 편향 측정이 중요한 이유다 — 모델을 만들기 전에 데이터의 불균형을 먼저 잡지 않으면, 아무리 정확한 모델도 불공정한 결정을 빠르게 대량으로 내린다.
Clarify의 학습 후 분석은 SHAP(SHapley Additive exPlanations) 값으로 "각 특성이 예측에 얼마나 기여했나"를 계산한다. 게임이론의 Shapley 값에서 온 개념으로, 특성을 넣고 빼며 예측 변화의 공정한 몫을 배분한다. 규제 산업(금융·의료)에서 "왜 이 고객이 거절됐나"를 설명해야 할 때, SHAP 기반 특성 기여도가 그 근거가 된다.
분류 데이터에서 한 클래스가 극단적으로 적은 것이 클래스 불균형이다. 사기(0.1%), 희귀 질병, 장비 고장처럼 정작 중요한 쪽이 소수인 경우가 많다. 그대로 두면 모델이 다수 클래스만 찍어도 높은 정확도가 나와 소수 클래스를 무시한다.
기법
방식
데이터 양
주의점
언더샘플링
다수 클래스를 줄임
감소
정보 손실 가능
오버샘플링(복제)
소수 클래스를 복제
증가
같은 점 반복 → 과적합 위험
SMOTE
소수 클래스 합성 샘플 생성
증가
단순 복제보다 일반화↑, 클래스가 겹치면 노이즈 합성
class weight
소수 클래스 오답에 큰 페널티
그대로
데이터를 안 늘려 메모리·시간 이득
from imblearn.over_sampling import SMOTE# SMOTE: 소수 클래스의 합성 샘플을 보간으로 생성smote = SMOTE(random_state=42)X_resampled, y_resampled = smote.fit_resample(X_train, y_train)print(y_resampled.value_counts()) # 두 클래스가 균형을 이룸
[원본] 다수 ●●●●●●●●●●●●●●●● 소수 ▲▲
[언더샘플링] 다수 ●●●● 소수 ▲▲ ← 다수를 버림(정보 손실)
[복제] 다수 ●●●●●●●●●●●●●●●● 소수 ▲▲▲▲▲▲▲▲ ← 같은 점 반복
[SMOTE] 다수 ●●●●●●●●●●●●●●●● 소수 ▲△△▲△△▲△ ← △는 이웃 사이 보간점
⚠️ 함정: 리샘플링은 반드시 분할 후 학습 데이터에만 적용한다. 분할 전에 전체 데이터에 SMOTE를 돌리면, 합성된 샘플이 검증·테스트 셋에 섞여 데이터 누수가 생기고 성능이 과대평가된다. 검증·테스트 셋은 실제 운영 분포(불균형 그대로)를 반영해야 평가가 정직하다.
SMOTE(Synthetic Minority Over-sampling Technique)는 2002년 제안된 기법으로, 소수 클래스 표본 사이를 보간(interpolation)해 새 합성 샘플을 만든다. 단순 복제는 같은 점을 여러 번 놓아 모델이 그 점을 외우게(과적합) 하지만, SMOTE는 이웃 표본 사이의 새로운 점을 만들어 결정 경계를 부드럽게 한다. 다만 특성 공간에서 클래스가 겹치면 노이즈를 합성할 수 있어 만능은 아니다.
Test 셋은 마지막에 단 한 번만 본다. 테스트 결과를 보고 모델을 고치기 시작하면 테스트 셋이 사실상 검증 셋이 되어버려, 일반화 성능을 더는 정직하게 못 잰다.
from sklearn.model_selection import train_test_split# 1차: train+val vs test / 2차: train vs val (불균형이면 stratify로 비율 유지)X_temp, X_test, y_temp, y_test = train_test_split( X, y, test_size=0.15, stratify=y, random_state=42)X_train, X_val, y_train, y_val = train_test_split( X_temp, y_temp, test_size=0.176, stratify=y_temp, random_state=42)
stratify=y는 분할 시 각 셋의 클래스 비율을 원본과 같게 유지하는 층화 분할(stratified split)이다. 불균형 데이터에서 무작위 분할하면 어떤 셋엔 소수 클래스가 거의 안 들어갈 수 있어, 반드시 층화한다.
데이터 성격별 분할 방식
데이터 성격
분할 방식
이유
일반 정형, 균형
무작위 분할
단순하고 충분
클래스 불균형
층화 분할(stratify)
각 셋의 클래스 비율 유지
시계열
시간 분할(과거=train, 미래=test)
미래로 과거를 예측하면 누수
그룹형(같은 사용자 여러 행)
그룹 단위 분할
같은 사용자가 흩어지면 성능 부풀려짐
데이터가 적음
k-fold 교차검증
분할 운에 따른 변동을 줄임
[시계열 올바른 분할]
──────── train ────────┤── val ──┤── test ──▶ 시간
(과거로 학습, 미래로 평가)
[시계열 잘못된 분할 — 무작위]
░█░░█░█░░█░█░█░░█░█░ ← 미래 조각이 train에 섞여 look-ahead bias 발생
시계열 데이터는 무작위 분할이 금물이다. 미래 데이터로 학습해 과거를 예측하는 미래 누수(look-ahead bias)가 생기기 때문이다. 시계열은 시점을 기준으로 "과거=train, 미래=test"로 나누는 시간 분할을 쓴다. 또 데이터가 적을 땐 단일 분할 대신 k-fold 교차검증으로 데이터를 k번 나눠 평균 성능을 본다.
① 데이터 확보
│
② 분할 (층화 / 시간 / 그룹) ← 가장 먼저!
│
├─ train ─→ ③ 변환기 fit ─→ ④ 리샘플링(SMOTE 등) ─→ ⑤ 학습
│
├─ validation ─→ 변환기 transform만 ─→ 하이퍼파라미터 선택
│
└─ test ──────→ 변환기 transform만 ─→ 최종 1회 평가