Remarque: le code derrière cette réponse se trouve ici .
Supposons que nous ayons des données échantillonnées dans deux groupes différents, rouge et bleu:
Ici, nous pouvons voir quel point de données appartient au groupe rouge ou bleu. Cela facilite la recherche des paramètres qui caractérisent chaque groupe. Par exemple, la moyenne du groupe rouge est d'environ 3, la moyenne du groupe bleu est d'environ 7 (et nous pourrions trouver les moyennes exactes si nous le voulions).
C'est ce qu'on appelle généralement l' estimation du maximum de vraisemblance . Compte tenu de certaines données, nous calculons la valeur d'un paramètre (ou de paramètres) qui explique le mieux ces données.
Imaginez maintenant que nous ne pouvons pas voir quelle valeur a été échantillonnée dans quel groupe. Tout nous semble violet:
Ici, nous savons qu'il existe deux groupes de valeurs, mais nous ne savons pas à quel groupe appartient une valeur particulière.
Pouvons-nous encore estimer les moyennes du groupe rouge et du groupe bleu qui correspondent le mieux à ces données?
Oui, souvent nous le pouvons! La maximisation des attentes nous donne un moyen de le faire. L'idée très générale derrière l'algorithme est la suivante:
- Commencez par une première estimation de ce que pourrait être chaque paramètre.
- Calculez la probabilité que chaque paramètre produise le point de données.
- Calculez les poids pour chaque point de données en indiquant s'il est plus rouge ou plus bleu en fonction de la probabilité qu'il soit produit par un paramètre. Combinez les pondérations avec les données ( attente ).
- Calculez une meilleure estimation des paramètres en utilisant les données ajustées en poids ( maximisation ).
- Répétez les étapes 2 à 4 jusqu'à ce que l'estimation du paramètre converge (le processus arrête de produire une estimation différente).
Ces étapes nécessitent des explications supplémentaires, je vais donc parcourir le problème décrit ci-dessus.
Exemple: estimation de la moyenne et de l'écart type
J'utiliserai Python dans cet exemple, mais le code devrait être assez facile à comprendre si vous n'êtes pas familier avec ce langage.
Supposons que nous ayons deux groupes, rouge et bleu, avec les valeurs distribuées comme dans l'image ci-dessus. Plus précisément, chaque groupe contient une valeur tirée d'une distribution normale avec les paramètres suivants:
import numpy as np
from scipy import stats
np.random.seed(110) # for reproducible results
# set parameters
red_mean = 3
red_std = 0.8
blue_mean = 7
blue_std = 2
# draw 20 samples from normal distributions with red/blue parameters
red = np.random.normal(red_mean, red_std, size=20)
blue = np.random.normal(blue_mean, blue_std, size=20)
both_colours = np.sort(np.concatenate((red, blue))) # for later use...
Voici à nouveau une image de ces groupes rouges et bleus (pour vous éviter d'avoir à faire défiler vers le haut):
Lorsque nous pouvons voir la couleur de chaque point (c'est-à-dire à quel groupe il appartient), il est très facile d'estimer la moyenne et l'écart type de chaque groupe. Nous transmettons simplement les valeurs rouge et bleu aux fonctions intégrées de NumPy. Par exemple:
>>> np.mean(red)
2.802
>>> np.std(red)
0.871
>>> np.mean(blue)
6.932
>>> np.std(blue)
2.195
Mais que faire si nous ne pouvons pas voir les couleurs des points? Autrement dit, au lieu de rouge ou de bleu, chaque point a été coloré en violet.
Pour essayer de récupérer les paramètres d'écart moyen et standard pour les groupes rouge et bleu, nous pouvons utiliser la maximisation des attentes.
Notre première étape ( étape 1 ci-dessus) consiste à deviner les valeurs des paramètres pour la moyenne et l'écart type de chaque groupe. Nous n'avons pas à deviner intelligemment; nous pouvons choisir tous les nombres que nous aimons:
# estimates for the mean
red_mean_guess = 1.1
blue_mean_guess = 9
# estimates for the standard deviation
red_std_guess = 2
blue_std_guess = 1.7
Ces estimations de paramètres produisent des courbes en cloche qui ressemblent à ceci:
Ce sont de mauvaises estimations. Les deux moyens (les lignes pointillées verticales) semblent loin de tout type de «milieu» pour des groupes de points sensibles, par exemple. Nous voulons améliorer ces estimations.
L'étape suivante ( étape 2 ) consiste à calculer la probabilité que chaque point de données apparaisse sous le paramètre actuel suppose:
likelihood_of_red = stats.norm(red_mean_guess, red_std_guess).pdf(both_colours)
likelihood_of_blue = stats.norm(blue_mean_guess, blue_std_guess).pdf(both_colours)
Ici, nous avons simplement mis chaque point de données dans la fonction de densité de probabilité pour une distribution normale en utilisant nos estimations actuelles à la moyenne et à l'écart type pour le rouge et le bleu. Cela nous indique, par exemple, qu'avec nos estimations actuelles, le point de données à 1,761 est beaucoup plus susceptible d'être rouge (0,189) que bleu (0,00003).
Pour chaque point de données, nous pouvons transformer ces deux valeurs de vraisemblance en poids ( étape 3 ) afin qu'elles totalisent 1 comme suit:
likelihood_total = likelihood_of_red + likelihood_of_blue
red_weight = likelihood_of_red / likelihood_total
blue_weight = likelihood_of_blue / likelihood_total
Avec nos estimations actuelles et nos poids nouvellement calculés, nous pouvons maintenant calculer de nouvelles estimations pour la moyenne et l'écart type des groupes rouge et bleu ( étape 4 ).
Nous calculons deux fois la moyenne et l'écart type en utilisant tous les points de données, mais avec des pondérations différentes: une fois pour les poids rouges et une fois pour les poids bleus.
L'intuition clé est que plus le poids d'une couleur sur un point de données est élevé, plus le point de données influence les estimations suivantes pour les paramètres de cette couleur. Cela a pour effet de "tirer" les paramètres dans la bonne direction.
def estimate_mean(data, weight):
"""
For each data point, multiply the point by the probability it
was drawn from the colour's distribution (its "weight").
Divide by the total weight: essentially, we're finding where
the weight is centred among our data points.
"""
return np.sum(data * weight) / np.sum(weight)
def estimate_std(data, weight, mean):
"""
For each data point, multiply the point's squared difference
from a mean value by the probability it was drawn from
that distribution (its "weight").
Divide by the total weight: essentially, we're finding where
the weight is centred among the values for the difference of
each data point from the mean.
This is the estimate of the variance, take the positive square
root to find the standard deviation.
"""
variance = np.sum(weight * (data - mean)**2) / np.sum(weight)
return np.sqrt(variance)
# new estimates for standard deviation
blue_std_guess = estimate_std(both_colours, blue_weight, blue_mean_guess)
red_std_guess = estimate_std(both_colours, red_weight, red_mean_guess)
# new estimates for mean
red_mean_guess = estimate_mean(both_colours, red_weight)
blue_mean_guess = estimate_mean(both_colours, blue_weight)
Nous avons de nouvelles estimations pour les paramètres. Pour les améliorer à nouveau, nous pouvons revenir à l'étape 2 et répéter le processus. Nous faisons cela jusqu'à ce que les estimations convergent, ou après qu'un certain nombre d'itérations aient été effectuées ( étape 5 ).
Pour nos données, les cinq premières itérations de ce processus ressemblent à ceci (les itérations récentes ont une apparence plus forte):
On voit que les moyennes convergent déjà sur certaines valeurs, et les formes des courbes (régies par l'écart type) deviennent également plus stables.
Si nous continuons pendant 20 itérations, nous nous retrouvons avec ce qui suit:
Le processus EM a convergé vers les valeurs suivantes, qui s'avèrent très proches des valeurs réelles (où nous pouvons voir les couleurs - pas de variables cachées):
| EM guess | Actual | Delta
----------+----------+--------+-------
Red mean | 2.910 | 2.802 | 0.108
Red std | 0.854 | 0.871 | -0.017
Blue mean | 6.838 | 6.932 | -0.094
Blue std | 2.227 | 2.195 | 0.032
Dans le code ci-dessus, vous avez peut-être remarqué que la nouvelle estimation de l'écart type a été calculée en utilisant l'estimation de l'itération précédente pour la moyenne. En fin de compte, peu importe si nous calculons d'abord une nouvelle valeur pour la moyenne, car nous cherchons simplement la variance (pondérée) des valeurs autour d'un point central. Nous verrons toujours les estimations des paramètres converger.