Last Week Had a Better Model. Nobody Can Find It
Nobody Can Find Last Week's Model
한국어 원문으로 표시합니다.
목표
학습 한 번을 '실행(run)' 이라는 기록 단위로 만들고, 파라미터·지표·코드와 데이터 지문을 원장에 남겨 나중에 그 실행을 되살릴 수 있게 만듭니다. 마지막에는 기록을 남기지 않은 실행이 어떻게 사라지는지를 실제 로그로 확인합니다.
왜 중요한가
모델을 잘 만드는 일과 그 모델을 다시 만드는 일은 다른 일입니다. 학습은 난수 씨앗·데이터 버전·전처리 코드에 모두 의존하는데, 그중 하나만 적어 두지 않아도 두 달 뒤에는 같은 숫자가 나오지 않습니다. 실험 추적 도구가 하는 일은 화려한 그래프가 아니라 한 번의 학습을 다시 세울 수 있을 만큼의 사실을 강제로 적게 하는 것입니다. 여기서는 그 최소 집합을 직접 설계해 봅니다 — 도구가 자동으로 채워 주던 칸을 손으로 채워 보면, 나중에 그 칸이 비어 있는 원장을 보고 바로 알아채게 됩니다.
단계
- 실험 정의 — 무엇을 최대화할지 먼저 적습니다.
- 첫 실행을 원장에 한 줄로 남깁니다.
- 파라미터를 바꿔 가며 실행을 4개 이상으로 늘립니다.
- 모든 실행에 코드·데이터 지문을 붙입니다.
- 목표 지표로 최고 실행을 원장에서 다시 뽑습니다.
- 기록만 보고 그 실행을 재현합니다.
- 기록 없이 사라진 실행을 조사합니다.
- 원장을 스스로 서는 꾸러미로 내보냅니다.
참고
- 작업은 전부
/root/mlops아래에서 합니다. 먼저mkdir -p /root/mlops를 하세요. - 재료는
/opt/fixtures/mlops에 있습니다. 데이터 설명은DATA-CARD.md를 보세요. - 학습기 실행 예:
python3 /opt/fixtures/mlops/train_model.py --train /opt/fixtures/mlops/train.csv --valid /opt/fixtures/mlops/valid.csv --lr 0.1 --epochs 40 --seed 7 - 채점기는 원장에 적힌 인자로 학습기를 다시 돌려 지표를 대조합니다. 숫자를 지어 쓰면 걸립니다.
- 흔한 실수 —
matches나reproducible을 문자열"true"로 적는 것, 그리고 원장을 덮어쓰기(w)로 열어 앞 실행을 지우는 것입니다. - 실습 파드에는 볼륨이 없습니다. 세션이 끝나면
/root는 통째로 사라지니, 남기고 싶은 것은 8단계의 꾸러미처럼 따로 복사해 두세요.
무엇을 잘한 것으로 볼지 먼저 적는다
/root/mlops/experiment.json 에 실험 정의를 저장하세요. name 은 churn-baseline, objective_metric 은 valid_accuracy, direction 은 max, owner 는 책임자 이름(2자 이상), dataset 은 /opt/fixtures/mlops/train.csv 입니다.
지표를 고르기 전에 실험을 시작하면 나중에 유리한 숫자를 고르게 됩니다. 무엇을 최대화할지 먼저 못 박아 두세요.
첫 실행을 원장에 한 줄로 남긴다
/opt/fixtures/mlops/train_model.py 를 한 번 돌리고 그 결과를 /root/mlops/runs.jsonl 에 JSON 한 줄로 남기세요. 한 줄에 run_id·params(lr·epochs·seed)·metrics(valid_accuracy·train_accuracy)·data(train·valid 경로)·started_at 이 들어갑니다. 지표는 실행 출력을 그대로 옮겨 적습니다.
학습기는 표준 출력으로 JSON 한 덩이를 냅니다. 그 값을 손으로 베끼지 말고 파이썬에서 받아 원장에 쓰면 옮겨 적다 틀릴 일이 없습니다.
네 번 더 돌리고 비교할 수 있게 만든다
서로 다른 lr·epochs·seed 조합으로 실행을 늘려 /root/mlops/runs.jsonl 에 4개 이상을 남기세요. run_id 는 실행마다 달라야 하고, 같은 파라미터 조합을 두 번 적으면 안 됩니다. 모든 줄의 지표는 그 인자로 다시 돌렸을 때 나오는 값과 같아야 합니다.
파라미터 목록을 코드에 두고 반복하면 다섯 줄이 한 번에 만들어집니다. 채점기는 각 줄의 인자로 학습기를 다시 돌려 지표를 대조합니다.
코드와 데이터에 지문을 찍는다
/root/mlops/runs.jsonl 의 모든 줄에 code_sha256 과 data_sha256 을 넣으세요. code_sha256 은 /opt/fixtures/mlops/train_model.py 의, data_sha256 은 /opt/fixtures/mlops/train.csv 의 SHA-256 16진 문자열입니다.
파라미터가 같아도 학습 코드나 데이터가 바뀌면 다른 실험입니다. hashlib.sha256 으로 파일 바이트를 해시하세요.
가장 좋은 실행을 원장에서 다시 뽑는다
/root/mlops/experiment.json 의 목표 지표와 방향을 읽어 /root/mlops/runs.jsonl 에서 가장 좋은 실행을 고르고 /root/mlops/best.json 에 run_id·metric·value·selected_by 를 저장하세요. 동점이면 원장에서 먼저 나온 실행을 고릅니다.
눈으로 고르지 말고 코드로 고르세요. 사람이 고르면 다음 주에 다시 고를 때 다른 답이 나옵니다.
기록만 보고 그 실행을 되살린다
/root/mlops/best.json 이 고른 실행의 파라미터를 /root/mlops/runs.jsonl 에서 읽어 학습기를 다시 돌리고, 결과를 /root/mlops/reproduce.json 에 run_id·params·valid_accuracy·matches·code_sha256·data_sha256 으로 저장하세요. matches 는 원장의 값과 같으면 불리언 true 입니다.
재현은 '기억' 이 아니라 '기록' 으로 하는 것입니다. 원장에 적힌 인자만 쓰고, 문자열 "true" 는 불리언이 아닙니다.
기록 없이 사라진 실행을 조사한다
/opt/fixtures/mlops/ghost_run.log 는 지난주 누군가 돌린 실행의 로그 조각입니다. /root/mlops/incident.json 에 ghost_metric(로그에 적힌 값)·best_recorded_metric(/root/mlops/runs.jsonl 의 최고값)·gap(둘의 차, 소수점 넷째 자리 반올림)·reproducible·missing_fields(로그만으로는 알 수 없는 실행 기록 필드를 사전순으로)·recovery_plan(40자 이상)을 저장하세요.
로그에서 읽히는 것은 지표 하나뿐입니다. 실행 기록이 갖춰야 할 필드 중 무엇이 비어 있는지 세어 보면 왜 재현할 수 없는지가 드러납니다.
세션이 끝나기 전에 원장을 들고 나간다
/root/mlops/export/ 아래에 runs.jsonl·experiment.json·best.json 을 복사하고 /root/mlops/export/manifest.json 에 experiment·run_count·best_run_id·files 를 저장하세요. files 의 각 항목은 path(디렉터리 없이 파일 이름만)·sha256·bytes 를 담고, best_run_id 는 담은 원장에서 다시 계산한 값이어야 합니다.
실습 파드에는 볼륨이 없어 세션이 끝나면 /root 가 사라집니다. 꾸러미는 혼자 서야 합니다 — 원장만 있고 목표가 없으면 나중에 해석할 수 없습니다.