나머지는 전부 플랫폼이 합니다. 트레이너는 데이터를 내려받지 않고, MLflow run 을 만들지 않고, 모델을 등록하지 않고, 표준 출력 규약을 지키지 않습니다.

#항목할 일
1지표mlflow.log_metrics({...}, step=epoch) 또는 mlflow.autolog(). stepepoch. 총 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/* > val/*loss > train/*loss > 그 밖 > lr/* 입니다.

  • 프레임워크가 이미 MLflow 콜백을 갖고 있으면(ultralytics 등) 그것으로 충분합니다.

  • 학습이 끝난 뒤 더 남길 값이 있으면 run 이 이미 닫혔을 수 있으니, run id 를 받는 클라이언트로 씁니다. 닫힌 run 에도 기록됩니다.

    from mlflow import MlflowClient
    MlflowClient().log_metric(run_id, "eval/mAP50", 0.71, step=0)
  • 지표 기록이 실패했다고 학습을 죽이지 마세요. 예외를 잡아 로그만 남기는 편이 낫습니다.

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추론 콘솔정규화 레시피. 모델이 스스로 전처리하면 적지 않습니다. 적으면 콘솔이 전처리를 한 번 더 합니다

등록은 플랫폼이 합니다

학습이 성공하면 플랫폼이 그 run 에 로깅된 모델을 찾아 레지스트리에 새 버전으로 등록하고 Staging 으로 올립니다. 모델 이름은 학습을 제출할 때 화면에서 정합니다. 트레이너는 register_model() 을 부르지 않습니다. 부르면 같은 모델의 버전이 둘 생깁니다.

등록이 실패해도 학습은 완료로 남습니다. 사유는 모델 레지스트리 등록 단계에 표시됩니다.

3. 종료 코드와 중단

컨테이너 종료학습 상태
코드 0완료
그 밖의 코드실패. 로그 끝부분이 오류 사유로 남습니다
사용자가 중지한 뒤의 종료코드와 상관없이 중지됨

화면에서 중지 를 누르면 플랫폼이 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 사용 규칙 두 가지

주소·토큰·실험·run 이 모두 환경 변수로 이미 설정돼 있습니다. mlflow.autolog() 한 줄이나 mlflow.log_metrics(...) 만으로 연결됩니다.

run 은 하나만 씁니다. 두 번째 start_run() 부터는 별도 run 이 생겨 화면과 자동 등록 대상에서 빠집니다. fold 별 학습처럼 결과가 여럿이면 지표 이름으로 구분해 한 run 에 기록하세요(fold0/loss, fold1/loss).

2026-09-21 기준 플랫폼에 맞춰 작성했습니다.

© Geo-MLOps