Lemma
수학, 거꾸로
도입 · 점수 → 확률 → 자신감

모델은 왜 틀린 답에 그렇게 자신만만할까?

모델은 자신이 맞는지 알지 못한다. 가진 건 점수뿐이다. 는 그 점수를 확률처럼 보이는 수로 바꾸고, 는 모델이 정답에 매긴 확률을 벌점으로 환산한다. 함정은 뻔한 자리에 숨어 있다. 형편없는 점수도 얼마든지 자신만만한 확률이 될 수 있다. 이 페이지가 다루는 건 그게 전부다. 나머지는 거기서 따라 나오는 이야기.

Softmax는 진실을 확인하지 않는다. 점수끼리 비교할 뿐이다. softmax가 돌려주는 건 라벨 위의 지, 진실 증명서가 아니다.

위젯 — 점수 요리기
승자 · cat(62.9%)
정답 · cat p_정답 62.9%
손실 · −log p_정답 0.464
cat62.9%dog23.1%fox14.0%
정답 · ★
이걸 해보자. 정답을 cat으로 두고, 뷰를 손실로 바꾼 다음, fox의 로짓을 5.0까지 올려라. 막대는 fox가 ~95%로 이기고, cat 막대 위로 솟은 주황색 기둥이 손실 — 4를 넘긴다. 모델은 자신만만하게 틀렸다. 이제 온도를 0.1 쪽으로 끌어라: 자신감이 올라가고, 손실 기둥은 솟는다. 진실은 한 번도 안 움직였다. softmax는 진실을 확인하지 않는다 — 점수끼리 비교할 뿐이다.
흐름
1

점수는 확률이 아니다

분류기의 마지막 층은 클래스마다 하나씩 세 개의 수를 내놓는다. 양수일 필요도, 합이 특정 값이어야 할 필요도 없다. 가공되지 않은 , 그러니까 날것의 점수일 뿐이다. z=[2.0,1.0,0.5]z = [2.0, 1.0, 0.5]는 모델이 클래스 0 쪽으로 가장 크게 “기울었다”는 것만 말해 줄 뿐, “클래스 0일 확률이 65%“라고 한 적은 없다. 그런 수는 아직 어디에도 없다.

세 실수를 합이 1인 양수 셋으로 옮겨 줄 함수가 필요하다. 그런 함수는 얼마든지 있다. 우리가 굳이 softmax를 쓰는 이유, 왜 하필 이 함수인지가 다음 절의 핵심이다.

2

Softmax — 지수 취하고, 정규화

softmax는 두 단계로 끝난다. 먼저 각 값을 ee의 거듭제곱으로 올린다: [e2.0,e1.0,e0.5][7.39,2.72,1.65][e^2.0, e^1.0, e^0.5] ≈ [7.39, 2.72, 1.65]. 이걸로 일단 모두 양수가 됐다. 다음으로 전체 합으로 나눠 합이 1이 되게 맞춘다: [0.629,0.231,0.140]≈ [0.629, 0.231, 0.140]. 이게 끝이다. 모두 양수, 합은 1 — 영락없이 확률 분포 모양이다.

exp 단계는 아무렇게나 고른 게 아니다. ee의 실수 거듭제곱은 늘 양수이므로 양수성이 보장되고, softmax가 로짓의 차이에만 반응하게 만든다. 모든 로짓에 같은 상수를 더해도 결과가 변하지 않는다는 뜻이다. 이 성질의 여파는 만만치 않다. softmax는 점수의 절대적 크기를 알지 못한다. 누가 누구보다 얼마나 앞서 있는지, 그것만 본다.

import numpy as np

# Three logits — raw scores. No truth check anywhere.
z = np.array([2.0, 1.0, 0.5])

# Numerically stable softmax: subtract max before exp.
def softmax(z, T=1.0):
    s = z / T
    s = s - s.max()
    e = np.exp(s)
    return e / e.sum()

p = softmax(z)
# p ≈ [0.659, 0.242, 0.099]   (sums to 1)
# Same logits + 100 give the same p — softmax depends only on differences.
softmax(z + 100)
# → [0.659, 0.242, 0.099]
3

교차 엔트로피 — 정답에 부여한 확률을 벌하라

이제 확률 벡터가 손에 있다. 정답이 클래스 0이라고 하자. 우리가 들여다볼 값은 p0p_0, 모델이 정답 자리에 매긴 수다. 훈련 신호는 p0=1p_0 = 1일 때 0(완벽), p0p_0가 작을 때 커야 한다(자신만만하게 틀린 경우). 이 조건을 가장 단순하게 채워 주는 게 logp0−\log p_0, 곧 다. 정답이 하나인 분류 문제에서 교차 엔트로피는 그 정답의 음의 로그우도와 정확히 일치한다. 같은 수를 두 이름으로 부르는 셈이다(CE는 진실을 분포로 본다 — 여기선 원-핫. 은 진실을 관측 하나로 본다).

를 쓰는 이유는 단순하다. 독립적인 관측은 가능도를 곱으로 합치고, log는 곱셈을 덧셈으로 풀어 준다. 그러면 데이터셋 전체 손실이 깔끔한 합으로 정리된다: L=Σlogp(yixi)L = Σ −\log p(y_i | x_i). 로그 모듈이 단 한 줄로 들어 올리는 바로 그 항등식이다.

# In PyTorch, log_softmax + nll_loss is the numerically stable cross-entropy.
import torch
import torch.nn.functional as F

z = torch.tensor([[2.0, 1.0, 0.5]])      # logits, batch of 1
y = torch.tensor([0])                     # true class — 'cat' at index 0

log_p = F.log_softmax(z, dim=1)           # avoids log(softmax) overflow
loss  = F.nll_loss(log_p, y)
# loss ≈ 0.417  =  −log p_true  =  −log(0.659)
#
# Why log_softmax not log(softmax)?
# log(softmax) computes exp() first → overflow when logits are large.
# log_softmax keeps things in log-space the whole way through.
4

온도 — 자신감 다이얼

softmax(z)softmax(z) 자리에 softmax(z/T)softmax(z/T)를 끼워 넣어 보자. T < 1이면 로짓을 작은 수로 나누는 셈이라 차이가 증폭되고 — 1등이 멀찍이 달아난다. T > 1이면 차이가 줄어들어 분포가 평평해진다. 위젯에서 그대로 드러난다. T를 0.1 쪽으로 끌면 막대 하나가 천장을 친다. 1등이 바뀌는 것도 아니다. 틀렸다는 사실이 달라지는 것도 아니다. 달라지는 건 모델이 보고하는 자신감뿐이고, 그 자신감 위에 얹힌 교차 엔트로피 손실이 함께 출렁일 뿐이다.

5

자신감 ≠ 진실 — 함정, 명시적으로

로짓을 [5.0,1.0,0.5][5.0, 1.0, 0.5]로 놓아 보자. 모델이 클래스 0에 단단히 마음을 굳힌 상태다. softmax를 통과시키면 p[0.978,0.018,0.011]p ≈ [0.978, 0.018, 0.011], 자신감 97.8%다. 그런데 진실은 이 계산과 아무 상관이 없다. 실제 정답이 클래스 1이라면 p정답=0.018p_{\text{정답}} = 0.018, 손실은 log0.0184.0−\log 0.018 ≈ 4.0. 모델은 자신만만하면서 동시에 틀렸다. 온도를 더 낮추면 자신감은 부풀고, 손실은 더 빠르게 치솟는다.

그래서 “모델이 97%라고 했으니 맞겠지”는 범주를 헷갈린 말이다. 확률처럼 보인다현실과 들어맞는다는 서로 다른 이야기다. 이 둘을 정렬시키는 작업은 따로 이름이 있다. 캘리브레이션. 그러려면 훈련 중에는 모델이 보지 못하는 데이터가 필요하다.

# The trap: a wrong score can produce a confident probability.
import numpy as np

z = np.array([5.0, 1.0, 0.5])        # model is sure of class 0
true_idx = 1                          # but truth is class 1
p = softmax(z)
# p ≈ [0.978, 0.018, 0.011]
# 97.8% confidence — and wrong.
loss = -np.log(p[true_idx])
# loss ≈ 4.0   (huge — log explodes as p_true → 0)
#
# Lower the temperature, and confidence rises further while truth is unchanged.
softmax(z, T=0.5)[0]                 # ≈ 0.99964   (even more sure)
이제 깨봐

교차 엔트로피는 정확도와 양쪽 방향 모두로 어긋날 수 있다. 로짓을 [2.0,1.9,1.8][2.0, 1.9, 1.8]로, 정답을 클래스 0으로 두면 argmax는 맞췄다. 그런데 p정답0.37p_{\text{정답}} ≈ 0.37이라 손실은 0.99≈ 0.99. 답은 맞췄는데 손실은 형편없다. 이번엔 [5.0,1.0,0.5][5.0, 1.0, 0.5]에 정답 클래스 1로 뒤집어 보자. argmax는 틀렸는데 손실은 4.0≈ 4.0까지 튄다. 훈련 손실이 가장 낮은 모델과 정답을 가장 많이 맞히는 모델은 같지 않다. 최적화하는 지표와 실제로 신경 쓰는 지표가 다른 셈이다. 출시된 모델들이 숨는 자리가 바로 이 틈이다.

Softmax는 진실을 확인하지 않는다. 점수를 비교하고, 지수를 취하고, 정규화할 뿐이다. 교차 엔트로피는 정답이 놓인 자리의 막대를 그대로 읽어 낸다. 자신감과 옳음은 별개의 것이고, 모델이 계산하는 건 그중 앞의 하나뿐이다.

exercises · 손으로 풀기
1막대 읽기

위젯에서 로짓을 [2.0,1.0,0.5][2.0, 1.0, 0.5]로 두고 온도를 1.0에서 0.1까지 내려 보자. 온도마다 어느 클래스가 1등인가? 1등의 확률은 어떻게 움직이는가? 이제 T를 5.0까지 올려 보자. 막대는 어떻게 되는가? T를 아무리 흔들어도 1등이 바뀌지 않는 이유는 무엇인가?

2손으로 계산 · softmax계산기 없이

softmax([2,1,0])softmax([2, 1, 0])T = 1에서 어림해 보자. e2.72e ≈ 2.72, e27.39e² ≈ 7.39면 충분하다. 어림한 값이 막대와 맞는지 비교해 보자.

3손실 쓰기

손실은 logp정답−\log p_{\text{정답}}이다. p정답{0.9,0.5,0.1,0.01}p_{\text{정답}} \in \{0.9, 0.5, 0.1, 0.01\}인 경우를 자연 로그로 계산해 보자. 0.10.10.010.01 사이에 벌어진 간격은 교차 엔트로피가 자신만만한 오답을 어떻게 대하는지에 대해 무엇을 말해 주는가?

4사악한 것 · 자신감 ≠ 진실

모델이 틀린 클래스에 0.990.99를 몰아주는 바람에 정답에는 0.010.01밖에 안 남았다. 손실은 얼마인가? 누군가 “그래도 99%나 자신했으니 거의 맞은 셈 아니야?”라고 묻는다면, 확률처럼 보인다실제로 그럴 가능성이 높다를 가르는 한 문장으로 답해 보자.

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

ML 강의는 보통 softmax와 교차 엔트로피를 “분류 레시피”라며 한 호흡에 묶어 가르친다. 지수 취하고, 정규화하고, 정답 자리의 로그를 떼어 내면 끝. Lemma는 이 둘을 일부러 떼어 놓는다. softmax는 점수를 확률처럼 보이는 무언가로 압축하고, 교차 엔트로피는 모델이 정답에 매긴 확률을 벌점으로 바꾼다. 그저 관습으로 한데 붙어 있을 뿐, 하는 일은 따로다. 한 덩어리로 다루는 순간 §5의 함정, 곧 모델이 자신 있게 틀리는 그 장면이 시야에서 사라진다. 레시피는 그런 신호를 보내 주지 않는다.

용어집 · 이 페이지에서 쓰임 · 7
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가 들어간 이유는 *독립 가능도의 곱셈*을 *덧셈*으로 바꾸기 때문이다.
distribution·분포
_불확실성의 모양_. 가능한 결과들에 확률이 어떻게 배분돼 있는가. 이산 분포는 각 결과에 수를 매긴다 — `P(X = "고양이") = 0.6, P(X = "강아지") = 0.3, P(X = "새") = 0.1`. 연속 분포는 구간 위에 *밀도*를 배분한다 — 정확히 한 점에 확률은 없고, 구간 위에서만 있다. 이산은 합이, 연속은 적분이 1이어야 한다 — _무엇인가는 일어나야_ 하니까. 확률 하나는 수 한 개이고, 분포는 그 뒤에 있는 모양 전체다. 모델이 예측하는 양, 자산이 낼 수 있는 수익, 픽셀이 가질 수 있는 값 — 대부분은 단일 수가 아니라 분포다. 그리고 그 분포의 *퍼짐*이 종종 중심값보다 더 중요하다.
logit·로짓
모델이 정규화 전에 내놓는 실숫값 점수 — 마지막 선형층의 원시 출력으로, 양수일 의무도 합이 무엇이어야 할 의무도 없다. 로짓은 어떤 값이든 가질 수 있고, 의미를 가지는 건 _차이_ `zᵢ − zⱼ`뿐이다. softmax가 차이에만 의존하기 때문. 로짓 하나만으로는 의미가 없다. softmax를 거쳐야 비로소 "확률"이라는 해석을 얻는다 — 그것도 0과 1 사이의 어떤 숫자라는 의미에서일 뿐이다.
negative log-likelihood·음의 로그우도
NLL이라고 줄여 쓴다. 확률 모델과 관측 데이터가 주어졌을 때, *우도*는 모델이 데이터에 부여한 확률, *로그우도*는 그 로그, _음의_ 로그우도는 부호를 뒤집어 최소화가 의미를 가지게 한 형태. 정답 `y`와 예측 분포 `p`가 있는 분류 한 건의 경우 NLL = `−log p_y` — 원-핫 타겟에서는 교차 엔트로피와 동일. `−log p` 형태는 *독립 관측의 곱셈*을 *덧셈*으로 만들고, 자신만만하지만 _틀린_ 예측이 무한대로 발산하게 만든다.
logarithm·로그
지수의 역연산. log_b(x) 는 'b를 몇 제곱하면 x가 되는가'를 묻는다.
temperature·온도
`softmax(z/T)`에 적용되는 양의 스칼라 `T`. `T`가 낮으면 분포가 _예리해지고_ 가장 큰 로짓이 지배 — 모델이 더 자신만만해 보인다. `T`가 높으면 _평탄해져서_ 모든 클래스가 비슷한 몫을 가짐 — 모델이 더 불확실해 보인다. `T = 1`은 변형 없는 softmax. 중요한 점은 `T`가 *어느 클래스가 이기는가*를 바꾸지 않고, *이긴 확률이 얼마나 강하게 보고되는가*만 바꾼다는 것. 언어 모델의 샘플링 조절, 디스틸레이션의 부드러운 타겟, 캘리브레이션의 과확신 보정에 쓰인다.