모델은 왜 틀린 답에 그렇게 자신만만할까?
모델은 자신이 맞는지 알지 못한다. 가진 건 점수뿐이다.
Softmax는 진실을 확인하지 않는다. 점수끼리 비교할 뿐이다. softmax가 돌려주는 건 라벨 위의
점수는 확률이 아니다
분류기의 마지막 층은 클래스마다 하나씩 세 개의 수를 내놓는다. 양수일 필요도, 합이 특정 값이어야 할 필요도 없다. 가공되지 않은
세 실수를 합이 1인 양수 셋으로 옮겨 줄 함수가 필요하다. 그런 함수는 얼마든지 있다. 우리가 굳이 softmax를 쓰는 이유, 왜 하필 이 함수인지가 다음 절의 핵심이다.
Softmax — 지수 취하고, 정규화
softmax는 두 단계로 끝난다. 먼저 각 값을 의 거듭제곱으로 올린다: . 이걸로 일단 모두 양수가 됐다. 다음으로 전체 합으로 나눠 합이 1이 되게 맞춘다: . 이게 끝이다. 모두 양수, 합은 1 — 영락없이 확률 분포 모양이다.
exp 단계는 아무렇게나 고른 게 아니다. 의 실수 거듭제곱은 늘 양수이므로 양수성이 보장되고, 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]교차 엔트로피 — 정답에 부여한 확률을 벌하라
이제 확률 벡터가 손에 있다. 정답이 클래스 0이라고 하자. 우리가 들여다볼 값은 , 모델이 정답 자리에 매긴 수다. 훈련 신호는 일 때 0(완벽), 가 작을 때 커야 한다(자신만만하게 틀린 경우). 이 조건을 가장 단순하게 채워 주는 게 , 곧
# 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.온도 — 자신감 다이얼
자리에 를 끼워 넣어 보자.
자신감 ≠ 진실 — 함정, 명시적으로
로짓을 로 놓아 보자. 모델이 클래스 0에 단단히 마음을 굳힌 상태다. softmax를 통과시키면 , 자신감 97.8%다. 그런데 진실은 이 계산과 아무 상관이 없다. 실제 정답이 클래스 1이라면 , 손실은 . 모델은 자신만만하면서 동시에 틀렸다. 온도를 더 낮추면 자신감은 더 부풀고, 손실은 더 빠르게 치솟는다.
그래서 “모델이 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)교차 엔트로피는 정확도와 양쪽 방향 모두로 어긋날 수 있다. 로짓을 로, 정답을 클래스 0으로 두면 argmax는 맞췄다. 그런데 이라 손실은 . 답은 맞췄는데 손실은 형편없다. 이번엔 에 정답 클래스 1로 뒤집어 보자. argmax는 틀렸는데 손실은 까지 튄다. 훈련 손실이 가장 낮은 모델과 정답을 가장 많이 맞히는 모델은 같지 않다. 최적화하는 지표와 실제로 신경 쓰는 지표가 다른 셈이다. 출시된 모델들이 숨는 자리가 바로 이 틈이다.
Softmax는 진실을 확인하지 않는다. 점수를 비교하고, 지수를 취하고, 정규화할 뿐이다. 교차 엔트로피는 정답이 놓인 자리의 막대를 그대로 읽어 낸다. 자신감과 옳음은 별개의 것이고, 모델이 계산하는 건 그중 앞의 하나뿐이다.
위젯에서 로짓을 로 두고 온도를 1.0에서 0.1까지 내려 보자. 온도마다 어느 클래스가 1등인가? 1등의 확률은 어떻게 움직이는가? 이제 T를 5.0까지 올려 보자. 막대는 어떻게 되는가? T를 아무리 흔들어도 1등이 바뀌지 않는 이유는 무엇인가?
를 T = 1에서 어림해 보자. , 면 충분하다. 어림한 값이 막대와 맞는지 비교해 보자.
손실은 이다. 인 경우를 자연 로그로 계산해 보자. 과 사이에 벌어진 간격은 교차 엔트로피가 자신만만한 오답을 어떻게 대하는지에 대해 무엇을 말해 주는가?
모델이 틀린 클래스에 를 몰아주는 바람에 정답에는 밖에 안 남았다. 손실은 얼마인가? 누군가 “그래도 99%나 자신했으니 거의 맞은 셈 아니야?”라고 묻는다면, 확률처럼 보인다와 실제로 그럴 가능성이 높다를 가르는 한 문장으로 답해 보자.
ML 강의는 보통 softmax와 교차 엔트로피를 “분류 레시피”라며 한 호흡에 묶어 가르친다. 지수 취하고, 정규화하고, 정답 자리의 로그를 떼어 내면 끝. Lemma는 이 둘을 일부러 떼어 놓는다. softmax는 점수를 확률처럼 보이는 무언가로 압축하고, 교차 엔트로피는 모델이 정답에 매긴 확률을 벌점으로 바꾼다. 그저 관습으로 한데 붙어 있을 뿐, 하는 일은 따로다. 한 덩어리로 다루는 순간 §5의 함정, 곧 모델이 자신 있게 틀리는 그 장면이 시야에서 사라진다. 레시피는 그런 신호를 보내 주지 않는다.