충분통계량: 데이터 백만 개를 숫자 세 개로
네 부품 가운데 t(x)는 데이터에서 무엇을 뽑아내는 자리일까? 데이터 100만 개를 숫자 세 개로 요약해도 정보 손실이 없다고 하면, 직관적으로는 말이 안 된다. 어떻게 가능할까?
에딩턴의 권고와 피셔의 답 — 어느 요약이 더 많이 아는가
1914년 천문학자 에딩턴(Arthur Eddington)은 책에서, 측정값이 얼마나 흩어졌는지 잴 때 교과서 대부분의 권고와 달리 편차의 제곱보다 편차의 절댓값을 평균 내는 편이 낫다고 썼다. 두 요약은 같은 흩어짐 σ를 겨냥한다. 어느 쪽이 데이터를 더 잘 쓸까?
1920년 피셔(Ronald Fisher)는 정규분포에서 두 요약을 계산으로 견주었다. 표본이 크면 절댓값 쪽으로 어림한 σ가 제곱 쪽보다 더 많이 흔들린다(분산이 약 1.14배, 정확히는 π − 2 배). 제곱 쪽이 이긴다. 그런데 피셔는 여기서 멈추지 않고, 두 요약이 함께 어떻게 흔들리는지를 따라가다 더 강한 사실을 찾았다. 제곱으로 만든 요약의 값을 알고 나면, 절댓값 요약이 어떻게 나올지는 σ와 상관이 없어진다. 그는 「σ에 대해 표본이 주는 정보는 모두 제곱으로 만든 값 안에 요약되어 있다」고 적었다. 절댓값 요약은 덜 좋은 요약에 그치지 않았다. 제곱 요약을 안 뒤에는 σ에 대해 아무것도 더 알려 주지 않는 요약이었다. 피셔는 1922년 이 성질에 「충분」이라는 이름을 붙였다.
야구 타율 — 영상을 지워도 되는 까닭
야구 타자의 기록 — 시즌 전체의 모든 타석을 영상으로 가지고 있다고 하자. 하지만 타율을 계산하려면 두 숫자면 된다: 안타 수와 타석 수. 영상을 지워도 타율에 관해서는 아무것도 잃지 않는다. 타석 수를 빼먹으면 안 된다는 점도 보라. 안타 수만으로는 타율을 모른다.
독립인 데이터 n개의 로그우도(모형이 관측된 데이터에 준 확률의 로그)를 지수족 골격으로 쓰면 이렇다.
θ가 데이터와 만나는 곳은 첫 항 하나뿐이고, 거기서 데이터는 합 Σt(xᵢ)로만 등장한다. 둘째 항은 개수 n만 안다. 셋째 항은 θ와 상관이 없다. 그러니 θ에 대해 알아낼 수 있는 모든 것은 (n, Σt(xᵢ)) 안에 있다. 가우시안이면 t(x) = (x, x²) 이므로 (n, Σx, Σx²), 숫자 세 개다. 수십만 명의 키를 평균과 표준편차 한 쌍, 이를테면 (174, 5.8)(설명을 위해 잡은 값이다)로 요약해도 되는 것이 이 때문이다.
점이 놓인 모양이 전혀 다른 두 데이터라도 이 숫자들이 같으면 θ에 대해 알려 주는 것이 같다. 아래 파이썬이 두 데이터의 로그우도를 직접 견준다.
고정된 크기의 요약 — 지수족만의 성질
모든 분포족에 충분통계량이 있긴 하다. 최악의 경우 데이터 전체(순서만 버린 것)가 그것이다. 지수족에서는 n이 아무리 커져도 요약의 크기가 고정된다.
거꾸로도 거의 맞다. 분포가 0이 아닌 범위(받침)가 모수에 따라 변하지 않는 매끄러운 족에서, 표본 크기와 상관없이 고정된 개수의 충분통계량을 갖는 것은 지수족뿐이다. 다르무아(Darmois), 쿠프먼(Koopman), 피트먼(Pitman)의 1935–36년 논문들에서 나온 결과라 세 사람의 이름을 붙여 부른다. 피셔가 「충분」이라는 이름을 붙인 뒤 십여 년 만에, 어떤 가족이 고정된 크기의 요약을 허락하는가라는 물음에 답이 나온 것이다.
조건이 빠지면 틀린다. 균등분포 U(0, θ)는 최댓값 max xᵢ 하나가 충분통계량이지만 지수족이 아니다. θ가 바뀌면 분포가 0이 아닌 구간 자체가 바뀌기 때문이다.
파이썬 — 충분통계량만 남기기
import numpy as np
rng = np.random.default_rng(0)
data = rng.normal(174, 5.8, 1_000_000)
n, s1, s2 = len(data), data.sum(), (data**2).sum() # 충분통계량 (n, Σx, Σx²)
del data # 원본은 버린다
mu = s1 / n
sigma = np.sqrt(s2 / n - mu**2)
print(f"(n, Σx, Σx²) 만으로 복원: μ̂ = {mu:.4f}, σ̂ = {sigma:.4f}")
# 같은 (n, Σx, Σx²) 를 가진 두 데이터셋은 어떤 (μ, σ) 에서도 로그우도가 같다
x1 = np.array([1.0, 2.0, 6.0])
c = x1.mean(); r = np.sqrt(((x1 - c)**2).sum())
u = np.array([1, -2, 1]) / np.sqrt(6) # 합이 0 인 단위벡터
x2 = c + r * u # 같은 Σx, Σx² 를 갖는 다른 셋
loglik = lambda x, m, s: np.sum(-0.5 * ((x - m) / s)**2 - np.log(s * np.sqrt(2 * np.pi)))
print("두 셋:", x1, np.round(x2, 4))
for m, s in [(0, 1), (3, 2), (5, 0.7)]:
print(f" μ={m}, σ={s}: {loglik(x1, m, s):.4f} {loglik(x2, m, s):.4f}")
# (n, Σx, Σx²) 만으로 복원: μ̂ = 174.0058, σ̂ = 5.8039
# 두 셋: [1. 2. 6.] [ 4.5275 -0.0551 4.5275]
# μ=0, σ=1: -23.2568 -23.2568
# μ=3, σ=2: -6.5863 -6.5863
# μ=5, σ=0.7: -28.2174 -28.2174
수확
“지수족이 특별한 이유 = 표본이 늘어도 크기가 고정된 충분통계량 (n, Σt(xᵢ)). 받침이 모수와 무관하다는 조건 아래에서는 그런 족이 지수족뿐이다.”
문제 4. 두 타자의 타율 합치기
타자 A는 10타수 4안타(0.400), 타자 B는 200타수 60안타(0.300)다. 두 사람의 기록을 한 사람 것처럼 합친 타율은 얼마인가? 두 사람이 타율만 적어 두었다면 합친 타율을 낼 수 있는가? 무엇을 적어 두어야 하는가?
함께 풀기

0.400과 0.300의 평균, 0.350이요.

합친 기록은 몇 타수 몇 안타예요?

210타수 64안타… 0.305요. 0.350이 아니네요. 10타수밖에 안 되는 A를 200타수짜리 B와 같은 몫으로 쳤어요.

타율 두 개만 남겼으면 합친 값을 못 내. 타수를 모르면 누구 몫이 얼마인지 모르니까. (타수, 안타 수)를 남기면 더하기만 하면 돼. 타율은 비율이라 더해지지 않고, 타수와 안타 수는 합이라 더해져.

위의 (n, Σt(xᵢ))도 합이었죠. 데이터가 두 묶음으로 나뉘어 있으면요?

묶음마다 (n, Σt)를 구해서 더하면 전체의 (n, Σt)가 돼요. 나눠서 요약해도, 합치면 한 번에 요약한 것과 같아요.
문제 5. 합이 같은 두 푸아송 데이터
한 시간에 걸려 온 전화 수를 두 시간 세었다. 데이터 A = (0, 4), B = (2, 2). 둘 다 n = 2, Σx = 4다. (가) 푸아송 λ = 1, 2, 3, 5 에서 두 데이터의 로그우도를 구하라. 같은가? (나) 충분통계량이 같은데 로그우도가 다르다면, 두 데이터가 λ에 대해 알려 주는 것은 정말 같은가?
함께 풀기

λ = 2 에서 A는 −4.406, B는 −2.614 예요. 달라요. 위 파이썬에서는 합이 같은 두 셋의 로그우도가 소수 넷째 자리까지 같았는데요. 푸아송에서는 (n, Σx)가 충분통계량이 아닌가 봐요.

다른 λ 에서도 재고, 두 값의 차이를 봐요.

λ = 1 이면 −5.178, −3.386, λ = 3 이면 −4.784, −2.992, λ = 5 면 −6.740, −4.949 예요. 차이가 전부 1.792 예요.

차이가 λ와 상관이 없어. 로그우도 Σ(xᵢ log λ − λ − log xᵢ!) 에서 λ가 들어간 부분은 Σx 와 n 으로만 쓰이고, 두 셋이 다른 건 −Σ log xᵢ! 뿐이야. A는 log 0! + log 4! = log 24, B는 2 log 2! = log 4, 차이는 log 6 = 1.792. 캐리어의 합이야. 가우시안은 캐리어가 0이라 차이도 0이었던 거고.

λ를 두고 판단할 때 쓰는 건 무엇이죠?

λ를 바꿀 때 로그우도가 어떻게 오르내리는지요. 두 곡선이 위아래로 1.792만큼 떨어져 있을 뿐 모양이 같으니까, 가장 높은 곳도 λ = 2 로 같고 두 λ 사이의 우도 비도 같아요. 충분하다는 건 로그우도가 같다는 게 아니라 λ에 따라 변하는 부분이 같다는 뜻이에요.

확률 자체는 2통씩인 B가 여섯 배 높은데, λ를 고르는 데는 똑같은 표를 주는 거네요.

적분상수만 다른 두 함수가 도함수는 같은 것 같아.
문제 6. GPU 두 장에 나뉜 배치 정규화
배치 정규화는 한 배치에 든 값들의 평균과 분산으로 값을 고르게 맞춘다. 여러 GPU에 나뉜 배치를 하나로 보고 정규화하는 방식(PyTorch 의 SyncBatchNorm 등)에서는 평균과 분산을 모든 GPU의 값을 통틀어 구한다. 한 배치 128개를 GPU 두 장이 나눠 받았다. GPU A: 32개, 평균 0.5, 분산 1.0. GPU B: 96개, 평균 −0.1, 분산 0.25. (가) 128개 전체의 평균과 분산은? (나) 두 GPU가 서로 무엇을 주고받으면 되는가?
함께 풀기

평균은 (0.5 − 0.1)/2 = 0.2, 분산은 (1.0 + 0.25)/2 = 0.625 요.

타율 때처럼 개수가 다르잖아. 평균은 (32 × 0.5 + 96 × (−0.1))/128 = 0.05. 분산도 개수로 가중하면 (32 × 1.0 + 96 × 0.25)/128 = 0.4375.

둘 다 합으로 다시 해 봐요. GPU마다 (n, Σx, Σx²)부터요.

A는 (32, 16, 32 × (1.0 + 0.5²) = 40), B는 (96, −9.6, 96 × (0.25 + 0.1²) = 24.96). 더하면 (128, 6.4, 64.96). 평균은 6.4/128 = 0.05, 분산은 64.96/128 − 0.05² = 0.505 예요.

0.4375가 아니라 0.505… 내가 빠뜨린 0.0675는 두 GPU의 평균 0.5와 −0.1이 서로 떨어져 있어서 생긴 흩어짐이야. 각자 자기 평균 둘레에서 잰 분산만 모으면 그 몫이 빠져.

그러니까 (나)는 평균과 분산이 아니라 합 세 개를 주고받으면 되네요. 타율 문제랑 같아요. 안타 수와 타수 대신 n, Σx, Σx²를 보내면 더하기만 하면 끝이에요.

그래요. 정규분포의 충분통계량이 합이라서, 나눠서 모은 요약을 그대로 더할 수 있어요.