지난주 모델이 더 좋았는데 아무도 못 찾는다 · 돌린 사람만 아는 숫자 · 이론
실행 하나를 되살리는 최소 기록
한 줄 요약
학습 한 번을 되살릴 수 있게 만드는 최소 기록 단위가 실행(run)이고, 실험 추적은
그 기록을 사람의 기억이 아니라 파일에 강제로 남기게 하는 장치다.
왜 이게 필요했나
모델을 만드는 일은 대체로 조용하다. 주피터 노트북에서 학습률을 조금 올리고,
에폭을 늘려 보고, 씨앗을 바꿔 다시 돌린다. 숫자가 좋아지면 기쁘고 나빠지면
다시 되돌린다. 이 과정에서 실제로 남는 것은 마지막으로 돌린 셀의 출력 하나뿐이다.
그리고 2주 뒤, 누군가 "지난주에 더 좋은 게 나왔다고 하지 않았나요" 라고 묻는다.
그때 재현하려고 하면 세 가지가 한꺼번에 문제가 된다. 어떤 파라미터였는지, 어떤
데이터였는지, 그리고 그때의 학습 코드가 지금 코드와 같은지. 셋 중 하나만 비어
있어도 같은 숫자는 다시 나오지 않는다. 더 나쁜 것은 **비슷한 숫자가 나온다는
점**이다. 완전히 달라지면 무언가 틀렸다는 것을 알지만, 0.78 대신 0.76 이 나오면
사람은 "원래 이 정도였나" 하고 넘어간다. 그렇게 넘어간 기록은 다시는 복구되지 않는다.
MLflow 문서는 실행을 "데이터 과학 코드를 한 번 실행한 것" 으로 정의하고, 각 실행이
메타데이터(지표·파라미터·시작과 종료 시각)와 산출물(모델 가중치·이미지 같은 출력
파일)을 함께 기록한다고 적고 있다([MLflow Tracking](https://mlflow.org/docs/latest/ml/tracking/)).
정의가 이렇게 생긴 이유는 분명하다. 이 네 가지가 있어야 그 실행을 다시 세울 수 있고,
하나라도 빠지면 그 실행은 '있었다는 소문' 이 된다.
어떻게 동작하나
추적 도구가 실제로 하는 일은 단순하다. 학습이 시작될 때 실행 하나를 열고, 코드가log_param 과 log_metric 을 부를 때마다 그 값을 저장소에 적고, 끝나면 닫는다.
별다른 서버를 세우지 않으면 로컬 디렉터리에 쌓인다. 저장 위치를 바꾸고 싶으면
추적 환경을 따로 설정한다.
import mlflowwith mlflow.start_run(): mlflow.log_param("lr", 0.001) # 학습 코드 mlflow.log_metric("val_loss", val_loss)실행이 여러 개 쌓이면 그다음은 질의다. MLflow 는 MlflowClient.search_runs 로
"이 실험에서 검증 손실이 가장 낮은 실행" 같은 조회를 지원하고, MLflow 3 부터는search_logged_models 로 지표·파라미터 조건을 SQL 비슷한 문자열로 걸러 모델을
찾을 수 있다. 여기서 중요한 것은 정렬 기준을 코드로 적는다는 점이다. 사람이
표를 눈으로 훑어 고르면 다음 주에 다시 고를 때 다른 답이 나온다.
기록해야 할 것의 목록은 도구마다 조금씩 다르지만, 재현이라는 목적에서 역산하면
대개 같은 자리에 도착한다. 파라미터, 지표, 그리고 입력의 정체다. MLflow 의
데이터셋 추적은 이 세 번째를 위해 데이터셋마다 이름과 다이제스트(지문), 원본
위치를 담은 객체를 실행에 붙인다([MLflow Dataset Tracking](https://mlflow.org/docs/latest/ml/dataset/)).
지문이 필요한 이유는 파일 이름이 버전이 아니기 때문이다. train.csv 는 어제도
오늘도 train.csv 다.
자동 기록(autolog)은 이 모든 것을 라이브러리가 대신 적어 주는 기능이고, 지원
목록에 있는 프레임워크라면 mlflow.autolog() 한 줄로 켠다
([Automatic Logging](https://mlflow.org/docs/latest/ml/tracking/autolog)). 편하지만,
무엇이 적히는지 모르면 빠진 것도 모른다. 그래서 처음 한 번은 손으로 적어 보는 편이 낫다.
현장에서 만나는 모습
가장 흔한 사고는 도구를 안 쓴 것이 아니라 절반만 쓴 것이다. 파라미터는 적혀
있는데 데이터 버전이 없다. 지표는 있는데 코드 커밋이 없다. 그러면 표는 화려한데
아무 줄도 재현되지 않는다. 표가 있으니 다들 문제를 못 느끼다가, 규제 대응이나
사고 조사처럼 "이 모델이 어떻게 만들어졌는지 증명하라" 는 요구가 들어올 때 한꺼번에
드러난다.
두 번째는 저장 위치다. 컨테이너 안에서 학습을 돌리고 결과를 컨테이너 안에 적으면,
그 파드가 사라질 때 기록도 함께 사라진다. 실습 파드에도 볼륨이 없어서 세션이 끝나면/root 가 통째로 없어지는데, 이건 불편한 제약이 아니라 현실의 축소판이다. 기록은
계산이 일어난 곳 바깥에 남아야 한다.
세 번째는 이름이다. 실행 식별자를 test, test2, test_final 로 짓는 습관은 그날은
편하지만 한 달 뒤에는 아무것도 알려 주지 않는다. 식별자는 사람이 읽으라고 있는 것이
아니라 다른 기록과 이어 붙이라고 있는 것이므로, 짧고 겹치지 않으면 충분하다. 대신
"무엇을 시도했는가" 는 파라미터와 메모에 적는다.
다음 실습에서 할 것
JSON Lines 파일 하나로 실험 원장을 직접 만든다. 실행마다 파라미터·지표·코드와
데이터의 SHA-256 지문을 남기고, 목표 지표로 최고 실행을 코드로 뽑고, 그 기록만
보고 같은 숫자를 다시 만들어 본다. 마지막에는 파라미터가 적히지 않은 채 사라진
실행의 로그를 열어, 무엇이 없어서 재현할 수 없는지를 항목으로 세어 본다.