지도 완성: 지수족 하나를 다루는 한 벌의 연장
자리마다 따로 만든 도구들을 한 상자에 담으면, 정말로 서로 맞물릴까? 장마다 등식을 하나씩 따로 증명했으니, 한 등식에서 쓴 F와 다른 등식에서 쓴 F가 같은 것인지는 아직 아무도 한 번에 확인하지 않았다. 지금까지의 도구가 정말 지수족 하나를 다루는 한 벌의 연장이라면, 한곳에 모아도 어긋나지 않아야 한다. 코드로 확인하는 것이 가장 정직하다. 한 클래스 안에 F, η, 피셔, F*, 브레그만, KL을 넣고, 장마다 따로 증명했던 등식들이 한 번에 성립하는지 본다.
파이썬
결과가 셋인 분포를 지수족으로 쓴다. 결과가 둘이면 파라미터가 하나라 피셔 정보가 숫자 하나로 나오고, 결과가 셋이면 파라미터가 둘이라 피셔가 2 × 2 행렬로 나오니, 결과가 셋인 분포는 행렬로서의 피셔를 볼 수 있는 가장 작은 경우다. t(x)는 앞의 두 결과를 가리키는 원-핫이고, 셋째 결과가 기준이다.
import numpy as np
class ExponentialFamily:
"""유한 표본 공간 위의 지수족 p_θ(x) = exp(θ·t(x) − F(θ) + k(x))"""
def __init__(self, t, k=None):
self.t = np.asarray(t, float) # 모양 (결과 수, 파라미터 수)
self.k = np.zeros(len(self.t)) if k is None else np.asarray(k, float)
def F(self, th): # 로그정규화자
return np.log(np.sum(np.exp(self.t @ th + self.k)))
def prob(self, th):
return np.exp(self.t @ th + self.k - self.F(th))
def eta(self, th): # η = ∇F(θ) = E[t]
return self.prob(th) @ self.t
def fisher(self, th): # I(θ) = ∇²F(θ) = Cov[t]
p, e = self.prob(th), self.eta(th)
d = self.t - e
return (d * p[:, None]).T @ d
def theta_of(self, eta, th=None): # η → θ (뉴턴법)
th = np.zeros(self.t.shape[1]) if th is None else th
for _ in range(50):
th = th - np.linalg.solve(self.fisher(th), self.eta(th) - eta)
return th
def Fstar(self, eta): # 르장드르 쌍대 F*(η) = θ·η − F(θ)
th = self.theta_of(eta)
return th @ eta - self.F(th)
def bregman(self, a, b): # B_F(a : b)
return self.F(a) - self.F(b) - self.eta(b) @ (a - b)
def kl(self, th1, th2): # KL(p_θ1 ‖ p_θ2) = B_F(θ2 : θ1)
return self.bregman(th2, th1)
fam = ExponentialFamily(t=[[1, 0], [0, 1], [0, 0]]) # 결과 셋, 셋째를 기준으로
th1, th2 = np.array([0.9, 0.4]), np.array([-0.3, 1.1])
p1, p2 = fam.prob(th1), fam.prob(th2)
e1, e2 = fam.eta(th1), fam.eta(th2)
print("p1 =", np.round(p1, 4), " p2 =", np.round(p2, 4))
print(f"KL 정의대로 {np.sum(p1 * np.log(p1 / p2)):.6f}")
print(f"B_F(θ2:θ1) {fam.kl(th1, th2):.6f}")
print(f"B_F(θ1:θ2) {fam.bregman(th1, th2):.6f} ← 순서를 바꾸면 KL(p2‖p1)")
Bs = fam.Fstar(e1) - fam.Fstar(e2) - fam.theta_of(e2) @ (e1 - e2)
print(f"B_F*(η1:η2) {Bs:.6f}")
print(f"F*(η1) = −H(p1) {fam.Fstar(e1):.6f} {np.sum(p1 * np.log(p1)):.6f}")
print("피셔 = Cov[t]\n", np.round(fam.fisher(th1), 4))
d = 1e-3 * np.array([1.0, -2.0])
print(f"KL(θ‖θ+dθ) {fam.kl(th1, th1 + d):.4e} ½dθᵀI dθ {0.5 * d @ fam.fisher(th1) @ d:.4e}")
# p1 = [0.4967 0.3013 0.202 ] p2 = [0.1561 0.6331 0.2107]
# KL 정의대로 0.342603
# B_F(θ2:θ1) 0.342603
# B_F(θ1:θ2) 0.298423 ← 순서를 바꾸면 KL(p2‖p1)
# B_F*(η1:η2) 0.342603
# F*(η1) = −H(p1) -1.032088 -1.032088
# 피셔 = Cov[t]
# [[ 0.25 -0.1497]
# [-0.1497 0.2105]]
# KL(θ‖θ+dθ) 8.4513e-07 ½dθᵀI dθ 8.4536e-07
KL = 브레그만(순서를 뒤집어서), 르장드르 쌍대, 음의 엔트로피, 피셔 = Cov[t], KL의 2차 근사 ½dθᵀI dθ가 한 객체 안에서 동시에 맞는다. 클래스에 새 t(x)와 k(x)만 넣으면 같은 연장이 다른 지수족에서도 그대로 돈다.
이 절의 결론
“θ 좌표의 브레그만은 인자 순서를 뒤집은 KL이고, η 좌표의 브레그만은 인자 순서를 그대로 둔 KL이며, F*는 음의 엔트로피, 피셔는 F의 두 번 미분이자 t의 공분산이다. 장마다 따로 증명한 등식들이 F 하나에서 함께 나오고, 한 클래스 안에서 함께 맞는다.”
지도가 완성되어, 키 분포를 점 하나로 놓았을 때 시작된 여행이 여기서 하나의 그림이 된다. 그 그림을 한 문단으로 줄이면 이렇다. 두 분포를 잇는 곧은 길에는 확률을 더해서 섞는 m-측지선(m은 mixture)과, 확률을 곱해서(로그 확률을 더해서) 섞는 e-측지선(e는 exponential)이 있다. e-측지선으로 이어 붙인 모델 면(e-평탄한 모델 면. 지수족이 그렇다) 위로 현실 p를 KL이 가장 작은 점 q까지 내리면, 모델 면 안의 어떤 r을 골라도 KL(p‖r) = KL(p‖q) + KL(q*‖r)이 선다. p에서 q*로 내려오는 m-측지선과 모델 면이 직각으로 만나서 KL이 피타고라스 정리처럼 나뉘는 것이다. EM 같은 교대 최소화가 한 번에 줄인 KL을 정확히 잴 수 있는 것도 KL의 피타고라스 등식 덕분이다. 확률을 섞는 m-측지선은 분포를 넓게 펴는 방향이고, 확률을 곱하는 e-측지선은 봉우리를 뾰족하게 세우는 방향이다. 그리고 두 측지선 사이를 번역한 것이 log였다.
마지막으로, 이 지도 위의 자가 좌표를 바꿔도 같은 자인지 확인해 보자. 먼저 손에 익은 종이 지도부터.
문제 12. 가로 세로 축척이 다른 지도
어느 지도는 가로 1 cm가 실제 1 km, 세로 1 cm가 실제 2 km다. (가) 지도 위에서 오른쪽으로 3 cm, 위로 2 cm 떨어진 두 곳의 실제 거리는? (나) 같은 두 곳을 가로 1 cm = 0.5 km, 세로 1 cm = 1 km인 다른 지도에 옮겨 그리면, 지도 위에서 가로·세로로 몇 cm 떨어지고 실제 거리는 얼마인가? (다) 두 지도에서 「지도 위 가로·세로 칸 수로 실제 거리를 구하는 규칙」을 적어 비교하라.
함께 풀기

지도 위 거리가 √(3² + 2²) = 3.61 cm고, 축척이 1 km와 2 km 사이니까 평균 1.5를 곱해서 5.41 km요.

위로 2 cm 간 부분만 따로 보면 실제로 몇 km예요?

4 km요. 오른쪽 3 cm는 3 km고요. 그럼 실제로는 3과 4가 직각으로 만나니까 5 km네요. 지도 위 거리를 먼저 재고 축척을 곱하면 안 되고, 방향마다 축척을 따로 곱한 뒤에 재야 했어요.

(나)는 지도 위로 가로 6 cm, 세로 4 cm라서 지도 위 거리는 7.21 cm인데, 실제 거리는 √(0.5² × 6² + 1² × 4²) = √(9 + 16) = 5 km. 지도 위 숫자는 다른데 실제 거리는 같아.

(다) 규칙을 적으면 첫 지도는 (실제 거리)² = 1 × (가로 칸)² + 4 × (세로 칸)², 둘째 지도는 0.25 × (가로 칸)² + 1 × (세로 칸)²예요. 방향마다 축척이 제곱으로 들어가요.

그 1과 4, 0.25와 1 같은 숫자를 무엇이라고 불렀죠?

계량의 성분이요. 성분은 지도마다 다르고, 그걸로 잰 길이는 하나예요.

같은 과제를 A4로 뽑든 B5로 뽑든 내용은 같고 쪽수만 달라지는 거랑 같네요. 쪽수가 지도마다 다른 계량 성분이고, 내용이 하나뿐인 길이고요.
문제 13. KL의 헤시안은 좌표를 바꿔도 피셔인가 (킬러)
현실 p = (0.5, 0.3, 0.2)로 두고 모델 q를 합이 1인 확률들의 영역(심플렉스) 안에서 움직여 KL(p‖q)를 q의 함수로 본다. (가) 좌표 ξ(크사이) = (q₁, q₂)에서 q = p일 때의 헤시안을 유한차분(작은 간격 h만큼 옮긴 값의 차로 미분을 어림하기)으로 구하고, 피셔 행렬 gij = δij/pi + 1/p₃와 비교하라. δij는 i = j이면 1, 아니면 0이다. (나) 자연 파라미터 θi = log(qi/q₃)에서도 KL의 헤시안을 유한차분으로 구하라. θ 좌표의 피셔 행렬은 Cov[t] = diag(p₁, p₂) − (p₁, p₂)(p₁, p₂)ᵀ이다. (다) 두 행렬이 "같은 계량"이라는 것을 좌표 변환으로 보여라.
함께 풀기

(가)부터요. 유한차분 간격 h = 0.1로 했는데 inf가 나와요.

q₁과 q₂를 둘 다 0.1씩 늘리면 q₃는 얼마예요?

0.2 − 0.2 = 0이요. 합이 1인 확률들의 영역, 심플렉스 밖으로 나갔네요.

그럼 간격을 줄여 봐요.

h = 0.05로 줄였더니 [[7.79, 5.75], [5.75, 9.29]]가 나와요. 이론값은 [[7, 5], [5, 8.33]]이고요. 10%쯤 안 맞아요.

그럼 KL의 헤시안이 피셔라는 건 근사라서 이 정도는 틀리는 거 아니에요?

KL을 피셔 이차식 ½dθᵀI dθ로 근사하는 건 어느 범위에서의 이야기였어요?

dθ가 작을 때요. h = 0.05면 p₃ = 0.2에 비해 작지가 않네요.

얼마나 작아야 할까요? 몇 가지 넣어 봐요.

h = 1e-3이면 [[7.0003, 5.0003], [5.0003, 8.3337]], 1e-4면 소수 넷째 자리까지 [[7, 5], [5, 8.3333]]이요.

더 줄이면요?

1e-7에서는 [[6.9985, 4.9998], …]로 다시 벗어나요. 줄였는데 왜 나빠지죠?

그만큼 작은 간격이면 KL 값 두 개의 차이도 아주 작겠죠. 컴퓨터가 그 차이를 정확히 들고 있을까요?

아, 반올림 오차가 드러나는 구간이에요. 간격이 너무 크면 3차 항이, 너무 작으면 반올림 오차가 끼어요. 중간에 좋은 구간이 있어요.

(나)는 θ 좌표 헤시안이 [[0.25, −0.15], [−0.15, 0.21]]로 Cov[t]와 맞아요. 그런데 (가)의 [[7, 5], [5, 8.33]]과는 숫자가 전혀 달라요. 같은 계량이면 같아야 하지 않나요?

극좌표에서 계량 성분이 직교좌표와 같게 나와요?

아니요, 반지름 r과 각도 φ로 쓰면 dr² + r²dφ²이니까 달라요. 좌표를 바꾸면 성분도 바뀌어요.

그럼 ξ 좌표의 성분을 θ 좌표로 옮기려면 무엇이 있어야 해요?

자코비안 J = ∂ξ/∂θ요. 그걸 곱하면 되겠네요. J를 계산하면 [[0.25, −0.15], [−0.15, 0.21]]이고, GξJ를 하면…

단위행렬이 나와요. θ 좌표의 피셔가 단위행렬이라는 건 말이 안 되는데요.

계량은 벡터를 몇 개 받아서 숫자를 내요?

두 개요. g(u, v). 그러니까 벡터 두 개가 각각 좌표 변환을 거쳐야 해요. J를 한 번이 아니라 양쪽에 곱해야 해요.

지도 문제에서 축척을 제곱해 넣었던 거랑 같네.

응. JᵀGξJ = [[0.25, −0.15], [−0.15, 0.21]]. θ 좌표의 피셔와 정확히 같아요.

그럼 잘못 곱한 GξJ가 단위행렬이었던 건 우연일까요? J의 숫자를 다시 봐요. 어디서 본 행렬이에요?

(나)의 θ 좌표 피셔랑 숫자가 같아요. ξ = (q₁, q₂)는 η이기도 하니까, J = ∂η/∂θ가 θ 좌표의 피셔 자체네요.

그러면 Gξ는요?

η 좌표의 피셔는 θ 좌표 피셔의 역행렬이니까, 둘을 곱하면 단위행렬이에요. 틀린 계산이 쌍대성을 보여 준 거예요.

두 사람이 이 장의 두 결론을 다시 확인했어요. 민준 학생은 KL의 2차 근사가 맞는 범위가 얼마나 좁은지를 숫자로 찾았고, 서연 학생은 계량이 좌표가 아니라 기하의 것이라는 걸 확인했어요. 성분은 좌표마다 다르고, 계량은 하나예요.

좌표마다 계량 성분이 다른 건 단위가 다른 거네요. 과제에서 cm와 m를 섞으면 숫자가 달라 보여도 같은 길이인 거랑 같네요. 넓이를 바꿀 때는 단위 변환을 두 번 곱해야 하는 것까지요.

선형대수에서 이차형식은 PᵀAP로 바뀐다고 외웠는데, 그게 왜 양쪽인지 이제 몸으로 알겠어요.
문제 14. 확률로 배우느냐 로짓으로 배우느냐
성공 확률 q 하나로 된 베르누이 모델을 데이터의 성공 비율 0.8에 맞춘다. 손실은 크로스 엔트로피 −[0.8 log q + 0.2 log(1 − q)], 지금 q = 0.5, 학습률은 0.1이다. (가) q를 그대로 파라미터로 삼아 경사하강을 한 스텝, 로짓 θ = log(q/(1 − q))를 파라미터로 삼아 한 스텝 업데이트한 뒤의 q를 각각 구하라. (나) 각 좌표의 피셔 정보(q 좌표에서는 1/(q(1 − q)), θ 좌표에서는 q(1 − q))로 그래디언트를 나눈 스텝으로 다시 하라. (다) 학습률을 0.01로 줄이면 (나)의 두 결과는 어떻게 되는가?
함께 풀기

(가) q로 미분하면 그래디언트가 −(0.8/0.5 − 0.2/0.5) = −1.2라서 q = 0.5 + 0.12 = 0.62. 로짓으로 미분하면 그래디언트가 q − 0.8 = −0.3이라 θ = 0.03, q = 0.5075예요.

같은 모델, 같은 데이터, 같은 학습률인데 한 스텝에 간 곳이 0.62와 0.5075예요. 둘 중 하나는 틀린 거 아니에요?

두 좌표에서 「0.1만큼 한 스텝」은 같은 길이예요?

아, 가로 세로 축척이 다른 지도처럼 좌표 한 칸이 실제로 얼마인지가 좌표마다 달라요. q 좌표로 0.1과 θ 좌표로 0.1은 분포로 보면 다른 거리예요. 둘 다 틀린 게 아니라 서로 다른 좌표 눈금으로 잰 스텝이에요.

그래서 (나)에서 피셔로 나누는구나. q 좌표는 0.5 + 0.1 × 1.2 / 4 = 0.53. θ 좌표는 0.1 × 0.3 / 0.25 = 0.12만큼 가서 q = 0.52996. 거의 같은데 정확히는 안 같아. 피셔가 좌표와 상관없는 계량이라면 정확히 같아야 하지 않아?

피셔로 잰 길이가 KL과 맞는 건 어느 범위에서였죠?

2차 근사가 맞는 범위, 스텝이 작을 때요.

그럼 (다)에서 학습률을 줄여 확인해 봐요.

학습률 0.01이면 0.503과 0.5029999로, 차이가 0.1일 때의 약 4 × 10−5에서 약 4 × 10−8로 줄어요. 스텝이 작을수록 두 좌표의 결과가 하나로 모여요.

그래디언트를 피셔로 나눠서, 어느 좌표로 배우든 같은 분포로 가게 만든 스텝이에요. 피셔로 나눈 스텝에는 자연 그래디언트라는 이름이 붙어 있어요.

마감까지 남은 시간을 「일」로 세느냐 「시간」으로 세느냐에 따라 「하나 줄이기」가 전혀 다른 일이 되는 거랑 같네요. q 좌표의 0.1과 θ 좌표의 0.1이 「일」과 「시간」이고, 피셔로 나누는 건 단위를 맞추는 거고요.