코드만 테스트하면 되는가?

일반적인 소프트웨어에서 CI/CD는 잘 정립된 개념이다. 코드를 커밋하면 자동으로 테스트가 실행되고, 통과하면 빌드되어 배포된다. 그런데 ML 시스템에서는 이 접근법만으로 충분하지 않다.

ML 시스템의 동작은 코드, 데이터, 모델이라는 세 가지 축에 의해 결정된다. 코드가 완벽하더라도 학습 데이터에 문제가 있으면 모델이 잘못된 예측을 내놓는다. 모델의 정확도가 높더라도 서빙 파이프라인의 전처리 로직이 학습 시점과 다르면 프로덕션에서 오류가 발생한다. 결국 세 가지를 모두 검증하는 CI/CD 체계가 필요한 것이다.

이 포스트에서는 ML CI/CD가 기존 소프트웨어 CI/CD와 어떻게 다른지 살펴보고, 데이터 검증, 모델 검증, 파이프라인 검증을 아우르는 자동화 전략을 다룬다.

ML CI/CD와 소프트웨어 CI/CD의 차이

소프트웨어 CI/CD에서 테스트 대상은 코드이다. 단위 테스트, 통합 테스트, E2E 테스트를 통해 코드의 정확성을 검증하고, 통과하면 배포한다. 결정론적이며, 동일한 입력에 동일한 출력이 보장된다.

ML CI/CD에서는 상황이 다르다. 모델 학습은 본질적으로 비결정론적이다. 동일한 코드와 데이터로 학습하더라도 랜덤 시드, GPU 연산 순서, 하이퍼파라미터 탐색 등에 의해 결과가 달라질 수 있다. 테스트의 합격 기준도 "통과 혹은 실패"가 아니라 "성능 지표가 일정 임계값 이상인가"로 바뀌는 것이다.

구분소프트웨어 CI/CDML CI/CD
테스트 대상코드코드 + 데이터 + 모델
결과 판정통과/실패성능 임계값 기반
재현성결정론적비결정론적 요소 존재
아티팩트바이너리, 컨테이너모델 파일, 메타데이터
배포 후 검증기능 테스트모델 성능 모니터링

이러한 차이를 인식하지 못하고 소프트웨어 CI/CD 방법론을 그대로 적용하면, 코드는 문제없이 빌드되지만 모델은 프로덕션에서 실패하는 상황이 발생하게 된다.

데이터 테스트

ML 파이프라인에서 가장 먼저 검증해야 하는 것은 데이터이다. 모델의 품질은 데이터의 품질에 직접적으로 의존하기 때문이다.

데이터 테스트는 스키마 검증, 통계적 검증, 비즈니스 규칙 검증으로 나눌 수 있다. 스키마 검증은 데이터의 구조가 예상과 일치하는지 확인한다. 컬럼의 존재 여부, 데이터 타입, 허용 범위 등을 검사하는 것이다. 통계적 검증은 데이터의 분포가 정상 범위 내에 있는지 확인한다. 평균, 분산, 결측값 비율 등의 통계량이 이전 데이터셋과 유의미하게 다르지 않은지를 검증한다. 비즈니스 규칙 검증은 도메인 특화된 제약 조건을 검사한다. 나이가 음수일 수 없다거나, 가격이 특정 범위를 벗어날 수 없다는 등의 규칙이다.

import great_expectations as gx

context = gx.get_context()
validator = context.sources.pandas_default.read_csv("training_data.csv")

# 스키마 검증
validator.expect_column_to_exist("age")
validator.expect_column_values_to_be_of_type("age", "int64")

# 범위 검증
validator.expect_column_values_to_be_between("age", min_value=0, max_value=120)

# 결측값 검증
validator.expect_column_values_to_not_be_null("target", mostly=0.99)

# 분포 검증
validator.expect_column_mean_to_be_between("income", min_value=30000, max_value=80000)

이러한 데이터 검증을 파이프라인의 첫 단계에 배치하면, 잘못된 데이터가 학습 과정으로 흘러들어가는 것을 사전에 차단할 수 있다.

모델 테스트

데이터 검증을 통과한 후에는 학습된 모델 자체를 검증해야 한다. 모델 테스트는 성능 검증, 공정성 검증, 견고성 검증의 세 가지 차원에서 이루어진다.

성능 검증은 모델의 주요 지표가 사전에 정의한 임계값을 충족하는지 확인한다. 단순히 절대적인 성능만 볼 것이 아니라, 현재 프로덕션에 배포된 모델과 비교하는 것이 중요하다. 새 모델의 성능이 기존 모델보다 떨어진다면 배포할 이유가 없는 것이다.

def validate_model(new_model, baseline_model, test_data):
    new_metrics = evaluate(new_model, test_data)
    baseline_metrics = evaluate(baseline_model, test_data)

    # 절대 임계값 검증
    assert new_metrics["accuracy"] >= 0.85, "정확도가 최소 기준 미달"
    assert new_metrics["latency_p99"] <= 100, "레이턴시가 100ms 초과"

    # 상대 비교 검증
    assert new_metrics["f1"] >= baseline_metrics["f1"] * 0.98, \
        "F1 스코어가 기존 모델 대비 2% 이상 하락"

공정성 검증은 모델이 특정 그룹에 대해 편향된 예측을 하지 않는지 확인한다. 성별, 연령, 인종 등의 민감한 속성에 따라 모델의 성능이 크게 달라지지 않아야 한다. 견고성 검증은 엣지 케이스나 적대적 입력에 대해 모델이 안정적으로 동작하는지 확인한다.

파이프라인 검증

개별 구성 요소가 정상이더라도 전체 파이프라인이 올바르게 동작하는지는 별도로 검증해야 한다. 데이터 수집부터 전처리, 학습, 평가, 서빙까지의 흐름이 끊기지 않고 실행되는지를 확인하는 것이다.

파이프라인 검증에서 특히 주의해야 하는 것은 학습-서빙 불일치(training-serving skew)이다. 학습 시점의 전처리 로직과 서빙 시점의 전처리 로직이 다르면, 모델이 아무리 정확해도 프로덕션에서 잘못된 결과를 내놓게 된다. 전처리 코드를 학습과 서빙에서 공유하거나, 입출력 스냅샷을 비교하는 테스트를 통해 이를 방지해야 한다.

자동 재학습 트리거

모니터링 시스템에서 드리프트가 감지되거나 성능이 임계값 아래로 떨어지면, 자동으로 재학습 파이프라인을 실행하는 체계를 구축할 수 있다. 이를 자동 재학습 트리거라고 한다.

트리거 방식은 크게 세 가지가 있다. 시간 기반 트리거는 일정 주기(매일, 매주)로 재학습을 수행한다. 구현이 단순하지만, 불필요한 재학습이 발생할 수 있다. 성능 기반 트리거는 모니터링 지표가 임계값을 벗어났을 때 재학습을 실행한다. 데이터 기반 트리거는 새로운 데이터가 일정량 이상 축적되었을 때 재학습을 실행한다.

과연 완전 자동 재학습이 항상 바람직한가? 반드시 그렇지는 않다. 자동 재학습된 모델이 검증 없이 프로덕션에 배포되면 오히려 문제를 확대할 수 있다. 재학습은 자동화하되, 배포 전 검증 단계를 반드시 포함시키는 것이 안전한 접근이다.

모델 검증 게이트

재학습된 모델이 프로덕션에 배포되기 전에 반드시 통과해야 하는 검증 단계를 모델 검증 게이트(validation gate)라고 한다. 이 게이트는 자동화된 품질 관문의 역할을 하며, 성능이 기준에 미달하는 모델이 프로덕션에 나가는 것을 차단한다.

# 모델 검증 게이트 설정 예시
validation_gates:
  performance:
    accuracy_min: 0.85
    f1_min: 0.80
    latency_p99_max_ms: 100
  comparison:
    metric: f1
    threshold: 0.98  # 기존 모델 대비 최소 98%
  data_quality:
    missing_rate_max: 0.05
    drift_psi_max: 0.2

검증 게이트를 통과한 모델만 모델 레지스트리에 "승인됨(approved)" 상태로 등록되며, 이후 카나리 배포나 A/B 테스트를 통해 점진적으로 프로덕션에 반영되는 흐름이다.

실험 추적과의 통합

CI/CD 파이프라인은 실험 추적 시스템과 긴밀하게 연동되어야 한다. 자동 재학습이 실행될 때마다 하이퍼파라미터, 학습 데이터 버전, 성능 지표, 모델 아티팩트가 실험 추적 시스템에 자동으로 기록되어야 하는 것이다. MLflow, Weights & Biases 같은 도구를 파이프라인에 통합하면, 어떤 데이터로 어떤 설정의 모델이 학습되어 어떤 성능을 보였는지를 추적할 수 있다. 이러한 추적이 없으면 문제가 발생했을 때 원인을 파악하기가 극도로 어려워진다.

GitHub Actions 예시

GitHub Actions를 활용한 ML CI/CD 파이프라인의 구조를 살펴보면, 일반적인 CI/CD와 어떤 점이 다른지 구체적으로 이해할 수 있다.

name: ML CI/CD Pipeline

on:
  push:
    paths:
      - 'src/**'
      - 'data/**'
      - 'configs/**'

jobs:
  data-validation:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Validate training data
        run: python scripts/validate_data.py --config configs/data_schema.yaml
      - name: Check for data drift
        run: python scripts/check_drift.py --reference data/reference.parquet

  model-training:
    needs: data-validation
    runs-on: [self-hosted, gpu]
    steps:
      - uses: actions/checkout@v4
      - name: Train model
        run: python scripts/train.py --config configs/training.yaml
      - name: Log to experiment tracker
        run: python scripts/log_experiment.py
      - name: Upload model artifact
        uses: actions/upload-artifact@v4
        with:
          name: trained-model
          path: outputs/model/

  model-validation:
    needs: model-training
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Download model artifact
        uses: actions/download-artifact@v4
        with:
          name: trained-model
      - name: Run validation gates
        run: python scripts/validate_model.py --baseline models/production/
      - name: Check fairness metrics
        run: python scripts/check_fairness.py

  deploy-canary:
    needs: model-validation
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - name: Register model
        run: python scripts/register_model.py --status approved
      - name: Deploy canary (10%)
        run: python scripts/deploy.py --strategy canary --weight 10

이 파이프라인에서 주목할 점은 데이터 검증이 학습보다 선행하고, 모델 학습 후에는 검증 게이트를 거치며, 최종적으로 카나리 배포로 안전하게 반영된다는 것이다. 각 단계가 실패하면 이후 단계는 실행되지 않으므로, 문제가 있는 모델이 프로덕션에 도달하는 것을 구조적으로 방지할 수 있다.

정리

ML CI/CD는 코드뿐만 아니라 데이터와 모델까지 검증 대상에 포함해야 한다는 점에서 일반 소프트웨어 CI/CD와 근본적으로 다르다. 데이터 검증으로 입력의 품질을 보장하고, 모델 검증 게이트로 성능 기준을 강제하며, 파이프라인 검증으로 학습-서빙 일관성을 확인하는 다층적 접근이 필요하다. 자동 재학습 트리거와 실험 추적 시스템의 통합까지 갖추면, 모델의 전체 라이프사이클을 자동화하고 추적할 수 있는 체계가 완성된다.

다음 포스트에서는 ML 파이프라인 오케스트레이션과 워크플로우 관리 도구를 살펴본다.