개념
왜 샘플링(Sampling)이 중요한가
샘플링은 인공지능과 기계 학습(machine learning; ML) 전반에서 네 가지 기본 역할을 맡습니다.
생성(Generation). 언어 모델, 확산 모델, 생성적 적대 신경망(GAN)은 모두 샘플링을 통해 출력을 만들어 냅니다. 어떤 샘플링 알고리즘을 쓰느냐가 창의성(creativity), 일관성(coherence), 다양성(diversity)을 직접 조절합니다. 온도, 상위-k, 핵심 표본(nucleus) 샘플링은 엔지니어가 매일 만지는 손잡이입니다.
훈련(Training). 확률적 경사 하강법(stochastic gradient descent)은 미니배치(mini-batch)를 샘플링합니다. 드롭아웃(dropout)은 비활성화할 뉴런(neuron)을 샘플링합니다. 데이터 증강(data augmentation)은 무작위 변환(random transformation)을 샘플링합니다. 중요도 샘플링은 강화 학습(PPO, TRPO)에서 경사 분산(gradient variance)을 줄이기 위해 표본에 가중치를 다시 부여합니다.
추정(Estimation). 기계 학습에서 다루는 수많은 양은 닫힌 형태의 해가 없습니다. 데이터 분포 전체에 대한 기대 손실(expected loss), 에너지 기반 모델(energy-based model)의 분배 함수(partition function), 베이즈 추론(Bayesian inference)의 증거(evidence) 등이 그렇습니다. 몬테카를로 추정(Monte Carlo estimation)은 이런 값을 표본 평균으로 근사합니다.
탐색(Exploration). MCMC 알고리즘은 베이즈 추론에서 사후 분포(posterior distribution)를 탐색합니다. 진화 전략(evolutionary strategy)은 매개변수 섭동(parameter perturbation)을 샘플링합니다. 톰슨 샘플링(Thompson sampling)은 다중 슬롯머신(bandit) 문제에서 탐색(exploration)과 활용(exploitation)의 균형을 잡습니다.
핵심 어려움은 다음과 같습니다. 직접 표본을 뽑을 수 있는 분포는 균등 분포(uniform), 정규 분포(normal)처럼 단순한 분포뿐입니다. 나머지 모든 분포에 대해서는, 단순한 표본을 목표 분포(target distribution)의 표본으로 바꾸는 방법이 필요합니다.
모든 샘플링 방법은 여기서 출발합니다. 균등 난수 생성기(uniform random number generator)는 [0, 1) 범위 안의 값을 만들며, 같은 길이의 부분 구간(sub-interval)은 같은 확률을 갖습니다.
U ~ Uniform(0, 1)
P(a <= U <= b) = b - a for 0 <= a <= b <= 1
Properties:
E[U] = 0.5
Var(U) = 1/12
원소가 n개 있는 이산 집합(discrete set)에서 균등하게 표본을 뽑으려면 U를 하나 생성한 뒤 floor(n * U)를 반환합니다. 연속 구간(continuous range) [a, b]에서 표본을 뽑으려면 a + (b - a) * U를 계산합니다.
핵심 통찰은 균등 난수 하나만으로도 어떤 분포에서든 표본 하나를 만들기에 충분한 무작위성(randomness)이 담겨 있다는 점입니다. 남은 문제는 적절한 변환(transformation)을 찾는 일뿐입니다.
누적분포함수(cumulative distribution function; CDF)는 값을 확률로 대응(mapping)시킵니다.
F(x) = P(X <= x)
Properties:
F is non-decreasing
F(-inf) = 0
F(+inf) = 1
F maps the real line to [0, 1]
역 누적분포함수(inverse CDF)는 확률을 다시 값으로 되돌려 보냅니다. U ~ Uniform(0, 1)일 때 X = F_inverse(U)는 목표 분포를 따릅니다.
Algorithm:
1. u ~ Uniform(0, 1)을 생성합니다.
2. F_inverse(u)를 반환합니다.
Why it works:
P(X <= x) = P(F_inverse(U) <= x) = P(U <= F(x)) = F(x)
Exponential distribution example:
PDF: f(x) = lambda * exp(-lambda * x), x >= 0
CDF: F(x) = 1 - exp(-lambda * x)
F(x) = u를 x에 대해 풉니다.
u = 1 - exp(-lambda * x)
exp(-lambda * x) = 1 - u
x = -ln(1 - u) / lambda
(1 - U)와 U는 같은 distribution을 가지므로:
x = -ln(u) / lambda
F_inverse를 닫힌 형태(closed form)로 쓸 수 있을 때 이 방법은 완벽하게 동작합니다. 정규 분포에는 닫힌 형태의 역 누적분포함수가 존재하지 않으므로 박스-뮬러(Box-Muller) 변환이나 수치 근사(numerical approximation) 같은 다른 방법을 씁니다.
이산 분포에서의 적용: 이산 분포(discrete distribution)에서는 누적합(cumulative sum)으로 누적분포함수를 만들고, U를 하나 생성한 뒤 누적합이 U를 처음 넘는 인덱스를 찾습니다. Lesson 06의 sample_categorical이 정확히 이 방식입니다.
거절 샘플링(Rejection Sampling)
누적분포함수를 역함수로 풀 수는 없지만 목표 확률밀도함수(target PDF)를 상수 배수(constant factor)까지 계산할 수 있는 상황이라면, 거절 샘플링(rejection sampling)을 사용할 수 있습니다.
목표 분포: p(x) (값을 계산할 수 있음, 정규화되지 않아도 됨)
제안 분포: q(x) (표본을 직접 뽑을 수 있음)
상한: 모든 x에 대해 p(x) <= M * q(x)를 만족하는 M
알고리즘:
1. x ~ q(x)를 뽑는다.
2. u ~ Uniform(0, 1)을 뽑는다.
3. u < p(x) / (M * q(x))이면 x를 수락한다.
4. 그렇지 않으면 기각하고 1단계로 돌아간다.
수락률 = 1/M
상한(bound) M이 빡빡(tight)할수록 수락률(acceptance rate)이 높아집니다. 저차원(low dimension; 1-3차원)에서는 거절 샘플링이 잘 동작합니다. 그러나 고차원에서는 제안 분포(proposal)의 부피(volume) 대부분이 기각되기 때문에 수락률이 지수적으로 감소합니다. 이것이 거절 샘플링의 차원의 저주(curse of dimensionality)입니다.
예시: 절단 정규 분포(truncated normal)에서 표본 뽑기. 절단된 구간 위에서 균등 제안(uniform proposal)을 사용합니다. 포락선(envelope) M은 해당 구간 안 정규 분포 확률밀도함수의 최댓값으로 잡습니다.
예시: 반원(semicircle)에서 표본 뽑기. 외접 직사각형(bounding rectangle) 안에서 균등하게 점을 제안합니다. 그 점이 반원 안에 있으면 수락합니다. 몬테카를로로 원주율(pi)을 계산하는 방식과 동일합니다. 수락률은 면적 비율인 pi/4가 됩니다.
중요도 샘플링(Importance Sampling)
때로는 목표 분포 p(x)에서 직접 표본이 필요한 것이 아니라, p(x) 아래에서의 기댓값(expectation)을 추정하면 충분하고, 실제 보유한 표본은 다른 분포 q(x)에서 뽑은 것인 경우가 있습니다.
Goal: estimate E_p[f(x)] = integral of f(x) * p(x) dx
Rewrite:
E_p[f(x)] = integral of f(x) * (p(x)/q(x)) * q(x) dx
= E_q[f(x) * w(x)]
where w(x) = p(x) / q(x) are the importance weights.
Estimator:
E_p[f(x)] ~ (1/N) * sum(f(x_i) * w(x_i)) where x_i ~ q(x)
이 아이디어는 강화 학습에서 매우 중요합니다. PPO(Proximal Policy Optimization)에서는 이전 정책(old policy) pi_old 아래에서 궤적을 수집하지만, 실제로 최적화하고 싶은 것은 새로운 정책(new policy) pi_new입니다. 이때의 중요도 가중치(importance weight)는 pi_new(a|s) / pi_old(a|s)입니다. PPO는 이 가중치를 잘라내(clip) 새 정책이 이전 정책에서 너무 멀리 벗어나지 않게 막습니다.
중요도 샘플링 추정량(estimator)의 분산은 q가 p와 얼마나 비슷한지에 좌우됩니다. q가 p와 크게 다르면, 일부 표본이 거대한 가중치를 받아 추정값 전체를 좌우하게 됩니다. 자기 정규화 중요도 샘플링(self-normalized importance sampling)은 가중치의 합으로 나눠 이런 문제를 줄여 줍니다.
E_p[f(x)] ~ sum(w_i * f(x_i)) / sum(w_i)
몬테카를로 추정(Monte Carlo Estimation)
몬테카를로 추정(Monte Carlo estimation)은 무작위 표본의 평균으로 적분을 근사합니다. 큰수의 법칙(law of large numbers)이 수렴을 보장해 줍니다.
목표: 정의역 D 위의 I = integral of g(x) dx 추정
방법:
1. x_1, ..., x_N을 D 위에서 균등하게 뽑는다.
2. I ~ (D의 부피 / N) * sum(g(x_i))
오차: O(1 / sqrt(N)) (차원과 무관)
오차율(error rate)은 차원과 무관합니다. 그래서 격자 기반 적분(grid-based integration)이 사실상 불가능한 고차원 환경에서 몬테카를로 방법이 지배적인 도구로 쓰입니다.
원주율(pi) 추정:
[-1, 1] x [-1, 1] 영역에서 (x, y)를 균등하게 뽑는다.
단위원 안에 들어가는 점의 개수를 센다. x^2 + y^2 <= 1
pi ~ 4 * (원 안의 점 개수) / (전체 점 개수)
기댓값 추정:
E[f(X)] ~ (1/N) * sum(f(x_i)) (단, x_i ~ p(x))
표본 평균은 참 기댓값으로 수렴한다.
추정량의 분산 = Var(f(X)) / N
마르코프 연쇄 몬테카를로(Markov Chain Monte Carlo; MCMC): 메트로폴리스-헤이스팅스
MCMC는 정상 분포(stationary distribution)가 목표 분포 p(x)가 되도록 설계된 마르코프 연쇄(Markov chain)를 만들어 사용합니다. 충분히 많은 단계가 지나면, 연쇄가 만들어 내는 표본은 사실상 p(x)에서 뽑힌 표본으로 간주할 수 있습니다.
목표 분포: p(x) (정규화 상수까지만 알아도 됨)
제안 분포: q(x'|x) (현재 상태가 주어졌을 때 다음 상태를 제안하는 방식)
메트로폴리스-헤이스팅스 알고리즘:
1. 어떤 x_0에서 시작한다.
2. t = 1, 2, ..., T에 대해:
a. x' ~ q(x'|x_t)를 제안한다.
b. 수락 비율을 계산한다.
alpha = [p(x') * q(x_t|x')] / [p(x_t) * q(x'|x_t)]
c. 확률 min(1, alpha)로 수락한다.
- u ~ Uniform(0,1)에 대해 u < alpha이면 x_{t+1} = x'
- 그렇지 않으면 x_{t+1} = x_t
3. 앞의 B개 표본은 버린다. (번-인 구간)
4. 나머지 표본을 반환한다.
대칭 제안(symmetric proposal; q(x'|x) = q(x|x'))에서는 위 비율이 p(x')/p(x)로 단순해집니다. 이것이 원형의 메트로폴리스(Metropolis) 알고리즘입니다.
왜 동작하는가. 수락 규칙(acceptance rule)은 상세 균형(detailed balance)을 보장합니다. 즉, x에 있다가 x'로 이동할 확률과, x'에 있다가 x로 이동할 확률이 같아집니다. 상세 균형은 p(x)가 연쇄의 정상 분포라는 사실을 함의합니다.
실무에서 고려할 점:
- 번-인(burn-in): 연쇄가 평형(equilibrium)에 도달하기 전의 초기 표본을 버립니다.
- 솎아내기(thinning): 자기 상관(autocorrelation)을 줄이기 위해 k번째 표본만 남깁니다.
- 제안 크기(proposal scale): 너무 작으면 연쇄가 천천히 움직입니다. 수락률은 높지만 탐색이 느려집니다. 반대로 너무 크면 대부분의 제안이 기각되어 제자리에서 머뭅니다.
- 고차원에서 가우시안 제안(Gaussian proposal)의 최적 수락률은 대략 0.234로 알려져 있습니다.
깁스 샘플링(Gibbs Sampling)
깁스 샘플링은 다변량 분포(multivariate distribution)를 위한 MCMC의 특수한 경우입니다. 모든 차원을 한 번에 움직이는 대신, 조건부 분포(conditional distribution)에서 변수 하나씩 갱신하는 방식입니다.
목표 분포: p(x_1, x_2, ..., x_d)
알고리즘:
매 반복 t마다:
x_1^{t+1} ~ p(x_1 | x_2^t, x_3^t, ..., x_d^t) 를 뽑는다.
x_2^{t+1} ~ p(x_2 | x_1^{t+1}, x_3^t, ..., x_d^t) 를 뽑는다.
...
x_d^{t+1} ~ p(x_d | x_1^{t+1}, x_2^{t+1}, ..., x_{d-1}^{t+1}) 를 뽑는다.
깁스 샘플링을 적용하려면 각 조건부 분포 p(x_i | x_{-i})에서 표본을 뽑을 수 있어야 합니다. 다음과 같은 모델에서 자연스럽게 어울립니다.
- 베이즈 망(Bayesian network): 조건부 분포가 그래프 구조에서 곧바로 얻어집니다.
- 가우시안 혼합(Gaussian mixture): 조건부 분포가 가우시안입니다.
- 이징 모형(Ising model): 각 스핀(spin)의 조건부 분포가 이웃(neighbor)에만 의존합니다.
정확한 조건부 분포에서 표본을 뽑기 때문에 상세 균형이 자동으로 만족되며, 수락률은 항상 1입니다.
한계. 변수들이 매우 강하게 상관되어 있으면 깁스 샘플링은 혼합(mixing)이 느려집니다. 한 번에 변수 하나씩만 갱신하기 때문에, 분포를 가로지르는 큰 대각 방향의 이동을 만들어 내기 어렵습니다.
온도 샘플링(Temperature Sampling; LLM에서 사용)
언어 모델은 어휘의 각 토큰에 대해 로짓 z_1, ..., z_V를 출력합니다. 소프트맥스(softmax)는 이를 확률로 변환합니다. 온도(temperature)는 소프트맥스에 넣기 전에 로짓을 다시 스케일링하는 역할을 합니다.
p_i = exp(z_i / T) / sum(exp(z_j / T))
T = 1.0: 표준 소프트맥스 (원래 분포 그대로)
T -> 0: argmax (결정론적이며 항상 가장 큰 로짓을 가진 토큰 선택)
T -> inf: 균등 분포 (모든 토큰이 같은 확률)
T < 1.0: 분포를 더 뾰족하게 만든다. 더 확신 있고 덜 다양한 출력.
T > 1.0: 분포를 더 평탄하게 만든다. 덜 확신 있고 더 다양한 출력.
왜 동작하는가. T < 1로 로짓을 나누면 로짓 사이의 차이가 증폭됩니다. 예를 들어 z_1 = 2, z_2 = 1일 때 T = 0.5로 나누면 4와 2가 되어 간격이 더 벌어집니다. 소프트맥스 이후 가장 높은 로짓을 가진 토큰이 훨씬 큰 확률 질량을 가져갑니다.
실무 기준:
- T = 0.0: 그리디 디코딩(greedy decoding), 사실 기반 질의응답(factual Q&A)에 적합합니다.
- T = 0.3-0.7: 약간 창의적이며 코드 생성(code generation)에 적합합니다.
- T = 0.7-1.0: 균형 잡힌 설정으로, 일반 대화에 적합합니다.
- T = 1.0-1.5: 창의적 글쓰기(creative writing), 브레인스토밍에 적합합니다.
- T > 1.5: 점점 더 무작위적이 되어 드물게만 유용합니다.
온도는 가능한 토큰 집합 자체를 바꾸지는 않습니다. 단지 각 토큰에 할당되는 확률 질량(probability mass)을 조정할 뿐입니다.
상위-k 샘플링(Top-k Sampling)
상위-k 샘플링은 확률이 가장 높은 k개 토큰만으로 후보 집합(candidate set)을 제한한 뒤, 다시 정규화(renormalize)하고 그 안에서 표본을 뽑는 방법입니다.
알고리즘:
1. 모든 V개 토큰에 대해 소프트맥스 확률을 계산한다.
2. 확률을 내림차순으로 정렬한다.
3. 상위 k개 토큰만 남긴다.
4. 다시 정규화한다. p_i' = p_i / sum(p_j for j in top-k)
5. 정규화된 분포에서 표본을 뽑는다.
k = 1: 그리디 디코딩
k = V: 필터링 없음 (표준 샘플링)
k = 40: 흔히 쓰는 값으로, 가능성이 매우 낮은 토큰의 긴 꼬리를 제거한다.
상위-k 샘플링은 어휘 분포의 긴 꼬리(long tail)에 있는, 오타(typo)나 무의미한 문자열 같은 아주 낮은 확률의 토큰이 선택되는 것을 막아 줍니다. 다만 k가 문맥과 무관하게 고정되어 있다는 한계가 있습니다. 모델이 매우 확신해 한 토큰이 95% 확률을 차지하는 경우에도 k=40이면 여전히 다른 39개 토큰을 후보로 허용합니다. 반대로 모델이 불확실해 확률이 1000개 토큰에 퍼져 있는 상황이면, k=40은 그럴듯한 선택지(plausible option)들을 잘라 버릴 수 있습니다.
상위-p 샘플링(Top-p, Nucleus Sampling)
상위-p 샘플링은 후보 집합의 크기를 동적으로 조정합니다. 고정된 개수의 토큰을 유지하는 대신, 누적 확률(cumulative probability)이 p를 넘는 가장 작은 토큰 집합을 유지합니다.
알고리즘:
1. 모든 V개 토큰에 대해 소프트맥스 확률을 계산한다.
2. 확률을 내림차순으로 정렬한다.
3. 상위 k개 확률의 합이 p 이상이 되는 가장 작은 k를 찾는다.
4. 그 k개 토큰만 남긴다.
5. 다시 정규화한 뒤 표본을 뽑는다.
p = 0.9: 확률 질량 90%를 덮는 토큰만 유지한다.
p = 1.0: 필터링 없음.
p = 0.1: 매우 제한적이며, 거의 그리디에 가깝다.
모델이 확신할 때는 핵심 표본 샘플링(nucleus sampling)이 토큰을 적게 유지합니다. 예를 들어 2-3개 정도만 남길 수 있습니다. 반대로 모델이 불확실할 때는 200개 같은 많은 토큰을 유지합니다. 이런 적응적(adaptive) 동작 덕분에 핵심 표본 샘플링은 일반적으로 상위-k 샘플링보다 더 좋은 텍스트를 만들어 냅니다.
자주 쓰이는 조합:
- 온도 0.7 + 상위-p 0.9: 범용 설정에 적합합니다.
- 온도 0.0(그리디): 결정론적 결과가 필요한 작업에 적합합니다.
- 온도 1.0 + 상위-k 50: Fan et al. (2018)의 원 논문 설정입니다.
상위-k와 상위-p는 함께 사용할 수도 있습니다. 먼저 상위-k를 적용한 뒤 남은 집합에 상위-p를 다시 적용합니다.
재매개변수화 기법(Reparameterization Trick; VAE에서 사용)
변분 오토인코더(variational autoencoder; VAE)는 입력을 잠재 공간(latent space)의 분포로 인코딩(encode)하고, 그 분포에서 표본을 뽑은 뒤 다시 디코딩(decode)해 입력을 재구성(reconstruct)하면서 학습합니다. 문제는 샘플링 연산을 거쳐 역전파를 할 수 없다는 점입니다.
표준 샘플링 (미분 불가능):
z ~ N(mu, sigma^2)
무작위성이 경사 전파(gradient flow)를 막는다.
d/d_mu [N(mu, sigma^2)에서 뽑은 표본] = ???
재매개변수화 기법은 무작위성과 매개변수를 분리하는 방식으로 이 문제를 해결합니다.
재매개변수화된 샘플링:
epsilon ~ N(0, 1) (매개변수와 무관한 고정 잡음)
z = mu + sigma * epsilon (매개변수의 결정론적 함수)
이제 z는 mu, sigma에 대한 결정론적이고 미분 가능한 함수다.
d(z)/d(mu) = 1
d(z)/d(sigma) = epsilon
경사는 mu와 sigma를 통해 흐른다.
이 방법이 동작하는 이유는 N(mu, sigma^2)와 mu + sigma * N(0, 1)이 같은 분포이기 때문입니다. 핵심 통찰은 무작위성을 매개변수와 무관한 원천(epsilon)으로 옮기고, 표본을 매개변수에 대한 미분 가능한 변환으로 다시 쓰는 것입니다.
VAE 학습 루프:
- 인코더가 각 입력에 대해
mu와 log(sigma^2)를 출력합니다.
epsilon ~ N(0, 1)을 뽑습니다.
z = mu + sigma * epsilon을 계산합니다.
z를 디코딩해 입력을 재구성합니다.
- 4, 3, 2, 1단계를 거꾸로 통과시키며 역전파를 수행합니다. 3단계가 미분 가능하기 때문에 가능합니다.
재매개변수화 기법이 없으면 표준적인 역전파만으로는 VAE를 학습할 수 없습니다. 이 하나의 통찰이 VAE를 실용적인 모델로 만들었습니다.
검벨-소프트맥스(Gumbel-Softmax; Differentiable Categorical Sampling)
재매개변수화 기법은 가우시안 같은 연속 분포(continuous distribution)에서는 잘 동작합니다. 그러나 이산적 범주형 분포(discrete categorical distribution)에는 다른 접근이 필요합니다. 검벨-소프트맥스(Gumbel-Softmax)는 범주형 샘플링에 대한 미분 가능한 근사(differentiable approximation)를 제공합니다.
검벨-맥스 기법(Gumbel-Max trick; 미분 불가능):
로그 확률 log(p_1), ..., log(p_k)를 가진 범주형 분포에서 표본을 뽑으려면:
1. 각 범주(category)에 대해 g_i ~ Gumbel(0, 1)을 뽑는다.
(u ~ Uniform(0, 1)일 때 g = -log(-log(u)))
2. argmax(log(p_i) + g_i)를 반환한다.
이렇게 하면 정확히 범주형 분포에서 뽑은 표본을 얻을 수 있다.
검벨-소프트맥스(Gumbel-Softmax; 미분 가능한 근사):
강한 argmax 대신 부드러운 소프트맥스를 사용한다.
y_i = exp((log(p_i) + g_i) / tau) / sum(exp((log(p_j) + g_j) / tau))
tau(온도)는 근사의 강도를 제어한다.
tau -> 0: 원-핫 벡터에 가까워짐(강한 범주형)
tau -> inf: 균등 분포에 가까워짐(1/k, 1/k, ..., 1/k)
tau = 1.0: 부드러운 근사
검벨-소프트맥스는 이산 표본의 연속적 완화(continuous relaxation)를 만들어 줍니다. 출력은 강한 원-핫(hard one-hot) 벡터가 아니라 확률 벡터, 곧 부드러운 원-핫(soft one-hot)입니다. 경사는 소프트맥스를 통해 흐릅니다. 학습의 순전파(forward pass)에서는 직선 추정기(straight-through estimator)를 함께 사용할 수도 있습니다. 순전파에서는 강한 argmax를 쓰고, 역전파(backward pass)에서는 부드러운 검벨-소프트맥스 경사를 사용하는 방식입니다.
활용처:
- VAE의 이산 잠재 변수(discrete latent variable)
- 신경망 구조 탐색(neural architecture search)에서 이산 연산 선택
- 강한 어텐션(hard attention) 메커니즘
- 이산 행동(discrete action)을 사용하는 강화 학습
층화 샘플링(Stratified Sampling)
표준 몬테카를로 샘플링은 우연히 표본 공간(sample space) 안에 빈 구간을 남길 수 있습니다. 층화 샘플링(stratified sampling)은 표본 공간을 여러 층(strata)으로 나누고 각 층에서 표본을 뽑아, 전체 영역이 고르게 덮이도록 강제합니다.
표준 몬테카를로:
[0, 1] 위에서 N개 점을 균등하게 뽑는다.
어떤 구간에는 점이 몰리고, 어떤 구간에는 빈 곳이 생길 수 있다.
층화 샘플링:
[0, 1]을 동일한 크기의 N개 층으로 나눈다.
[0, 1/N), [1/N, 2/N), ..., [(N-1)/N, 1)
각 층 안에서 점을 하나씩 균등하게 뽑는다.
x_i = (i + u_i) / N (단, u_i ~ Uniform(0, 1), i = 0, ..., N-1)
층화 샘플링은 표준 몬테카를로 방법보다 분산(variance)이 항상 작거나 같습니다.
Var(층화) <= Var(표준 몬테카를로)
f(x)가 매끄럽게 변할 때 개선 효과가 가장 크다.
구간별 상수 함수(piecewise-constant function)에서는 층화 샘플링이 정확하다.
활용처:
- 수치 적분(numerical integration; 준-몬테카를로(quasi-Monte Carlo))
- 학습 데이터 분할(training data split): 각 폴드(fold)에서 클래스 균형을 보장하는 데 사용
- 층화와 결합한 중요도 샘플링(stratification을 결합한 importance sampling)
- 신경 광휘장(Neural Radiance Fields; NeRF)에서 카메라 광선(camera ray)을 따라 층화 샘플링을 사용합니다.
확산 모델(Diffusion Model)과의 연결
확산 모델(diffusion model)은 샘플링 과정을 통해 이미지를 생성합니다. 정방향 과정(forward process)은 T단계에 걸쳐 이미지에 가우시안 잡음을 더해 결국 순수 잡음(pure noise)으로 만듭니다. 역방향 과정(reverse process)은 잡음 제거(denoise)를 학습해, 원래 이미지를 한 단계씩 복원해 갑니다.
정방향 과정 (이미 정의됨):
x_t = sqrt(alpha_t) * x_{t-1} + sqrt(1 - alpha_t) * epsilon
(단, epsilon ~ N(0, I))
T단계가 지나면: x_T ~ N(0, I) (순수 잡음)
역방향 과정 (학습으로 얻음):
x_{t-1} = (1/sqrt(alpha_t)) * (x_t - (1 - alpha_t)/sqrt(1 - alpha_bar_t) * epsilon_theta(x_t, t)) + sigma_t * z
(단, z ~ N(0, I))
잡음 제거 단계 하나하나가 곧 샘플링 단계 하나에 해당한다.
이 lesson에서 다룬 방법들과의 연결을 정리하면 다음과 같습니다.
- 각 잡음 제거 단계는 재매개변수화 기법을 사용합니다. 잡음을 표본으로 뽑고 결정론적 변환(deterministic transform)을 적용합니다.
- 잡음 스케줄(noise schedule)
{alpha_t}는 온도 담금질(temperature annealing)의 한 형태를 제어하는 역할을 합니다.
- 학습은 몬테카를로 추정으로 증거 하한(evidence lower bound; ELBO)을 근사합니다.
- 확산 모델의 조상 샘플링(ancestral sampling)은 마르코프 연쇄에 해당합니다. 각 단계는 직전 상태에만 의존합니다.
전체 이미지 생성 과정 자체가 반복적인 샘플링입니다. 잡음에서 출발해, 매 단계에서 학습된 잡음 제거 모델에 조건부로(conditioned) 조금 덜 시끄러운 버전을 표본으로 뽑아 가는 셈입니다.
만들어 보기
Step 1: 균등 샘플링과 역 CDF 샘플링
import math
import random
def sample_uniform(a, b):
return a + (b - a) * random.random()
def sample_exponential_inverse_cdf(lam):
u = random.random()
return -math.log(u) / lam
지수 분포(exponential) 표본 10,000개를 만들고, 평균이 1/lambda에 가까운지 확인합니다.
Step 2: 거절 샘플링
def rejection_sample(target_pdf, proposal_sample, proposal_pdf, M):
while True:
x = proposal_sample()
u = random.random()
if u < target_pdf(x) / (M * proposal_pdf(x)):
return x
절단 정규 분포에서 표본을 뽑은 뒤, 히스토그램으로 분포 모양을 확인합니다.
Step 3: 중요도 샘플링
def importance_sampling_estimate(f, target_pdf, proposal_pdf, proposal_sample, n):
total = 0
for _ in range(n):
x = proposal_sample()
w = target_pdf(x) / proposal_pdf(x)
total += f(x) * w
return total / n
균등 제안 분포를 사용해 정규 분포 아래에서의 E[X^2]를 추정한 뒤, 알려진 정답 mu^2 + sigma^2와 비교합니다.
Step 4: 몬테카를로로 pi 추정
def monte_carlo_pi(n):
inside = 0
for _ in range(n):
x = random.uniform(-1, 1)
y = random.uniform(-1, 1)
if x*x + y*y <= 1:
inside += 1
return 4 * inside / n
Step 5: 메트로폴리스-헤이스팅스(Metropolis-Hastings) MCMC
def metropolis_hastings(target_log_pdf, proposal_sample, proposal_log_pdf, x0, n_samples, burn_in):
samples = []
x = x0
for i in range(n_samples + burn_in):
x_new = proposal_sample(x)
log_alpha = (target_log_pdf(x_new) + proposal_log_pdf(x, x_new)
- target_log_pdf(x) - proposal_log_pdf(x_new, x))
if math.log(random.random()) < log_alpha:
x = x_new
if i >= burn_in:
samples.append(x)
return samples
두 개의 가우시안 혼합으로 이루어진 이봉(bimodal) 분포에서 표본을 뽑고, 연쇄의 이동 궤적(trajectory)을 시각화합니다.
Step 6: 깁스 샘플링
def gibbs_sampling_2d(conditional_x_given_y, conditional_y_given_x, x0, y0, n_samples, burn_in):
x, y = x0, y0
samples = []
for i in range(n_samples + burn_in):
x = conditional_x_given_y(y)
y = conditional_y_given_x(x)
if i >= burn_in:
samples.append((x, y))
return samples
Step 7: 온도 샘플링
def softmax(logits):
max_l = max(logits)
exps = [math.exp(z - max_l) for z in logits]
total = sum(exps)
return [e / total for e in exps]
def temperature_sample(logits, temperature):
scaled = [z / temperature for z in logits]
probs = softmax(scaled)
return sample_from_probs(probs)
토큰 로짓 집합 위에서, 온도가 출력 분포를 어떻게 바꾸는지 직접 확인합니다.
Step 8: 상위-k와 상위-p 샘플링
def top_k_sample(logits, k):
indexed = sorted(enumerate(logits), key=lambda x: -x[1])
top = indexed[:k]
top_logits = [l for _, l in top]
probs = softmax(top_logits)
idx = sample_from_probs(probs)
return top[idx][0]
def top_p_sample(logits, p):
probs = softmax(logits)
indexed = sorted(enumerate(probs), key=lambda x: -x[1])
cumsum = 0
selected = []
for token_idx, prob in indexed:
cumsum += prob
selected.append((token_idx, prob))
if cumsum >= p:
break
sel_probs = [pr for _, pr in selected]
total = sum(sel_probs)
sel_probs = [pr / total for pr in sel_probs]
idx = sample_from_probs(sel_probs)
return selected[idx][0]
Step 9: 재매개변수화 기법
def reparam_sample(mu, sigma):
epsilon = random.gauss(0, 1)
return mu + sigma * epsilon
def reparam_gradient(mu, sigma, epsilon):
dz_dmu = 1.0
dz_dsigma = epsilon
return dz_dmu, dz_dsigma
직접 샘플링에서는 경사가 흐르지 않지만, 재매개변수화된 표본에서는 경사가 흐른다는 점을 보여 줍니다.
Step 10: 검벨-소프트맥스
def gumbel_sample():
u = random.random()
return -math.log(-math.log(u))
def gumbel_softmax(logits, temperature):
gumbels = [math.log(p) + gumbel_sample() for p in logits]
return softmax([g / temperature for g in gumbels])
온도를 낮출수록 출력이 원-핫 벡터(one-hot vector)에 가까워진다는 사실을 시각적으로 확인합니다.
모든 시각화를 포함한 전체 구현은 code/sampling.py에 있습니다.
사용하기
NumPy와 SciPy를 사용한 실무용(production) 구현은 다음과 같습니다.
import numpy as np
rng = np.random.default_rng(42)
exponential_samples = rng.exponential(scale=2.0, size=10000)
print(f"지수 분포 표본 평균: {exponential_samples.mean():.4f} (기댓값 2.0)")
from scipy import stats
normal = stats.norm(loc=0, scale=1)
print(f"CDF(1.96) 값: {normal.cdf(1.96):.4f}")
print(f"역 CDF(0.975) 값: {normal.ppf(0.975):.4f}")
logits = np.array([2.0, 1.0, 0.5, 0.1, -1.0])
temperature = 0.7
scaled = logits / temperature
probs = np.exp(scaled - scaled.max()) / np.exp(scaled - scaled.max()).sum()
token = rng.choice(len(logits), p=probs)
print(f"뽑힌 토큰 인덱스: {token}")
규모가 큰 MCMC 작업에는 다음과 같은 전용 라이브러리를 사용합니다.
- PyMC: NUTS(적응형 HMC)를 포함한 본격적인 베이즈 모델링 도구
- emcee: 앙상블(ensemble) MCMC 표본기
- NumPyro/JAX: GPU 가속 MCMC
이미 직접 구현해 본 경험이 있기 때문에, 이제 이런 라이브러리 호출 내부에서 어떤 일이 벌어지는지를 이해한 상태로 사용할 수 있습니다.
산출물 만들기
이 lesson의 최종 산출물은 다음과 같습니다.
code/sampling.py: 역 누적분포함수, 거절 샘플링, 중요도 샘플링, 몬테카를로, MCMC, LLM 샘플링, 재매개변수화, 검벨-소프트맥스 데모(demo)를 포함합니다.
outputs/skill-sampling-strategy.md: 생성, 추정, 추론(inference) 상황에서 적절한 샘플링 방법을 고르는 방법을 정리한 skill 문서입니다.
연습문제
-
(쉬움) 코시 분포(Cauchy distribution)에 대한 역 누적분포함수 샘플링을 구현합니다. 누적분포함수는 F(x) = 0.5 + arctan(x)/pi입니다. 표본 10,000개를 만들고, 실제 확률밀도함수와 히스토그램을 비교합니다. 중심에서 멀리 떨어진 극단값(extreme value), 즉 두꺼운 꼬리(heavy tail)가 어떻게 나타나는지 관찰합니다.
-
(중간) Uniform(0, 1)을 제안 분포로 사용해 베타(Beta) 분포 Beta(2, 5)의 표본을 거절 샘플링으로 생성합니다. 수락된 표본과 실제 베타 분포 확률밀도함수를 함께 그려 비교합니다. 이론적인 수락률은 얼마인가요?
-
(중간) 몬테카를로 방법으로 0부터 pi까지 sin(x)의 정적분을 추정합니다. 표본 수를 1,000, 10,000, 100,000으로 바꿔 가며 오차를 비교합니다. 오차가 O(1/sqrt(N)) 비율로 줄어드는지 확인합니다.
-
(어려움) exp(-(x^2 * y^2 + x^2 + y^2 - 8*x - 8*y) / 2)에 비례하는 2차원 분포에서 메트로폴리스-헤이스팅스로 표본을 뽑는 코드를 구현합니다. 표본과 연쇄 궤적을 그래프로 그리고, 제안 분포의 표준편차를 바꿔 가며 동작을 비교합니다.
-
(어려움) 완전한 텍스트 생성 데모를 만듭니다. 로짓이 주어진 10개 단어 어휘에 대해, (a) 그리디, (b) 온도 0.7, (c) 상위-k=3, (d) 상위-p=0.9 설정으로 각각 20 토큰 길이의 출력을 생성합니다. 5회씩 반복 실행해 출력 다양성을 비교합니다.
핵심 용어
| 용어 | 흔한 설명 | 실제 의미 |
|---|
| 샘플링(sampling) | "무작위 값 뽑기" | 확률 분포에 따라 값을 생성하는 행위로, 모든 생성형 AI의 기반이다. |
| 균등 분포(uniform distribution) | "모든 값이 똑같이 가능함" | [a, b] 안의 모든 값이 확률 밀도 1/(b-a)를 갖는 분포이며, 모든 샘플링 방법의 출발점이다. |
| 역 누적분포함수(inverse CDF) | "확률 변환" | F_inverse(U)로 균등 표본을 알려진 누적분포함수의 분포에서 뽑은 표본으로 바꾼다. 정확하고 효율적이다. |
| 거절 샘플링(rejection sampling) | "제안 후 수락/기각" | 단순 제안 분포에서 값을 생성한 뒤, 목표/제안 비율에 비례하는 확률로 수락한다. 정확하지만 표본 낭비가 생긴다. |
| 중요도 샘플링(importance sampling) | "표본 재가중치" | q(x) 표본에 p(x)/q(x) 가중치를 곱해 p(x) 아래의 기댓값을 추정한다. 강화 학습의 PPO에서 핵심 역할을 한다. |
| 몬테카를로(Monte Carlo) | "무작위 표본 평균" | 적분을 표본 평균으로 근사한다. 오차는 차원과 무관하게 O(1/sqrt(N))이다. |
| MCMC | "수렴하는 무작위 보행" | 정상 분포가 목표 분포인 마르코프 연쇄를 만든다. 메트로폴리스-헤이스팅스가 기초 알고리즘이다. |
| 메트로폴리스-헤이스팅스(Metropolis-Hastings) | "좋은 방향은 수락, 나쁜 방향도 가끔 수락" | 밀도 비율에 따라 제안된 이동을 수락한다. 상세 균형이 목표 분포로의 수렴을 보장한다. |
| 깁스 샘플링(Gibbs sampling) | "변수 한 번에 하나씩" | 다른 변수를 고정한 채 각 변수를 조건부 분포에서 갱신한다. 수락률은 항상 100%다. |
| 온도(temperature) | "확신도 손잡이" | 소프트맥스 전에 로짓을 T로 나눈다. T<1은 분포를 뾰족하게, T>1은 평탄하게 만든다. |
| 상위-k 샘플링(top-k sampling) | "상위 k개만 유지" | 확률이 가장 높은 k개 토큰만 남기고 재정규화한 뒤 표본을 뽑는다. 후보 집합 크기가 고정된다. |
| 핵심 표본 샘플링(nucleus sampling; top-p) | "확률이 큰 토큰만 유지" | 누적 확률이 p를 넘는 가장 작은 토큰 집합을 유지한다. 후보 집합 크기가 적응적으로 바뀐다. |
| 재매개변수화 기법(reparameterization trick) | "무작위성을 밖으로 빼기" | z = mu + sigma * epsilon, epsilon ~ N(0,1) 형태로 써서 샘플링을 미분 가능하게 만든다. VAE 학습에 필수다. |
| 검벨-소프트맥스(Gumbel-Softmax) | "부드러운 범주형 샘플링" | 검벨 잡음과 온도가 들어간 소프트맥스로 범주형 샘플링을 미분 가능하게 근사한다. |
| 층화 샘플링(stratified sampling) | "고른 분포 강제" | 표본 공간을 여러 층으로 나누고 각 층에서 표본을 뽑는다. 단순 몬테카를로보다 분산이 작거나 같다. |
| 번-인(burn-in) | "준비 운동 구간" | 마르코프 연쇄가 정상 분포에 도달하기 전의 초기 MCMC 표본을 버리는 구간이다. |
| 상세 균형(detailed balance) | "가역성 조건" | p(x) * T(x->y) = p(y) * T(y->x)이며, p가 마르코프 연쇄의 정상 분포가 되기 위한 충분 조건이다. |
| 확산 샘플링(diffusion sampling) | "반복적인 잡음 제거" | 잡음에서 시작해 학습된 잡음 제거 단계를 반복하면서 데이터를 생성한다. 각 단계는 조건부 샘플링 연산이다. |
더 읽을거리