왜 하필 Cross Entropy인가 — BCE에서 KL Divergence까지
분류 문제에 MSE 대신 Cross Entropy를 쓰는 이유를 정보량과 엔트로피에서 출발해 BCE, MLE, KL Divergence까지 하나의 흐름으로 정리.
분류 모델을 짤 때 손실 함수 자리에 CrossEntropyLoss를 넣는 건 거의 반사적이다. 그런데 왜 하필 그건지, 왜 회귀에서 잘 쓰던 MSE(Mean Squared Error)는 안 되는지, BCELoss와는 뭐가 다른지, 그리고 논문마다 나오는 KL Divergence와는 무슨 관계인지를 이어서 설명하려면 생각보다 막힌다.
이 글은 그 사슬을 처음부터 끝까지 한 번에 잇는 것을 목표로 한다. 정보량에서 시작해서 엔트로피, Cross Entropy, BCE, 최대우도추정, KL Divergence 순서로 간다.
시작 질문: MSE는 왜 안 되나
이진 분류에서 모델이 $z$라는 logit을 뱉고 sigmoid를 통과시켜 $\hat y = \sigma(z)$를 만든다고 하자. 정답은 $y \in {0, 1}$이다. MSE를 쓰면 손실은 이렇다.
\[L_{\text{MSE}} = \frac{1}{2}(\hat y - y)^2\]$z$에 대한 기울기를 구해보면 문제가 드러난다.
\[\frac{\partial L_{\text{MSE}}}{\partial z} = (\hat y - y)\,\sigma'(z) = (\hat y - y)\,\hat y (1 - \hat y)\]$\sigma’(z) = \hat y(1-\hat y)$라는 항이 붙어 있다. 이 값은 $\hat y$가 0이나 1에 가까울 때 0으로 죽는다. 그런데 $\hat y$가 0에 가까운데 정답이 1인 상황은 모델이 가장 크게 틀린 상황이다. 즉 MSE + sigmoid 조합은 가장 크게 틀렸을 때 기울기가 가장 작아진다. 학습이 필요한 지점에서 학습이 멈춘다.
숫자로 보면 심각성이 분명해진다. $y=1$인데 모델이 $z=-8$을 뱉은, 완전히 틀린 경우다.
| $\hat y$ | $\partial L / \partial z$ | |
|---|---|---|
| MSE + sigmoid | 0.000335 | $-0.000335$ |
| BCE + sigmoid | 0.000335 | $-0.999665$ |
같은 상황인데 기울기 크기가 약 3000배 차이난다. MSE는 사실상 아무것도 배우지 못한다.
기울기 문제를 걷어내더라도 더 근본적인 이유가 남는다. 분류 모델의 출력은 숫자가 아니라 확률분포다. 두 확률분포가 얼마나 다른지를 재는 데 유클리드 거리를 쓸 이유가 없다. 확률분포에는 확률분포의 척도가 있고, 그 척도는 정보 이론에서 나온다.
1. 정보량 — 놀람의 크기
“내일 해가 뜬다”는 정보량이 거의 0이다. “내일 눈이 온다”는 8월이라면 정보량이 크다. 드문 사건일수록 정보량이 크다는 직관을 수식으로 옮기려면 어떤 함수여야 할까. 세 가지 조건을 걸어보자.
- 확률이 낮을수록 정보량이 크다 (단조 감소)
- 확률이 1인 사건의 정보량은 0이다
- 독립인 두 사건을 함께 관측한 정보량은 각각의 합이다: $I(pq) = I(p) + I(q)$
3번이 결정적이다. 곱을 합으로 바꾸는 연속함수는 로그뿐이다. 1번과 2번까지 맞추려면 부호를 뒤집어야 한다.
\[I(x) = -\log p(x)\]로그의 밑이 2면 단위는 bit, 자연로그면 nat이다. 딥러닝 구현은 거의 항상 자연로그를 쓴다.
2. 엔트로피 — 평균 놀람
정보량은 사건 하나에 대한 값이다. 분포 전체에 대해 평균을 내면 엔트로피다.
\[H(P) = \mathbb{E}_{x \sim P}\left[-\log P(x)\right] = -\sum_x P(x) \log P(x)\]Shannon의 source coding theorem에 따르면 이 값은 $P$를 따르는 데이터를 인코딩하는 데 필요한 최소 평균 비트 수다. 공정한 동전은 $H = 1$ bit, 항상 앞면만 나오는 동전은 $H = 0$ bit다. 후자는 결과를 전송할 필요조차 없으니 당연하다.
엔트로피는 분포 하나의 고유한 성질이다. 여기까지는 모델이 등장하지 않는다.
3. Cross Entropy — 틀린 코드북을 쓸 때의 비용
이제 두 분포를 등장시킨다. 실제 데이터는 $P$를 따르는데, 우리는 그걸 모르고 $Q$가 맞다고 믿고 코드북을 만들었다. 이때 실제로 드는 평균 비트 수가 Cross Entropy다.
\[H(P, Q) = \mathbb{E}_{x \sim P}\left[-\log Q(x)\right] = -\sum_x P(x) \log Q(x)\]기댓값은 실제 분포 $P$로 잡고, 로그 안에는 내 예측 $Q$가 들어간다. 이 비대칭이 핵심이다.
$Q = P$일 때 이 값은 최소가 되고, 그 최솟값이 정확히 $H(P)$다. 따라서 언제나
\[H(P, Q) \ge H(P)\]내 믿음이 실제와 어긋난 만큼 추가 비용을 지불한다. 그 초과분이 뒤에 나올 KL Divergence다.
4. 딥러닝에서의 Cross Entropy
$P$를 정답 라벨 분포, $Q$를 모델 예측 분포로 두면 손실 함수가 된다. 라벨이 one-hot이면 $P$는 정답 클래스 $c$에서만 1이고 나머지는 0이므로 합이 한 항만 남는다.
\[L = -\sum_k P(k) \log Q(k) = -\log Q(c)\]정답 클래스에 부여한 확률의 음의 로그. 이게 실무에서 마주치는 Cross Entropy의 전부다. 정답에 1을 주면 손실 0, 정답 확률이 0으로 가면 손실이 무한대로 발산한다.
import torch
import torch.nn.functional as F
logits = torch.tensor([[2.0, 1.0, 0.1]])
target = torch.tensor([0])
# softmax([2.0, 1.0, 0.1]) = [0.6590, 0.2424, 0.0986]
# CE = -log(0.6590) = 0.4170
F.cross_entropy(logits, target)
5. BCE — 클래스가 둘일 때
Binary Cross Entropy는 별개의 개념이 아니라 클래스가 2개인 특수 케이스다. $P = (y,\ 1-y)$, $Q = (\hat y,\ 1-\hat y)$를 위 정의에 그대로 대입하면 나온다.
\[L_{\text{BCE}} = -\left[\,y \log \hat y + (1-y)\log(1-\hat y)\,\right]\]그럼 왜 이름을 따로 붙였을까. 두 가지 이유가 있다.
첫째는 표현의 경제성이다. 클래스가 2개면 $\hat y$ 하나만 알면 나머지는 $1 - \hat y$로 결정된다. 출력 노드 2개 + softmax는 파라미터가 중복된다. 노드 1개 + sigmoid로 충분하다.
둘째가 실질적으로 더 중요하다. multi-label 문제를 다룰 수 있다. softmax는 출력의 합을 1로 강제하므로 클래스들이 상호배타적이라고 가정한다. 반면 sigmoid + BCE는 각 클래스를 독립된 이진 문제로 취급해서 합에 제약이 없다. 사진 한 장에 “고양이”와 “실내”가 동시에 참일 수 있는 문제라면 BCE를 써야 한다.
| CE + softmax | BCE + sigmoid | |
|---|---|---|
| 출력 노드 | 클래스 수 $K$개 | 클래스당 1개 |
| 출력 합 | 항상 1 | 제약 없음 |
| 클래스 가정 | 상호배타 (multi-class) | 독립 (multi-label) |
| 대표 문제 | “이 사진은 개인가 고양이인가” | “이 사진에 뭐뭐가 있는가” |
6. 왜 이게 “옳은” 손실인가 — 최대우도추정
여기까지는 정보 이론 쪽 해석이었다. 통계 쪽에서 접근하면 같은 식이 전혀 다른 경로로 도출된다.
파라미터 $\theta$인 모델이 데이터 ${(x_i, y_i)}$를 만들어냈을 가능성(우도)을 최대화하고 싶다. 샘플이 독립이라면 우도는 곱이고, 로그를 씌우면 합이 된다.
\[\hat\theta = \arg\max_\theta \sum_i \log Q_\theta(y_i \mid x_i) = \arg\min_\theta \sum_i -\log Q_\theta(y_i \mid x_i)\]우변이 정확히 Cross Entropy의 합이다.
Cross Entropy 최소화 = 최대우도추정
여러 후보 중 그럴듯해 보여서 고른 손실이 아니다. “데이터를 가장 잘 설명하는 파라미터를 찾는다”는 통계학의 표준 원리를 분류 문제에 적용하면 필연적으로 나오는 식이다.
7. KL Divergence — 그래서 실제로 줄이고 있는 것
두 분포의 차이 자체를 재고 싶다면 Cross Entropy는 조금 불편하다. $Q = P$인 완벽한 경우에도 값이 0이 아니라 $H(P)$이기 때문이다. 그 바닥값을 빼주자.
\[D_{KL}(P \parallel Q) = \sum_x P(x) \log \frac{P(x)}{Q(x)}\]로그 안의 분수를 쪼개면 정체가 드러난다.
\[D_{KL}(P \parallel Q) = \sum_x P(x)\log P(x) - \sum_x P(x) \log Q(x) = -H(P) + H(P, Q)\]정리하면 이 글 전체를 관통하는 한 줄이 나온다.
\[\boxed{\,H(P, Q) = H(P) + D_{KL}(P \parallel Q)\,}\]Cross Entropy = 데이터 자체의 엔트로피 + 내 예측이 틀린 만큼의 초과 비용.
그리고 여기서 실무적으로 중요한 결론이 따라 나온다. 학습 중에 정답 분포 $P$는 고정되어 있으므로 $H(P)$는 $\theta$에 대한 상수다. 상수를 빼도 argmin은 변하지 않는다.
\[\arg\min_\theta H(P, Q_\theta) = \arg\min_\theta D_{KL}(P \parallel Q_\theta)\]Cross Entropy를 최소화하는 것과 KL Divergence를 최소화하는 것은 완전히 동일한 최적화 문제다. 그래서 굳이 $H(P)$를 계산하지 않고 더 싼 Cross Entropy를 쓴다.
라벨이 one-hot이면 한 걸음 더 나간다. 확률 1인 사건 하나뿐이라 $H(P) = 0$이고, 따라서
\[H(P, Q) = D_{KL}(P \parallel Q)\]두 값이 같아진다. 앞의 예제에서 CE가 0.4170이었는데, $D_{KL}$을 직접 계산해도 $1 \cdot \log(1/0.6590) = 0.4170$으로 같은 값이다.
KL의 성질 두 가지
항상 0 이상이다. $D_{KL}(P\parallel Q) \ge 0$이고, 등호는 $P = Q$일 때만 성립한다 (Gibbs’ inequality). 앞서 $H(P,Q) \ge H(P)$라고 했던 것의 다른 표현이다.
대칭이 아니다. $D_{KL}(P \parallel Q) \ne D_{KL}(Q \parallel P)$이고 삼각부등식도 성립하지 않는다. 그래서 “KL 거리”라는 말은 엄밀히는 틀렸다. divergence라고 부르는 이유다.
이 비대칭은 성가신 결점이 아니라 실제로 쓰이는 성질이다.
- Forward KL $D_{KL}(P \parallel Q)$: $P$가 큰 곳에서 $Q$가 작으면 $\log(P/Q)$가 폭발한다. $Q$는 $P$가 존재하는 영역을 전부 덮으려 한다 (mass-covering). 지도학습이 여기 해당한다.
- Reverse KL $D_{KL}(Q \parallel P)$: 기댓값을 $Q$로 잡으므로 $Q$가 0인 영역은 아예 벌점을 받지 않는다. $Q$는 $P$의 봉우리 하나에 안전하게 몰린다 (mode-seeking). 변분추론이나 RLHF의 KL penalty가 여기 해당한다.
KL을 직접 쓰는 경우
Cross Entropy로 대체 가능한데도 KL을 명시적으로 계산하는 상황이 있다. $P$가 one-hot이 아닐 때다.
- Knowledge distillation: teacher의 softmax 분포를 $P$로 쓴다. $H(P) \ne 0$이지만 student 파라미터에 대해서는 여전히 상수라, 사실 CE를 써도 최적해는 같다. 그래도 “두 분포를 맞춘다”는 의도를 드러내려고 관례적으로 KL을 쓴다.
- VAE: latent 분포를 prior에 붙들어두는 정규화 항이 KL이다. 이건 손실의 일부라 상수항이 아니고, 실제로 값 자체가 필요하다.
- PPO 등 RL: 새 정책이 기존 정책에서 너무 멀어지지 않게 KL로 제약한다.
8. 다시 기울기 — Cross Entropy가 학습을 살리는 이유
첫 절의 문제로 돌아가자. BCE의 $z$에 대한 기울기를 구해보면 이렇게 된다.
\[\frac{\partial L}{\partial \hat y} = \frac{\hat y - y}{\hat y(1-\hat y)}, \qquad \frac{\partial \hat y}{\partial z} = \hat y(1 - \hat y)\] \[\frac{\partial L}{\partial z} = \hat y - y\]분모의 $\hat y(1-\hat y)$가 sigmoid의 미분과 정확히 상쇄된다. MSE를 죽이던 포화 항이 사라지고, 기울기가 오차 그 자체가 된다. 크게 틀릴수록 크게 움직인다.
softmax + CE에서도 똑같은 일이 벌어진다.
\[\frac{\partial L}{\partial z_i} = \hat y_i - y_i\]이건 우연이 아니다. sigmoid와 softmax는 exp로 만들어진 함수이고, Cross Entropy는 log를 취한다. log와 exp가 서로를 지운다. 정보 이론에서 유도된 손실과 확률로 정규화하는 활성 함수가 짝이 맞도록 설계되어 있는 것이다.
9. 구현할 때 자주 밟는 지뢰
logit을 넘겨라, 확률을 넘기지 말고. F.cross_entropy와 nn.BCEWithLogitsLoss는 내부에서 log-sum-exp trick으로 수치 안정성을 확보한다. 직접 softmax를 거친 뒤 log를 취하면 확률이 0으로 언더플로했을 때 -inf가 나오고 그대로 NaN이 된다.
nn.CrossEntropyLoss는 softmax를 이미 포함한다. 모델 마지막에 softmax를 넣고 이 손실을 쓰면 softmax가 두 번 걸린다. 에러 없이 조용히 학습만 안 되는, 가장 찾기 짜증나는 종류의 버그다. nn.BCEWithLogitsLoss와 nn.BCELoss의 관계도 동일하다.
# 나쁨 — softmax 두 번
logits = model(x)
loss = nn.CrossEntropyLoss()(F.softmax(logits, dim=-1), target)
# 좋음 — raw logit 그대로
logits = model(x)
loss = nn.CrossEntropyLoss()(logits, target)
F.kl_div의 인자 규약은 직관과 다르다. 첫 인자는 확률이 아니라 log 확률이고, 두 번째 인자가 확률이다 (log_target=False 기준). 게다가 기본 reduction='mean'은 원소 단위 평균이라 수식의 정의와 맞지 않는다. 분포 단위로 맞추려면 reduction='batchmean'을 써야 한다.
Label smoothing은 $P$를 건드리는 기법이다. one-hot을 살짝 뭉개서 $P = (1-\epsilon, \epsilon/(K-1), \ldots)$로 만든다. 이러면 $H(P) \ne 0$이 되어 CE와 KL의 값이 더 이상 같지 않다. 다만 여전히 상수 차이라 최적화 대상으로서는 동일하다. 손실 값의 절대 크기를 다른 실험과 비교할 때만 주의하면 된다.
정리
- 정보량 $-\log p$는 “곱을 합으로” 조건에서 필연적으로 나오는 형태다.
- 엔트로피 $H(P)$는 분포 하나의 고유한 최소 인코딩 비용이다.
- Cross Entropy $H(P,Q)$는 실제 $P$인데 $Q$를 믿고 인코딩할 때의 비용이고, 항상 $H(P)$ 이상이다.
- BCE는 클래스가 2개인 CE다. 다만 sigmoid와 짝지어 multi-label을 다룰 수 있다는 실질적 차이가 있다.
- CE 최소화는 최대우도추정과 같다. 임의로 고른 손실이 아니다.
- $H(P,Q) = H(P) + D_{KL}(P\parallel Q)$이고, 학습 중 $H(P)$는 상수다. 따라서 CE 최소화와 KL 최소화는 같은 문제이며, one-hot 라벨에서는 값까지 같다.
- KL은 비대칭이라 거리가 아니다. 그 비대칭이 mass-covering과 mode-seeking을 가른다.
- log와 exp가 상쇄되어 기울기가 $\hat y - y$로 떨어지는 것이, MSE 대신 CE를 쓰는 실전적 이유다.