컴퓨터공학 300 주제 시리즈의 279번째 글이다. 전체 지도는 여기.

한 줄 요약

MLOps 는 데이터 수집부터 학습·검증·배포·모니터링·재학습까지 머신러닝 시스템의 생애 주기를 재현 가능하고 자동화된 파이프라인으로 운영하는 실천이다. DevOps 에 “데이터와 모델도 변한다”는 사실을 더한 것이다.

왜 필요한가

주피터 노트북에서 정확도 0.92 가 나온 모델을 운영에 올리는 순간 질문이 쏟아진다. 그 모델은 어떤 데이터, 어떤 코드, 어떤 설정으로 만들었나. 다시 만들 수 있나. 운영에서 들어오는 데이터는 학습 때와 같은 분포인가. 성능이 떨어지면 누가, 언제 알아차리나.

Sculley 외(2015)의 “Hidden Technical Debt in Machine Learning Systems” 는 실제 ML 시스템에서 모델 코드는 작은 일부이고, 그 주변의 데이터 수집, 특징 추출, 설정, 서빙, 모니터링 인프라가 훨씬 크며 그곳에 기술 부채가 쌓인다고 지적했다. 일반 소프트웨어는 코드를 바꾸지 않으면 동작이 그대로지만, ML 시스템은 코드를 하나도 안 바꿔도 세상이 바뀌면 성능이 떨어진다.

핵심 개념

세 가지가 함께 버전 관리되어야 한다

모델 = f( 코드 , 데이터 , 설정·하이퍼파라미터 )  + 난수 씨앗, 라이브러리 버전, 하드웨어

일반 소프트웨어는 코드만 버전 관리하면 빌드를 재현할 수 있다. ML 은 데이터 스냅샷과 설정까지 묶어야 같은 모델이 나온다. 실험 추적 도구(MLflow 등)는 매 학습 실행마다 이 정보와 결과 지표, 산출물을 기록한다. 모델 레지스트리는 모델 버전마다 그 출처와 상태(검증 중, 운영 중, 폐기)를 관리한다.

생애 주기

데이터 수집 → 검증 → 특징 추출 → 학습 → 평가 → 등록 → 배포 → 모니터링
     ▲                                                          │
     └──────────────────── 재학습 트리거 ◀──────────────────────┘

Google Cloud 의 MLOps 문서는 성숙도를 단계로 나눈다. 수동으로 학습·배포하는 단계(레벨 0), 학습 파이프라인을 자동화해 새 데이터로 지속 학습하는 단계(레벨 1), 파이프라인 자체까지 CI/CD 로 빌드·테스트·배포하는 단계(레벨 2). 처음부터 레벨 2 를 목표로 할 필요는 없다. 재현 가능한 학습 스크립트와 기본 모니터링부터가 대부분의 팀에 가장 큰 효과를 준다.

배포 방식

방식 설명 쓰임
배치 예측 주기적으로 전체를 예측해 저장 추천 목록, 위험 점수
온라인 서빙 요청마다 API 로 예측 사기 탐지, 검색 순위
섀도 배포 새 모델이 실제 트래픽을 받되 결과는 쓰지 않음 위험 없이 비교
카나리·A/B 일부 트래픽만 새 모델로 실제 효과 측정

쿠버네티스 위에서는 KServe 같은 서빙 프레임워크나 Kubeflow 파이프라인으로 이 과정을 구성하기도 한다.

무엇이 변하나 — 드리프트

종류 뜻 예
데이터(공변량) 드리프트 입력 분포 P(x) 변화 신규 가입자 연령대 변화
개념 드리프트 입력과 정답의 관계 P(y|x) 변화 사기 수법이 바뀜
학습-서빙 불일치 학습과 운영의 특징 계산이 다름 학습은 배치 SQL, 운영은 다른 코드로 재구현

정답 라벨은 늦게 온다(대출 부도는 몇 달 뒤에 안다). 그래서 성능을 직접 재기 전에 입력 분포의 변화를 먼저 감시한다. 흔히 쓰는 지표가 PSI(population stability index)다. 기준 분포와 현재 분포를 같은 구간으로 나눠

PSI = Σ (현재비율 − 기준비율) · ln(현재비율 / 기준비율)

를 계산한다. 실무에서는 0.1 미만은 안정, 0.25 이상은 큰 변화로 보는 경험칙이 널리 쓰이지만 공식 표준은 아니다. 데이터와 업무에 맞춰 임계값을 정한다.

ML 에 특화된 테스트

  • 데이터 테스트: 스키마, 결측률, 값 범위, 고유값 수가 기대 범위인가.
  • 모델 테스트: 기준 모델·이전 버전보다 나은가, 중요한 세부 집단(지역, 연령대)에서 성능이 떨어지지 않았나.
  • 인프라 테스트: 학습 파이프라인이 같은 입력에 같은 결과를 내는가, 서빙 지연이 예산 안인가.

직접 해 보기

학습 때 분포와 운영 분포의 PSI 를 계산하고, 재현 가능한 실행 기록을 남긴다.

import hashlib, json, math, random
random.seed(7)
def psi(expected, actual, bins=5):
    lo, hi = min(expected), max(expected)
    edges = [lo + (hi - lo) * i / bins for i in range(1, bins)]
    def dist(xs):
        c = [0] * bins
        for x in xs: c[sum(x > e for e in edges)] += 1
        return [max(v / len(xs), 1e-4) for v in c]
    e, a = dist(expected), dist(actual)
    return sum((ai - ei) * math.log(ai / ei) for ei, ai in zip(e, a))
train  = [random.gauss(50, 10) for _ in range(2000)]
same   = [random.gauss(50, 10) for _ in range(2000)]
moved  = [random.gauss(58, 12) for _ in range(2000)]
print(f"PSI 같은 분포 = {psi(train, same):.3f}")
print(f"PSI 이동한 분포 = {psi(train, moved):.3f}")
# 재현 가능한 실행 기록: 데이터 지문 + 설정 + 지표
data_hash = hashlib.sha256(json.dumps([round(x, 6) for x in train]).encode()).hexdigest()[:12]
run = {"model": "churn-lr", "version": 3, "data_sha256": data_hash,
       "params": {"C": 1.0, "seed": 7}, "metrics": {"auc": 0.81}, "code_rev": "abc1234"}
print(json.dumps(run, ensure_ascii=False))

실행 결과:

PSI 같은 분포 = 0.002
PSI 이동한 분포 = 0.485
{"model": "churn-lr", "version": 3, "data_sha256": "f7db9564c3e8", "params": {"C": 1.0, "seed": 7}, "metrics": {"auc": 0.81}, "code_rev": "abc1234"}

같은 분포에서 뽑은 표본은 PSI 가 거의 0 이다. 평균이 50 에서 58 로, 표준편차가 10 에서 12 로 바뀐 표본은 0.485 로 크게 뛰었다. 정답 라벨 없이도 “학습 때와 다른 데이터가 들어오고 있다”는 경보를 낼 수 있다.

실행 기록에는 데이터의 해시, 설정, 지표, 코드 리비전이 함께 들어 있다. 몇 달 뒤 “그때 그 모델이 왜 그렇게 동작했나”를 물을 때 이 한 줄이 출발점이 된다. 데이터 해시가 다르면 같은 코드라도 다른 모델이라는 뜻이다.

현업에서는

  • 모니터링은 서비스 지표와 같이 본다. 예측 지연, 오류율 같은 일반 지표와 함께 입력 분포, 예측값 분포, (도착하는 대로) 실제 성능을 같은 대시보드에 둔다. 예측값 분포가 갑자기 한쪽으로 쏠리면 상류 데이터 파이프라인의 고장인 경우가 많다.
  • 학습-서빙 불일치가 가장 흔한 사고다. 특징 계산 로직을 한 곳에 두고 학습과 서빙이 같이 쓰게 한다. 피처 스토어가 해결하려는 문제가 이것이다.
  • 롤백 경로를 먼저 만든다. 모델 레지스트리에 이전 버전이 남아 있고, 버튼 하나(또는 커밋 하나)로 되돌릴 수 있어야 한다. GitOps 로 운영하는 클러스터라면 모델 버전도 매니페스트의 이미지 태그·경로로 선언해 두면 배포 이력과 롤백이 코드와 같은 방식으로 관리된다.
  • LLM 시대에도 같다. 프롬프트, 검색 색인, 모델 버전이 모두 “설정”이다. 같이 버전 관리하고, 평가 세트로 회귀를 막는다.

확인 문제

  1. ML 시스템의 재현에 코드 외에 무엇이 필요한가?
  2. 데이터 드리프트와 개념 드리프트의 차이는?
  3. 정답 라벨이 몇 달 뒤에 오는 문제에서 모델 이상을 빨리 알아차리는 방법은?
  4. 학습-서빙 불일치란 무엇이며 어떻게 줄이는가?
  5. 섀도 배포의 장점은?

풀이

  1. 데이터 스냅샷(또는 그 해시), 설정·하이퍼파라미터, 난수 씨앗, 라이브러리와 환경 버전.
  2. 데이터 드리프트는 입력 분포 P(x) 의 변화이고, 개념 드리프트는 입력과 정답의 관계 P(y x) 의 변화다.
  3. 입력 특징과 예측값의 분포 변화(PSI 등)를 감시하고, 일부 표본에 빠르게 라벨을 붙여 확인한다.
  4. 학습 때와 운영 때 특징 계산 방식이 달라 같은 입력에 다른 특징이 들어가는 문제. 특징 계산 코드를 하나로 공유하고(피처 스토어 등), 운영 입력을 기록해 학습 데이터와 비교한다.
  5. 실제 트래픽으로 새 모델을 검증하면서도 사용자에게는 영향을 주지 않는다.

더 읽을거리 (References)