최근 예측 모델링 프로젝트를 검토하면서 데이터 누수(Data Leakage)에 대해 다시 생각해보는 기회가 있었다. 코드를 검토하면서 정보 누수가 의심되는 지점들을 발견했다. 그 과정을 겪으며 떠올린 생각들을 정리해두려 한다.
데이터 모델링은 마치 복잡한 미로를 탐험하는 것과 같다. 각 데이터 포인트는 갈림길이고, 우리는 그 중 하나를 선택해 나아간다. 하지만 어느 순간 너무도 매끄럽게 풀리는 길을 만나면 의심해야 한다. 그 길은 우연히 발견된 지름길일 수도 있지만, 사실은 미래의 정보를 몰래 들여다본 정보 누수의 흔적일지도 모른다.
- 정보 누수란?
- 인코딩 과정에서의 정보 누수
- 정보 누수를 방지하기 위한 전략
- 결론
정보 누수는 모델이 학습 과정에서 예측 시점에 사용할 수 없는 정보를 부적절하게 사용하는 경우를 말한다. 이는 마치 시험 전에 답안을 미리 보는 것과 같아서, 실제 상황에서 모델의 성능을 왜곡시키는 원인이 된다.
정보 누수는 다양한 방식으로 발생하는데 그 중 몇 가지 대표적인 사례를 살펴보려 한다.
특징 누수는 모델이 학습할 때, 예측 시점에서는 사용할 수 없는 정보를 포함하는 변수를 사용할 경우 발생한다. 예를 들어, 환자의 생존 여부를 예측하는 모델에 '사망 여부'라는 변수가 포함되어 있다면, 이는 명백한 누수다. 모델이 실제로 예측해야 할 정답을 이미 알고 있는 셈이기 때문이다.
이번 프로젝트에서도 비슷한 일이 있었다. 차년도 예측을 위한 파생변수를 생성하는 과정에서, train/test 데이터를 분리하기 전에 평균 금액을 기반으로 변수를 만든 것이다. 그 당시에는 자연스러운 흐름처럼 보였지만, 사실상 미래의 정보를 과거에 반영한 셈이었다.
이후에는 해당 변수를 차년도 예측 전용으로 따로 관리했지만, 또 다른 문제가 발생했다. 미래 데이터에서는 알 수 없는 변수임에도 불구하고 input에 포함시키려다 보니, 실제 운영 환경에서 매년 모델을 재학습하거나 변수를 업데이트하기 어려운 구조가 되어버린 것이다.
이 경험을 통해, 예측 모델을 설계할 때는 무엇보다도 예측의 목적을 명확히 정의하고, 모델이 사용하는 변수들이 실제 운영 환경에서도 지속적으로 확보 가능한 정보여야 한다는 것을 깨달았다. 결국, 초기 세팅이 가장 중요하다는 사실을 다시 한 번 깊이 인식했다.
학습 예제 누수는 데이터의 행 간에 정보가 부적절하게 공유될 때 발생한다. 예를 들어, 교차 검증이나 훈련/테스트 분할 이전에 전처리를 수행하거나, 훈련·검증·테스트 세트 사이에 중복된 샘플이 존재할 경우 발생할 수 있다. 이런 경우 모델은 실제로 일반화되지 않은 패턴을 학습하게 되고, 결과적으로 실제 데이터에 대한 예측 성능이 저하될 수 있다.
시간 순서가 중요한 데이터에서 무작위로 데이터를 분할하면 정보 누수가 발생할 수 있다. 예를 들어, 주식 가격 데이터를 예측하는 모델을 구축할 때, 데이터를 무작위로 분할하면 미래의 주가 정보가 훈련 데이터에 포함되어 모델의 성능이 왜곡될 수 있다. 이를 방지하기 위해서는 데이터를 시간 순서대로 분할하여 미래의 정보가 과거의 학습에 영향을 미치지 않도록 해야한다.
이번 프로젝트에서도 이런 유형의 누수를 마주한 적이 있다. 초기에 동료가 진행한 테스트 모델링에서 문제가 발생했다. 시계열 예측은 아니었지만, 데이터가 시간 순으로 쌓여 있는 형태였고, 차년도 데이터를 예측하는 구조였기 때문에 시간 기반으로 데이터를 분할했어야 했다. 하지만 초기 테스트에서는 데이터를 무작위로 랜덤하게 분할해버렸고, 그 결과 성능이 상당히 높게 나타났다.
문제는 그 다음이었다. 정식으로 예측 모델을 구축했을 때, 성능이 기대보다 현저히 낮게 나왔고, 왜 이전 테스트에선 잘 나왔는지에 대한 설명을 해야 하는 상황이 생겼다. 이미 성능 기대치가 높아진 상태였기 때문에, 실무적으로도 난감한 부분이 있었다.
이 경험을 통해 느낀 점은, 단순한 테스트라고 해도 모델이 실제로 배포될 시점을 항상 염두에 두고 설계해야 한다는 것이다. 데이터 분할 방식 하나만 잘못 선택해도, 모델 성능에 대한 신뢰가 흔들릴 수 있다는 걸 배웠다
전체 데이터를 스케일링한 후 훈련과 테스트 세트로 분할하면, 테스트 데이터의 통계 정보가 훈련 과정에 유출될 수 있다. 이는 모델이 실제 예측 시 사용할 수 없는 정보를 학습하게 만들어 성능을 왜곡시킨다. 따라서 스케일링은 데이터를 분할한 후, 훈련 세트의 통계치를 기반으로 각각의 세트에 적용해야 한다.
결측치를 대체할 때 전체 데이터의 평균이나 중앙값을 사용하는 것은 테스트 데이터의 정보를 훈련 과정에 유출시키는 결과를 초래할 수 있다. 이를 방지하기 위해서는 훈련 세트의 통계 정보를 사용하여 결측치를 채우고, 그 방식을 테스트 세트에 동일하게 적용해야 한다.
데이터에 중복된 샘플이 포함되어 있을 때, 이를 제거하지 않고 분할하면 동일한 샘플이 훈련과 테스트 세트에 모두 나타날 수 있다. 이는 모델이 이미 본 데이터를 테스트하는 결과를 낳아 성능을 부풀릴 수 있다. 따라서 분할 전에 중복 데이터를 확인하고 제거하는 것이 중요하다.
강한 레이블 상관관계를 갖는 데이터 포인트들이 다른 분할로 나뉘어 들어가는 경우, 그룹 누수가 발생할 수 있다. 예를 들어, 동일한 환자의 의료 데이터가 훈련과 테스트 세트에 각각 포함되면, 모델은 환자의 특성을 이미 학습한 상태에서 예측을 수행하게 되어 성능이 왜곡될 수 있다. 이를 방지하기 위해서는 데이터의 생성 과정을 이해하고, 그룹별로 데이터를 분할하는 것이 필요하다.
범주형 데이터를 숫자로 변환하는 인코딩 과정에서도 정보 누수는 생각보다 쉽게 발생할 수 있다. 특히 레이블 인코딩이나 원-핫 인코딩을 수행할 때, 테스트 데이터의 정보를 포함한 상태로 인코딩을 진행하면 안 된다. 이는 모델이 미래의 정보를 미리 학습하는 꼴이 되어, 실제 예측 상황에서 일관되지 않은 결과를 낳을 수 있다.
따라서 인코딩은 반드시 훈련 데이터에 대해서만 수행하고, 그때 생성된 매핑 또는 변환 기준을 테스트 데이터에 그대로 적용해야 한다. 이를 지키지 않으면, 마치 시험 전에 정답지를 미리 본 학생처럼 모델이 실제 상황과는 다른 조건에서 훈련되는 것이다.
그렇다면 이런 경우는 어떨까? 테스트 데이터셋에 훈련 데이터셋에는 없던 새로운 범주형 값이 등장한다면? 예를 들어, 훈련 데이터에는 없던 새로운 지역명이나 제품 코드가 테스트 데이터에 포함된 경우다. 이때 모델은 해당 값을 어떻게 해석해야 할지 몰라 혼란스러워진다.
이런 상황을 대비해 보통은 'rare' 클래스나 'unknown' 카테고리로 대체하는 전략을 사용한다. 또는 사전에 범주의 빈도를 기준으로 희귀 범주들을 하나로 묶어버리거나, 훈련 데이터에서 유의미한 범주만 남기고 나머지는 미리 처리해두는 방식도 있다.
나는 이번 프로젝트에서 이 과정을 통해, 인코딩은 단순한 숫자 변환이 아니라 모델이 세상을 이해하는 언어를 정의하는 과정이라는 걸 다시 한 번 느꼈다. 변환 과정이 조금만 어긋나도, 모델은 잘못된 언어를 배우게 되고, 그 결과는 예측 성능 저하로 고스란히 돌아온다. 특히 범주형 데이터가 많고 복잡한 문제일수록, 이 과정을 얼마나 깔끔하게 처리하느냐가 모델의 신뢰도를 좌우하게 된다.
정보 누수를 막기 위해서는 예측 모델링의 전체 흐름을 설계할 때부터 세심한 기준을 마련해야 한다. 단순히 코드 한 줄로 해결되는 문제가 아니라, 데이터를 어떻게 바라보고 관리하느냐에 따라 누수 가능성이 구조적으로 사라지거나 생겨나기 때문이다.
데이터 분할 후 전처리를 수행한다
모든 전처리는 데이터를 훈련/검증/테스트로 분할한 이후에 진행한다. 특히 스케일링, 결측치 대체, 이상치 처리 같은 작업은 모델의 입력값을 직접적으로 변경하기 때문에, 전체 데이터를 기준으로 하면 테스트 세트의 통계 정보를 학습 데이터가 미리 알아버리는 결과를 낳는다. 실무에서는 sklearn.pipeline
과 ColumnTransformer
등을 활용하여 fit은 훈련 데이터로만, transform은 전 데이터에 적용되도록 구성하는 것이 안전하다.
훈련 데이터 기반으로 인코딩 규칙을 정의한다
범주형 변수의 경우, 훈련 데이터로부터만 인코딩 기준을 도출하고, 그 방식을 테스트와 운영 데이터에 동일하게 적용한다. 만약 테스트 데이터에 훈련 데이터에는 없던 새로운 범주가 등장하면 '기타(etc)'나 'unknown' 같은 클래스로 묶는 전략을 취한다. 이 과정을 통해 미래에도 적용 가능한 인코딩 로직을 확보할 수 있다. 희귀 범주에 대한 사전 처리도 이와 연관된 중요한 전략이다.
시간 흐름이 있는 데이터는 반드시 순서를 고려한다
시계열 데이터나 연도별 데이터를 다룰 때는 랜덤 셔플을 피하고, 시간 순서대로 분할한다. 예측하려는 대상이 미래의 어떤 값이라면, 그보다 이전 시점의 데이터만으로 모델을 학습시켜야 실제 상황을 재현할 수 있다. 이를 간과하면, 데이터가 뒤섞이면서 정보 누수가 생기고, 과도한 성능을 기록한 모델이 배포 이후 무력해지는 일이 발생한다.
데이터 생성 및 수집 과정을 이해하고 분리 전략을 설계한다
고객 ID, 환자 번호, 제품 코드 등 특정 단위로 데이터가 반복되는 경우, 동일 그룹이 훈련과 테스트에 동시에 포함되지 않도록 주의해야 한다. GroupKFold나 Leave-One-Group-Out과 같은 기법을 사용하면 그룹 단위의 누수를 방지할 수 있다.
모델 입력 변수를 다시 한 번 검토한다
모델이 예측 시점에 사용할 수 없는 정보를 사용하고 있지는 않은지 점검해야 한다. 특히 파생변수를 생성할 때, 해당 변수의 정의가 미래의 정보로부터 파생된 것은 아닌지 꼼꼼히 따져야 한다. 변수 하나가 누수의 단초가 될 수 있기 때문이다.
정보 누수는 처음에는 눈에 잘 띄지 않는다. 하지만 모델 성능이 지나치게 좋거나, 배포 이후 성능이 급락한다면 의심부터 해야 할 대상이다.
모델은 현실을 예측하기 위한 도구다. 현실은 미래의 정보를 절대 제공하지 않기 때문에, 우리가 만드는 모델도 정직하게 현재까지만 알고 있어야 한다. 미로를 탐험할 때 지름길이 항상 최선의 선택이 아니듯, 모델링에서도 편한 길보다는 검증된 절차를 거치는 정석의 길이 결국엔 가장 빠르고 안정적인 결과를 가져온다.
모델링에서 가장 중요한 건 성능이 아니라 신뢰성 있는 구조라는 사실을, 나는 이번 프로젝트를 통해 다시 한 번 확인했다. 그리고 그 구조의 뿌리는 언제나 정보 누수 없는 데이터 처리로부터 시작된다.