Le problème
Je veux ajuster les paramètres du modèle d'une population de mélange 2-gaussien simple. Étant donné tout le battage médiatique autour des méthodes bayésiennes, je veux comprendre si pour ce problème l'inférence bayésienne est un meilleur outil que les méthodes d'ajustement traditionnelles.
Jusqu'à présent, MCMC fonctionne très mal dans cet exemple de jouet, mais peut-être que j'ai juste oublié quelque chose. Voyons donc le code.
Les outils
J'utiliserai python (2.7) + pile scipy, lmfit 0.8 et PyMC 2.3.
Un cahier pour reproduire l'analyse peut être trouvé ici
Générez les données
Générons d'abord les données:
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])
L'histogramme de samples
ressemble à ceci:
un "pic large", les composants sont difficiles à repérer à l'œil nu.
Approche classique: ajuster l'histogramme
Essayons d'abord l'approche classique. En utilisant lmfit, il est facile de définir un modèle à 2 pics:
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'
Enfin, nous ajustons le modèle avec l'algorithme simplex:
fit_res = model.fit(data, x=x_data, method='nelder')
print fit_res.fit_report()
Le résultat est l'image suivante (les lignes pointillées rouges sont des centres ajustés):
Même si le problème est assez difficile, avec des valeurs initiales et des contraintes appropriées, les modèles ont convergé vers une estimation assez raisonnable.
Approche bayésienne: MCMC
Je définis le modèle dans PyMC de manière hiérarchique. centers
et sigmas
sont la distribution a priori des hyperparamètres représentant les 2 centres et 2 sigmas des 2 gaussiens. alpha
est la fraction de la première population et la distribution antérieure est ici une Bêta.
Une variable catégorielle choisit entre les deux populations. Je crois comprendre que cette variable doit avoir la même taille que les données ( samples
).
Enfin mu
, ce tau
sont des variables déterministes qui déterminent les paramètres de la distribution normale (elles dépendent de la category
variable et basculent donc aléatoirement entre les deux valeurs pour les deux populations).
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])
Ensuite, je lance le MCMC avec un nombre d'itérations assez long (1e5, ~ 60s sur ma machine):
mcmc = pm.MCMC(model)
mcmc.sample(100000, 30000)
De plus, les centres des gaussiens ne convergent pas aussi bien. Par exemple:
Alors qu'est-ce qui se passe ici? Suis-je en train de faire quelque chose de mal ou MCMC ne convient pas à ce problème?
Je comprends que la méthode MCMC sera plus lente, mais l'ajustement de l'histogramme trivial semble être extrêmement efficace pour résoudre les populations.
proposal_distribution
etproposal_sd
pourquoi l' utilisationPrior
est mieux pour les variables?