Lemma
수학, 거꾸로
도입 · 신뢰도 대 빈도

70%를 믿어도 될까?

모델이 “70% 확신”이라고 말한다. 그런 예측을 많이 모아 보면 _열에 일곱_이 정말 맞을까? 가끔은. 대개는 아니다. 화면에 찍힌 수와 장기 빈도는 _서로 다른 두 양_이고, 둘을 맞추는 일이 이다. 요즘 신경망은 처음부터 이걸 거의 못 맞춘다. 처방은 스칼라 하나 — 자신 있게 틀리기에 나온 그 다이얼 — 를 따로 빼둔 정답에 맞춰 조정하는 것뿐이다.

신뢰도는 수다. 진실은 빈도다. 캘리브레이션은 그 사이의 격차다. 보정은 예측한 를 관측 빈도와 견주는 일이다. 온도 적합은 1차원 최적화 — 스칼라 하나, 목적 함수 하나, 하강 하나다.

위젯 — 신뢰도 다이어그램
상태과신
T (진실)0.55
선택 구간[0.8, 0.9]
ECE0.086
예측 확률관측 정확도0.00.00.50.51.01.0
국소 기울기0.56
각 막대는 예측 확률이 0.1 폭 구간에 들어간 예제들을 묶고, 그 막대의 높이는 실제로 맞춘 비율이다. 초록 점선 대각선이 완벽한 캘리브레이션. T = 0.55일 때 높은 신뢰도 쪽 막대들이 대각선 아래로 처진다 — 모델이 0.9라 말하면 실제론 약 96%만 맞다. 막대를 클릭하면 그 구간이 선택 구간: 갈색 선은 그 점에서의 접선, 국소 선형 근사다. 기울기가 1에 가깝다면 "곡선이 진실과 평행, 단지 평행 이동"; 1과 다르다면 캘리브레이션 오차가 신뢰도에 따라 달라진다는 뜻 — 그래서 단 하나의 스칼라 (온도) 가 곡선 전체를 대각선 쪽으로 회전시킬 수 있다.
흐름
1

확률이 약속하는 것

모델이 어떤 클래스에 대해 0.70.7이라는 수를 내놓을 때, 그 수는 무엇을 뜻해야 할까? 정직한 계약은 장기 빈도다. 모델이 “70%“라고 찍은 예제들을 잔뜩 모았을 때 그중 약 70%70\%가 실제로 맞아야 한다는 것. 이 계약은 가 지켜주지 않는다. 도, 훈련 과정 자체도 마찬가지다. 훈련은 본 데이터에서 손실을 낮추는 행동에만 점수를 준다. 확률과 빈도가 맞물리는지는 별개의 성질이고, 모델은 그걸 부수효과로 얻거나 — 못 얻거나 — 둘 중 하나다.

자신 있게 틀리기는 softmax가 왜 확률처럼 보이는 것을 만드는지 보여주었다. 이 페이지의 질문은 그다음이다: 보이는 대로의 의미를 갖고 있는가?

2

신뢰도 다이어그램

캘리브레이션을 눈으로 보는 표준 방법은 이다. 예측을 신뢰도 구간별로 묶는다 — 가령 폭 0.10.1의 구간 열 개. 각 구간마다 두 수를 찍는다. x축에는 평균 예측 확률, y축에는 실제로 맞춘 비율. 완벽한 캘리브레이션은 대각선 y=xy = x. 위 위젯이 정확히 이 그림이고, 미스캘리 정도를 조절할 수 있는 합성 모델로 만들어졌다.

막대가 대각선 _아래_로 처져 있으면 — 모델은 0.90.9라 말하지만 실제로는 0.780.78만 맞는다 — 그게 _과신_의 표시다. 반대로 막대가 _위_로 솟아 있으면 모델이 너무 겸손한 것이다. 0.60.6이라 말해놓고 실제로는 0.80.8이 맞는다. 각 구간의 세로 간격을 그 구간의 예측 개수로 가중평균하면 하나의 수로 묶인다 — 기대 캘리브레이션 오차, ECE다.

import numpy as np

# Bin predictions; for each bin compute mean predicted prob and observed
# accuracy. The vertical gaps are the calibration error, by bin.
def reliability(probs, labels, n_bins=10):
    edges = np.linspace(0, 1, n_bins + 1)
    out = []
    for lo, hi in zip(edges[:-1], edges[1:]):
        mask = (probs >= lo) & (probs < hi if hi < 1 else probs <= hi)
        if mask.sum() == 0:
            out.append((float((lo + hi) / 2), None, 0))
            continue
        mean_p   = float(probs[mask].mean())
        accuracy = float(labels[mask].mean())   # labels ∈ {0, 1}
        out.append((mean_p, accuracy, int(mask.sum())))
    return out

# Toy: 1000 examples drawn from a known truth(p), with labels sampled
# Bernoulli(truth(p)). The model SAYS p; reality returns truth(p).
rng = np.random.default_rng(0)
probs = rng.uniform(0, 1, size=1000)
truth = lambda p, T=0.55: 1 / (1 + np.exp(-np.log(p / (1 - p)) / T))
labels = rng.binomial(1, truth(probs))
reliability(probs, labels, n_bins=10)
# → [(0.05, 0.18, ...), ..., (0.95, 0.78, ...)]
# At "95% confident", reality returns ~78% — the model is overconfident.
3

실제 모델은 — 예측 가능하게 — 과신한다

이미지 분류기, 언어 모델, 표 데이터 신경망 어디서나 같은 경험적 패턴이 보고된다. 막대는 대각선 _아래_에 깔려 있고, 격차는 신뢰도가 높은 쪽일수록 벌어진다. “아주 확신한다”는 모델은 자기 수가 약속한 것보다 자주 틀리고, “잘 모르겠다”는 모델은 대체로 정직하다. 대각선 위에 겹쳐 그려보면 그 모양은 0.5 쪽으로 살짝 눌린 시그모이드처럼 생겼다. 우연이 아니다. 진짜 사후확률이 모델의 출력 확률을 σ(logit(p)/T)σ(logit(p)/T)에 (T &lt; 1로) 통과시킨 결과라면 정확히 이 모양이 나온다.

위젯에서 TT0.550.55 (“과신” 프리셋) 로 옮겨보자. 오른쪽 꼬리의 막대들이 뚝 처진다 — 0.90.9 구간이 0.780.78 근처까지 내려앉는다. ECE는 치솟는다. 1.551.55로 옮기면 곡선이 대각선 위로 뒤집힌다. _과소확신_이다. 0.60.6이라고 찍은 구간이 0.70.7 근처에 떨어진다.

# Expected calibration error (ECE): weighted average of bin gaps.
def ece(probs, labels, n_bins=10):
    bins = reliability(probs, labels, n_bins)
    n = sum(c for _, _, c in bins)
    return sum(c * abs(p - a) for p, a, c in bins if a is not None) / n

ece(probs, labels, 10)        # ≈ 0.13   (13% calibration gap on average)
# 0 means perfect — every bar lies on the diagonal. ~0.05 is "lab-grade
# calibrated"; modern deep nets often start at 0.10–0.30 out of the box.
4

한 구간에서 선형화 — 국소적 처방

위젯의 막대를 클릭해보자. 그 자리에 뜨는 갈색 선이 구간 중심에서의 캘리브레이션 곡선 이다 — 선형화 모듈의 그 트릭을 그대로 끌어왔다. 국소적으로 보면 곡선은 기울기 mm, 절편 cc인 직선과 다를 게 없다. actual(p)mp+cactual(p) ≈ m·p + c. 수 두 개. 이 동네에서 막대와 대각선의 차이는 (m1)p+c(m − 1)·p + c이고, 접선 하나의 기울기와 절편만 보면 바로 나오는 양이다.

이게 왜 중요할까. 어떤 종류의 보정이 필요한지를 곧장 알려주기 때문이다. 기울기가 1≈ 1이면 “여기서 곡선은 대각선과 나란하고 단지 평행이동했을 뿐” — 상수 하나만 더해주면 끝이다. 기울기가 1≠ 1이면 격차 자체가 신뢰도에 따라 바뀐다. 제대로 된 보정은 곡선을 옆으로 미는 게 아니라 대각선 쪽으로 돌려놓아야 한다. 그 회전을 매개변수 하나 — 온도 — 로 전역에서 한 번에 해주는 게 다음 절의 이야기다.

# Local linearization at one bin: y ≈ accuracy(c) + slope·(p - c).
# If slope ≈ 1, the curve is parallel to truth — a constant shift, easy to
# fix. If slope ≠ 1, the gap CHANGES with confidence, which is exactly
# what one scalar (temperature) can rotate away.
def local_slope(p_centers, accuracies, i):
    # central difference; falls back to one-sided at the edges.
    if i == 0:
        return (accuracies[1] - accuracies[0]) / (p_centers[1] - p_centers[0])
    if i == len(p_centers) - 1:
        return (accuracies[-1] - accuracies[-2]) / (p_centers[-1] - p_centers[-2])
    return (accuracies[i+1] - accuracies[i-1]) / (p_centers[i+1] - p_centers[i-1])

# At the bin centered at 0.85, the slope tells you the "local fix":
# slope == 1 means subtract a constant; slope < 1 means stretch toward 0.5.
5

온도 스케일링 — 사후, 단 하나의 스칼라

레시피는 이렇다. 훈련된 모델을 그대로 둔다. 재훈련 없음. 아키텍처 변경 없음. 따로 빼둔 검증 셋의 원시 을 가져와, 스칼라 하나 T &gt; 0로 나눠 softmax(z/T)softmax(z / T)를 계산한다. TT는 검증 셋의 로그 손실을 최소화하도록 맞춘다 — 1차원 최적화, 1초면 끝난다. 이게 이다. argmax는 그대로 유지된다 (모든 로짓을 같은 상수로 나눴으니까). 정확도도 손대지 않은 채, 신뢰도만 다시 조정된다.

작동 원리는 단순하다. 로짓을 T &gt; 1로 나누면 softmax가 평평해진다 — 모든 출력 확률이 균등분포 1/K1/K 쪽으로 끌려간다. 예제를 잔뜩 모아 보면, 이 효과는 오른쪽 꼬리 (과신 영역) 의 막대를 가운데보다 훨씬 더 많이 끌어내려, 막대를 눌러놓던 시그모이드 휨을 정확히 되돌린다. 흔한 실패 양식에 대해 놀랍도록 값싼 처방이고, 신뢰도 다이어그램이 대각선 아래로 휘어 있다면 가장 먼저 꺼내볼 카드다.

# Temperature scaling: divide every logit by T before softmax.
# argmax is preserved (accuracy unchanged); only confidence is rescaled.
def softmax(z, T=1.0):
    s = z / T
    s = s - s.max(axis=-1, keepdims=True)
    e = np.exp(s)
    return e / e.sum(axis=-1, keepdims=True)

# Fit T on a held-out validation set by minimizing log-loss in T.
from scipy.optimize import minimize_scalar

def fit_temperature(logits, y):
    def nll(T):
        p = softmax(logits, T=T)
        # negative log-likelihood of the true class
        return -np.log(p[np.arange(len(y)), y] + 1e-12).mean()
    res = minimize_scalar(nll, bounds=(0.05, 10.0), method="bounded")
    return float(res.x)

# T > 1 → softer; T < 1 → sharper. Modern LLMs ship with T ≈ 1.5–3 to
# tame overconfidence in the high-probability tail.
이제 깨봐

온도 스케일링은 미스캘리브레이션이 입력 공간 어디서나 같은 모양이라고 — 그래서 전역 회전 한 번으로 고칠 수 있다고 — 가정한다. 그런데 실제 모델은 입력의 갈래마다 다르게 어긋난다. 쉬운 예제는 자신만만하게 맞고, 어려운 예제는 자신만만하게 틀리고, 분포 바깥 입력에서는 터무니없이 자신만만해진다. TT 하나로는 그 격차들을 평균내는 데 그치고, 자칫하면 양쪽 영역 모두 손대지 않은 쪽보다 못하게 만들 수도 있다. 정직한 신호는 이렇다. 검증 셋의 ECE는 떨어지는데, 따로 빼둔 신뢰도 다이어그램의 오른쪽 꼬리는 여전히 휘어 있다. 이때 필요한 건 더 큰 T가 아니다. “이 입력이 내가 본 것들 근처인가?”를 따로 묻는 모델 — 선택적 예측, 컨포멀 집합, 밀도 추정기 — 이고, 이건 softmax 바깥에 사는 별개의 장치다.

신뢰도는 수다. 진실은 빈도다. 캘리브레이션은 그 사이의 격차다. 신뢰도 다이어그램이 격차를 눈에 띄게 만들고, 한 구간의 접선이 국소적 처방을 알려주며, 스칼라 하나 — 온도 — 가 곡선 전체를 대각선 쪽으로 돌려놓는다.

exercises · 손으로 풀기
1구간 읽기

위젯에서 T=0.55T = 0.55 (과신) 로 두자. 0.850.85 중심 구간을 클릭하고 막대 높이를 읽어보자. 모델이 “85% 확신”이라고 찍은 예측 100개 중 실제로 맞은 건 대략 몇 개일까? 같은 작업을 0.550.55 구간에서도 해보자. 모델은 어떤 종류의 입력 앞에서는 여전히 정직한가?

2대각선 아래 · 그 뜻계산기 없이

친구가 신뢰도 다이어그램을 보고는 오른쪽 절반의 막대가 모두 대각선 _아래_에 있는 걸 가리키며 “좋다 — 모델이 겸손하네”라고 말한다. _겸손한_과 _과신하는_을 한 문장으로 구분해 답해보자. “대각선 아래”는 어느 쪽에 해당하는가?

3국소 선형 적합

과신 상태 (T=0.55T = 0.55) 에서 0.850.85 구간을 클릭하면 위젯이 국소 기울기를 알려준다. 그 기울기가 m=0.6m = 0.6, 막대 높이가 0.740.74 (p=0.85p = 0.85) 라 하자. 국소 선형 근사를 actual(p)mp+cactual(p) ≈ m·p + c 꼴로 써보자 — cc만 계산하면 된다. 그 식으로 p=0.95p = 0.95에서의 캘리브레이션 오차를 예측해보자.

4왜 교차 엔트로피 → 과신

자신 있게 틀리기에서 다뤘듯, 교차 엔트로피는 logp정답−\log p_{\text{정답}}이다. 이 손실을 최소화하도록 훈련된 모델은 왜 훈련 셋에서 _과신_할 유인이 생기는지, 그 유인이 왜 따로 빼둔 데이터의 좋은 캘리브레이션으로는 이어지지 않는지를 — 수식 없이 — 말로 스케치해보자.

왜 교과서는 이렇게 안 가르치나

ML 강의는 softmax, 교차 엔트로피, 정확도까지는 가르친다. 캘리브레이션은 거의 가르치지 않는다. 한쪽에는 문화의 문제 — 리더보드가 추적하는 건 정확도다 — 가 있고, 다른 한쪽에는 역사의 문제가 있다. 고전 학습 이론은 결정 경계 (argmax가 정한다) 에는 공을 들였지만, 모델이 그 과정에서 내놓는 확률에는 관심이 적었다. 그러나 “70% 확신”이라고 말하는 모든 출시 모델은 암묵적으로 캘리브레이션 주장을 하고 있는 셈이다. Lemma가 softmax 옆에 신뢰도 다이어그램을 나란히 두는 이유가 그것이다. 독자는 확률처럼 보인다빈도와 맞아떨어진다 사이의 격차를 — 따로 빼둔 정답에 비춘 별개의 측정으로, 교차 엔트로피 훈련을 아무리 더 해도 대신할 수 없는 무언가로 — 눈으로 볼 수 있어야 한다.

용어집 · 이 페이지에서 쓰임 · 9
calibration·캘리브레이션
모델이 내놓은 *예측 확률*과 실제로 일어난 *빈도*의 일치. "70% 확신"이라고 라벨한 예제들에서 실제로 70% 가까이 맞춘다면 그 모델은 _캘리브레이트되어 있다_. 캘리브레이션은 *신뢰도 다이어그램(reliability diagram)*으로 그린다 — 가로축은 예측 확률, 세로축은 관측 정확도; 완벽한 캘리브레이션은 대각선 `y = x`. 현대의 신경망은 대부분 _과신(overconfident)_ 한다 — 높은 확률로 예측한 것이 주장만큼 자주 맞지 않는다 — 그리고 표준 처방은 _온도 스케일링(temperature scaling)_, 곡선을 대각선 쪽으로 끌어당기는 단 하나의 다이얼이다. 캘리브레이션은 정확도와 다르다: 작은 데이터셋에서 100% 정확한 분류기가 끔찍하게 미스캘리브레이트될 수도 있고, 모든 개별 예측에서 틀리는 모델이라도 *빈도*만 맞으면 캘리브레이트되어 있다.
temperature·온도
`softmax(z/T)`에 적용되는 양의 스칼라 `T`. `T`가 낮으면 분포가 _예리해지고_ 가장 큰 로짓이 지배 — 모델이 더 자신만만해 보인다. `T`가 높으면 _평탄해져서_ 모든 클래스가 비슷한 몫을 가짐 — 모델이 더 불확실해 보인다. `T = 1`은 변형 없는 softmax. 중요한 점은 `T`가 *어느 클래스가 이기는가*를 바꾸지 않고, *이긴 확률이 얼마나 강하게 보고되는가*만 바꾼다는 것. 언어 모델의 샘플링 조절, 디스틸레이션의 부드러운 타겟, 캘리브레이션의 과확신 보정에 쓰인다.
distribution·분포
_불확실성의 모양_. 가능한 결과들에 확률이 어떻게 배분돼 있는가. 이산 분포는 각 결과에 수를 매긴다 — `P(X = "고양이") = 0.6, P(X = "강아지") = 0.3, P(X = "새") = 0.1`. 연속 분포는 구간 위에 *밀도*를 배분한다 — 정확히 한 점에 확률은 없고, 구간 위에서만 있다. 이산은 합이, 연속은 적분이 1이어야 한다 — _무엇인가는 일어나야_ 하니까. 확률 하나는 수 한 개이고, 분포는 그 뒤에 있는 모양 전체다. 모델이 예측하는 양, 자산이 낼 수 있는 수익, 픽셀이 가질 수 있는 값 — 대부분은 단일 수가 아니라 분포다. 그리고 그 분포의 *퍼짐*이 종종 중심값보다 더 중요하다.
softmax·소프트맥스
로짓 벡터 `z = (z₁, …, zₙ)`을 합이 1인 양수 벡터로 바꾸는 함수: `softmax(z)ᵢ = exp(zᵢ) / Σⱼ exp(zⱼ)`. 두 가지 사실을 잊지 말 것. (1) 로짓끼리의 _차이_ `zᵢ − zⱼ`에만 의존한다 — 모든 로짓에 같은 상수를 더해도 결과는 그대로. (2) 정확히 0 또는 1을 출력하지 않는다 (극한일 뿐). 출력은 확률 분포처럼 _보이지만_, 그 확률이 실제 세계의 빈도와 일치한다는 보증은 없다.
cross-entropy·교차 엔트로피
분류기 훈련에 쓰이는 손실 함수. 정답 클래스 `y`와 `n`개 클래스에 대한 예측 분포 `p`가 주어졌을 때, 교차 엔트로피는 `−log p_y` — 모델이 정답에 부여한 확률의 음의 로그. 타겟이 원-핫이면 음의 로그우도와 같다. 자신만만하게 _틀린_ 답을 가혹하게 처벌하고 (`p_y → 0`이면 손실 `→ ∞`), 자신만만하게 _맞춘_ 답을 보상한다 (`p_y → 1`이면 손실 `→ 0`). log가 들어간 이유는 *독립 가능도의 곱셈*을 *덧셈*으로 바꾸기 때문이다.
reliability diagram·신뢰도 다이어그램
*캘리브레이션*을 그리는 표준 그래프. 예측을 신뢰도 구간별로 묶고 (`[0.0–0.1]`, `[0.1–0.2]`, …, `[0.9–1.0]`) , 각 구간에서 *평균 예측 확률*을 x축, _관측 정확도_ (그 구간 예측 중 실제로 맞은 비율) 를 y축에 찍는다. 완벽히 캘리브레이트된 모델은 대각선 `y = x` 위를 따라간다. x=0.9에서 막대가 대각선 *아래*에 있다면, 모델이 "90% 확신"이라 말했지만 — 예컨대 — 75%만 맞춘다는 뜻; _과신(overconfident)_. 대각선 위쪽이면 _과소확신(underconfident)_. 각 구간의 수직 간격을 그 구간의 예측 개수로 가중평균한 값이 _기대 캘리브레이션 오차(ECE)_.
tangent line·접선
곡선과 한 점에서만 닿고 그 점에서 곡선의 방향과 *일치*하는 직선. 접점에서의 기울기가 그 점에서의 함수의 미분값: `m_접선 = f'(a)`. 두 교점이 합쳐질 때 할선이 도달하는 극한 — 곡선의 *순간 방향*을 직선으로 가시화한 것. 삼각함수의 *탄젠트*와는 다른 개념 (같은 단어).
logit·로짓
모델이 정규화 전에 내놓는 실숫값 점수 — 마지막 선형층의 원시 출력으로, 양수일 의무도 합이 무엇이어야 할 의무도 없다. 로짓은 어떤 값이든 가질 수 있고, 의미를 가지는 건 _차이_ `zᵢ − zⱼ`뿐이다. softmax가 차이에만 의존하기 때문. 로짓 하나만으로는 의미가 없다. softmax를 거쳐야 비로소 "확률"이라는 해석을 얻는다 — 그것도 0과 1 사이의 어떤 숫자라는 의미에서일 뿐이다.
temperature scaling·온도 스케일링
과신하는 분류기를 가장 단순하게 보정하는 방법. 모델은 _로짓_ `z`를 내고, 소프트맥스가 그것을 확률로 바꾼다. *온도 스케일링*은 소프트맥스 *전*에 모든 로짓을 단 하나의 스칼라 `T > 0`으로 나눈다: `softmax(z / T)`. `T = 1`은 모델을 그대로 둔다; `T > 1`은 분포를 평평하게 만든다 (덜 확신); `T < 1`은 더 뾰족하게 만든다 (더 확신). 핵심은, 모든 로짓을 같은 상수로 나눠도 *argmax는 변하지 않는다*는 점 — 예측된 클래스는 바뀌지 않으니 정확도도 그대로다. 변하는 건 신뢰도 숫자뿐. `T`는 검증 셋에서 로그 손실을 최소화하도록 맞춘다; 매개변수 하나, 스칼라 하나, 아키텍처 변경 없음 — 신뢰도 다이어그램이 대각선 아래로 휠 때 보통 가장 먼저 시도할 처방.