트레이너의 책임은 세 가지, 나머지는 플랫폼이 처리. 트레이너가 하지 않는 일: 데이터 다운로드, 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 로 계산. param epochs 가 없으면 진행률 막대 없이 "학습 중" 으로만 표시(학습은 계속 진행).

  • 지표 이름은 자유. 서버가 이름 패턴으로 우선순위를 매겨 기본 곡선 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_packagespip 로 설치할 수 없는 시스템 패키지(예: 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).

2026-10-02 기준 플랫폼으로 작성

© MLOps Central Platform