위험을 고려한 의사결정

먼저 읽을 장 21.2 예측분포와 구간의 평가

예측은 그 자체로 끝이 아니다. 고장 확률은 점검 여부로, 물성 예측은 합성 여부로, 발열량 예측은 냉각 설계로 이어진다. 이 마지막 단계에서 많은 연구가 기본값에 기댄다. 확률이 0.5를 넘으면 경보, 예측 평균으로 설계, 평균이 목표를 넘는 후보만 합성. 이 기본값들은 틀렸을 때의 비용이 대칭이고 불확실성이 어디서나 같다는 암묵적인 가정 위에 있고, 그 가정은 대개 틀린다.

이 장은 21.1–21.3장에서 얻은 예측분포와 불확실성을 실제 결정 규칙으로 바꾸는 방법을 다룬다. 12.3장에서 점 예측을 "손실을 최소화하는 결정"으로 보았다. 여기서는 그 관점을 연구와 운영의 결정으로 넓힌다. 기대 효용을 최대화하는 선택, 비용으로 정하는 threshold, 불확실할 때 판단을 보류하는 선택적 예측, 그리고 모델을 쓰지 말아야 할 범위를 명시하는 것이다.

학습 목표

  • 예측을 의사결정으로 연결한다
  • 비용과 위험을 반영한 결정 규칙을 설계한다

예측에서 결정으로

왜 필요한가. 같은 예측이라도 결정의 결과(효용·비용)가 다르면 최선의 행동이 다르다. 미탐지가 치명적인 안전 점검과 오경보가 비싼 대량 선별에서, 같은 고장 확률 0.2에 대한 최선의 행동은 반대일 수 있다. 결정 규칙은 예측과 비용 구조를 함께 입력으로 받아야 한다.

정의. 행동 aa를 고르면, 아직 모르는 결과 yy에 따라 효용 U(a,y)U(a, y)를 얻는다(비용이면 부호를 바꾼다). 예측분포 p(y∣x)p(y \mid x)가 있으면 기대 효용

Uˉ(a)=Ey∼p(y∣x)[U(a,y)]\bar U(a) = \mathbb{E}_{y \sim p(y \mid x)}\big[U(a, y)\big]

을 최대화하는 행동 a⋆=arg⁡max⁡aUˉ(a)a^\star = \arg\max_a \bar U(a)가 최적 결정이다. 12.3장의 "손실의 기댓값을 최소화하는 점 예측"이 행동이 숫자 하나인 특수한 경우다. 이 규칙이 실제로 최적이려면 예측분포가 맞아야 한다. 그래서 21.2장의 보정이 결정의 전제 조건이 된다.

직관. 물성 예측 모델이 후보 물질 2000개에 대해 평균과 표준편차를 낸다. 물성이 목표 tt를 넘는 물질을 찾으면 효용 10, 합성 한 번에 비용 1이 든다. "예측 평균이 목표를 넘는 후보만 합성한다"는 규칙은 불확실성을 무시한다. 평균은 목표보다 낮아도 불확실성이 커서 목표를 넘을 가능성이 충분한 후보를 놓친다. 기대 효용으로 보면 합성할 조건은 10⋅P(y>t)−1>010 \cdot P(y > t) - 1 > 0, 즉 P(y>t)>0.1P(y > t) > 0.1이다.

import numpy as np
from scipy.stats import norm

rng = np.random.default_rng(0)
N = 2000
mu = rng.normal(0.0, 1.0, N)                    # 후보 물질의 예측 평균 (표준화된 물성)
sd = rng.uniform(0.2, 1.5, N)                   # 예측 표준편차: 후보마다 다르다
y = mu + sd * rng.normal(size=N)                # 실제 물성 (예측분포가 보정되어 있다고 가정)
t, gain, cost = 1.5, 10.0, 1.0                  # 목표 물성 t를 넘으면 +10, 합성 비용 1

p_hit = norm.sf(t, mu, sd)                      # P(y > t), (N,)
rules = {
    "평균이 목표 이상 (mu > t)": mu > t,
    "기대 효용 > 0 (P > 0.1)": gain * p_hit - cost > 0,
    "모두 합성": np.ones(N, bool),
}
for name, go in rules.items():
    hits = np.sum(go & (y > t))
    utility = gain * hits - cost * go.sum()
    print(f"{name:22s} 합성 {go.sum():4d}개  성공 {hits:3d}개  총 효용 {utility:6.0f}")
평균이 목표 이상 (mu > t)     합성  127개  성공  86개  총 효용    733
기대 효용 > 0 (P > 0.1)    합성  673개  성공 232개  총 효용   1647
모두 합성                  합성 2000개  성공 262개  총 효용    620

평균 규칙은 127개를 합성해 86개를 찾았다. 성공률은 높지만 많은 성공을 놓쳤다. 기대 효용 규칙은 673개를 합성해 232개를 찾고, 총 효용이 두 배를 넘는다(1647 대 733). 모두 합성하면 262개를 다 찾지만 비용 때문에 총 효용이 가장 낮다. 불확실성은 피해야 할 것만이 아니라 탐색의 가치를 담고 있다. 이 생각은 베이지안 최적화와 실험 설계(25.3장)의 획득 함수로 이어진다.

연속 결정과 분위수. 결정이 연속값일 때도 같다. 장비의 냉각 용량 cc를 정하는데, 1 kW 부족하면 비용 cu=9c_u = 9, 1 kW 남으면 co=1c_o = 1이라 하자. 기대 비용을 cc로 미분해 0으로 두면 최적 용량은 예측분포의

τ=cucu+co=0.9\tau = \frac{c_u}{c_u + c_o} = 0.9

분위수다. 12.3장에서 pinball loss의 최적해가 τ\tau 분위수였던 것과 같은 구조다. 장비마다 예측 표준편차가 다른 경우를 본다.

m = 20000
heat_mu_kw = rng.uniform(5.0, 15.0, m)          # 장비별 예측 발열량 평균 (kW)
heat_sd_kw = rng.uniform(0.5, 3.0, m)           # 예측 표준편차: 장비마다 다르다
heat_kw = heat_mu_kw + heat_sd_kw * rng.normal(size=m)   # 실제 발열량
C_UNDER, C_OVER = 9.0, 1.0                      # 냉각 부족 1 kW당 9, 과잉 1 kW당 1
tau = C_UNDER / (C_UNDER + C_OVER)              # 최적 분위수 0.9

def mean_cost(cap_kw):
    short = np.maximum(heat_kw - cap_kw, 0)
    return np.mean(C_UNDER * short + C_OVER * np.maximum(cap_kw - heat_kw, 0))

z = norm.ppf(tau)
for name, cap in (("평균으로 설계", heat_mu_kw),
                  ("0.9 분위수로 설계", heat_mu_kw + z * heat_sd_kw),
                  ("0.9 분위수, sd를 절반으로 과소추정", heat_mu_kw + z * 0.5 * heat_sd_kw),
                  ("평균 + 고정 여유 (같은 평균 용량)", heat_mu_kw + z * heat_sd_kw.mean())):
    print(f"{name:24s} 평균 용량 {cap.mean():5.2f} kW   평균 비용 {mean_cost(cap):.3f}")
평균으로 설계                  평균 용량 10.02 kW   평균 비용 6.973
0.9 분위수로 설계              평균 용량 12.26 kW   평균 비용 3.052
0.9 분위수, sd를 절반으로 과소추정   평균 용량 11.14 kW   평균 비용 3.847
평균 + 고정 여유 (같은 평균 용량)    평균 용량 12.26 kW   평균 비용 3.457

평균으로 설계하면 절반 가까이 냉각이 부족해 평균 비용이 6.973이다. 0.9 분위수로 설계하면 3.052로 절반 이하가 된다. 같은 평균 용량(12.26 kW)이라도 모든 장비에 같은 여유를 더하면 3.457로, 불확실성에 맞춰 여유를 나눈 쪽보다 나쁘다. 불확실한 장비에는 여유를 더 주고 확실한 장비에서는 아낀 것이다. 표준편차를 절반으로 과소추정하면(과신) 3.847로 나빠진다. 결정의 질은 불확실성 추정의 질에 직접 묶여 있다.

비용 기반 threshold

왜 필요한가. 이진 결정(경보를 낼까, 점검할까, 양성으로 판정할까)에서 분류 모델의 확률을 결정으로 바꾸는 threshold가 필요하다. 0.5는 두 오류의 비용이 같을 때만 최적이다.

정의. 고장 확률 pp가 보정되어 있을 때, 경보를 내면 기대 비용은 (1−p) cFP(1-p)\,c_{FP}(정상인데 점검), 내지 않으면 p cFNp\,c_{FN}(고장인데 놓침)이다. 경보가 나은 조건은

(1−p) cFP<p cFN  ⟺  p>cFPcFP+cFN(1-p)\,c_{FP} < p\,c_{FN} \iff p > \frac{c_{FP}}{c_{FP} + c_{FN}}

이다. 정답일 때의 비용이 0이 아니면 그 차이로 바꿔 쓴다(Elkan, 2001). 미탐지가 오경보보다 20배 비싸면 threshold는 1/21≈0.0481/21 \approx 0.048이다. 16.6장의 threshold 선택이 정밀도·재현율 같은 지표를 보고 고르는 것이라면, 여기서는 비용으로 직접 정한다.

보정이 전제다. 이 식은 pp가 실제 확률일 때만 맞다. 보정이 틀린 확률에서는 같은 식이 엉뚱한 threshold를 준다. 이때는 검증 데이터에서 비용을 직접 최소화하는 threshold를 고르거나, 먼저 확률을 재보정한다(21.2장). 고장률이 약 7%인 설비에서 보정된 모델과, 순위는 같지만 보정이 틀린 모델을 비교한다.

sig = lambda z: 1 / (1 + np.exp(-z))
s = rng.normal(size=40000)                      # 이상 점수의 근거가 되는 신호
logit = -3.5 + 1.5 * s                          # 참 로짓: 고장률 약 7%
fault = rng.uniform(size=s.size) < sig(logit)
C_FP, C_FN = 1.0, 20.0                          # 불필요한 점검 1, 놓친 고장 20
val, te = slice(0, 20000), slice(20000, None)

def cost_per_1000(p, thr, idx):
    alarm = p[idx] > thr
    return 1000 * np.mean(C_FP * (alarm & ~fault[idx]) + C_FN * (~alarm & fault[idx]))

models = {"보정된 모델": sig(logit), "보정이 틀린 모델": sig(2.5 * logit + 3.0)}
thr_bayes = C_FP / (C_FP + C_FN)
grid = np.linspace(0.001, 0.6, 600)
for name, p in models.items():
    thr_val = grid[np.argmin([cost_per_1000(p, g, val) for g in grid])]   # 검증 데이터로 고른 threshold
    row = "  ".join(f"thr {thr:.3f}: {cost_per_1000(p, thr, te):5.1f}" for thr in (0.5, thr_bayes, thr_val))
    print(f"{name:9s} 1000대당 비용  {row}")
보정된 모델    1000대당 비용  thr 0.500: 1184.8  thr 0.048: 522.5  thr 0.049: 520.1
보정이 틀린 모델 1000대당 비용  thr 0.500: 870.0  thr 0.048: 557.7  thr 0.013: 521.4

보정된 모델에서는 기본값 0.5가 1000대당 1184.8, 비용으로 정한 0.048이 522.5로 절반 이하다. 검증 데이터에서 고른 threshold(0.049)가 이론값과 거의 같다. 보정이 틀린 모델에서는 이론 threshold가 557.7로 덜 좋고, 검증으로 고른 0.013에서 521.4로 보정된 모델과 같은 수준을 회복한다. 두 모델은 순위가 같으므로, threshold만 제대로 고르면 같은 결정을 내릴 수 있다. 보정이 틀렸을 때 threshold는 숫자의 의미를 잃고, 검증 데이터가 그 의미를 되찾아 준다.

비용을 어떻게 정하나. 비용은 모델이 아니라 연구자와 이해관계자가 정한다. 점검 한 번의 인건비, 고장 한 번의 손실, 놓친 진단의 임상적 결과를 같은 단위로 비교하는 일은 어렵고, 정확한 숫자를 얻기 어려운 경우가 많다. 그래도 비용의 비율만 정하면 threshold가 정해진다. 비율을 하나로 정하기 어려우면 여러 비율에서 결정과 비용이 어떻게 바뀌는지 표로 보고한다. 어떤 비율에서 결정이 뒤집히는지가 그 자체로 유용한 정보다.

선택적 예측

왜 필요한가. 모든 사례에 답할 필요가 없는 경우가 많다. 모델이 불확실한 사례는 사람이 검토하거나, 추가 측정을 하거나, 판단을 미룰 수 있다. 이것을 선택적 예측(selective prediction) 또는 보류(abstention, reject option)라 한다.

coverage–risk 곡선. 확신도 점수(분류에서는 예측 class의 확률 max⁡(p,1−p)\max(p, 1-p), 회귀에서는 예측 표준편차의 역수)로 사례를 정렬하고, 확신이 큰 순서로 일부에만 답한다. 답한 비율을 coverage(21.2·21.3장의 구간 포함률과는 다른 뜻이다), 답한 사례의 오류율을 risk라 하고, coverage를 1에서 줄여 가며 risk를 그린 것이 coverage–risk 곡선이다. 확신도가 좋은 불확실성 추정이라면 coverage를 줄일수록 risk가 빠르게 준다. 딥러닝 분류기에서 원하는 risk를 높은 확률로 보장하도록 확신도 threshold를 고르는 방법도 연구되었다(Geifman & El-Yaniv, 2017).

Chow의 규칙. 보류에도 비용 dd가 든다고 하자(오류 비용을 1로 두었을 때). 답하면 기대 비용은 오류 확률 1−max⁡kpk1 - \max_k p_k, 보류하면 dd이므로, 보정된 확률에서 최적 규칙은

보류  ⟺  max⁡kpk<1−d\text{보류} \iff \max_k p_k < 1 - d

이다(Chow, 1970). 보류가 싸면(dd가 작으면) 많이 보류하고, 비싸면 확실하지 않아도 답한다. d≥0.5d \ge 0.5이면 이진 분류에서 보류할 일이 없다.

신호가 강한 집단 A(80%)와 약한 집단 B(20%)가 섞인 이진 분류에서 coverage를 줄여 가며 오류율과 집단별 응답률을 본다.

n = 20000
group_b = rng.uniform(size=n) < 0.2             # 집단 B: 전체의 20%, 신호가 약하다
signal = rng.normal(size=n) * np.where(group_b, 0.8, 2.5)
p1 = sig(signal)                                # 보정된 class-1 확률
label = rng.uniform(size=n) < p1
conf = np.maximum(p1, 1 - p1)                   # 예측 class의 확률
wrong = (p1 > 0.5) != label

print("coverage  오류율   집단A 응답률  집단B 응답률")
for cov in (1.0, 0.8, 0.6):
    answer = conf >= np.quantile(conf, 1 - cov)  # 확신이 큰 순서로 cov 비율만 답한다
    print(f"  {cov:.1f}     {wrong[answer].mean():.3f}     {answer[~group_b].mean():.3f}       {answer[group_b].mean():.3f}")

d = 0.2                                         # 보류 비용 (오류 비용 1 대비)
reject = conf < 1 - d                           # Chow 규칙
print(f"보류 없음 평균 비용 {wrong.mean():.3f}   Chow 규칙(d = {d}) 평균 비용 {np.mean(np.where(reject, d, wrong)):.3f}"
      f"  보류율 {reject.mean():.3f}")
coverage  오류율   집단A 응답률  집단B 응답률
  1.0     0.221     1.000       1.000
  0.8     0.167     0.854       0.579
  0.6     0.111     0.692       0.224
보류 없음 평균 비용 0.221   Chow 규칙(d = 0.2) 평균 비용 0.143  보류율 0.520

coverage를 0.6으로 줄이면 오류율이 0.221에서 0.111로 절반이 된다. Chow 규칙(d=0.2d = 0.2)은 평균 비용을 0.221에서 0.143으로 줄인다. 대가는 두 가지다. 보류율이 0.520으로 절반을 넘으므로 그만큼의 사례를 사람이 처리해야 하고, 보류가 집단 B에 몰린다. coverage 0.6에서 집단 A는 69%에 답을 받지만 집단 B는 22%만 받는다.

확신도의 한계. 선택적 예측은 확신도가 오류를 잘 가려낸다는 가정 위에 있다. 21.1장에서 보았듯 학습 범위 밖의 입력에도 softmax 확률이 1에 가까울 수 있다. 그런 입력은 확신도로 보류되지 않고 자신 있게 틀린다. 그래서 선택적 예측은 OOD 탐지(21.4장)와 함께 쓴다. "확신이 작으면 보류"와 "학습 범위 밖이면 보류"는 다른 규칙이다.

적용 범위

모델 적용 범위의 명시. 모든 모델에는 믿을 수 있는 입력의 범위가 있다. 학습 데이터의 조건 범위(온도, 농도, 재료 계열), 검증한 일반화 대상(21.4장), 확인한 측정 장비가 그 범위를 정한다. 화학·독성학의 QSAR 모델에서는 이것을 적용 영역(applicability domain)이라 부르고 모델과 함께 보고하는 관행이 있다. 적용 범위는 다음처럼 구체적으로 쓴다.

  • 입력 범위: "온도 15–35 °C, C-rate 1–2C에서 학습·검증. 45 °C는 외삽으로 오차가 네 배."
  • 검증한 일반화: "같은 공정의 새 셀에서 검증. 다른 제조사 셀은 검증하지 않음."
  • 운영 중 점검: "입력이 학습 범위 밖이거나 drift 경보가 나면 예측을 쓰지 않고 측정한다."

범위 밖의 입력에 대한 행동을 미리 정해 두는 것이 핵심이다. 그 행동이 보류, 추가 측정, 보수적 기본값 중 무엇인지는 앞 절의 비용 분석으로 정한다.

집단별 편향. 결정 규칙은 집단마다 다른 결과를 낳을 수 있다. 앞 절에서 보류가 신호가 약한 집단에 몰린 것처럼, 하나의 threshold가 집단마다 다른 오경보율·미탐지율을 만든다. 모델이 어떤 집단에서 덜 보정되어 있으면(21.2장의 집단별 calibration) 같은 threshold가 그 집단에서는 비용 최적이 아니다. 결정 규칙을 평가할 때는 전체 비용과 함께 집단별 비용·오류율·보류율을 보고한다. 과학 연구에서도 이 문제는 그대로다. 잘 연구된 재료 계열, 측정이 많은 지역, 표본이 많은 환자군에서만 잘 맞고 나머지에서 보류하거나 틀리는 모델은, 연구 자원을 이미 잘 아는 곳에 더 몰리게 만든다.

실습

이상 탐지 모델의 threshold를 오경보·미탐지 비용으로 결정하고, 불확실할 때 판단을 보류하는 규칙을 평가한다. 코드는 labs/reliability/decision-making/alarm_policy.py다. 데이터는 설비 센서를 흉내 낸 합성 데이터다(실제 설비 데이터가 아니다). 특징 6개, 고장률 약 5%이고, 모델은 scikit-learn의 로지스틱 회귀다. 비용은 오경보 1, 미탐지 25, 사람의 검토(보류) 0.6이다. 네 정책을 비교한다. A는 기본값 0.5, B는 이론 threshold cFP/(cFP+cFN)c_{FP}/(c_{FP}+c_{FN}), C는 검증 데이터에서 비용을 최소화한 threshold, D는 C에 보류 구간을 더한 것이다(검토하면 정답을 안다고 가정). 마지막으로 센서 하나가 0.5만큼 어긋난 데이터에 정책 D를 적용한다. 집필 시점의 실행 결과는 다음과 같다(시드 0).

보정 점검 (시험): 구간  평균 예측  관측 빈도  개수
  [0.00, 0.02)  0.007     0.007   11363
  [0.02, 0.05)  0.032     0.028    3984
  [0.05, 0.10)  0.071     0.062    2100
  [0.10, 0.30)  0.167     0.160    1955
  [0.30, 1.00)  0.485     0.510     598

정책                         threshold   사례당 비용   보류율   미탐지율(고장 중)
A  0.5                          0.500       0.9923    0.000   0.845
B  이론 c_FP/(c_FP+c_FN)          0.038       0.4259    0.000   0.156
C  검증에서 최적                      0.030       0.4387    0.000   0.122
D  C + 보류 [0.020, 0.360]        0.030       0.3481    0.410   0.079

센서 0 drift +0.5 후 정책 D: 사례당 비용 0.3914, 보류율 0.521, 경보율 0.467 (drift 전 0.337)

결과를 네 가지로 읽는다.

  • 보정을 먼저 확인했다. 확률 구간별 평균 예측과 관측 빈도가 가깝다. 그래서 이론 threshold를 쓸 근거가 있다.
  • 기본값 0.5는 고장의 85%를 놓친다. 사례당 비용 0.99로, 비용 기반 threshold(0.43)의 두 배가 넘는다.
  • 이론 threshold(B)가 검증으로 고른 threshold(C)보다 조금 낫다. 확률이 보정되어 있으면 검증 데이터로 threshold를 다시 고르는 것은 표본 잡음만 더할 수 있다. 보정이 틀렸을 때와 반대다.
  • 보류는 비용을 0.43에서 0.35로 줄이고 미탐지율을 0.079로 낮추지만, 사례의 41%를 사람이 검토해야 한다. 이 검토량을 감당할 수 있는지는 모델 밖의 질문이다. 센서 drift가 생기면 경보율이 0.337에서 0.467로, 보류율이 0.52로 늘고 비용도 오른다. 결정 규칙은 21.4장의 drift 감시와 함께 운영해야 한다.

이어서 연습문제로 비용 기반 threshold와 검증 데이터에서의 threshold 선택을 구현한다.

실패 사례

  • 0.5 threshold를 기본값으로 쓰는 것. 두 오류의 비용이 같고 확률이 보정되어 있을 때만 최적이다. 비용 비율을 정하고 threshold를 유도하거나 검증 데이터에서 고른다.
  • 보정을 확인하지 않고 이론 threshold를 쓰는 것. 정확도·AUC가 높아도 확률의 크기가 틀릴 수 있다. reliability diagram부터 본다(21.2장).
  • 예측 평균으로 결정하는 것. 비용이 비대칭이면 최적 결정은 평균이 아니라 분위수이고, 탐색 문제에서는 불확실성 자체가 가치를 가진다.
  • 보류의 비용과 분포를 보고하지 않는 것. 보류는 오류율을 줄이지만 누군가 그 사례를 처리해야 하고, 보류가 특정 집단에 몰릴 수 있다.
  • 확신도만으로 보류하는 것. 학습 범위 밖의 입력은 자신 있게 틀린다. 적용 범위와 OOD 점검을 함께 쓴다.
  • 결정 규칙을 한 번 정하고 다시 보지 않는 것. 분포 이동이 생기면 확률의 보정과 함께 최적 threshold도 바뀐다.

통과 기준

  • 자신의 모델(또는 실습 모델) 출력을 비용 비율을 명시한 결정 규칙(threshold, 분위수, 보류 규칙 중 해당하는 것)으로 바꾸고, 기본 규칙(0.5, 평균) 대비 기대 비용을 시험 데이터에서 비교해 보고한다.
  • 결정 규칙의 집단별(또는 조건별) 오류율·보류율과, 모델을 쓰지 않을 적용 범위를 함께 명시한다.
  • 연습문제 cost-threshold의 테스트를 통과한다.

참고 문헌

  • Chow (1970). On optimum recognition error and reject tradeoff. IEEE Transactions on Information Theory, 16(1), 41–46. — 오류–보류 trade-off와 최적 보류 규칙
  • Elkan (2001). The foundations of cost-sensitive learning. Proceedings of the Seventeenth International Joint Conference on Artificial Intelligence (IJCAI 2001), 973–978. — 비용 행렬과 최적 결정 threshold
  • Geifman, El-Yaniv (2017). Selective classification for deep neural networks. Advances in Neural Information Processing Systems 30 (NIPS 2017). — 딥러닝 분류기의 선택적 분류와 risk 보장