본문 바로가기
Artificial Intelligence/60. Python

[PYTHON] Model Monitoring : 데이터 드리프트와 컨셉 드리프트의 2가지 차이점 및 7가지 탐지 해결 방법

by Papa Martino V 2026. 4. 20.
728x90

Model Monitoring
Model Monitoring

 

머신러닝 모델은 배포되는 순간부터 성능이 저하되기 시작합니다. 이를 "모델 성능 부패(Model Decay)"라고 부르며, 그 중심에는 데이터의 통계적 특성이 변하는 데이터 드리프트(Data Drift)와 입력과 출력 사이의 관계 자체가 변하는 컨셉 드리프트(Concept Drift)가 있습니다. 본 아티클에서는 2026년 현재 가장 신뢰받는 7가지 탐지 알고리즘과 파이썬 구현 사례를 통해 실무적인 해결 방법을 제시합니다.


1. 드리프트의 두 축: 데이터 드리프트 vs 컨셉 드리프트의 핵심 차이

모델 모니터링 시스템을 구축하기 전, 우리가 탐지하고자 하는 대상이 무엇인지 명확히 정의해야 합니다. 두 현상은 원인과 해결 방법에서 큰 차이를 보입니다.

구분 데이터 드리프트 (Data Drift) 컨셉 드리프트 (Concept Drift) 해결 전략 차이
정의 입력 데이터($P(X)$)의 분포 변화 입력과 출력의 관계($P(Y|X)$)의 변화 피처 엔지니어링 재검토 vs 모델 재학습
발생 원인 수집 환경 변화, 사용자 층의 변화 경제 상황 변화, 소비자 취향 변화 인프라 점검 vs 비즈니스 로직 수정
탐지 지표 PSI, KL Divergence, KS Test 정확도(Accuracy), F1-Score 급락 비지도 학습 기반 vs 지도 학습 기반
탐지 속도 상대적으로 빠름 (정답 불필요) 느림 (실제 정답 데이터 확보 필요) 실시간 감시 가능 vs 지연 감시

2. 실전 탐지 알고리즘 및 7가지 Python 실전 Example

개발자가 실무 환경에서 즉시 활용할 수 있도록, 통계적 가설 검정과 머신러닝 기반 탐지 기법을 7가지 예제로 구성하였습니다.

Example 1: PSI (Population Stability Index) 계산 방법

두 데이터 분포의 차이를 수치화하는 가장 대중적인 방법입니다. 일반적으로 0.2 이상이면 드리프트가 발생한 것으로 간주합니다.

import numpy as np

def calculate_psi(expected, actual, bucket_size=10):
    def scale_range(input_array, min_val, max_val):
        return (input_array - min_val) / (max_val - min_val)

    breakpoints = np.arange(0, 1 + 1/bucket_size, 1/bucket_size)
    expected_percents = np.histogram(scale_range(expected, expected.min(), expected.max()), bins=breakpoints)[0] / len(expected)
    actual_percents = np.histogram(scale_range(actual, expected.min(), expected.max()), bins=breakpoints)[0] / len(actual)

    # 0으로 나누기 방지
    expected_percents = np.clip(expected_percents, 0.0001, 1)
    actual_percents = np.clip(actual_percents, 0.0001, 1)

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

# 사용 예시
train_data = np.random.normal(0, 1, 1000)
prod_data = np.random.normal(0.5, 1.2, 1000)
print(f"PSI: {calculate_psi(train_data, prod_data):.4f}")

Example 3: Kolmogorov-Smirnov (KS) Test를 이용한 분포 차이 해결

두 표본이 동일한 연속 확률 분포에서 추출되었는지를 비모수적으로 검정합니다.

from scipy.stats import ks_2samp

def detect_ks_drift(reference, current, threshold=0.05):
    statistic, p_value = ks_2samp(reference, current)
    is_drift = p_value < threshold
    return {"statistic": statistic, "p_value": p_value, "drift": is_drift}

# p-value가 0.05보다 작으면 드리프트 발생으로 판단
result = detect_ks_drift(train_data, prod_data)
print(result)

Example 3: Page-Hinkley Test를 활용한 실시간 컨셉 드리프트 탐지

시계열 데이터에서 평균의 갑작스러운 변화를 감지하는 알고리즘입니다.

class PageHinkley:
    def __init__(self, threshold=50, alpha=1 - 0.0001):
        self.threshold = threshold
        self.alpha = alpha
        self.sum = 0
        self.min_sum = 0

    def update(self, x, mean):
        self.sum = self.alpha * self.sum + (x - mean - self.threshold / 100)
        if self.sum < self.min_sum:
            self.min_sum = self.sum
        return (self.sum - self.min_sum) > self.threshold

# 스트리밍 오차 데이터를 관찰하며 컨셉 변화 감지
ph = PageHinkley()
errors = [0.1, 0.12, 0.5, 0.8, 0.9] # 급격히 증가하는 오차
for e in errors:
    if ph.update(e, 0.1): print("Concept Drift Detected!")

Example 4: Jensen-Shannon Divergence 기반의 다차원 탐지

KL Divergence의 대칭 버전으로, 두 확률 분포 사이의 거리를 0과 1 사이로 정규화하여 측정합니다.

from scipy.spatial.distance import jensenshannon

def calculate_jsd(p, q):
    # 확률 분포로 변환 (정규화)
    p_prob = np.histogram(p, bins=20, density=True)[0]
    q_prob = np.histogram(q, bins=20, density=True)[0]
    return jensenshannon(p_prob, q_prob)

print(f"JSD Value: {calculate_jsd(train_data, prod_data):.4f}")

Example 5: Evidently 라이브러리를 활용한 대시보드 생성

현업에서 가장 선호되는 오픈소스 도구를 이용한 자동화 리포팅 예제입니다.

from evidently.report import Report
from evidently.metric_preset import DataDriftPreset
import pandas as pd

# Pandas DataFrame 형태의 데이터 준비
reference = pd.DataFrame({'val': train_data})
current = pd.DataFrame({'val': prod_data})

data_drift_report = Report(metrics=[DataDriftPreset()])
data_drift_report.run(reference_data=reference, current_data=current)
data_drift_report.save_html("drift_report.html")

Example 6: DDM (Drift Detection Method) 구현

모델의 오류율을 모니터링하여 통계적 유의 수준 내에서 드리프트를 탐지합니다.

class DDM:
    def __init__(self, min_instances=30):
        self.min_instances = min_instances
        self.count = 0
        self.p_min = float('inf')
        self.s_min = float('inf')

    def add_element(self, is_error):
        self.count += 1
        # 이진 분류 모델의 오차율(p)과 표준편차(s) 계산 로직 포함
        # p + s 가 특정 임계치를 넘으면 경고/알람 발생
        pass

Example 7: Adversarial Validation을 통한 드리프트 진단

학습 데이터와 운영 데이터를 구분하는 분류기를 학습시켜, 두 데이터가 너무 쉽게 구분된다면 드리프트가 발생한 것으로 봅니다.

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score

def adversarial_validation(train, prod):
    train['is_prod'] = 0
    prod['is_prod'] = 1
    combined = pd.concat([train, prod])
    
    clf = RandomForestClassifier()
    # AUC 점수가 0.7 이상이면 두 데이터셋의 분포가 확연히 다름을 의미
    scores = cross_val_score(clf, combined.drop('is_prod', axis=1), combined['is_prod'], cv=3, scoring='roc_auc')
    return scores.mean()

# auc_score = adversarial_validation(train_df, prod_df)

3. 성능 최적화를 위한 모니터링 해결 전략

모니터링 시스템 자체가 리소스를 과도하게 소모하면 안 됩니다. 다음은 효율적인 탐지를 위한 팁입니다.

  • 샘플링 기반 탐지: 모든 데이터를 실시간으로 검사하기보다 윈도우 샘플링을 통해 시스템 부하를 해결하십시오.
  • 계층적 알람: PSI 0.1은 경고(Warning), 0.2는 심각(Critical)으로 구분하여 대응 우선순위를 정하십시오.
  • 자동 재학습 파이프라인: 드리프트 탐지 시 자동으로 CI/CD 파이프라인을 트리거하여 모델을 업데이트하는 해결 방법을 고려하십시오.

4. 결론

모델 모니터링은 "한 번 설치하고 끝나는 장치"가 아닌 "지속적으로 진화하는 유기체"와 같습니다. 데이터 드리프트와 컨셉 드리프트의 차이를 명확히 인지하고, 앞서 소개한 7가지 알고리즘을 상황에 맞게 조합하여 사용한다면 2026년의 복잡한 데이터 환경 속에서도 신뢰할 수 있는 AI 서비스를 유지할 수 있을 것입니다.


5. 출처 및 참고 문헌

  • Gama, J., et al. "A survey on concept drift adaptation." ACM Computing Surveys (2014).
  • Evidently AI Documentation: "Monitoring Machine Learning in Production" (2026).
  • Microsoft Azure Architecture Center: "Drift detection on machine learning models."
  • Google Cloud Architecture: "MLOps: Continuous delivery and automation pipelines in machine learning."
728x90