콘텐츠로 이동
Data Prep
상세

Conformal Prediction

불확실성 정량화(Uncertainty Quantification)를 위한 분포 무관(Distribution-Free) 프레임워크


개요

Conformal Prediction(CP)은 기계학습 모델의 예측에 대해 통계적으로 유효한 불확실성 구간/집합을 제공하는 방법론이다. 2021년 Angelopoulos와 Bates의 튜토리얼 논문 이후 실무 적용이 급격히 증가했으며, 2025년 현재 의료 진단, 자율주행, 금융 리스크 관리 등 고위험 의사결정 영역에서 핵심 기법으로 자리잡았다.

핵심 특징

특성 설명
Distribution-Free 데이터 분포에 대한 가정 불필요
Model-Agnostic 어떤 ML 모델과도 결합 가능 (신경망, 트리, SVM 등)
Finite-Sample Guarantee 점근적(asymptotic)이 아닌 유한 표본에서 보장
Post-hoc 사전 학습된 모델에 적용 가능 (재학습 불필요)

수학적 기초

문제 설정

  • 학습 데이터: \((X_1, Y_1), \ldots, (X_n, Y_n)\)
  • 테스트 입력: \(X_{n+1}\)
  • 목표: 실제 값 \(Y_{n+1}\)을 포함하는 예측 집합 \(\mathcal{C}(X_{n+1})\) 구성

Coverage Guarantee

사용자가 지정한 오류율 \(\alpha \in (0,1)\)에 대해:

\[P(Y_{n+1} \in \mathcal{C}(X_{n+1})) \geq 1 - \alpha\]

예: \(\alpha = 0.1\)이면 90% 확률로 예측 집합이 실제 값을 포함

핵심 가정: Exchangeability

데이터 \((X_1, Y_1), \ldots, (X_{n+1}, Y_{n+1})\)교환 가능(exchangeable)해야 한다:

  • i.i.d. 데이터는 자동으로 exchangeable
  • i.i.d.보다 약한 조건 (일부 시계열, 분포 이동에도 확장 가능)

Split Conformal Prediction

가장 널리 사용되는 변형. 계산 효율성과 구현 단순성이 장점.

알고리즘

Input: 학습 데이터 D, 테스트 입력 x_test, 오류율 α
Output: 예측 집합 C(x_test)

1. D를 학습셋 D_train과 캘리브레이션셋 D_cal로 분할
2. D_train으로 모델 f 학습
3. D_cal의 각 (x_i, y_i)에 대해 non-conformity score 계산:
   s_i = score(x_i, y_i, f)
4. 분위수 계산:
   q = (1-α)-quantile of {s_1, ..., s_m, ∞}
   (정확히는 ceil((m+1)(1-α))/m 번째 값)
5. 예측 집합 구성:
   C(x_test) = {y : score(x_test, y, f) ≤ q}

Non-Conformity Score 설계

태스크에 따른 score 함수 선택:

태스크 Score 함수 예측 집합
회귀 \(s = \|y - \hat{f}(x)\|\) 구간 \([\hat{f}(x) - q, \hat{f}(x) + q]\)
분류 \(s = 1 - \hat{p}(y \mid x)\) 확률 높은 클래스 집합
분류 (APS) 누적 확률 기반 적응적 크기 집합

Python 구현

회귀: Prediction Interval

import numpy as np
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.model_selection import train_test_split

def conformal_prediction_interval(X, y, X_test, alpha=0.1):
    """
    Split Conformal Prediction for regression.
    Returns prediction intervals with (1-alpha) coverage guarantee.
    """
    # 1. 데이터 분할
    X_train, X_cal, y_train, y_cal = train_test_split(
        X, y, test_size=0.2, random_state=42
    )

    # 2. 모델 학습
    model = GradientBoostingRegressor(n_estimators=100, random_state=42)
    model.fit(X_train, y_train)

    # 3. 캘리브레이션 셋에서 residual (non-conformity score) 계산
    y_cal_pred = model.predict(X_cal)
    scores = np.abs(y_cal - y_cal_pred)

    # 4. 분위수 계산 (finite-sample correction)
    n_cal = len(y_cal)
    q_level = np.ceil((n_cal + 1) * (1 - alpha)) / n_cal
    q_hat = np.quantile(scores, q_level, method='higher')

    # 5. 예측 구간 생성
    y_test_pred = model.predict(X_test)
    lower = y_test_pred - q_hat
    upper = y_test_pred + q_hat

    return lower, upper, y_test_pred

# 사용 예시
from sklearn.datasets import make_regression

X, y = make_regression(n_samples=1000, n_features=10, noise=20)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

lower, upper, pred = conformal_prediction_interval(X_train, y_train, X_test, alpha=0.1)

# Coverage 검증
coverage = np.mean((y_test >= lower) & (y_test <= upper))
print(f"Empirical Coverage: {coverage:.3f}")  # 약 0.90
print(f"Average Interval Width: {np.mean(upper - lower):.2f}")

분류: Prediction Sets (APS)

import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split

def adaptive_prediction_sets(X, y, X_test, alpha=0.1):
    """
    Adaptive Prediction Sets (APS) for classification.
    Returns prediction sets with (1-alpha) coverage guarantee.
    """
    # 1. 데이터 분할
    X_train, X_cal, y_train, y_cal = train_test_split(
        X, y, test_size=0.2, random_state=42
    )

    # 2. 모델 학습
    model = RandomForestClassifier(n_estimators=100, random_state=42)
    model.fit(X_train, y_train)

    # 3. 캘리브레이션: APS score 계산
    prob_cal = model.predict_proba(X_cal)
    n_cal = len(y_cal)
    scores = []

    for i in range(n_cal):
        # 확률 내림차순 정렬
        sorted_idx = np.argsort(-prob_cal[i])
        cumsum = np.cumsum(prob_cal[i][sorted_idx])
        # 실제 클래스까지의 누적 확률
        true_class_rank = np.where(sorted_idx == y_cal[i])[0][0]
        scores.append(cumsum[true_class_rank])

    scores = np.array(scores)

    # 4. 분위수 계산
    q_level = np.ceil((n_cal + 1) * (1 - alpha)) / n_cal
    q_hat = np.quantile(scores, q_level, method='higher')

    # 5. 예측 집합 생성
    prob_test = model.predict_proba(X_test)
    prediction_sets = []

    for i in range(len(X_test)):
        sorted_idx = np.argsort(-prob_test[i])
        cumsum = np.cumsum(prob_test[i][sorted_idx])
        # 누적 확률이 q_hat 이하인 클래스들 포함
        included = cumsum <= q_hat
        # 최소 1개 포함
        included[0] = True
        pred_set = sorted_idx[included].tolist()
        prediction_sets.append(pred_set)

    return prediction_sets, model.classes_

# 사용 예시
from sklearn.datasets import make_classification

X, y = make_classification(n_samples=1000, n_classes=5, n_informative=10, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

pred_sets, classes = adaptive_prediction_sets(X_train, y_train, X_test, alpha=0.1)

# Coverage 검증
coverage = np.mean([y_test[i] in pred_sets[i] for i in range(len(y_test))])
avg_size = np.mean([len(s) for s in pred_sets])
print(f"Empirical Coverage: {coverage:.3f}")  # 약 0.90
print(f"Average Set Size: {avg_size:.2f}")

Conformalized Quantile Regression (CQR)

회귀에서 조건부 적응적 구간(입력에 따라 구간 폭 변화)을 제공하는 방법.

핵심 아이디어

  1. Quantile Regression으로 \(\hat{q}^{lo}(x)\), \(\hat{q}^{hi}(x)\) 학습
  2. Conformal로 구간 보정
from sklearn.ensemble import GradientBoostingRegressor

def conformalized_quantile_regression(X, y, X_test, alpha=0.1):
    """
    CQR: Conformalized Quantile Regression.
    Returns adaptive prediction intervals.
    """
    X_train, X_cal, y_train, y_cal = train_test_split(X, y, test_size=0.2)

    # Quantile Regressors 학습
    alpha_lo, alpha_hi = alpha/2, 1 - alpha/2

    model_lo = GradientBoostingRegressor(
        loss='quantile', alpha=alpha_lo, n_estimators=100
    )
    model_hi = GradientBoostingRegressor(
        loss='quantile', alpha=alpha_hi, n_estimators=100
    )

    model_lo.fit(X_train, y_train)
    model_hi.fit(X_train, y_train)

    # 캘리브레이션
    q_lo_cal = model_lo.predict(X_cal)
    q_hi_cal = model_hi.predict(X_cal)

    # Non-conformity score: max(q_lo - y, y - q_hi)
    scores = np.maximum(q_lo_cal - y_cal, y_cal - q_hi_cal)

    n_cal = len(y_cal)
    q_level = np.ceil((n_cal + 1) * (1 - alpha)) / n_cal
    q_hat = np.quantile(scores, q_level, method='higher')

    # 예측 구간
    lower = model_lo.predict(X_test) - q_hat
    upper = model_hi.predict(X_test) + q_hat

    return lower, upper

CQR vs Split CP 비교

특성 Split CP CQR
구간 폭 고정 (모든 x에서 동일) 적응적 (x에 따라 변화)
이분산성 처리 불가 가능
계산 비용 낮음 중간 (2개 모델 학습)
조건부 coverage 약함 개선됨

응용 분야

의료 진단

환자 데이터 → 질병 확률 예측 → Conformal Set
예: 폐 CT 분석에서 {정상, 결절, 암} 중 
    실제 진단을 90% 확률로 포함하는 집합 제공

의사 결정 지원: - 집합 크기 1 → 모델 확신 높음 - 집합 크기 2+ → 추가 검사 필요

자율주행

  • 객체 탐지: 보행자 위치 예측 구간
  • 궤적 예측: 주변 차량의 이동 범위
  • 의사결정: 불확실성 높으면 보수적 행동

금융 리스크

  • VaR (Value at Risk) 추정
  • 신용 점수 구간
  • 포트폴리오 손실 범위

확장 기법

Distribution Shift 대응

방법 설명
Weighted CP 중요도 가중치로 분포 이동 보정
Online CP 시간에 따른 적응적 갱신
ACI (Adaptive CI) 실시간 coverage 모니터링 및 조정

조건부 Coverage 강화

Marginal coverage만으로는 특정 하위 그룹에서 coverage 부족 가능:

  • Mondrian CP: 그룹별 개별 캘리브레이션
  • Conditional Coverage: 조건부 보장 시도 (일반적으로 불가능)
  • Group-Balanced CP: 그룹 간 공정성 고려

장단점

장점

  1. 이론적 보장: 유한 표본에서 정확한 coverage 보장
  2. 범용성: 모든 ML 모델과 호환
  3. 구현 용이: 기존 파이프라인에 쉽게 추가
  4. 해석 가능: 불확실성을 직관적으로 표현

단점

  1. 데이터 분할: 캘리브레이션용 데이터 필요 (학습 데이터 일부 사용)
  2. Marginal Coverage: 조건부 coverage는 보장하지 않음
  3. Exchangeability: 심한 분포 이동 시 보장 약화
  4. 집합 크기: 모델 성능이 낮으면 큰 집합/구간 생성

실무 권장사항

캘리브레이션 셋 크기

# 최소 권장 크기
n_cal_min = int(np.ceil(1 / alpha))  # alpha=0.1이면 최소 10개

# 실용적 권장 크기 (안정적 분위수 추정)
n_cal_recommended = max(500, int(10 / alpha))

라이브러리

라이브러리 특징
MAPIE scikit-learn 호환, 다양한 방법론
crepes 경량화, 빠른 프로토타이핑
nonconformist 기초 CP 구현
puncc 시계열 특화
# MAPIE 예시
from mapie.regression import MapieRegressor
from sklearn.ensemble import RandomForestRegressor

model = RandomForestRegressor()
mapie = MapieRegressor(model, method="plus")
mapie.fit(X_train, y_train)
y_pred, y_pis = mapie.predict(X_test, alpha=0.1)
# y_pis[:, 0, 0]: lower bounds
# y_pis[:, 1, 0]: upper bounds

참고 문헌

  1. Angelopoulos, A. N., & Bates, S. (2023). Conformal Prediction: A Gentle Introduction. Foundations and Trends in Machine Learning, 16(4), 494-591. arXiv:2107.07511

  2. Romano, Y., Patterson, E., & Candes, E. (2019). Conformalized Quantile Regression. NeurIPS. arXiv:1905.03222

  3. Vovk, V., Gammerman, A., & Shafer, G. (2005). Algorithmic Learning in a Random World. Springer.

  4. Barber, R. F., et al. (2023). Conformal Prediction Beyond Exchangeability. Annals of Statistics.

  5. Kaiser, T., & Herzog, P. (2025). A Tutorial on Distribution-Free Uncertainty Quantification Using Conformal Prediction. Advances in Methods and Practices in Psychological Science.


작성일: 2026-02-09