Post

당뇨 합병증 예측 - 데이터 분할 전 SMOTE 적용은 왜 위험한가?

당뇨 합병증 예측 - 데이터 분할 전 SMOTE 적용은 왜 위험한가?

1. 문제 정의

의료 데이터 분석에서 클래스 불균형 문제는 매우 흔합니다. 예를 들어, 합병증이 있는 환자와 없는 환자의 비율이 5:95와 같이 심각하게 불균형할 수 있습니다. 이런 경우, SMOTE(Synthetic Minority Over-sampling Technique)와 같은 오버샘플링 기법을 사용하여 데이터 균형을 맞추는 것이 일반적입니다. 하지만, 데이터 분할 전에 SMOTE를 적용하는 것은 심각한 문제를 일으킬 수 있습니다.

2. 데이터 누출(Data Leakage)이란?

2.1 데이터 누출의 정의

데이터 누출은 테스트 데이터의 정보가 모델 훈련 과정에 유출되는 현상을 말합니다. 이는 모델이 테스트 데이터를 “미리 암기”하게 만들어, 과적합(Overfitting)을 유발하고, 모델의 실제 성능을 왜곡시킵니다.

2.2 SMOTE 적용 시 데이터 누출 발생

데이터 분할 전에 SMOTE를 적용하면, 테스트 데이터와 훈련 데이터가 동일한 합성 데이터를 공유하게 됩니다. 이는 모델이 테스트 데이터의 정보를 간접적으로 학습하게 되어, 평가 결과가 허위로 좋아지는 문제를 일으킵니다.

  1. 문제점:
    전체 데이터를 먼저 균형 맞추고 분할하면, 테스트 데이터 정보가 훈련 과정에 유출됩니다.
    예를 들어 SMOTE로 생성한 합성 데이터가 훈련/테스트 세트 모두에 포함될 수 있습니다.

  2. 결과:
    모델이 테스트 데이터를 “미리 암기”하게 되어 과적합(Overfitting) 발생 → 실전에서 성능 급락.

2.3 비현실적인 평가 환경

  1. 문제점: 실제 의료 현장은 불균형 데이터가 기본입니다. 테스트 세트를 인위적으로 균형 있게 만들면 모델의 실제 성능을 평가할 수 없습니다.

  2. 예시: 합병증 발생률이 5%인 데이터에서 테스트 세트를 50:50으로 만들면, 위양성/위음성 비율이 현실과 완전히 달라집니다.

2.4 통계적 독립성 위반

  1. 문제점: 데이터 분할 전 오버샘플링하면 훈련/테스트 데이터가 동일한 패턴을 공유하게 됩니다.

  2. 결과: 교차검증 시 과도하게 낙관적인 결과가 도출됩니다.

3. 올바른 데이터 처리 절차

A[원본 데이터] --> B[훈련/테스트 분할] B --> C[훈련 데이터만 균형 조정] B --> D[테스트 데이터는 원본 유지]

3.1 계층화 분할(Stratified Split)

클래스 비율을 유지하며 데이터를 분할합니다. 이는 불균형 데이터를 다룰 때 매우 중요합니다.

library(caret) set.seed(300) train_index <- createDataPartition(data_long$Complication, p = 0.7, list = FALSE, strata = data_long$Complication) # 클래스 비율 유지 ### 3.2 훈련 데이터만 균형 조정 훈련 데이터에만 SMOTE를 적용하고, 테스트 데이터는 원본 상태로 유지합니다.
# 훈련 데이터에만 SMOTE 적용 smote_train <- SMOTE(train_data[,-1], train_data$Complication) train_balanced <- smote_train$data # 테스트 데이터는 원본 상태 유지 test_balanced <- test_data # 전처리만 적용

4. 두 방법의 비교 시나리오

구분 올바른 방법 잘못된 방법 훈련 데이터 70% 원본 → SMOTE 적용 100% 원본 → SMOTE → 70% 선택 테스트 데이터 30% 원본 (불균형 유지) 30% SMOTE 데이터 (균형 인위 조성) 평가 신뢰도 실제 환경 반영 (높음) 가상 환경 반영 (낮음) 데이터 누출 없음 발생

5. 심각한 경우의 수: SMOTE 후 분할의 폐해

  1. 동일 환자 데이터가 훈련/테스트 세트에 동시 존재
  • 원본 데이터가 100명이고 SMOTE로 200명으로 증강 후 70:30 분할하면,
  • 원본 30명 + SMOTE 170명이 테스트 세트에 포함될 수 있음 → 데이터 중복 평가
  1. Time Series 데이터에서 완전한 재앙
  • 환자 추적 관찰 데이터에서 미래 시점 테스트 데이터가 훈련에 사용되는 꼴

6. 현실적인 불균형 대처법

6.1 평가 지표 변경

정확도 대신 F1-Score, AUC-ROC와 같은 불균형 데이터에 적합한 지표를 사용합니다.

library(pROC) roc_curve <- roc(test_data$Complication, pred_prob)

6.2 Cost-Sensitive Learning

클래스 가중치를 부여하여, 합병증이 있는 환자를 더 중요하게 다룹니다.

# 클래스 가중치 부여 (의료 현장에 맞춰 위음성 패널티 강화) class_weights <- ifelse(train_data$Complication == 1, 10, 1) # 합병증 10배 중요

6.3 임상적 특징 공학

의사와 협업하여 의미 있는 특징 조합을 생성합니다.

# 예: 고혈압 + 고혈당 조합 특징 train_data <- train_data %>% mutate(HighRisk = ifelse(SBP_group == "≥130" & FBS_group == "≥100", 1, 0))

7. 의료 데이터 분석 시 주의사항

False Negative 최소화:

합병증 예측에서는 위음성(False Negative)이 가장 위험

SMOTE 대신 Cost-Sensitive Learning 적용 고려

임상적 타당성 검증:

SMOTE로 생성된 가상 환자 데이터가 의학적으로 의미 있는지 전문가 검증 필수

예: “70대 고혈압 환자” 특성을 가진 샘플 생성 시 현실성 확인

계층화 샘플링(Stratified Sampling):

초기 데이터 분할 시 불균형 비율 유지하며 분할

8. 성능 향상을 위한 추가 전략

대체 불균형 처리 기법:

# ROSE(Random Over-Sampling Examples) library(ROSE) train_balanced <- ROSE(Complication ~ ., data = train_data)$data # ADASYN(Adaptive Synthetic Sampling) library(imbalance) train_balanced <- adasyn(train_data, k = 5, ratio = 0.8)

앙상블 기법 활용:

# EasyEnsemble library(e1071) ensemble_model <- bagging(Complication ~ ., data = train_balanced, nbagg = 50)

비용 민감 학습(Cost-Sensitive Learning):

# 가중치 부여 class_weights <- ifelse(train_balanced$Complication == 1, 5, 1) # 5:1 가중치 weighted_model <- randomForest(Complication ~ ., data = train_balanced, classwt = class_weights)

9. 결론

“테스트 데이터는 절대 훈련 데이터 처리 파이프라인에 노출시키지 말 것!” 의료 AI 모델의 신뢰성은 엄격한 데이터 분리에서 시작됩니다. 데이터 분할 전에 SMOTE를 적용하는 것은 데이터 누출을 유발하며, 모델의 실제 성능을 왜곡시킬 수 있습니다. 따라서, 훈련 데이터에만 SMOTE를 적용하고, 테스트 데이터는 원본 상태로 유지하는 것이 중요합니다.

This post is licensed under CC BY 4.0 by the author.