배포한 모델은 영원하지 않다

모델을 배포하고 나면 한동안은 잘 작동한다. 그런데 몇 주, 몇 달이 지나면서 예측 성능이 서서히 떨어지기 시작한다. 코드를 변경하지도, 모델을 재학습시키지도 않았는데 왜 이런 일이 발생하는 것일까?

원인은 대부분 데이터에 있다. 모델은 학습 시점의 데이터 분포를 기반으로 패턴을 학습한다. 그런데 현실 세계의 데이터는 시간이 지남에 따라 변한다. 사용자 행동이 달라지고, 시장 환경이 바뀌고, 계절적 요인이 개입한다. 학습 시점의 데이터와 현재 서빙 시점의 데이터 사이에 괴리가 생기면, 모델의 예측력은 필연적으로 저하되는 것이다.

이것이 모델 모니터링이 필요한 근본적인 이유이다. 배포 이후에도 모델의 상태를 지속적으로 관찰하고, 문제를 조기에 감지하여 대응해야 한다.

데이터 드리프트와 컨셉 드리프트

모델 성능 저하의 원인은 크게 두 가지로 구분된다. 데이터 드리프트(data drift)와 컨셉 드리프트(concept drift)이다.

데이터 드리프트는 입력 데이터의 분포가 변하는 현상이다. 예를 들어, 대출 심사 모델을 학습할 때 신청자의 평균 연령이 35세였는데, 서비스 운영 후 20대 신청자가 급증하여 평균 연령이 28세로 낮아진 경우가 이에 해당한다. 모델이 학습하지 못한 분포의 데이터가 들어오므로 예측의 신뢰도가 떨어지는 것이다.

컨셉 드리프트는 입력과 출력 사이의 관계 자체가 변하는 현상이다. 과거에는 특정 패턴이 사기 거래를 나타냈지만, 사기 수법이 진화하면서 동일한 패턴이 더 이상 사기를 의미하지 않게 되는 경우이다. 데이터의 분포는 유사하더라도 정답의 기준이 바뀐 것이므로, 모델을 근본적으로 재학습시켜야 한다.

과연 두 가지를 구분하는 것이 실무적으로 중요한가? 그렇다. 데이터 드리프트는 입력 데이터를 모니터링하는 것만으로 감지할 수 있지만, 컨셉 드리프트는 레이블(정답)이 확보되어야 비로소 확인할 수 있다. 감지 방법이 다르므로 대응 전략도 달라지는 것이다.

드리프트 감지를 위한 통계적 방법

드리프트를 감지하려면 학습 데이터의 분포와 현재 서빙 데이터의 분포를 비교해야 한다. 이를 위해 여러 통계적 검정 방법이 사용된다.

PSI(Population Stability Index)는 두 분포 사이의 차이를 정량화하는 지표이다. 변수의 값을 구간으로 나누고, 각 구간에 속하는 비율의 차이를 측정한다. PSI 값이 0.1 이하면 안정적, 0.1에서 0.2 사이면 주의, 0.2 이상이면 드리프트가 발생한 것으로 판단하는 것이 일반적인 기준이다.

import numpy as np

def calculate_psi(expected, actual, bins=10):
    breakpoints = np.linspace(0, 100, bins + 1)
    expected_percents = np.histogram(expected, breakpoints)[0] / len(expected)
    actual_percents = np.histogram(actual, breakpoints)[0] / len(actual)

    # 0으로 나누는 것을 방지
    expected_percents = np.clip(expected_percents, 0.001, None)
    actual_percents = np.clip(actual_percents, 0.001, None)

    psi = np.sum(
        (actual_percents - expected_percents)
        * np.log(actual_percents / expected_percents)
    )
    return psi

KS 검정(Kolmogorov-Smirnov test)은 두 분포의 누적 분포 함수 사이의 최대 거리를 측정하는 방법이다. p-value가 유의 수준(통상 0.05) 이하이면 두 분포가 유의미하게 다르다고 판단한다. 연속형 변수에 적합하며, 구현이 간단하다는 장점이 있다.

방법장점적합한 상황
PSI해석이 직관적, 임계값 기준 명확범주형 및 연속형 변수
KS 검정비모수적, 구현 간단연속형 변수
카이제곱 검정범주형 변수에 특화범주형 변수
Wasserstein 거리분포 형태 차이를 반영분포 간 미세한 변화 감지

실무에서는 하나의 방법만 사용하기보다, 변수의 타입과 특성에 따라 여러 방법을 조합하여 적용하는 것이 일반적이다.

모니터링 지표

드리프트 감지 외에도 서빙 시스템 전반에 걸쳐 관찰해야 하는 지표들이 있다. 이를 크게 모델 성능 지표, 서비스 성능 지표, 데이터 품질 지표의 세 범주로 나눌 수 있다.

모델 성능 지표는 모델의 예측이 얼마나 정확한지를 측정한다. 정확도, 정밀도, 재현율, F1 스코어, AUC 등이 이에 해당한다. 다만 프로덕션 환경에서는 실시간으로 레이블을 확보하기 어려운 경우가 많으므로, 예측값의 분포나 신뢰도 점수의 변화를 대리 지표로 활용하기도 한다.

서비스 성능 지표는 서빙 시스템의 건강 상태를 나타낸다. 응답 레이턴시(p50, p95, p99), 처리량(requests per second), 에러율 등을 추적한다. 모델 자체에는 문제가 없더라도 서빙 인프라의 성능이 저하되면 사용자 경험에 직접적인 영향을 미치기 때문이다.

데이터 품질 지표는 입력 데이터의 무결성을 검증한다. 결측값 비율, 이상치 빈도, 스키마 위반 건수 등을 모니터링한다. 데이터 파이프라인 상류에서 발생한 문제가 모델에 영향을 미치기 전에 차단하기 위한 것이다.

알림 전략

지표를 수집하는 것만으로는 충분하지 않다. 문제가 발생했을 때 적시에 알림을 받아 대응할 수 있어야 한다.

알림 설계에서 가장 어려운 것은 임계값의 설정이다. 임계값을 너무 낮게 설정하면 사소한 변동에도 알림이 발생하여 알림 피로(alert fatigue)를 유발한다. 반대로 너무 높게 설정하면 실제 문제를 놓치게 된다. 정적 임계값보다는 이동 평균이나 표준편차 기반의 동적 임계값을 사용하는 것이 대부분의 경우 효과적이다.

알림의 심각도를 계층화하는 것도 중요하다. 즉각적인 대응이 필요한 상황(모델 서빙 실패, 에러율 급증)과 추세적으로 관찰해야 하는 상황(점진적인 드리프트, 레이턴시 상승)을 구분하여 대응 우선순위를 명확히 해야 하는 것이다.

재학습 시점의 판단

드리프트가 감지되었다고 해서 무조건 재학습을 수행해야 하는 것은 아니다. 재학습 자체가 비용이 드는 작업이므로, 재학습이 실제로 필요한 시점을 판단하는 기준이 있어야 한다.

일반적으로 다음과 같은 조건들을 종합적으로 고려한다. 모델 성능 지표가 사전에 정의한 임계값 아래로 하락한 경우, 주요 입력 변수에서 지속적인 드리프트가 관측되는 경우, 비즈니스 KPI에 유의미한 영향이 나타나는 경우에는 재학습을 진행하는 것이 타당하다. 반면 일시적인 변동이거나 계절적 패턴에 의한 변화라면, 재학습보다는 지속적인 관찰이 더 적절한 대응일 수 있다.

드리프트 감지 도구

드리프트 감지와 모니터링을 처음부터 직접 구축하는 것은 상당한 공수가 필요하다. 이를 지원하는 오픈소스 도구들을 활용하면 구축 시간을 단축할 수 있다.

Evidently AI는 데이터 드리프트, 모델 성능, 데이터 품질에 대한 리포트를 자동으로 생성하는 Python 라이브러리이다. 대시보드 형태의 시각화를 제공하며, CI/CD 파이프라인에 통합하여 자동화된 검증 단계로 활용할 수도 있다.

WhyLabs는 모니터링 플랫폼으로, 데이터 프로파일링을 통해 드리프트와 이상 징후를 실시간으로 감지한다. whylogs라는 경량 로깅 라이브러리를 통해 서빙 시스템에 최소한의 오버헤드로 통합할 수 있다는 것이 특징이다.

Prometheus와 Grafana의 조합은 서비스 성능 지표 모니터링에 널리 사용되는 스택이다. 커스텀 메트릭을 정의하여 모델 관련 지표까지 통합 관리할 수 있으며, 알림 규칙 설정이 유연하다.

정리

모델은 배포된 순간부터 성능이 저하되기 시작할 수 있다. 데이터 드리프트와 컨셉 드리프트를 구분하고, 적절한 통계적 방법으로 감지하며, 체계적인 모니터링 체계를 구축하는 것이 프로덕션 ML 시스템의 안정성을 보장하는 핵심이다. 알림 전략과 재학습 시점의 판단까지 갖추었을 때 비로소 운영 가능한 ML 시스템이라 할 수 있다.

다음 포스트에서는 ML 시스템의 코드, 데이터, 모델을 아우르는 CI/CD 전략을 살펴본다.