Post

[MLOps 실전] Day 4: 모니터링과 드리프트 - 모델은 배포 후에 조용히 나빠진다

[MLOps 실전] Day 4: 모니터링과 드리프트 - 모델은 배포 후에 조용히 나빠진다

이 글은 AI(Claude)의 도움을 받아 작성하고, 작성자가 검토·편집했습니다.

서론: 배포는 끝이 아니라 시작이다

일반 소프트웨어는 배포 후 코드를 안 바꾸면 동작이 그대로다. ML 모델은 다르다. 코드가 그대로여도, 세상이 변하면 모델이 조용히 틀려간다. 사용자 행동이 바뀌고, 시장이 변하고, 새로운 패턴이 등장한다. 모델은 에러를 던지지 않고 그냥 점점 나쁜 예측을 낸다. 이 “조용한 실패”를 감지하는 것이 ML 모니터링의 핵심이다.

1. ML 모니터링의 두 층위

1
2
3
4
5
6
7
8
9
10
1. 운영 모니터링 (일반 서비스와 동일 — 대규모 시스템 Day 5):
   지연(p99), 처리량, 에러율, 자원 사용량
   → "서비스가 살아있나"

2. 모델 품질 모니터링 (ML 고유):
   예측 분포, 입력 데이터 분포, 실제 정확도, 드리프트
   → "모델이 여전히 맞나"

핵심: 운영 지표는 멀쩡한데 모델 품질만 무너지는 경우가 가장 위험
      (시스템은 200 OK를 반환하지만 예측은 쓰레기)

2. 드리프트의 종류

모델이 나빠지는 원인을 구분해야 대응이 다르다.

1
2
3
4
5
6
7
8
9
10
11
12
데이터 드리프트 (Data/Covariate Drift):
  입력 데이터의 분포가 변함
  예: 신규 사용자층 유입으로 연령 분포가 바뀜
  → 입력이 학습 때와 달라짐

컨셉 드리프트 (Concept Drift):
  입력-출력 관계 자체가 변함
  예: 코로나로 "정상" 구매 패턴의 정의가 바뀜
  → 같은 입력인데 정답이 달라짐 (더 근본적)

레이블 드리프트:
  타깃 분포가 변함 (예: 사기 비율이 급증)

3. 드리프트 탐지: 분포를 비교하기

학습 데이터(기준)와 현재 프로덕션 데이터의 분포를 통계적으로 비교한다.

1
2
3
4
5
방법:
  - PSI (Population Stability Index): 분포 변화 정도를 수치화
    PSI < 0.1 안정 / 0.1~0.25 주의 / > 0.25 심각한 드리프트
  - KS 검정: 두 분포가 같은지 통계 검정
  - KL 발산 / Jensen-Shannon: 분포 간 거리
1
2
3
4
5
6
7
8
9
10
def calculate_psi(baseline, current, bins=10):
    # 기준 분포로 구간을 나누고, 각 구간 비율 변화를 합산
    b_pct, _ = np.histogram(baseline, bins=bins)
    c_pct, _ = np.histogram(current, bins=bins)
    b_pct = b_pct / len(baseline) + 1e-6
    c_pct = c_pct / len(current) + 1e-6
    psi = np.sum((c_pct - b_pct) * np.log(c_pct / b_pct))
    return psi   # > 0.25 면 알림 (대규모 시스템 Day 5: 증상에 알림)

# Evidently, NannyML 같은 도구가 이를 자동화·시각화

4. 지연된 정답 문제

ML 모니터링의 가장 어려운 점: 실제 정확도를 바로 알 수 없다.

1
2
3
4
5
6
7
8
9
예: 대출 상환 예측 → 정답(상환 여부)은 6개월 뒤에야 확정
    추천 클릭 예측 → 클릭은 즉시 알지만 "좋은 추천"은 장기 지표

→ "모델이 지금 정확한가"를 즉시 못 앎

대응:
  - 즉시 알 수 있는 프록시 지표 모니터링 (예측 분포, 드리프트)
  - 정답이 도착하는 대로 지연 평가
  - 일부 샘플에 인간 레이블링으로 빠른 점검

이것이 드리프트(입력 분포)를 보는 이유다. 정답을 기다리지 않고 입력 변화로 위험을 조기 감지한다.

5. 무엇을 로깅할까

추후 디버깅·재학습·감사를 위해 추론을 기록한다.

1
2
3
4
5
6
7
8
9
10
11
def predict_and_log(features):
    pred = model.predict(features)
    # 추론 로그: 입력·출력·모델버전·시각 (LLM 앱 Day 5의 로깅과 같은 사고)
    log_inference({
        "timestamp": now(),
        "model_version": model.version,   # Day 3
        "features": features,             # 드리프트 분석용
        "prediction": pred,
        "request_id": request_id,         # 나중에 정답과 조인 (분산 Day 5의 추적 ID)
    })
    return pred

이 로그가 드리프트 분석, 재학습 데이터, 그리고 “왜 그때 그런 예측을 했나”의 감사 근거가 된다.

6. 알림과 자동 대응

1
2
3
4
5
6
7
8
9
10
알림 설계 (대규모 시스템 Day 5의 원칙 재사용):
  - 증상에 알림: 드리프트 임계 초과, 예측 분포 급변, 정확도(지연) 하락
  - 행동 가능하게: "PSI 0.3, 재학습 검토 필요"
  - 알림 피로 방지: 노이즈는 진짜 신호를 묻는다

자동 대응 단계:
  1. 관찰: 대시보드·정기 리포트
  2. 알림: 임계 초과 시 담당자 호출
  3. 자동 재학습 트리거: 드리프트 감지 → 재학습 파이프라인 실행 (Day 5)
  4. 폴백: 심각하면 이전 모델 버전으로 롤백 (Day 3)

7. Day 4 체크리스트

  1. 모델이 코드 변경 없이도 세상의 변화로 나빠짐을 이해했다.
  2. 운영 모니터링과 모델 품질 모니터링의 두 층위를 구분했다.
  3. 데이터 드리프트와 컨셉 드리프트의 차이를 파악했다.
  4. PSI 등으로 분포 변화를 탐지하고, 지연된 정답 문제에 프록시 지표로 대응했다.
  5. 추론 로깅과 증상 기반 알림·자동 대응 체계를 설계했다.

다음 편 예고

드리프트를 감지했다면, 모델을 다시 학습해 갱신해야 한다. 마지막 Day 5(시리즈 마무리)에서는 재학습과 CI/CD/CT — ML 시스템의 자동화 파이프라인을 완성하고 전체를 종합한다.

This post is licensed under CC BY 4.0 by the author.