Проблема
Я хочу відповідати модельним параметрам простої сукупності 2-гауссівської суміші. Враховуючи всю суєту навколо байєсівських методів, я хочу зрозуміти, чи є для цієї проблеми байєсівський висновок кращим інструментом для традиційних методів підгонки.
Поки MCMC в цьому іграшковому прикладі дуже погано працює, але, можливо, я просто щось не помітив. Тож давайте подивіться код.
Інструменти
Я буду використовувати python (2.7) + стек scipy, lmfit 0.8 та PyMC 2.3.
Зошит для відтворення аналізу можна знайти тут
Створення даних
Спочатку дозвольте генерувати дані:
from scipy.stats import distributions
# Sample parameters
nsamples = 1000
mu1_true = 0.3
mu2_true = 0.55
sig1_true = 0.08
sig2_true = 0.12
a_true = 0.4
# Samples generation
np.random.seed(3) # for repeatability
s1 = distributions.norm.rvs(mu1_true, sig1_true, size=round(a_true*nsamples))
s2 = distributions.norm.rvs(mu2_true, sig2_true, size=round((1-a_true)*nsamples))
samples = np.hstack([s1, s2])
Гістограма samples
виглядає так:
"широкий пік", компоненти важко помітити оком.
Класичний підхід: підходить гістограма
Спробуємо спершу класичний підхід. За допомогою lmfit легко визначити модель з двома піками:
import lmfit
peak1 = lmfit.models.GaussianModel(prefix='p1_')
peak2 = lmfit.models.GaussianModel(prefix='p2_')
model = peak1 + peak2
model.set_param_hint('p1_center', value=0.2, min=-1, max=2)
model.set_param_hint('p2_center', value=0.5, min=-1, max=2)
model.set_param_hint('p1_sigma', value=0.1, min=0.01, max=0.3)
model.set_param_hint('p2_sigma', value=0.1, min=0.01, max=0.3)
model.set_param_hint('p1_amplitude', value=1, min=0.0, max=1)
model.set_param_hint('p2_amplitude', expr='1 - p1_amplitude')
name = '2-gaussians'
Нарешті ми підходимо до моделі за допомогою симплексного алгоритму:
fit_res = model.fit(data, x=x_data, method='nelder')
print fit_res.fit_report()
Результатом є наступне зображення (червоними пунктирними лініями встановлені центри):
Навіть якщо проблема є настільки складною, з належними початковими значеннями та обмеженнями, моделі сходяться до цілком розумної оцінки.
Байєсівський підхід: MCMC
Я визначаю модель в PyMC ієрархічно. centers
і sigmas
є пріором розподілу для гіперпараметрів, що представляють 2 центри та 2 сигми двох гауссів. alpha
- частка першої сукупності, а попередній розподіл тут - бета-версія.
Категорична змінна вибирається між двома групами. Наскільки я розумію, що ця змінна повинна мати такий самий розмір, як дані ( samples
).
І, нарешті , mu
і tau
є детермінованими змінні , які визначають параметри нормального розподілу (вони залежать від category
змінної таким чином , вони випадковим чином перемикатися між двома значеннями для двох популяцій).
sigmas = pm.Normal('sigmas', mu=0.1, tau=1000, size=2)
centers = pm.Normal('centers', [0.3, 0.7], [1/(0.1)**2, 1/(0.1)**2], size=2)
#centers = pm.Uniform('centers', 0, 1, size=2)
alpha = pm.Beta('alpha', alpha=2, beta=3)
category = pm.Categorical("category", [alpha, 1 - alpha], size=nsamples)
@pm.deterministic
def mu(category=category, centers=centers):
return centers[category]
@pm.deterministic
def tau(category=category, sigmas=sigmas):
return 1/(sigmas[category]**2)
observations = pm.Normal('samples_model', mu=mu, tau=tau, value=samples, observed=True)
model = pm.Model([observations, mu, tau, category, alpha, sigmas, centers])
Потім я запускаю MCMC з досить довгою кількістю ітерацій (1e5, ~ 60s на моїй машині):
mcmc = pm.MCMC(model)
mcmc.sample(100000, 30000)
Також центри гауссів також не сходяться. Наприклад:
То що тут відбувається? Я щось роблю неправильно або MCMC не підходить для цієї проблеми?
Я розумію, що метод MCMC буде повільнішим, але тривіальна гістограма, схоже, працює набагато краще у вирішенні популяцій.
proposal_distribution
іproposal_sd
і чому використанняPrior
краще для категоріальних змінних?