지난주 모델이 더 좋았는데 아무도 못 찾는다 · 같은 이름, 다른 데이터 · 이론
이름은 버전이 아니다
한 줄 요약
데이터는 이름이 아니라 내용의 해시로 버전을 삼고, 정상 범위는 데이터에서 추론하지
말고 업무에서 선언해 두었다가 새 수집분을 기계로 대 본다.
왜 이게 필요했나
재현이 깨지는 자리는 대개 코드가 아니라 데이터다. 학습 스크립트는 git 이 지켜
주지만 train.csv 는 아무도 지켜 주지 않는다. 어느 날 상류 파이프라인이 중복 적재를
한 번 하면 행이 늘고, 다음 주에 누가 결측 처리를 바꾸면 값이 달라진다. 파일 이름은
그대로고 학습은 정상 종료하며 지표는 조금 움직인다. 이 조합이 가장 오래 숨는다.
두 번째 문제는 '정상' 의 정의가 어디에도 적혀 있지 않다는 것이다. 월 요금이 음수인
행이 들어와도 파이썬은 float 로 잘 읽고, 모델은 그 값으로 학습해 버린다. 사람이
눈치채는 것은 몇 주 뒤 예측이 이상해졌을 때인데, 그때는 이미 그 데이터로 학습한
모델이 운영에 있다.
어떻게 동작하나
데이터 버전은 지문으로 잡는다. 파일 바이트의 SHA-256 을 계산해 두면 같은 이름의
파일이 같은 파일인지 한 번에 답할 수 있다([hashlib](https://docs.python.org/3/library/hashlib.html)).
MLflow 의 데이터셋 객체도 같은 발상으로 이름·다이제스트·원본 위치·스키마·프로파일을
함께 들고 다닌다([MLflow Dataset Tracking](https://mlflow.org/docs/latest/ml/dataset/)).
다이제스트가 실행 기록에 붙어 있으면 "이 모델은 어떤 데이터로 만들어졌나" 가
추측이 아니라 조회가 된다.
정상 범위는 스키마로 선언한다. TensorFlow Data Validation 문서는 스키마를 "입력
데이터가 만족해야 할 성질을 코드로 적은 것" 으로 설명하고, 통계를 스키마와 대조해
이상을 찾는 방식을 쓴다. 같은 문서가 자주 만나는 문제로 꼽는 것이 결측값, **레이블이
특징으로 섞여 들어가 모델이 정답을 미리 보는 것**, 그리고 기대 범위를 벗어난 값이다
([TensorFlow Data Validation](https://www.tensorflow.org/tfx/guide/tfdv)).
계약(선언) monthly_fee 0~200, late_payments 필수관찰(계산) train.csv 의 monthly_fee 는 9.3 ~ 88.69검증(대조) week2.csv 240행 중 4행이 계약 위반검증 규칙 자체는 몇 줄이면 된다. 필수 열이 비었는가, 숫자로 읽히는가, 선언한
범위 안에 드는가. 어려운 것은 규칙이 아니라 위반을 만났을 때 무엇을 할지를
미리 정해 두는 일이다. 통째로 멈출 것인지, 위반 행만 빼고 갈 것인지, 경고만
남기고 그대로 학습할 것인지는 열마다 다를 수 있다.
여기서 선언과 관찰을 나누는 것이 중요하다. 오늘 데이터의 최솟값·최댓값을 그대로
규칙으로 삼으면, 오늘 우연히 들어온 이상치가 내일의 규칙이 된다. 범위는 업무가
정하고, 데이터는 그 범위 안에 드는지 확인만 한다.
분할 누수는 또 다른 축이다. scikit-learn 문서는 누수를 "예측 시점에는 쓸 수 없는
정보가 모델을 만드는 데 쓰이는 것" 으로 정의하고, 그 결과 성능 추정치가 지나치게
낙관적이 되어 실제 새 데이터에서는 더 나쁘다고 설명한다. 가장 흔한 원인으로는
학습과 평가 부분집합을 제대로 분리하지 않은 것을 든다
([Common pitfalls](https://scikit-learn.org/stable/common_pitfalls.html)).
현장에서 만나는 모습
계약 검증을 넣고 나서 처음 며칠은 경보가 쏟아진다. 대부분은 데이터가 나빠서가
아니라 계약이 현실보다 좁게 적혀 있어서다. 이때 검증을 꺼 버리면 다시 원점이므로,
경보를 하나씩 읽고 계약을 고치는 편이 낫다. 그 과정이 곧 "우리 데이터가 어떤
모양인가" 를 팀이 처음으로 합의하는 과정이 된다.
두 번째는 조용히 버리는 습관이다. 계약을 어긴 행을 try/except 로 건너뛰면 학습은
계속 돌지만 표본이 줄어든 사실은 아무 데도 남지 않는다. 몇 달 뒤 "왜 성능이
떨어졌지" 로 돌아온다. 버린 행은 격리 파일로 남겨 두면 나중에 이유를 되짚을 수 있다.
세 번째는 누수를 지표로 잡으려는 시도다. 기억력이 큰 모델에서는 누수가 지표를
크게 올리지만, 단순한 선형 모델에서는 거의 움직이지 않는다. 그래서 분할은 숫자를
보고 의심하는 것이 아니라 식별자가 겹치는지로 검사해야 한다.
네 번째는 데이터 카드를 나중으로 미루는 것이다. 이 데이터가 어디서 왔고 무엇을
말할 수 없는지를 적어 두지 않으면, 다음 사람은 그 한계를 모른 채 쓴다. 합성
데이터로 만든 모델을 실제 고객 응대에 그대로 붙이는 사고가 그렇게 난다. 카드는
길 필요가 없다 — 출처, 지문, 분할 규칙, 그리고 "이걸로는 이런 말을 할 수 없다"
두어 줄이면 충분하고, 그 두어 줄이 몇 달 뒤 회의 한 번을 줄인다.
다음 실습에서 할 것
세 분할의 지문을 뜨고, 업무 규칙으로 데이터 계약을 선언하고, 새 수집분을 대 봐서
위반 행을 격리한다. 그다음 학습과 평가에 같은 고객이 들어간 망가진 분할을 찾아내고,
겹침을 걷어 낸 뒤 지표가 실제로 얼마나 움직이는지를 직접 재 본다.