1장 — 미시상태와 거시상태

스털링 근사: 큰 수는 로그로 다룬다

동전 100개만 되어도 경우의 수가 10²⁹을 넘었다. 기체 1몰이면 동전이 10²³개인 셈이니, 경우의 수는 계산기에 넣을 수조차 없다. 경우의 수는 N에 대해 지수적으로 커지므로 로그를 취해야 다룰 수 있다. 그런데 이항계수 C(N, n) = N! / (n!(N − n)!)에는 계승(팩토리얼, N! = 1 × 2 × … × N)이 들어 있다. ln N!을 곧이곧대로 구하려면 ln 1 + ln 2 + … + ln N을 모두 더해야 하는데, 동전이 10²³개면 더할 항도 10²³개이고 N이 바뀔 때마다 처음부터 다시 더해야 한다. 계승의 로그를 간단한 식으로 쓸 수는 없을까?

역사: 드무아브르와 스털링

18세기 초 프랑스 출신의 수학자 드무아브르는 동전 던지기 확률을 계산하다가 같은 벽에 부딪혔고, 큰 N에서 N!이 대략 N^(N + 1/2) e^(−N)에 어떤 상수를 곱한 값이라는 것을 알아냈다. 그러나 그 상수가 무엇인지는 정하지 못했다. 1730년 스코틀랜드의 수학자 제임스 스털링이 그 상수가 √(2π)(π는 원주율 파이, pi)임을 밝혔고, 오늘날 이 근사에는 그의 이름이 붙어 있다. 드무아브르는 이 근사를 동전 던지기에 썼다. 동전을 3,600번 던질 때 앞면 개수가 어디에 얼마의 확률로 나오는지를 셈했고, 그 과정에서 오늘날 정규분포라 부르는 종 모양 곡선을 처음 이끌어 냈다(1738년 『우연의 이론』(The Doctrine of Chances) 2판).

근사식과 동전의 경우의 수

로그를 취하고 N이 클 때 중요한 항만 남기면 다음 식이 된다. 이것을 스털링 근사 (계승의 로그를 간단히 쓰는 근사, Stirling’s approximation)라 한다.

ln⁡N!≈Nln⁡N−N(N이 클 때)\ln \textcolor{#1f77b4}{N}! \approx \textcolor{#1f77b4}{N} \ln \textcolor{#1f77b4}{N} - \textcolor{#1f77b4}{N} \qquad (\textcolor{#1f77b4}{N} \text{이 클 때})
N계승을 취하는 큰 수 (예: 동전 개수)\begin{array}{ll} \textcolor{#1f77b4}{N} & \text{계승을 취하는 큰 수 (예: 동전 개수)} \end{array}

이것을 이항계수에 넣고 정리하면 다음을 얻는다. 유도는 아래 문제 10에서 함께 해 보자.

ln⁡C(N,n)≈N h ⁣(nN),h(p)=−pln⁡p−(1−p)ln⁡(1−p)\ln \textcolor{#d62728}{C}(\textcolor{#1f77b4}{N}, \textcolor{#17becf}{n}) \approx \textcolor{#1f77b4}{N}\, \textcolor{#9467bd}{h}\!\left(\frac{\textcolor{#17becf}{n}}{\textcolor{#1f77b4}{N}}\right), \qquad \textcolor{#9467bd}{h}(\textcolor{#e377c2}{p}) = -\textcolor{#e377c2}{p} \ln \textcolor{#e377c2}{p} - (1-\textcolor{#e377c2}{p})\ln(1-\textcolor{#e377c2}{p})
C(N,n)N개 중 n개가 앞면인 경우의 수 (이항계수)N동전 개수n그중 앞면인 동전 개수h(p)동전 하나당 경우의 수의 로그p앞면 비율 (n/N)\begin{array}{ll} \textcolor{#d62728}{C}(\textcolor{#1f77b4}{N}, \textcolor{#17becf}{n}) & \text{N개 중 n개가 앞면인 경우의 수 (이항계수)} \\ \textcolor{#1f77b4}{N} & \text{동전 개수} \\ \textcolor{#17becf}{n} & \text{그중 앞면인 동전 개수} \\ \textcolor{#9467bd}{h}(\textcolor{#e377c2}{p}) & \text{동전 하나당 경우의 수의 로그} \\ \textcolor{#e377c2}{p} & \text{앞면 비율 (n/N)} \end{array}
직접 움직여 보기경우의 수와 h 곡선새 창에서 열기 ↗

다시 말해 W ≈ e^(N·h(p))이다. 여기서 h(p)는 p = 1/2에서 최대(ln 2 ≈ 0.693)이고 양 끝에서 0이 되는 함수다. 경우의 수가 N에 대해 지수적으로 커진다는 사실이 이 식에 그대로 드러나 있는 셈이다.

집중 현상이 이 식에서 나온다

이 식을 쓰면 동전 100개에서 본 집중 현상이 왜 일어나는지 설명할 수 있다. 앞면 비율이 p인 거시상태와 1/2인 거시상태의 경우의 수 비는 대략 다음과 같다.

W(p)W(1/2)≈e−N [ h(1/2)−h(p) ]\frac{\textcolor{#d62728}{W}(\textcolor{#e377c2}{p})}{\textcolor{#d62728}{W}(1/2)} \approx e^{-\textcolor{#1f77b4}{N}\,[\,\textcolor{#9467bd}{h}(1/2) - \textcolor{#9467bd}{h}(\textcolor{#e377c2}{p})\,]}
W(p)앞면 비율이 p인 거시상태의 경우의 수p앞면 비율N동전 개수h(p)동전 하나당 경우의 수의 로그e자연상수\begin{array}{ll} \textcolor{#d62728}{W}(\textcolor{#e377c2}{p}) & \text{앞면 비율이 p인 거시상태의 경우의 수} \\ \textcolor{#e377c2}{p} & \text{앞면 비율} \\ \textcolor{#1f77b4}{N} & \text{동전 개수} \\ \textcolor{#9467bd}{h}(\textcolor{#e377c2}{p}) & \text{동전 하나당 경우의 수의 로그} \\ e & \text{자연상수} \end{array}
직접 움직여 보기집중 현상새 창에서 열기 ↗

이를테면 p = 0.6이면 h(0.5) − h(0.6) ≈ 0.020이다. 이 값을 넣고 N을 바꿔 가며 비를 구하면 다음과 같다.

N W(0.6) / W(0.5)
100 근사 e^(−2.0) ≈ 0.13, 정확한 값 0.136
10²³ e^(−2 × 10²¹)

지수 안의 차이 0.020은 작지만, 거기에 N이 곱해져 있다는 점이 중요하다. 그러므로 N이 커지면 가장 큰 거시상태 외의 것은 급격히 무시할 만해진다. 동전 100개에서 앞면 개수가 50 근처에 몰렸던 집중 현상의 이유가 바로 이것이다.

여기서 한 가지를 확인해 두자. 근사식이니 오차가 있을 텐데, 그 오차는 괜찮을까? 작은 N과 큰 N에서 근사가 얼마나 맞는지는 아래 문제 10에서 직접 견주어 보자.

ML에서: 확률은 곱하지 않고 로그로 더한다

ML에서도 로그가 먼저 눈에 띄는 까닭은 같다. 문장의 확률은 토큰마다의 확률을 모두 곱한 것이라, 문장이 길어질수록 지수적으로 작아진다. 그래서 모델은 확률을 곱하는 대신 로그 확률을 더하고, 손실도 log-likelihood로 쓴다. 곱을 그대로 계산하면 무슨 일이 생기는지는 아래 문제에서 확인해 보자. 경우의 수에 로그를 씌운 것과 똑같이, 지수적으로 커지거나 작아지는 수를 더하기 문제로 바꾸는 것이다.

문제 10. 스털링 근사 유도와 확인

ln N! ≈ N ln N − N을 써서 ln C(N, n) ≈ N·h(n/N)을 유도하라. h(p) = −p ln p − (1 − p) ln(1 − p). (풀어 본 뒤 위젯 1의 「문제 10」 단추로 작은 N과 큰 N을 불러와 확인해 보자.)

김민준 M03
김민준

대입하고 정리하면 ln C ≈ −n ln n − (N − n) ln(N − n). 이게 N·h죠?

선생님 T02
선생님

n = 0을 넣어 볼까요. 앞면이 하나도 없는 경우는 W = 1이니까 ln W는 0이어야 하죠?

김민준 M04
김민준

−N ln N… 0이 아니네요.

선생님 T14
선생님

N ln N 항을 빠뜨렸어요. 전부 쓰면 N ln N − n ln n − (N − n) ln(N − n)이고, 스털링의 −N 항들은 −N + n + (N − n) = 0으로 정확히 상쇄됩니다. 이제 N ln N = n ln N + (N − n) ln N으로 나눠 붙이면 −n ln(n/N) − (N − n) ln((N − n)/N) = N·h(n/N)이 나와요. 식을 유도하면 이렇게 쉬운 경계값을 넣어 보는 습관을 들이세요.

이서연 S05
이서연

저는 유도는 했는데, 확인해 보니 안 맞았어요. N = 4, n = 2면 ln 6 = 1.79인데 4·h(0.5) = 4 ln 2 = 2.77이에요.

선생님 T14
선생님

식이 틀린 게 아니라 N = 4가 너무 작은 거예요. 스털링 근사는 N이 클 때 쓰는 근사입니다. 한 항 더 정확히 쓰면 ln C(N, N/2) ≈ N ln 2 − ½ ln(πN/2)예요.

N 정확한 값 N ln 2 비율
4 1.79 2.77 0.65
100 66.78 69.31 0.96
10²³ 약 6.9 × 10²² 약 6.9 × 10²² 1과 10⁻²¹ 수준 차이

오차는 ln N 정도로만 자라고 값 자체는 N에 비례해 자라니까, 비율은 1로 갑니다. 1조 원 예산에서 몇십 원 반올림을 신경 쓰지 않는 것과 같아요. 반대로 네 명에게 설문하고 "국민의 50%"라고 말하면 안 되듯이, 작은 N에서 근사식을 믿으면 안 됩니다.

이서연 S09
이서연

근사식은 작은 N 하나가 아니라 N을 키우면서 비율로 확인해야 하는군요.

문제 11. 확률을 곱하면 0이 되는 이유

언어 모델이 토큰 200개짜리 문장의 토큰마다 확률 0.5를 준다. 문장 전체의 확률을 float32로 곱해서 구하면 무엇이 나오는가? 로그 확률의 합과 토큰당 평균 loss를 구하라.

김민준 M11
김민준

torch.prod로 곱했더니 0.0이 나와요. 로그를 씌우니까 −inf고요. 모델이 이 문장을 절대 안 만든다는 뜻인가요?

선생님 T14
선생님

참값은 2⁻²⁰⁰ ≈ 6 × 10⁻⁶¹이라 0은 아니에요. float32가 나타낼 수 있는 가장 작은 양수가 10⁻⁴⁵ 근처라서 그보다 작아지는 순간 0으로 잘린 거예요.

이서연 S11
이서연

경우의 수를 로그로 다룬 것처럼 곱하기 전에 로그를 씌우면 되겠네요. 200 × ln 0.5 = −138.6이고, 토큰당 평균 loss는 0.693, 곧 ln 2예요.

김민준 M09
김민준

10²³개짜리 경우의 수를 계산기에 못 넣던 거랑 같은 문제였네. 그래서 cross_entropy 함수가 확률 대신 로짓을 받아 안에서 로그로 계산하는구나.