놀라움과 정보

크로스 엔트로피: 잘못된 코드표

“딥러닝에서 늘 줄이는 cross-entropy loss. 그 이름에 왜 엔트로피가 들어 있고, 왜 0까지는 내려가지 않는가?”

약어 사전으로 먼저: 잘못 믿고 만든 코드표

자주 쓰는 말에 짧은 약어를 붙여 두면 글이 짧아진다. 네 가지 말 「가」「나」「다」「라」를 0과 1로 적어 보내는데, 네 말이 실제로 나오는 비율이 (½, ¼, ⅛, ⅛)라고 하자. 스무고개에서 자주 나오는 답부터 물었듯, 가장 흔한 말에 한 자리, 다음 말에 두 자리, 나머지 둘에 세 자리 코드를 준다(0, 10, 110, 111). 이런 목록을 정보이론에서는 코드표(결과마다 붙인 0·1 코드의 목록)라 부른다. 그런데 비율을 모르고 네 말이 똑같이 나온다고 믿었다면, 모두 두 자리 코드(00, 01, 10, 11)를 주었을 것이다.

말 실제로 나오는 비율 비율대로 만든 코드표 고르게 나온다고 믿고 만든 코드표
가 ½ 1자리 2자리
나 ¼ 2자리 2자리
다 ⅛ 3자리 2자리
라 ⅛ 3자리 2자리
한 말에 드는 평균 1.75자리 2자리

잘못 믿고 만든 코드표로 적으면 한 말에 2자리가 들고, 그 가운데 1.75자리는 실제 비율을 알았어도 들었을 자릿수, 나머지 0.25자리는 잘못 믿은 탓에 괜히 더 쓴 자릿수다. 한국어 빈도에 맞춘 약어 사전으로 영어를 적으면 자주 쓰는 말에 긴 약어가 붙어 비효율이 생기는 것과 같다. 코드 한 자리는 예/아니오 답 하나, 곧 1 bit다. 그래서 확률 q를 믿고 만든 코드의 길이는 −log₂ q, 곧 q를 믿는 사람의 놀라움이다.

정의

현실 분포가 p인데 모델 q가 매긴 놀라움 −log qi로 평균을 내면 크로스 엔트로피다. 위 표에서는 현실 분포 p = (½, ¼, ⅛, ⅛), 고르게 믿은 모델 q = (¼, ¼, ¼, ¼)이고, 크로스 엔트로피가 2 bit, 현실 분포 p 자신의 엔트로피가 1.75 bit다.

H(p,q)=−∑ipilog⁡qiKL(p ∥ q)=H(p,q)−H(p)=∑ipilog⁡piqi\begin{aligned} \textcolor{#9467bd}{H}(\textcolor{#d6479f}{p}, \textcolor{#7f8f10}{q}) &= -\sum_i \textcolor{#d6479f}{p}_i \log \textcolor{#7f8f10}{q}_i \\ \textcolor{#c2398a}{\mathrm{KL}}(\textcolor{#d6479f}{p}\,\|\,\textcolor{#7f8f10}{q}) &= \textcolor{#9467bd}{H}(\textcolor{#d6479f}{p}, \textcolor{#7f8f10}{q}) - \textcolor{#9467bd}{H}(\textcolor{#d6479f}{p}) = \sum_i \textcolor{#d6479f}{p}_i \log \frac{\textcolor{#d6479f}{p}_i}{\textcolor{#7f8f10}{q}_i} \end{aligned}
H(p,q)크로스 엔트로피: 현실 p 에서 모델 q 의 평균 놀라움H(p)현실 자신의 엔트로피KLKL 발산 (두 분포가 얼마나 다른지 재는 양)pi현실에서 결과 i 의 확률 (평균을 내는 무게)qi모델이 결과 i 에 준 확률i결과의 번호\begin{array}{ll} \textcolor{#9467bd}{H}(\textcolor{#d6479f}{p}, \textcolor{#7f8f10}{q}) & \text{크로스 엔트로피: 현실 } p \text{ 에서 모델 } q \text{ 의 평균 놀라움} \\ \textcolor{#9467bd}{H}(\textcolor{#d6479f}{p}) & \text{현실 자신의 엔트로피} \\ \textcolor{#c2398a}{\mathrm{KL}} & \text{KL 발산 (두 분포가 얼마나 다른지 재는 양)} \\ \textcolor{#d6479f}{p}_i & \text{현실에서 결과 } i \text{ 의 확률 (평균을 내는 무게)} \\ \textcolor{#7f8f10}{q}_i & \text{모델이 결과 } i \text{ 에 준 확률} \\ i & \text{결과의 번호} \end{array}

KL의 정의가 여기서 뜻을 얻는다. log(pi/qi) = (−log qi) − (−log pi). 결과 i가 일어났을 때 모델 q의 놀라움 −log qi에서 현실 p 자신의 놀라움 −log pi를 뺀 것, "괜히 더 놀란 양"이다. KL은 그 초과 놀라움을 현실의 빈도로 평균 낸 것이다. 그리고 H(p, q) = H(p) + KL(p‖q). 크로스 엔트로피는 현실 p가 정한 엔트로피 H(p)와 모델 q의 낭비 KL(p‖q)로 나뉜다. 위 표에서 괜히 더 쓴 0.25 bit가 KL(p‖q)이다.

세 양의 단위는 log의 밑을 따라, 모두 nat이거나 모두 bit다. qi = 0인데 pi > 0인 결과가 있으면 −log qi = ∞이므로 크로스 엔트로피도 KL도 무한대가 된다. 현실에서 일어나는 일을 "불가능"이라 한 모델은 무한히 놀란다.

직접 움직여 보기

불러오는 중…

ML에서: 학습이 줄이는 것

딥러닝을 해본 사람이라면 cross-entropy loss라는 이름을 본 적이 있을 것이다. 그 loss가 위에서 정의한 크로스 엔트로피다. 현실이 p인데 모델이 q라고 주장하면, 그 주장의 비용이 H(p, q)이고, 모델을 학습한다는 건 이 비용을 줄이는 것이다. 이 비용이 어디까지 내려갈 수 있는지는 아래 문제 9에서 따져 본다.

그런데 우리는 p를 모르고, 가진 것은 p에서 나온 샘플 x₁, …, xN뿐이다. 샘플마다 1/N의 확률을 준 분포를 경험분포 p̂라 부르는데, p 자리에 p̂를 넣으면 크로스 엔트로피가 우리에게 익숙한 식이 된다.

H(p^,qθ)=−1N∑n=1Nlog⁡qθ(xn)=−1N ℓ(θ)\begin{aligned} \textcolor{#9467bd}{H}(\hat{\textcolor{#d6479f}{p}}, \textcolor{#7f8f10}{q}_{\textcolor{#2e9e6e}{\theta}}) &= -\frac{1}{\textcolor{#a05000}{N}} \sum_{n=1}^{\textcolor{#a05000}{N}} \log \textcolor{#7f8f10}{q}_{\textcolor{#2e9e6e}{\theta}}(\textcolor{#6f8fa6}{x}_n) \\ &= -\frac{1}{\textcolor{#a05000}{N}}\, \textcolor{#b58a00}{\ell}(\textcolor{#2e9e6e}{\theta}) \end{aligned}
H크로스 엔트로피p^경험분포 (샘플마다 1/N 의 확률)qθ파라미터(분포를 정하는 숫자) θ 로 정해지는 모델θ모델의 파라미터xnn 번째 샘플ℓ로그우도 ∑nlog⁡qθ(xn) (우도 = 모델이 관측에 준 확률)N샘플 수\begin{array}{ll} \textcolor{#9467bd}{H} & \text{크로스 엔트로피} \\ \hat{\textcolor{#d6479f}{p}} & \text{경험분포 (샘플마다 } 1/N \text{ 의 확률)} \\ \textcolor{#7f8f10}{q}_{\textcolor{#2e9e6e}{\theta}} & \text{파라미터(분포를 정하는 숫자) } \theta \text{ 로 정해지는 모델} \\ \textcolor{#2e9e6e}{\theta} & \text{모델의 파라미터} \\ \textcolor{#6f8fa6}{x}_n & n \text{ 번째 샘플} \\ \textcolor{#b58a00}{\ell} & \text{로그우도 } \sum_n \log q_\theta(x_n) \text{ (우도 = 모델이 관측에 준 확률)} \\ \textcolor{#a05000}{N} & \text{샘플 수} \end{array}

평균 음의 로그우도(NLL, negative log-likelihood)가 곧 경험분포와의 크로스 엔트로피다. 그러니 최대우도추정(MLE, maximum likelihood estimation. 로그우도를 가장 크게 하는 파라미터를 고르는 방법)은, H(p̂)가 θ와 무관하기 때문에 KL(p̂‖qθ)를 가장 작게 만드는 θ를 찾는 일이다. "내 모델이 현실에서 얼마나 먼가"라는 질문의 답이 여기 있다. 학습은 데이터가 그려 준 현실 p̂에서 모델이 내놓은 분포 qθ까지의 KL을 줄이는 과정이다.

파이썬

import numpy as np

p = np.array([0.5, 0.3, 0.2])          # 현실
q = np.array([0.25, 0.25, 0.5])        # 모델

H_p  = -np.sum(p * np.log(p))
H_pq = -np.sum(p * np.log(q))
KL   =  np.sum(p * np.log(p / q))
print(f"H(p) = {H_p:.4f}   H(p,q) = {H_pq:.4f}   KL(p‖q) = {KL:.4f}")
print(f"H(p) + KL = {H_p + KL:.4f}")

# 경험분포와 최대우도: 크로스 엔트로피 = 평균 음의 로그우도
rng = np.random.default_rng(0)
x = rng.choice(3, size=1000, p=p)                 # 현실에서 1000개 관측
p_hat = np.bincount(x, minlength=3) / len(x)      # 경험분포
nll = -np.mean(np.log(q[x]))                      # 모델 q 의 평균 음의 로그우도
print(f"경험분포 {p_hat}   평균 NLL {nll:.4f}   H(p̂,q) {-np.sum(p_hat * np.log(q)):.4f}")
# H(p) = 1.0297   H(p,q) = 1.2477   KL(p‖q) = 0.2180
# H(p) + KL = 1.2477
# 경험분포 [0.473 0.315 0.212]   평균 NLL 1.2393   H(p̂,q) 1.2393

평균 NLL과 H(p̂, q)는 계산 순서만 다른 같은 숫자다.

이 절의 결론

“KL = 크로스 엔트로피 − 엔트로피. 모델 탓에 괜히 더 놀란 양의 평균.” “최대우도추정은 경험분포에서 모델까지의 KL을 줄이는 일이다.”

문제 7. 거꾸로 믿은 코드표

위 약어 사전에서 네 말 「가」「나」「다」「라」는 실제로 (½, ¼, ⅛, ⅛)의 비율로 나오는데, 누군가 비율을 거꾸로 (⅛, ⅛, ¼, ½)로 믿고 코드표를 만들었다. (가) 말마다 몇 자리 코드를 주었겠는가? 한 말에 평균 몇 자리가 드는가? (나) 잘못 믿은 탓에 괜히 더 쓴 자릿수는 몇 bit인가? 고르게 믿었을 때의 0.25 bit와 비교하라.

함께 풀기

김민준 M01
김민준

믿은 비율대로면 「라」가 가장 흔하다고 봤으니까 「라」 1자리, 「다」 2자리, 「가」「나」 3자리요. 평균은 (3 + 3 + 2 + 1)/4 = 2.25자리예요.

선생님 T01
선생님

그 평균은 어느 비율로 낸 거예요?

김민준 M04
김민준

네 말을 똑같이 쳤네요. 실제로는 「가」가 절반이니까 ½·3 + ¼·3 + ⅛·2 + ⅛·1 = 2.625자리예요. 코드 길이는 믿은 비율이 정하고, 평균은 실제 비율로 내야 하는구나.

이서연 S01
이서연

그럼 괜히 더 쓴 자릿수는 2.625 − 1.75 = 0.875 bit야. 고르게 믿은 0.25보다 세 배 넘게 커. 모르는 것보다 틀리게 아는 게 훨씬 비싸네.

선생님 T01
선생님

그래요. 코드 길이는 믿은 q가, 평균은 현실 p가 정해요. 그래서 크로스 엔트로피에서 log 안에는 q가, 앞에는 p가 와요.

김민준 M01
김민준

조교님이 모르는 문제는 차라리 비워 두라던 게 이거였네요. 틀린 답을 자신 있게 쓰면 감점이 더 큰 채점 방식이요.

문제 8. 크로스 엔트로피를 쪼개기

(가) 모든 i에서 pi > 0, qi > 0일 때 H(p, q) = H(p) + KL(p‖q)임을 보여라. (나) p = (0.5, 0.5), q = (0.9, 0.1)로 세 양을 계산하라. (다) q에 0인 칸이 있으면 어떻게 되는가? p에 0인 칸이 있으면? (위 위젯의 「문제 8」 단추를 누르면 셋째 결과의 확률을 0으로 둔 채 (나)의 두 분포를 불러올 수 있다.)

함께 풀기

김민준 M01
김민준

(나)부터 했어요. H(p,q) = 1.204, H(p) = 0.693, KL = 0.511. 더하면 1.204. 맞으니까 (가)도 성립해요.

선생님 T01
선생님

그건 (가)의 증명이에요, 예시예요?

김민준 M01
김민준

…예시네요. 숫자 하나 맞는 걸로는 모든 p, q를 말 못 하죠.

이서연 S01
이서연

식으로 하면 한 줄이에요. −log qi = −log pi + log(pi/qi)를 pi로 가중해서 더하면 끝이에요. (다)도 같은 식이니까 그냥 성립하겠죠.

선생님 T01
선생님

p = (0.5, 0.5), q = (1, 0)을 넣어 봐요.

이서연 S05
이서연

−log q₂ = −log 0이라… 무한대요. 로그를 쪼개는 단계에서 log 0을 유한한 수처럼 다뤘어요. 양쪽이 둘 다 ∞라서 “∞ = 0.693 + ∞” 꼴이 되긴 하는데, 그건 증명이 아니라 약속이에요.

선생님 T01
선생님

그래요. qi = 0이고 pi > 0인 칸이 있으면 크로스 엔트로피도 KL도 무한대로 정의해요. 반대로 pi = 0인 칸은요?

이서연 S01
이서연

pi = 0인 칸은 0 × log 0 = 0으로 약속했으니 합에서 빠져요. q가 거기에 뭘 주든 상관없어요. 현실에서 안 일어나는 일이니까요.

김민준 M01
김민준

그래서 분류 모델 출력에 softmax를 쓰는 거네요. softmax는 어떤 클래스에도 정확히 0을 안 주니까 loss가 무한대로 안 가요.

이서연 S01
이서연

해석학에서 "식이 성립한다"는 말 앞에 늘 "정의되는 범위에서"가 붙던 게 이런 이유였어요.

문제 9. 라벨이 엇갈리는 데이터에서 loss의 최솟값

이진 분류 데이터에 똑같은 입력이 여러 번 나오는데, 사람 평가자들이 붙인 라벨은 70%가 「1」, 30%가 「0」으로 엇갈려 있다. 모델은 이 입력에 「1」일 확률 q 하나를 낸다. (가) q = 0.99일 때 이 입력에서의 평균 cross-entropy loss(nat)는? (나) q를 바꾸어 loss를 가장 작게 하면 q와 loss는 얼마인가? (다) 모델을 아무리 키워도 왜 이 loss를 0으로 만들 수 없는지 크로스 엔트로피를 쪼갠 식으로 설명하라. (위 위젯의 「문제 9」 단추로 라벨 분포와 q = 0.99를 불러와 q를 옮겨 볼 수 있다.)

함께 풀기

김민준 M01
김민준

모델을 충분히 키우면 결국 1.0을 내서 loss가 0이 되지 않아요? 일단 q = 0.99면 −(0.7 ln 0.99 + 0.3 ln 0.01) ≈ 0.007 + 1.382 = 1.389 nat이에요. 1에 가까이 갔는데 오히려 커졌네요.

선생님 T01
선생님

라벨이 「0」인 30%에서 q = 1을 내면요?

김민준 M05
김민준

−ln 0이라 끝없이 커져요. 0으로 가려다 무한대로 가네요.

이서연 S01
이서연

(나)는 q로 미분하면 −0.7/q + 0.3/(1 − q) = 0이라 q = 0.7. loss는 −(0.7 ln 0.7 + 0.3 ln 0.3) ≈ 0.611 nat이에요. 라벨 분포 (0.7, 0.3)의 엔트로피 그대로네요.

선생님 T01
선생님

그 0.611은 H(p, q) = H(p) + KL(p‖q)의 두 항 가운데 어느 것이에요?

이서연 S01
이서연

최솟값에서 q = 0.7이니까 q = p예요. 그러면 KL이 0이고 H(p)만 남은 거예요.

선생님 T01
선생님

모델을 키우면 두 항 가운데 어느 쪽을 줄일 수 있어요?

이서연 S07
이서연

KL뿐이에요. H(p)는 라벨 분포가 정한 값이라 모델 크기와 상관없어요. loss가 내려갈 수 있는 가장 낮은 값은 0이 아니라 H(p)예요.

선생님 T01
선생님

그래요. 학습 곡선이 어떤 값에서 더 내려가지 않는다고 늘 모델 탓은 아니에요. 라벨이 엇갈리는 데이터라면 loss의 최솟값이 라벨 분포의 엔트로피예요.

김민준 M01
김민준

조교님 두 분의 채점이 엇갈리는 과제라면, 누가 점수를 예측해도 두 분 점수를 다 맞힐 수는 없는 거랑 같네요.