전체 지도

지도 완성: 지수족 하나를 다루는 한 벌의 연장

자리마다 따로 만든 도구들을 한 상자에 담으면, 정말로 서로 맞물릴까? 장마다 등식을 하나씩 따로 증명했으니, 한 등식에서 쓴 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], 발밑 이차식이 한 객체 안에서 동시에 맞는다. 클래스에 새 t(x)와 k(x)만 넣으면 같은 연장이 다른 지수족에서도 그대로 돈다.

수확

“θ 쪽 브레그만은 칸을 뒤집은 KL이고, η 쪽 브레그만은 칸을 그대로 둔 KL이며, F*는 음의 엔트로피, 피셔는 F의 두 번 미분이자 t의 공분산이다. 장마다 따로 증명한 등식들이 F 하나에서 함께 나오고, 한 클래스 안에서 함께 맞는다.”

지도가 완성되었다. 키 분포를 점 하나로 놓았을 때 시작된 여행이 여기서 하나의 그림이 된다. 그 그림을 한 문단으로 줄이면 이렇다. 확률을 더해서 섞는 길(m-측지선. m은 mixture)과 곱해서 섞는 길(e-측지선. e는 exponential)이 있고, 곱해서 섞는 길로 이어 붙인 모형(e-평탄한 모형, 지수족이 그렇다) 위로 현실 p를 KL이 가장 작은 점 q까지 내리면, 모형 안의 어떤 r을 골라도 KL(p‖r) = KL(p‖q) + KL(q*‖r)이 선다. p에서 q*로 내려오는 덧셈의 길과 모형이 직각으로 만나서 KL이 피타고라스 정리처럼 나뉘는 것이다. EM 같은 교대 최소화가 한 번에 줄인 양을 정확히 잴 수 있는 것도 이 등식 덕분이다. 덧셈은 무지의 방향이고, 곱셈은 확신의 방향이다. 그리고 그 사이를 번역한 것이 log였다.

마지막으로, 이 지도 위의 자가 좌표를 바꿔도 같은 자인지 확인해 보자. 먼저 손에 익은 종이 지도부터.

문제 9. 가로 세로 축척이 다른 지도

어느 지도는 가로 1 cm가 실제 1 km, 세로 1 cm가 실제 2 km다. (가) 지도 위에서 오른쪽으로 3 cm, 위로 2 cm 떨어진 두 곳의 실제 거리는? (나) 같은 두 곳을 가로 1 cm = 0.5 km, 세로 1 cm = 1 km인 다른 지도에 옮겨 그리면, 지도 위에서 가로·세로로 몇 cm 떨어지고 실제 거리는 얼마인가? (다) 두 지도에서 「지도 위 가로·세로 칸 수로 실제 거리를 구하는 규칙」을 적어 비교하라.

함께 풀기

김민준 M01
김민준

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

선생님 T01
선생님

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

김민준 M07
김민준

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

이서연 S01
이서연

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

이서연 S01
이서연

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

선생님 T01
선생님

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

이서연 S08
이서연

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

김민준 M01
김민준

같은 과제를 A4로 뽑든 B5로 뽑든 내용은 같고 쪽수만 달라지는 거랑 같네요.

문제 10. 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₃)에서도 같은 일을 하라. θ 좌표의 피셔 행렬은 Cov[t] = diag(p₁, p₂) − (p₁, p₂)(p₁, p₂)ᵀ이다. (다) 두 행렬이 "같은 계량"이라는 것을 좌표 변환으로 보여라.

함께 풀기

김민준 M01
김민준

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

선생님 T01
선생님

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

김민준 M01
김민준

0.2 − 0.2 = 0이요. 심플렉스, 곧 합이 1인 확률들의 영역 밖으로 나갔네요. h = 0.05로 줄였더니 [[7.79, 5.75], [5.75, 9.29]]가 나와요. 이론값은 [[7, 5], [5, 8.33]]이고요. 10%쯤 안 맞아요.

김민준 M05
김민준

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

선생님 T01
선생님

발밑의 이차식이라는 건 어느 범위에서의 이야기였어요?

김민준 M01
김민준

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]]이요.

선생님 T01
선생님

더 줄이면요?

김민준 M08
김민준

1e-7에서는 [[6.9985, 4.9998], …]로 다시 벗어나요. 반올림 오차가 드러나는 구간이에요. 간격이 너무 크면 3차 항이, 너무 작으면 반올림 오차가 끼어요. 중간에 좋은 구간이 있어요.

이서연 S01
이서연

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

선생님 T01
선생님

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

이서연 S01
이서연

아니요, 반지름 r과 각도 φ로 쓰면 dr² + r²dφ²이니까 달라요. 좌표를 바꾸면 성분도 바뀌어요. 그러면 야코비안 J = ∂ξ/∂θ를 곱하면 되겠네요. J를 계산하면 [[0.25, −0.15], [−0.15, 0.21]]이고, GξJ를 하면…

이서연 S05
이서연

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

선생님 T01
선생님

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

이서연 S09
이서연

두 개요. g(u, v). 그러니까 벡터 두 개가 각각 좌표 변환을 거쳐야 해요. J를 한 번이 아니라 양쪽에 곱해야 해요. 지도 문제에서 축척을 제곱해 넣었던 것과 같아요. JᵀGξJ = [[0.25, −0.15], [−0.15, 0.21]]. θ 좌표의 피셔와 정확히 같아요.

이서연 S01
이서연

그리고 제가 잘못 곱한 GξJ가 단위행렬이었던 것도 우연이 아니에요. J = ∂η/∂θ가 θ 좌표의 피셔 자체고, η 좌표의 피셔는 그 역행렬이니까요. 틀린 계산이 쌍대성을 보여 준 거예요.

선생님 T13
선생님

둘이 이 장 전체를 다시 한 셈이에요. 민준 학생은 "발밑"이 얼마나 좁은지를 숫자로 찾았고, 서연 학생은 계량이 좌표가 아니라 기하의 것이라는 걸 확인했어요. 성분은 좌표마다 다르고, 계량은 하나예요.

김민준 M01
김민준

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

이서연 S01
이서연

선형대수에서 이차형식은 PᵀAP로 바뀐다고 외웠는데, 그게 왜 양쪽인지 이제 몸으로 알겠어요.

문제 11. 확률로 배우느냐 로짓으로 배우느냐

성공 확률 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로 줄이면 (나)의 두 결과는 어떻게 되는가?

함께 풀기

김민준 M01
김민준

(가) 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예요.

김민준 M05
김민준

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

선생님 T01
선생님

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

김민준 M07
김민준

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

이서연 S01
이서연

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

선생님 T01
선생님

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

이서연 S07
이서연

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

선생님 T13
선생님

기울기를 피셔로 나눠서, 어느 좌표로 배우든 같은 분포로 가게 만든 걸음이에요. 이 걸음에는 자연 기울기라는 이름이 붙어 있어요.

김민준 M01
김민준

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