점수가 너무 좋을 때는 먼저 의심해야 한다
머신러닝을 하다 보면 기분 좋은 순간이 찾아옵니다. 모델을 학습시켰더니 정확도가 99%가 나왔습니다. 회귀 모델의 오차가 거의 0에 가깝습니다. 검증 데이터에서도 성능이 놀랍도록 좋습니다. 이제 배포만 하면 될 것 같습니다.
그런데 실전에 올리자마자 모델이 무너집니다. 고객 이탈을 예측한다던 모델은 이미 이탈한 고객만 잘 맞히고, 스팸 메일 분류기는 실제 메일함에서는 엉뚱한 메일을 스팸으로 보냅니다. 병원 재입원 위험을 예측하던 모델은 테스트에서는 훌륭했지만, 실제 진료 시점에는 쓸 수 없는 정보를 몰래 보고 있었습니다.
이럴 때 가장 먼저 의심해야 할 문제가 있습니다. 바로 데이터 누수입니다.
데이터 누수는 모델이 학습 과정에서 "실전 예측 시점에는 알 수 없는 정보"를 사용해 버리는 문제입니다. 쉽게 말해, 시험을 보기 전에 정답지를 슬쩍 본 것과 비슷합니다. 시험장에서는 정답지를 볼 수 없는데, 연습할 때 정답지를 본 채로 문제를 풀었다면 연습 점수는 당연히 높게 나옵니다. 하지만 그것은 실력이 아닙니다.
머신러닝에서도 마찬가지입니다. 데이터 누수를 막지 못하면 모델의 점수는 화려해 보일 수 있습니다. 하지만 그 점수는 실제 세계에서 작동할 능력을 보여 주지 않습니다. 오히려 가장 위험한 모델은 낮은 점수의 모델이 아니라, 잘못된 이유로 높은 점수를 받은 모델입니다.

데이터 누수란 무엇인가
머신러닝 모델은 특징을 보고 예측합니다. 지금까지 우리는 이 흐름을 반복해서 보았습니다.
데이터를 준비하고, 특징을 만들고, 모델이 예측하고, 실제값과 비교하고, 오차를 줄인 뒤, 새로운 데이터에 적용합니다.
이 흐름에서 가장 중요한 질문은 이것입니다.
"이 특징은 실제 예측 시점에도 알 수 있는 정보인가?"
예를 들어 고객 이탈 예측 모델을 만든다고 해 봅시다. 목표는 "이 고객이 다음 달에 서비스를 해지할 것인가?"를 미리 예측하는 것입니다. 예측 시점이 5월 1일이라면, 모델은 5월 1일까지 알 수 있는 정보만 사용해야 합니다. 고객의 가입 기간, 최근 접속 횟수, 지난달 결제 여부, 고객 문의 횟수 같은 정보는 사용할 수 있습니다.
하지만 해지일, 해지 처리 완료 여부, 마지막 환불 금액, 해지 후 설문 응답 같은 정보는 사용할 수 없습니다. 왜냐하면 이런 정보는 고객이 이미 해지한 뒤에야 생기기 때문입니다. 모델이 이런 정보를 사용하면 "미래를 예측한 것"이 아니라 "이미 일어난 일을 확인한 것"에 가깝습니다.
데이터 누수는 보통 아주 노골적으로 나타나지 않습니다. 컬럼 이름이 정답이라고 되어 있으면 누구나 알아차릴 수 있습니다. 문제는 더 교묘한 경우입니다. 어떤 컬럼은 겉으로 보기에는 평범한 특징처럼 보이지만, 실제로는 정답과 거의 같은 정보를 담고 있습니다.
예를 들어 이탈여부를 예측하는데 해지상담완료여부라는 특징이 들어가 있다면 어떨까요? 해지 상담은 대개 고객이 해지를 결심했거나 해지 절차에 들어간 뒤 발생합니다. 모델은 고객의 행동 패턴을 학습한 것이 아니라, 해지 과정에서 생긴 흔적을 보고 정답을 맞힌 것입니다.
좋은 특징은 예측 시점에 존재해야 합니다. 나쁜 특징은 예측 시점에는 없고, 정답이 결정된 뒤에야 생깁니다. 데이터 누수의 핵심은 바로 이 시간 차이에 있습니다.
누수는 왜 무서운가
데이터 누수가 무서운 이유는 성능이 나빠지는 것이 아니라, 오히려 성능이 좋아 보인다는 데 있습니다. 과적합은 보통 학습 데이터 성능과 테스트 데이터 성능의 차이로 어느 정도 눈치챌 수 있습니다. 학습 데이터에서는 잘 맞지만 테스트 데이터에서는 성능이 떨어진다면 "아, 너무 외웠구나"라고 의심할 수 있습니다.
하지만 데이터 누수는 검증 데이터와 테스트 데이터까지 오염시킬 수 있습니다. 그러면 모델은 테스트에서도 좋은 점수를 받습니다. 그래서 더 위험합니다. 우리는 테스트 점수를 믿고 모델을 배포하지만, 실제 서비스에서는 그 정보가 사라져 버립니다.
마치 연습 경기와 본 경기가 전혀 다른 규칙으로 진행되는 것과 같습니다. 연습 때는 상대 팀의 작전표를 보고 경기를 했습니다. 그래서 완벽하게 움직였습니다. 그런데 본 경기에서는 작전표가 없습니다. 그 순간 실력의 실체가 드러납니다.
데이터 누수가 있으면 모델은 우리가 원한 패턴을 배우지 않습니다. 고객의 행동 신호를 배우는 대신 해지 처리 흔적을 배웁니다. 메일의 문장 특징을 배우는 대신 이미 사람이 붙여 놓은 스팸 판정 표시를 봅니다. 집값을 예측하는 대신 거래가 끝난 뒤 확정된 세금 정보를 봅니다.
이런 모델은 숫자로는 훌륭하지만, 현실에서는 쓸 수 없습니다.
타깃에서 파생된 특징
가장 대표적인 누수는 타깃 라벨에서 파생된 특징입니다. 타깃 라벨은 모델이 맞혀야 하는 정답입니다. 그런데 특징 안에 정답의 흔적이 들어가 있으면 모델은 문제를 푸는 것이 아니라 정답을 읽는 셈이 됩니다.
타이타닉 생존 예측을 예로 들어 보겠습니다. 목표는 승객의 정보를 보고 생존 여부를 예측하는 것입니다. 성별, 나이, 객실 등급, 동승 가족 수 같은 정보는 당시 승객에게 주어진 정보입니다. 하지만 만약 데이터에 구조보트탑승여부라는 특징이 들어 있다면 어떨까요? 구조보트에 탔다는 사실은 생존 여부와 매우 강하게 연결됩니다. 이 특징을 사용하면 모델은 생존 가능성을 예측하는 것이 아니라, 생존 결과에 가까운 정보를 사용하는 것입니다.
스팸 메일 분류에서도 비슷한 일이 생깁니다. 메일 본문, 발신자 도메인, 제목, 링크 개수는 사용할 수 있습니다. 그러나 관리자검토결과, 신고처리상태, 스팸함이동시간 같은 특징이 들어가면 누수가 발생할 수 있습니다. 이런 정보는 메일을 분류한 뒤에 생기거나, 이미 사람이 판단한 결과를 반영하기 때문입니다.
고객 이탈 예측에서는 특히 이런 컬럼이 자주 등장합니다. 예를 들어 해지사유, 해지요청일, 환불완료여부, 잔여포인트소멸일 같은 정보는 얼핏 업무 데이터처럼 보입니다. 하지만 이탈을 미리 예측하는 모델에서는 매우 위험합니다. 대부분은 이탈이 발생한 뒤에 기록되기 때문입니다.
특징 이름만 보고 판단하기 어렵다면 질문을 바꿔 보아야 합니다.
"이 컬럼은 정답을 알기 전에 만들어졌는가, 정답을 알게 된 뒤 만들어졌는가?"
정답을 알게 된 뒤 만들어진 정보라면 대부분 누수 후보입니다.
미래 정보가 섞이는 순간
두 번째로 흔한 누수는 미래 정보입니다. 미래 정보란 예측 시점 이후에 발생한 데이터가 특징에 섞이는 것을 말합니다.
매출 예측 모델을 생각해 봅시다. 오늘 아침에 다음 주 매출을 예측해야 합니다. 이때 사용할 수 있는 정보는 오늘 아침까지의 판매 기록, 재고, 가격, 광고 계획, 요일, 계절 정보 등입니다. 그런데 다음 주가 지난 뒤에 집계된 방문자 수, 실제 광고 클릭 수, 배송 지연 건수, 반품률을 특징으로 넣었다면 어떻게 될까요? 모델 성능은 좋아질 수 있습니다. 하지만 예측 시점에는 그런 정보를 알 수 없습니다.
시간이 포함된 데이터에서는 항상 "기준 시점"이 필요합니다. 기준 시점은 모델이 예측을 하는 날짜와 시간입니다. 고객 이탈이라면 "언제 이탈을 예측하는가"가 기준 시점입니다. 대출 연체 예측이라면 "대출 승인 시점에 예측하는가, 상환 시작 후 3개월 시점에 예측하는가"가 중요합니다. 질병 위험 예측이라면 "진료 전 접수 시점인가, 검사 결과가 나온 뒤인가"를 구분해야 합니다.
같은 컬럼이라도 기준 시점에 따라 사용할 수 있기도 하고, 사용할 수 없기도 합니다. 예를 들어 최근 30일 접속 횟수는 5월 1일에 예측한다면 4월 1일부터 4월 30일까지의 접속 횟수여야 합니다. 그런데 실수로 5월 전체 접속 횟수를 계산해 넣으면, 5월 말에야 알 수 있는 정보가 5월 1일 예측에 들어갑니다.
이런 누수는 집계 특징을 만들 때 자주 발생합니다. "최근 평균", "누적 횟수", "지난 7일 합계" 같은 특징을 만들 때, 날짜 필터를 잘못 잡으면 미래 데이터가 슬쩍 들어옵니다. 코드는 멀쩡해 보이고 컬럼 이름도 그럴듯하지만, 모델은 시간 여행을 하고 있는 셈입니다.
중복 샘플이 만드는 조용한 누수
세 번째 누수는 중복 샘플에서 생깁니다. 같은 데이터나 거의 같은 데이터가 학습 데이터와 테스트 데이터에 동시에 들어가면, 모델은 처음 보는 데이터에 대한 능력을 평가받는 것이 아닙니다. 이미 본 문제를 다시 푸는 것입니다.
이미지 분류를 예로 들어 보겠습니다. 같은 사진을 약간 잘라낸 이미지, 밝기만 조금 바꾼 이미지, 크기만 조정한 이미지가 여러 장 있다고 합시다. 이 이미지들이 무작위로 학습 데이터와 테스트 데이터에 나뉘면 테스트 성능은 높아질 수 있습니다. 하지만 모델이 새로운 이미지를 잘 이해한 것인지, 거의 같은 이미지를 기억한 것인지는 알 수 없습니다.
텍스트 데이터에서도 비슷합니다. 같은 스팸 메일이 제목만 조금 바뀌어 여러 번 들어갈 수 있습니다. 고객 리뷰 데이터에서는 같은 사용자가 같은 문장을 여러 상품에 반복해서 남기기도 합니다. 의료 데이터에서는 한 환자의 여러 방문 기록이 서로 다른 샘플로 들어갈 수 있습니다. 이때 한 환자의 일부 기록이 학습 데이터에, 다른 기록이 테스트 데이터에 들어가면 모델은 환자 고유의 정보를 기억해 버릴 수 있습니다.
이 문제는 추천 시스템이나 고객 예측에서도 자주 나타납니다. 같은 고객의 행동 기록이 여러 행으로 나뉘어 있을 때, 행 단위로 무작위 분할을 하면 한 고객이 학습과 테스트에 동시에 등장합니다. 그러면 모델은 "새로운 고객에게도 잘 작동하는가"가 아니라 "이미 본 고객의 다른 기록도 맞힐 수 있는가"를 평가받게 됩니다.
중복 누수를 막으려면 분할 단위를 생각해야 합니다. 행을 기준으로 나눌지, 고객을 기준으로 나눌지, 상품을 기준으로 나눌지, 시간 구간을 기준으로 나눌지 결정해야 합니다. 모델이 실제로 만날 상황이 "새로운 행"인지 "새로운 고객"인지 "미래 기간"인지에 따라 평가 방법이 달라집니다.
전처리 순서가 잘못될 때 생기는 누수
데이터 누수는 특징 자체에서만 생기지 않습니다. 전처리 과정에서도 자주 발생합니다.
가장 흔한 실수는 데이터를 먼저 전처리한 뒤 학습 데이터와 테스트 데이터로 나누는 것입니다. 겉으로 보기에는 효율적인 순서처럼 보입니다. 전체 데이터를 한 번에 정리하고, 결측치를 채우고, 스케일을 맞추고, 범주형 값을 숫자로 바꾼 뒤 나누면 깔끔해 보입니다.
하지만 이 순서에는 함정이 있습니다. 테스트 데이터의 정보가 전처리 규칙을 만드는 데 사용될 수 있기 때문입니다.
예를 들어 키와 몸무게 같은 숫자 특징을 표준화한다고 합시다. 표준화는 보통 평균을 빼고 표준편차로 나누는 방식입니다. 만약 전체 데이터의 평균과 표준편차를 계산한 뒤 학습 데이터와 테스트 데이터로 나누면, 테스트 데이터의 분포가 이미 평균과 표준편차 계산에 들어갑니다. 모델이 테스트 데이터의 정답을 직접 본 것은 아니지만, 테스트 데이터의 통계 정보를 미리 본 셈입니다.
결측치 처리도 마찬가지입니다. 전체 데이터의 평균으로 결측치를 채운 뒤 분할하면 테스트 데이터의 정보가 학습 과정에 섞일 수 있습니다. 범주형 인코딩, PCA 같은 차원 축소, 특징 선택도 마찬가지입니다. 특히 타깃 값을 이용하는 인코딩이나 특징 선택은 더 위험합니다. 전체 데이터의 라벨 정보를 사용해 특징을 고른 뒤 테스트를 하면, 테스트 라벨을 간접적으로 본 것과 비슷해집니다.
올바른 순서는 반대입니다.
먼저 데이터를 학습 데이터, 검증 데이터, 테스트 데이터로 나눕니다. 그다음 학습 데이터에서만 전처리 규칙을 배웁니다. 그리고 그 규칙을 검증 데이터와 테스트 데이터에 그대로 적용합니다.
여기서 중요한 단어는 fit과 transform입니다. fit은 전처리 규칙을 배우는 과정입니다. 평균을 계산하고, 표준편차를 계산하고, 어떤 범주가 있는지 파악하고, PCA의 축을 찾는 일이 fit입니다. transform은 이미 배운 규칙을 적용하는 과정입니다.
테스트 데이터에는 fit을 하면 안 됩니다. 테스트 데이터에는 transform만 해야 합니다.
이 원칙 하나만 기억해도 많은 누수를 막을 수 있습니다.
| 작업 | 잘못된 방식 | 안전한 방식 |
|---|---|---|
| 결측치 채우기 | 전체 데이터 평균으로 채운 뒤 분할 | 학습 데이터 평균을 구해 검증·테스트에 적용 |
| 스케일링 | 전체 데이터로 평균과 표준편차 계산 | 학습 데이터로만 계산한 규칙을 적용 |
| 범주형 인코딩 | 전체 데이터의 범주를 미리 확인 | 학습 데이터에서 인코더를 학습하고 나머지에 적용 |
| 특징 선택 | 전체 라벨을 보고 중요한 특징 선택 | 학습 데이터 안에서만 특징 선택 수행 |
샘플링과 증강도 누수가 될 수 있다
분류 문제에서는 클래스 불균형을 다루기 위해 샘플링을 하기도 합니다. 예를 들어 사기 거래 탐지에서는 정상 거래가 훨씬 많고 사기 거래는 아주 적습니다. 이때 적은 클래스를 복제하거나, 비슷한 가짜 샘플을 만들어 균형을 맞추는 방법을 사용할 수 있습니다.
그런데 이 작업을 데이터 분할 전에 해 버리면 문제가 생깁니다. 복제된 샘플이나 매우 비슷한 샘플이 학습 데이터와 테스트 데이터에 동시에 들어갈 수 있기 때문입니다. 모델 입장에서는 테스트 데이터가 낯설지 않습니다. 이미 거의 같은 데이터를 학습 중에 봤기 때문입니다.
이미지 증강도 마찬가지입니다. 원본 이미지를 회전하거나 밝기를 바꾼 뒤 전체 증강 데이터를 무작위로 나누면, 원본은 학습 데이터에 들어가고 살짝 바뀐 버전은 테스트 데이터에 들어갈 수 있습니다. 그러면 테스트 점수가 실제보다 높아집니다.
안전한 원칙은 간단합니다. 먼저 나누고, 그다음 학습 데이터에만 샘플링이나 증강을 적용합니다. 검증 데이터와 테스트 데이터는 가능한 한 실제 세계에서 들어올 데이터의 모습을 유지해야 합니다. 그래야 평가 점수가 현실을 닮습니다.
예측 시점의 정보 흐름을 그려 보기
데이터 누수를 막는 가장 좋은 방법은 모델을 코드가 아니라 업무 흐름 속에서 보는 것입니다. 모델은 어느 날 갑자기 공중에 떠 있는 데이터프레임을 받지 않습니다. 실제 서비스에서는 정보가 시간 순서대로 만들어지고, 저장되고, 처리됩니다.
고객 이탈 예측을 다시 생각해 봅시다. 모델이 매월 1일 아침에 다음 30일 안에 이탈할 고객을 예측한다고 합시다. 그렇다면 매월 1일 아침 이전에 생성된 데이터만 특징으로 사용할 수 있습니다. 5월 이탈을 예측한다면 4월 말까지의 행동 기록이 입력이고, 5월 중 실제 이탈 여부가 라벨입니다.
이 구조를 명확히 하면 많은 것이 선명해집니다. 4월 접속 횟수는 사용할 수 있습니다. 5월 접속 횟수는 사용할 수 없습니다. 4월 고객센터 문의 횟수는 사용할 수 있습니다. 5월 해지 상담 여부는 사용할 수 없습니다. 5월 20일 환불 완료 여부는 당연히 사용할 수 없습니다.
모델을 만들기 전에 다음 네 가지 시점을 구분해 두면 좋습니다.
관측 기간: 특징을 만들기 위해 데이터를 모으는 기간입니다. 예를 들어 4월 1일부터 4월 30일까지의 행동 기록입니다.
예측 시점: 모델이 실제로 예측을 수행하는 순간입니다. 예를 들어 5월 1일 오전 9시입니다.
예측 대상 기간: 모델이 맞히려는 일이 발생하는 기간입니다. 예를 들어 5월 1일부터 5월 31일까지의 이탈 여부입니다.
라벨 확정 시점: 정답을 확정할 수 있는 시점입니다. 예를 들어 6월 1일에 5월 이탈 여부를 확정합니다.
이 네 가지를 정리하면 특징과 라벨이 섞이는 일을 줄일 수 있습니다. 특히 시간 기반 예측 문제에서는 이 구분이 거의 필수입니다.
특징마다 "사용 가능 시간"을 붙인다
실전 프로젝트에서는 특징이 수십 개, 많게는 수백 개가 됩니다. 이때 모든 특징을 머릿속으로만 점검하기는 어렵습니다. 그래서 각 특징에 "언제 알 수 있는 정보인가"를 붙여 보는 방법이 유용합니다.
예를 들어 고객 이탈 모델의 특징 목록을 만든다고 합시다.
| 특징 | 의미 | 예측 시점에 사용 가능한가 | 점검 메모 |
|---|---|---|---|
| 가입 후 경과일 | 가입일부터 예측일까지의 일수 | 가능 | 예측일 기준으로 계산 |
| 최근 30일 접속 횟수 | 예측일 전 30일간 접속 수 | 가능 | 미래 날짜 포함 금지 |
| 최근 결제 실패 횟수 | 예측일 전 결제 실패 수 | 가능 | 집계 기간 확인 |
| 해지요청일 | 고객이 해지를 요청한 날짜 | 대부분 불가 | 라벨 이후 정보일 수 있음 |
| 환불완료여부 | 환불 처리 여부 | 불가 | 이탈 이후 발생 가능 |
| 다음 달 사용량 | 예측 대상 기간의 사용량 | 불가 | 미래 정보 |
이런 표를 만들면 누수 후보가 빠르게 드러납니다. 특히 업무 데이터에서는 컬럼 이름만으로는 위험성을 알기 어렵습니다. 상태코드, 처리단계, 완료일, 최종등급 같은 이름은 도메인을 모르면 평범해 보입니다. 하지만 실제로는 정답이 정해진 뒤 갱신되는 컬럼일 수 있습니다.
그래서 좋은 머신러닝 프로젝트에서는 데이터 설명서가 중요합니다. 각 컬럼이 무엇을 의미하는지, 언제 생성되는지, 어떤 시스템에서 들어오는지, 나중에 수정될 수 있는지 알아야 합니다. 모델 성능은 알고리즘만으로 결정되지 않습니다. 데이터가 언제 어떻게 만들어졌는지 이해하는 능력이 성능을 지킵니다.
누수를 의심해야 하는 신호들
데이터 누수는 항상 쉽게 보이지 않습니다. 하지만 몇 가지 신호가 있습니다. 이런 신호가 나타나면 "모델이 정말 제대로 배운 걸까?"라고 질문해야 합니다.
검증 성능이 비현실적으로 높다.
간단한 모델인데도 복잡한 문제를 거의 완벽하게 맞힌다.
특정 특징 하나만으로 성능이 지나치게 높게 나온다.
실제 배포 후 성능이 검증 점수보다 크게 떨어진다.
시간 순서로 나누면 성능이 급격히 낮아진다.
중요한 특징을 살펴보니 정답 이후에 생기는 업무 컬럼이 상위에 있다.
물론 성능이 높다고 무조건 누수는 아닙니다. 문제 자체가 쉬울 수도 있고, 데이터 품질이 아주 좋을 수도 있습니다. 하지만 현실의 복잡한 문제에서 너무 완벽한 점수가 나온다면 기뻐하기 전에 먼저 확인해야 합니다.
특히 특징 중요도를 확인했을 때 이상한 컬럼이 상위에 있으면 주의해야 합니다. 예를 들어 고객 이탈 예측에서 가장 중요한 특징이 해지처리담당자ID라면 이상합니다. 스팸 분류에서 가장 중요한 특징이 검수완료시간이라면 이상합니다. 집값 예측에서 실거래신고완료일이 강력한 특징이라면 예측 시점에 사용할 수 있는지 확인해야 합니다.
모델은 도덕성이 없습니다. 주어진 데이터 안에서 점수를 올릴 수 있는 지름길이 있으면 그 지름길을 택합니다. 그래서 사람인 우리가 길을 정리해 주어야 합니다.
안전한 데이터 분할 전략
누수를 막으려면 데이터 분할 방식도 문제에 맞게 선택해야 합니다. 무작위 분할은 간단하고 자주 쓰이지만, 모든 문제에 안전한 것은 아닙니다.
시간이 중요한 문제라면 시간 순서대로 나누는 것이 좋습니다. 과거 데이터로 학습하고 미래 데이터로 검증해야 실제 배포 상황과 비슷합니다. 예를 들어 1월부터 9월까지 데이터로 학습하고, 10월 데이터로 검증하고, 11월 데이터로 테스트하는 식입니다. 이렇게 하면 미래 정보가 과거 학습에 섞이는 위험을 줄일 수 있습니다.
같은 사용자나 같은 상품이 여러 행에 등장하는 문제라면 그룹 단위로 나누어야 합니다. 고객 이탈 모델에서 한 고객의 여러 월별 기록이 있다면, 목적에 따라 고객 단위 분할이 필요할 수 있습니다. 의료 데이터에서는 같은 환자의 기록이 학습과 테스트에 동시에 들어가지 않도록 환자 단위로 나누어야 하는 경우가 많습니다.
데이터가 매우 불균형한 분류 문제에서는 라벨 비율도 고려해야 합니다. 다만 라벨 비율을 맞추는 것과 누수를 막는 것은 별개의 문제입니다. 라벨 비율을 맞추더라도 시간 순서나 그룹 경계를 깨면 누수가 생길 수 있습니다.
분할 전략은 단순히 통계적인 선택이 아닙니다. 모델이 실제로 사용될 상황을 흉내 내는 장치입니다. 평가 데이터는 모델에게 "미래에 만나게 될 낯선 세계"의 예고편이어야 합니다.
파이프라인으로 실수를 줄인다
실전에서는 전처리 단계가 많습니다. 결측치를 채우고, 숫자 값을 스케일링하고, 범주형 값을 인코딩하고, 새로운 특징을 만들고, 모델을 학습합니다. 사람이 매번 순서를 기억해서 처리하면 실수하기 쉽습니다.
이때 파이프라인이라는 방식이 도움이 됩니다. 파이프라인은 전처리와 모델 학습을 하나의 흐름으로 묶어 놓는 방법입니다. 핵심은 학습 데이터에서 배운 전처리 규칙을 검증 데이터나 테스트 데이터에 일관되게 적용하도록 만드는 것입니다.
파이프라인을 사용하면 다음과 같은 실수를 줄일 수 있습니다.
테스트 데이터에 실수로 fit을 다시 수행하는 문제를 줄인다.
교차검증마다 전처리 규칙이 올바르게 학습 폴드 안에서만 만들어지게 한다.
배포 환경에서도 학습 때와 같은 전처리 순서를 재사용할 수 있다.
어떤 전처리가 어떤 모델과 함께 쓰였는지 기록하기 쉬워진다.
물론 파이프라인이 모든 누수를 자동으로 막아 주지는 않습니다. 예측 시점 이후에 생긴 컬럼을 넣었다면 파이프라인도 그 컬럼을 그대로 사용합니다. 하지만 적어도 전처리 순서에서 생기는 누수는 크게 줄일 수 있습니다.
좋은 파이프라인은 모델을 더 똑똑하게 만드는 마법이 아닙니다. 모델이 공정한 시험을 보도록 시험장을 정리하는 장치에 가깝습니다.
실전 점검 질문
데이터 누수를 막으려면 모델을 학습하기 전에 질문을 던져야 합니다. 좋은 질문은 복잡한 수식보다 강력할 때가 많습니다.
먼저 각 특징에 대해 물어봅니다. 이 정보는 예측 시점에 실제로 존재하는가? 이 정보는 정답이 결정되기 전에 만들어지는가? 이 정보는 라벨을 직접 또는 간접적으로 포함하지 않는가? 집계 특징이라면 예측 시점 이후의 데이터가 섞이지 않았는가?
그다음 데이터 분할을 점검합니다. 같은 고객, 같은 환자, 같은 문서, 같은 이미지의 변형이 학습과 테스트에 동시에 들어가 있지 않은가? 시간 기반 문제인데 무작위로 섞어 버리지는 않았는가? 샘플링이나 증강을 분할 전에 수행하지는 않았는가?
마지막으로 전처리 순서를 확인합니다. 결측치 처리, 스케일링, 인코딩, PCA, 특징 선택을 전체 데이터에 대해 먼저 수행하지 않았는가? 테스트 데이터에 fit을 수행하지 않았는가? 교차검증 안에서도 전처리가 각 학습 폴드 안에서만 학습되는가?
이 질문들은 귀찮아 보이지만, 실제로는 모델을 살리는 질문입니다. 데이터 누수는 뒤늦게 발견할수록 비용이 커집니다. 이미 모델을 배포한 뒤 누수가 발견되면 성능 재평가, 데이터 재구성, 모델 재학습, 서비스 수정이 모두 필요할 수 있습니다.
높은 점수보다 믿을 수 있는 점수
머신러닝을 처음 배울 때 우리는 자연스럽게 높은 점수를 원합니다. 정확도 90%보다 95%가 좋아 보이고, 오차 10보다 5가 좋아 보입니다. 하지만 좋은 모델을 만드는 사람은 점수 자체보다 먼저 묻습니다.
"이 점수는 믿을 수 있는가?"
믿을 수 없는 99%보다 믿을 수 있는 80%가 낫습니다. 믿을 수 없는 점수는 우리를 잘못된 결정으로 이끕니다. 반대로 믿을 수 있는 점수는 부족하더라도 개선할 방향을 알려 줍니다.
데이터 누수를 막는 일은 모델의 성능을 일부러 낮추는 일이 아닙니다. 거품을 걷어 내는 일입니다. 모델이 실제로 얼마나 배웠는지, 새로운 데이터 앞에서 얼마나 버틸 수 있는지 확인하는 일입니다.
지금까지 우리는 좋은 특징을 만들고, 학습 데이터와 테스트 데이터를 나누고, 검증 데이터와 교차검증으로 모델을 비교하는 방법을 배웠습니다. 이번 장에서는 그 모든 과정이 공정해야 한다는 사실을 확인했습니다. 모델은 정답지를 보지 않아야 하고, 미래를 미리 보지 않아야 하며, 테스트 문제를 연습 중에 만나지 않아야 합니다.
다음 장에서는 이렇게 깨끗하게 준비된 실험 환경 위에서 여러 모델을 어떻게 선택하고 관리할지 살펴봅니다. 모델을 바꿔 보고, 하이퍼파라미터를 조정하고, 실험 결과를 비교하는 과정은 머신러닝 실전의 핵심입니다. 하지만 그 출발점은 언제나 같습니다.
정확한 비교는 깨끗한 데이터에서 시작됩니다.
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.