왜 모델 배포에서 품질 회귀가 위험한가

일반 서비스 배포는 에러율·지연시간 같은 시스템 지표로 회귀를 잡을 수 있다. 하지만 ML 모델은 HTTP 200을 정상 반환하면서도 예측 품질만 조용히 나빠지는 경우가 흔하다. 학습 데이터 분포가 바뀌었거나, 전처리 코드가 살짝 어긋났거나, 새 체크포인트가 특정 세그먼트에서만 성능이 떨어지는 식이다. 이런 회귀는 시스템 대시보드에 잡히지 않고, 며칠 뒤 비즈니스 지표가 흔들린 다음에야 발견된다. 그래서 트래픽 일부만 신규 모델에 흘려보내며 품질을 직접 비교하는 카나리 전략이 필요하다.

A/B 카나리의 기본 구조

핵심은 세 가지다. 첫째, 신규 모델(canary)에 소량 트래픽을 라우팅한다. 둘째, 동일 입력을 기존 모델(control)과 함께 처리하거나 로깅해 비교 가능한 상태를 만든다. 셋째, 품질 지표가 임계치를 넘으면 자동 롤백한다. 여기서 A/B와 순수 카나리를 구분하면 좋다.

구분목적트래픽판정 기준
카나리회귀 여부 확인1~5%안전성(에러·지연·기본 품질)
A/B 테스트어느 모델이 더 나은지10~50%통계적 유의성(전환율 등)

실무에서는 카나리로 먼저 "터지지 않는지"를 검증하고, 통과하면 A/B로 확장해 "실제로 더 좋은지"를 판단하는 2단계 흐름이 안정적이다.

트래픽 분할: 게이트웨이 설정 예시

가중치 기반 라우팅은 서비스 메시나 인그레스에서 처리하는 게 깔끔하다. 애플리케이션 코드에 분기를 심으면 롤백이 느려진다.

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: model-serving
spec:
  hosts: ["model.internal"]
  http:
    - route:
        - destination:
            host: model-serving
            subset: control
          weight: 95
        - destination:
            host: model-serving
            subset: canary
          weight: 5   # 여기만 조정해 점진 확대

weight 값만 올리면 5% → 25% → 50%로 확장할 수 있고, 문제가 생기면 canary weight를 0으로 되돌리는 것이 곧 롤백이다.

품질 지표를 어떻게 관측할까

정답 라벨이 즉시 오지 않는 온라인 환경에서는 대리 지표(proxy metric)를 쓴다. 예측 점수 분포, 클래스별 예측 비율, 신뢰도(confidence) 평균, 사용자 클릭·이탈 같은 행동 신호가 대표적이다. 두 모델의 출력을 함께 로깅해 두면 나중에 라벨이 도착했을 때 지연 평가도 가능하다.

import logging, json

def log_prediction(request_id, variant, features, output):
    logging.info(json.dumps({
        "request_id": request_id,
        "variant": variant,              # "control" or "canary"
        "score": float(output.score),
        "label_pred": int(output.label),
        "confidence": float(output.prob),
    }))

# control/canary가 같은 request_id로 남으므로 사후 페어 비교 가능

회귀 판정: 통계 비교 쿼리

단순히 "평균이 낮아졌다"로 판정하면 트래픽 노이즈에 흔들린다. 세그먼트별로 나눠 보고, 표본 수가 충분한지 함께 확인해야 한다.

SELECT
  variant,
  COUNT(*)                       AS n,
  AVG(confidence)                AS avg_conf,
  AVG(CASE WHEN label_pred=1 THEN 1 ELSE 0 END) AS positive_rate
FROM predictions
WHERE ts >= NOW() - INTERVAL '30 minutes'
GROUP BY variant
HAVING COUNT(*) > 500;   -- 표본 부족 구간은 판정 제외

positive_rate가 control 대비 특정 임계(예: 상대 5%)를 벗어나면 예측 분포가 밀렸다는 신호다. 라벨이 있는 지연 평가에서는 정밀도·재현율을 같은 방식으로 비교한다.

자동 롤백과 승격

판정 로직은 사람이 대시보드를 보는 대신 자동화해야 실효성이 있다. 관측 지표가 가드레일을 넘으면 canary weight를 0으로, 정상이면 단계적으로 올린다.

if canary["n"] < 500:
    action = "hold"                       # 표본 부족: 판단 보류
elif canary["positive_rate"] < control["positive_rate"] * 0.95:
    action = "rollback"                   # 분포 회귀 감지
elif canary["avg_conf"] < control["avg_conf"] * 0.9:
    action = "rollback"
else:
    action = "promote"                    # 다음 weight 단계로

실무 주의점

  • 표본 크기 함정: 초반 몇 분의 소량 트래픽으로 성급히 롤백하면 정상 모델도 튕겨낸다. 최소 표본과 관측 시간을 반드시 둔다.
  • 세그먼트 편향: 전체 평균은 같아도 특정 지역·기기·신규 사용자에서만 회귀하는 경우가 있다. 주요 세그먼트별로 나눠 판정한다.
  • 전처리 일치: 모델 자체보다 피처 파이프라인 불일치로 회귀가 더 자주 난다. 카나리는 모델 가중치뿐 아니라 전처리까지 포함해 배포한다.
  • 지연 라벨 반영: 온라인 대리 지표로 통과했더라도, 라벨 도착 후 재평가하는 배치 검증을 함께 돌려 최종 확인한다.

결국 카나리는 "확신을 얻는 절차"가 아니라 "틀렸을 때 빠르게 되돌리는 안전장치"다. 롤백을 값싸고 자동으로 만들어 두는 것이 가장 중요한 투자다.