Apprentissage de GMM par l'algorithme EM¶
%pylab inline
On définit une fonction pour calculer la probabilité qu'une valeur appartienne à une gaussienne à l'aide de sa fonction de densité ($\mu$ sa moyenne et $\sigma$ son écart-type)
$$\mathcal{N}(x, \mu, \sigma) = \frac{1}{\sqrt{2\pi}\sigma}\exp^{-\frac{1}{2\sigma^2}(x-\mu)^2}$$def gauss(x, mu, sigma):
""" fonction calculant la probabilité d'appartenance d'un point à une gaussienne (sorte de distance)
sigma est l'écart-type, c'est-à-dire sqrt(variance). """
return 1/(sqrt(2*pi)*sigma) * exp(-0.5*(x - mu)**2/sigma**2)
À quoi ressemble une gaussienne centrée-réduite typique $\mathcal{N}(0, 1)$ (de moyenne nulle et d'écart-type 1) ?
mu = 0.0
sigma = 1.0
G = [mu, sigma]
figure(figsize=(10,6))
plot(linspace(-5, 5, 1000), [gauss(x, mu, sigma) for x in linspace(-5, 5, 1000)])
La séquence de données à modéliser
data = [11, 9, 10, 18, 19, 18, 14, 15, 14]
L'ordre des données n'a pas d'importance pour une modélisation par gaussiennes
Modélisation par une seule gaussienne¶
# on calcule simplement la moyenne et l'écart-type des données
mu_init = mean(data)
sigma_init = sqrt(var(data)) # écart-type = sqrt(variance)
print("moyenne: ", mu_init)
print("écart-type: ", sigma_init)
# on représente les données et le modèle gaussien associé
figure(figsize=(10,6))
plot(linspace(0, 30, 1000), [gauss(x, mu_init, sigma_init) for x in linspace(0, 30, 1000)])
plot(data, [0]*len(data), 'ro')
Une estimation relative que le modèle correspond bien aux données peut être obtenue en calculant la log-vraisemblance. Toutefois, cette estimation en soit ne donne aucun indice sur la qualité absolue du modèle. Elle sert pour estimer l'amélioration du modèle d'une itération à l'autre.
La log-vraisemblance d'un système données-modéle est définie ainsi:
$$LL = \sum_{i=1}^N \log\big(\sum_{k=1}^M c_k\mathcal{N}(x_i, \mu_k, \sigma_k)\big)$$def calc_LL(data, mu, sigma, poids):
""" fonction qui retourne le Log-Likelihood (vraisemblance) d'un jeu de données par rapport à
un modèle GMM donné. """
ll = 0.0
for i in range(len(data)):
p = 0.0
for k in range(len(mu)):
p += poids[k] * gauss(data[i], mu[k], sigma[k])
ll += log(p)
return ll
calc_LL(data, [mu_init], [sigma_init], [1])
Modélisation par plusieurs gaussiennes¶
# on définit 3 gaussiennes avec des moyennes déterminées de manière empirique
# ou bien à partir des données (mu = mu_init ±sigma)
mu = [mu_init - sigma_init, mu_init, mu_init + sigma_init]
mu = [9, 10, 11]
# on garde les mêmes sigmas
sigma = [sigma_init] * 3
# on définit des pondérations identiques pour les 3 gaussiennes
poids = [1/3] * 3
mu, sigma, poids
# on représente les données et les 3 modèles gaussiens associés
figure(figsize=(10,6))
plot(linspace(0, 30, 1000), [poids[0]*gauss(x, mu[0], sigma[0]) for x in linspace(0, 30, 1000)])
plot(linspace(0, 30, 1000), [poids[1]*gauss(x, mu[1], sigma[1]) for x in linspace(0, 30, 1000)])
plot(linspace(0, 30, 1000), [poids[2]*gauss(x, mu[2], sigma[2]) for x in linspace(0, 30, 1000)])
plot(data, [0]*len(data), 'ro')
Il y a de gros recouvrements entre les différentes gaussiennes. il faut améliorer les modèles en appliquant l'algorithme EM
Quelle est sa log-vraisemblance ?
LL = []
LL.append(calc_LL(data, mu, sigma, poids))
print(LL[-1])
def expectation(mu, sigma, poids):
""" partie Expectation: on calcule les probabilités qu'une donnée appartienne à chacune des données. """
gamma_i_k = empty((len(data), len(mu)))
for i in range(len(data)):
for k in range(len(mu)):
gamma_i_k[i, k] = poids[k]*gauss(data[i], mu[k], sigma[k])
# on normalise
s = sum(gamma_i_k[i, :])
gamma_i_k[i, :] /= s
return gamma_i_k
gamma_i_k = expectation(mu, sigma, poids)
gamma_i_k
def maximisation(mu, sigma, poids, gamma_i_k):
""" partie Maximisation: on calcule les nouveaux paramètres des modèles. """
for k in range(len(mu)):
# les nouvelles moyennes
mu[k] = sum([gamma_i_k[i, k]*data[i] for i in range(len(data))])
mu[k] /= sum(gamma_i_k[:, k])
# les nouveaux écart-types
sigma[k] = sum([gamma_i_k[i, k]*(data[i] - mu[k])**2 for i in range(len(data))])
sigma[k] /= sum(gamma_i_k[:, k])
sigma[k] = sqrt(sigma[k]) # passage de la variance à l'écart-type
# les nouveaux poids
poids[k] = sum(gamma_i_k[:, k]) / len(data)
return mu, sigma, poids
mu, sigma, poids = maximisation(mu, sigma, poids, gamma_i_k)
mu, sigma, poids
# on représente les données et les 3 modèles gaussiens associés
figure(figsize=(10,6))
plot(linspace(0, 30, 1000), [poids[0]*gauss(x, mu[0], sigma[0]) for x in linspace(0, 30, 1000)])
plot(linspace(0, 30, 1000), [poids[1]*gauss(x, mu[1], sigma[1]) for x in linspace(0, 30, 1000)])
plot(linspace(0, 30, 1000), [poids[2]*gauss(x, mu[2], sigma[2]) for x in linspace(0, 30, 1000)])
plot(data, [0]*len(data), 'ro')
# calcul de la nouvelle vraisemblance
LL.append(calc_LL(data, mu, sigma, poids))
print(LL[-1])
# on itère le processus 4 fois
figure(figsize=(12,12))
for i in range(4):
gamma_i_k = expectation(mu, sigma, poids)
mu, sigma, poids = maximisation(mu, sigma, poids, gamma_i_k)
subplot(2, 2, i+1)
plot(linspace(0, 30, 1000), [poids[0]*gauss(x, mu[0], sigma[0]) for x in linspace(0, 30, 1000)])
plot(linspace(0, 30, 1000), [poids[1]*gauss(x, mu[1], sigma[1]) for x in linspace(0, 30, 1000)])
plot(linspace(0, 30, 1000), [poids[2]*gauss(x, mu[2], sigma[2]) for x in linspace(0, 30, 1000)])
plot(data, [0]*len(data), 'ro')
LL.append(calc_LL(data, mu, sigma, poids))
title("itération " + str(i) + ", LL= " + str(LL[-1]))
mu, sigma, poids
# on itère le processus 30 fois
figure(figsize=(15,30))
for i in range(30):
gamma_i_k = expectation(mu, sigma, poids)
mu, sigma, poids = maximisation(mu, sigma, poids, gamma_i_k)
subplot(10, 3, i+1)
plot(linspace(0, 30, 1000), [poids[0]*gauss(x, mu[0], sigma[0]) for x in linspace(0, 30, 1000)])
plot(linspace(0, 30, 1000), [poids[1]*gauss(x, mu[1], sigma[1]) for x in linspace(0, 30, 1000)])
plot(linspace(0, 30, 1000), [poids[2]*gauss(x, mu[2], sigma[2]) for x in linspace(0, 30, 1000)])
plot(data, [0]*len(data), 'ro')
LL.append(calc_LL(data, mu, sigma, poids))
title("itération " + str(i) + ", LL= " + str(LL[-1]))
Voyons l'évolution de la log-vraisemblance sur ces itérations successives du modèle
plot(LL)
En utilisant une bibliothèque dédiée¶
from sklearn import mixture
g = mixture.GaussianMixture(1, covariance_type='spherical')
g.fit(array(data).reshape(-1,1))
g.means_
sqrt(g.covariances_)
g.weights_
Z = [exp(g.score(x.reshape(-1,1))) for x in linspace(0, 30, 1000)]
plot(linspace(0, 30, 1000), Z)
plot(data, [0]*len(data), 'ro')
g3 = None
g3 = mixture.GaussianMixture(n_components=3,
covariance_type='spherical',
# n_init = 1,
# means_init = array([mu_init - sigma_init, mu_init, mu_init + sigma_init]).reshape(-1,1),
# means_init = array([10, 11, 9]).reshape(-1,1),
# precisions_init = array([sigma_init]*3),
# weights_init=[1/3, 1/3, 1/3],
# max_iter=1,
verbose=2,
verbose_interval=1,
# init_params = 'random',
# warm_start=True,
)
g3.fit(array(data).reshape(-1,1), 1)
g3.means_
sqrt(g3.covariances_)
g3.weights_
Z = [exp(g3.score_samples(x.reshape(-1,1))) for x in linspace(0, 30, 1000)]
plot(linspace(0, 30, 1000), Z)
plot(data, [0]*len(data), 'ro')
g3.n_iter_
g3 = None
g3 = mixture.GaussianMixture(n_components=3,
covariance_type='spherical',
n_init = 1,
# means_init = array([mu_init - sigma_init, mu_init, mu_init + sigma_init]).reshape(-1,1),
means_init = array([10, 11, 9]).reshape(-1,1),
precisions_init = array([sigma_init]*3),
weights_init=[1/3, 1/3, 1/3],
# max_iter=1,
verbose=2,
verbose_interval=1,
init_params = 'random',
warm_start=True,
)
g3.fit(array(data).reshape(-1,1), 1)
g3.means_