트레이너의 책임 3가지
트레이너의 책임 세 가지(지표 기록, pyfunc.log_model 한 번, 종료 코드)와 MLflow 사용 규칙, SIGTERM 처리
트레이너의 책임은 세 가지, 나머지는 플랫폼이 처리. 트레이너가 하지 않는 일: 데이터 다운로드, MLflow run 생성, 모델 등록, 표준 출력 규약 준수.
| # | 항목 | 할 일 |
|---|---|---|
| 1 | 지표 | mlflow.log_metrics({...}, step=epoch) 또는 mlflow.autolog(). step 은 epoch. 총 epoch 수는 mlflow.log_param("epochs", N) 으로. |
| 2 | 모델 | mlflow.pyfunc.log_model(...) 을 한 번. 레지스트리 등록은 플랫폼 담당. |
| 3 | 종료 | 성공 0, 실패 0 이외. 중단(SIGTERM) 시 가능하면 체크포인트를 남기고 종료. 이때 종료 코드는 무관. |
1. 지표: MLflow 에만
import mlflow
with mlflow.start_run(): # MLFLOW_RUN_ID 에 자동으로 이어진다
mlflow.log_param("epochs", EPOCHS) # 진행률 분모
for epoch in range(EPOCHS):
loss, acc = train_one_epoch(...)
mlflow.log_metrics({"train/loss": loss, "eval/accuracy": acc}, step=epoch)
-
step은 epoch. 화면 곡선의 x 축 값. -
진행률은
max(metric.step) + 1 / params.epochs로 계산. paramepochs가 없으면 진행률 막대 없이 "학습 중" 으로만 표시(학습은 계속 진행). -
지표 이름은 자유. 서버가 이름 패턴으로 우선순위를 매겨 기본 곡선 4개를 선택, 나머지는 선택지로 표시. 우선순위:
metrics/*>fitness>eval/*>speed/*>val/*loss>train/*loss> 기타 >lr/*. -
프레임워크에 MLflow 콜백이 있으면(ultralytics 등) 그것으로 충분.
-
학습이 끝난 뒤 기록할 값은 run id 를 인자로 받는 클라이언트로 기록. run 이 이미 종료되었어도 기록됨.
from mlflow import MlflowClient MlflowClient().log_metric(run_id, "eval/mAP50", 0.71, step=0) -
지표 기록이 실패해도 학습은 중단하지 않음. 예외를 잡아 로그만 남김. 키마다 따로 예외를 잡으면 이름 하나가 거부되어도 나머지는 기록됨.
-
MLflow 는 지표 이름에 영문, 숫자,
_,-,., 공백,/,:만 허용. 괄호는 거부.metrics/mAP50(B)같은 이름은 괄호를 제거해mAP50B로 기록.
학습 뒤 평가와 속도 테스트
마법사 ⑧ 단계의 모델 평가 실행과 속도 테스트는 experiment.json 의 evaluation.benchmark, evaluation.speed_test 로 전달. 지원하지 않으면 무시. 지원하면 아래 이름으로 기록해야 학습 상세의 후처리 결과 카드에 표시.
| 결과 | 지표 이름 (step 0) | 설명 |
|---|---|---|
| 평가 | eval/<이름> | 학습 중 사용한 이름을 그대로 사용. 예: 학습 중 metrics/mIoU 이면 eval/mIoU. |
| 속도 | speed/latency_ms_p50, speed/latency_ms_p95, speed/latency_ms_mean, speed/throughput, speed/samples | 지연은 밀리초, 처리량은 초당 호출 수. 포인트클라우드는 speed/points_per_s 추가 가능. |
- 순서는 모델 기록, 평가, 속도 테스트. 평가 중 메모리 부족으로 파드가 종료되어도 모델은 남아 있어야 함.
- 평가는 등록될 체크포인트로 수행. 마지막 epoch 가중치로 평가하면 화면의 수치가 배포되는 모델의 수치와 다름.
- 평가나 속도 테스트가 실패해도 종료 코드는 0 으로 유지(완료된 학습이 실패로 처리되지 않도록). 대신
-> evaluation failed: <사유>처럼evaluation이나speed test가 포함된 한 줄을 로그에 기록. - 모델 평가 실행 은 기본 활성화. 완료된 학습에
eval/*지표가 하나도 없으면 후처리 결과 카드에 "모델 평가 결과가 기록되지 않았습니다" 가 표시되고, 로그의 위 줄 확인 안내. - 중지(SIGTERM) 수신 후에는 평가와 속도 테스트 없이 종료.
- 속도는 배치 크기 1로 측정, 처음 몇 회(워밍업)는 제외. GPU 면 호출 앞뒤로
torch.cuda.synchronize()호출.
2. 모델: pyfunc.log_model 한 번
형식이 정해진 것은 모델 로깅뿐. 가중치 파일(.pt)만 아티팩트로 업로드하면 MLmodel 파일이 없어 서빙 이미지 생성 불가.
info = mlflow.pyfunc.log_model(
name="model",
python_model="predictor.py", # 서빙용 predict() 래퍼 (models-from-code)
artifacts={"weights": "/geo/work/best.pt"},
signature=signature, # 사실상 필수. 없으면 서빙 요청이 실패한다
pip_requirements=[ # 학습 환경 버전 그대로 고정
f"torch=={torch.__version__.split('+')[0]}",
"mlflow==3.13.0", # 플랫폼 고정 버전
],
metadata={"input_kind": "image_b64"}, # 추론 콘솔이 입력 위젯을 선택하는 힌트 (선택)
)
| 인자 | 누락하면 |
|---|---|
python_model | 서빙 컨테이너에는 학습 코드가 없음. predictor.py 한 파일이 모델 구조·가중치 로드·추론을 모두 처리해야 함. 파일 끝에 mlflow.models.set_model(...) 이 있어야 로드. |
artifacts | 가중치가 모델에 포함되지 않아 서빙 측에 로드할 파일 없음 |
signature | 입력이 모두 float64 로 변환되어 요청 실패. 컬럼 이름·dtype 이 곧 서빙 API 계약. |
pip_requirements | 서빙 이미지가 이 목록으로 환경 구성. 학습과 버전이 다르면 체크포인트가 로드되지 않음. |
metadata.apt_packages | pip 로 설치할 수 없는 시스템 패키지(예: OpenCV 의 libgl1)가 누락되어 서빙 컨테이너가 기동되지 않음 |
metadata 에서 플랫폼이 읽는 키:
| 키 | 읽는 곳 | 뜻 |
|---|---|---|
input_kind | 추론 콘솔 | 입력 위젯(image_b64 · tabular · timeseries …). 컬럼 이름 기반 추정보다 우선. |
apt_packages | 서빙 이미지 빌더 | 서빙 이미지에 설치할 시스템 패키지 |
class_names | 결과 오버레이 | 인덱스 → 이름 |
preprocessing | 추론 콘솔 | 정규화 레시피. 모델이 자체적으로 전처리하면 지정하지 않음. 지정하면 콘솔이 전처리를 한 번 더 수행. |
등록은 플랫폼이 수행
마법사 ⑪ 검토 단계에서 모델 레지스트리 등록 을 활성화하고(기본 비활성화) 등록 모델 이름 을 입력한 학습이 종료 코드 0 으로 끝나면, 플랫폼이 그 run 에 로깅된 모델을 찾아 레지스트리에 새 버전으로 등록 후 Staging 으로 전환. 등록을 비활성화한 학습과 중지한 학습은 등록하지 않음. 트레이너는 register_model() 을 호출하지 않음. 호출하면 같은 모델의 버전이 중복 생성.
등록이 실패해도 학습은 완료로 유지. 사유는 모델 레지스트리 등록 단계에 표시.
3. 종료 코드와 중단
| 컨테이너 종료 | 학습 상태 |
|---|---|
코드 0 | 완료 |
137 | 실패. 사유 앞에 메모리 부족(OOM) 안내 추가. 대개 파드 메모리 상한을 초과해 강제 종료된 경우(경로와 환경 변수 의 자원 한도). |
| 기타 코드 | 실패 |
| 사용자가 중지한 뒤의 종료 | 코드와 무관하게 중지됨 |
실패 사유 형식: trainer exited with code <코드>: <로그 마지막 5줄>. 플랫폼은 실패한 파드를 재시작하지 않음.
화면에서 중지 를 누르면 플랫폼이 Job 을 삭제하고, 파드는 SIGTERM 수신. 유예 시간(서버 설정 GEO_MLOPS_TRAINING_GRACE_PERIOD, 기본 60초) 안에 종료되지 않으면 SIGKILL 전송. 처리 예:
import signal
stop_requested = False
def on_sigterm(*_):
global stop_requested
stop_requested = True # 핸들러에서 종료하지 않고 플래그만 설정한다
signal.signal(signal.SIGTERM, on_sigterm)
for epoch in range(EPOCHS):
...
save_checkpoint()
if stop_requested: # 안전한 지점(epoch 경계)에서 확인
break
체크포인트는 MLflow 아티팩트로 업로드해야 의미가 있음. /geo/work 는 파드와 함께 삭제. SIGKILL 로 종료되어도 플랫폼이 MLflow run 을 KILLED 상태로 마감.
중지한 학습은 이어서 실행 불가. MLflow 에 저장한 체크포인트를 사전학습 가중치 로 업로드하면 새 학습의 시작 가중치로 선택 가능.
MLflow 사용 규칙 두 가지
주소·토큰·실험·run 은 모두 환경 변수로 전달. mlflow.autolog() 나 mlflow.log_metrics(...) 만 호출하면 연결.
run 은 하나만 사용. 두 번째 start_run() 부터는 별도 run 이 생성되어 화면과 자동 등록 대상에서 제외. fold 별 학습처럼 결과가 여럿이면 지표 이름으로 구분해 한 run 에 기록(fold0/loss, fold1/loss).