본문 바로가기

개발 공부/머신러닝

[Machine Learning] 데이터 누수란?

모델의 검증 점수가 높다고 해서 반드시 좋은 모델은 아니다.
학습 과정에 테스트 데이터나 정답 정보가 섞이면 점수는 높게 나오지만, 새로운 데이터에서는 성능이 크게 떨어질 수 있다.
이번 글에서는 데이터 누수가 생기는 대표적인 상황과 방지 방법을 코드로 정리한다.

 

1. 데이터 누수란?

데이터 누수(Data Leakage)는 학습에 사용하면 안 되는 정보가 피처, 전처리 과정 또는 데이터 분할 과정에 들어가는 현상이다.
쉽게 말하면 시험 문제를 풀기 전에 정답이나 시험지의 특징을 미리 본 것과 같다.

겉으로 보이는 결과 검증 정확도가 비정상적으로 높고 학습도 잘된 것처럼 보인다.

실제 결과 배포 후 처음 보는 데이터에서는 같은 정보를 얻을 수 없기 때문에 성능이 급격히 떨어진다. = 과대적합

 

2. 데이터 누수의 대표적인 두 종류


1) 전처리 누수

학습 데이터와 테스트 데이터를 나누기 전에 전체 데이터로 평균, 스케일, 범주 목록 같은 기준을 계산하면
테스트 데이터의 정보가 학습 과정에 섞인다.

  • 전체 데이터의 평균으로 결측값을 채움
  • 전체 데이터에 StandardScaler를 fit함
  • 전체 데이터로 인코더의 범주 목록을 학습함
  • 전체 데이터에서 피처를 선택한 뒤 분리함


2) 타겟 누수

예측할 정답과 직접 연결되거나 예측 시점에 아직 존재하지 않는 정보를 피처로 사용하는 경우다.

  • 대출 연체 예측에 연체 처리 완료 여부를 사용
  • 환자 질병 예측에 최종 진단 결과를 사용
  • 고객 이탈 예측에 해지 완료 날짜를 사용
  • 타겟 평균 인코딩을 전체 데이터에서 계산

판단 질문 이 값은 실제 예측을 실행하는 그 시점에도 알 수 있는가? 답이 아니면 타겟 누수일 가능성이 높다.

 

3. 잘못된 전처리 순서와 올바른 순서

잘못된 흐름 전체 데이터로 전처리 기준 계산 → 학습·테스트 분리 → 모델 학습

올바른 흐름 학습·테스트 분리✅ → 학습 데이터로만 fit → 학습·테스트에 transform → 모델 학습·평가

 

4. 결측값 처리에서 생기는 누수


잘못된 코드

# 전체 데이터의 평균에 테스트 정보까지 포함됨
df['age'] = df['age'].fillna(df['age'].mean())

X_tr, X_te, y_tr, y_te = train_test_split(X, y)

왜 문제인가: 테스트 데이터의 age 값이 전체 평균 계산에 사용된다.
아주 작은 정보라도 모델 개발 과정에 들어오면 공정한 평가가 아니다.


올바른 코드

from sklearn.impute import SimpleImputer

X_tr, X_te, y_tr, y_te = train_test_split(
X, y, test_size=0.2, random_state=42
)

imputer = SimpleImputer(strategy='median')
X_tr[['age']] = imputer.fit_transform(X_tr[['age']])
X_te[['age']] = imputer.transform(X_te[['age']])

fit 학습 데이터에서 중앙값 같은 전처리 기준을 계산한다.

transform 이미 계산한 기준으로 데이터를 변환한다. 테스트 데이터에서는 transform만 사용한다.

 

5. 스케일링에서 생기는 누수

잘못된 코드

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
X_scaled = scaler.fit_transform(X) # 전체 데이터로 평균·표준편차 계산
X_tr, X_te, y_tr, y_te = train_test_split(X_scaled, y)


올바른 코드

X_tr, X_te, y_tr, y_te = train_test_split(
X, y, test_size=0.2, random_state=42
)

scaler = StandardScaler()
X_tr_scaled = scaler.fit_transform(X_tr)
X_te_scaled = scaler.transform(X_te)

평균과 표준편차도 데이터에서 학습한 정보다. 따라서 스케일러 역시 학습 데이터에만 fit한다.

 

6. 인코딩에서 생기는 누수

원-핫 인코딩도 학습 데이터에서 범주와 출력 컬럼을 정한 뒤 테스트 데이터에는 같은 규칙을 적용해야 한다.

import category_encoders as ce

encoder = ce.OneHotEncoder(
cols=cat_cols,
use_cat_names=True,
handle_unknown='value',
handle_missing='value'
)

X_tr_encoded = encoder.fit_transform(X_tr)
X_te_encoded = encoder.transform(X_te)

  • 테스트 데이터에 fit_transform()을 다시 사용하지 않음
  • 학습·테스트의 최종 컬럼 이름과 순서가 같은지 확인
  • 새로운 범주가 들어올 때의 처리 방법을 미리 지정

 

7. 타겟 인코딩은 특히 조심하기

Mean Encoding이나 Target Encoding은 각 범주의 타겟 평균을 피처로 사용한다.
정답을 이용하는 방식이므로 전체 데이터에서 평균을 계산하면 누수가 매우 크게 발생한다.

교차검증의 각 학습 폴드 안에서만 타겟 통계를 계산하고, 해당 검증 폴드에는 학습한 통계만 적용한다.

  • 검증 폴드의 정답을 인코딩 계산에 사용하지 않는다.
  • 범주별 표본 수가 적으면 평균이 불안정하므로 스무딩을 고려한다.
  • 가능하면 Pipeline 또는 검증 내부에서 동작하는 인코더를 사용한다.

 

8. 시간 데이터에서 생기는 미래 정보 누수

시간 순서가 있는 데이터는 무작위로 나누면 미래 데이터가 과거 학습에 섞일 수 있다.
주가, 판매량, 센서, 고객 행동처럼 시간이 중요한 문제에서는 과거로 학습하고 미래로 평가해야 한다.

# 시간순 정렬 후 과거와 미래를 분리
df = df.sort_values('date')
cut = int(len(df) * 0.8)
train = df.iloc[:cut]
test = df.iloc[cut:]

예시: 다음 달 판매량을 예측하면서 다음 달에 집계된 할인율이나 판매 완료 수를 피처로 사용하면 미래 정보 누수다.

 

9. 중복 데이터도 누수가 될 수 있다

같은 행이나 거의 동일한 사용자의 기록이 학습과 테스트에 동시에 들어가면,
모델이 일반화한 것이 아니라 이미 본 대상을 기억해 높은 점수를 낼 수 있다.

  • 완전 중복 행을 데이터 분할 전에 확인
  • 한 사람에게 여러 기록이 있다면 사람 단위로 그룹을 나눔
  • 이미지 증강본이나 같은 장비·환자의 반복 측정값도 그룹 기준 분리를 검토

예시 환자 진단 모델에서 같은 환자의 검사 기록이 학습과 테스트에 나뉘면 환자 특성을 외워 평가 점수가 부풀 수 있다.

 

10. Pipeline으로 누수 줄이기

scikit-learn Pipeline을 사용하면 교차검증마다 전처리 단계가 학습 폴드에만 fit되므로,
수작업 순서에서 생기는 실수를 줄일 수 있다.

from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression

pipe = Pipeline([
('imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler()),
('model', LogisticRegression(max_iter=1000))
])

pipe.fit(X_tr, y_tr)
score = pipe.score(X_te, y_te)
print(score)

장점 결측값 처리 → 스케일링 → 모델 학습 순서가 하나로 묶이고,
테스트 데이터에는 학습된 단계가 자동으로 적용된다.

 

11. 데이터 누수 점검 체크리스트

  • 학습·테스트 분리 전에 데이터에서 통계값을 계산하지 않았는가?
  • imputer, scaler, encoder는 X_tr에만 fit했는가?
  • X_te에는 transform만 사용했는가?
  • 피처에 타겟 또는 타겟과 사실상 같은 정보가 포함되지 않았는가?
  • 모든 피처가 실제 예측 시점에 이용 가능한 정보인가?
  • 시간 데이터는 과거와 미래 순서에 맞게 분리했는가?
  • 동일한 사람·장비·이미지의 기록이 학습과 테스트에 동시에 들어가지 않았는가?
  • 피처 선택과 타겟 인코딩을 교차검증 바깥에서 수행하지 않았는가?
  • 점수가 지나치게 높다면 누수 가능성을 먼저 의심했는가?

 

정리

핵심 원칙 먼저 데이터를 나눈다. 전처리 기준은 학습 데이터에서만 배운다. 테스트 데이터는 마지막 평가 전까지 모르는 데이터로 남겨둔다.

데이터 누수는 코드가 정상 실행되기 때문에 발견하기 더 어렵다. 따라서 높은 점수만 확인하지 말고, 각 피처를 실제 예측 시점에 얻을 수 있는지와 모든 fit 작업이 학습 데이터 안에서만 이루어졌는지를 확인해야 한다.

반응형