Ecco un esempio di Expectation Maximization (EM) utilizzato per stimare la media e la deviazione standard. Il codice è in Python, ma dovrebbe essere facile da seguire anche se non si ha familiarità con la lingua.
La motivazione per EM
I punti rosso e blu mostrati di seguito sono disegnati da due diverse distribuzioni normali, ognuna con una media e una deviazione standard:
Per calcolare approssimazioni ragionevoli della media "vera" e dei parametri di deviazione standard per la distribuzione rossa, potremmo facilmente guardare i punti rossi e registrare la posizione di ciascuno, quindi usare le formule familiari (e similmente per il gruppo blu) .
Consideriamo ora il caso in cui sappiamo che ci sono due gruppi di punti, ma non possiamo vedere quale punto appartiene a quale gruppo. In altre parole, i colori sono nascosti:
Non è affatto ovvio come dividere i punti in due gruppi. Ora non siamo in grado di guardare solo le posizioni e calcolare le stime per i parametri della distribuzione rossa o blu.
È qui che EM può essere utilizzato per risolvere il problema.
Utilizzo di EM per stimare i parametri
Ecco il codice utilizzato per generare i punti mostrati sopra. Puoi vedere i mezzi effettivi e le deviazioni standard delle distribuzioni normali da cui sono stati estratti i punti. Le variabili red
e blue
mantengono le posizioni di ciascun punto rispettivamente nei gruppi rosso e blu:
import numpy as np
from scipy import stats
np.random.seed(110) # for reproducible random 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)))
Se potessimo vedere il colore di ogni punto, proveremmo a recuperare mezzi e deviazioni standard usando le funzioni di libreria:
>>> np.mean(red)
2.802
>>> np.std(red)
0.871
>>> np.mean(blue)
6.932
>>> np.std(blue)
2.195
Ma poiché i colori ci sono nascosti, inizieremo il processo EM ...
Innanzitutto, indoviniamo solo i valori dei parametri di ciascun gruppo ( passaggio 1 ). Queste ipotesi non devono essere buone:
# 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
Immaginazioni piuttosto brutte: i mezzi sembrano essere molto lontani da qualsiasi "mezzo" di un gruppo di punti.
Per continuare con EM e migliorare queste ipotesi, calcoliamo la probabilità di ogni punto di dati (indipendentemente dal suo colore segreto) che appare sotto queste ipotesi per la deviazione media e standard ( passaggio 2 ).
La variabile both_colours
contiene ogni punto dati. La funzione stats.norm
calcola la probabilità del punto in una distribuzione normale con i parametri indicati:
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)
Questo ci dice, ad esempio, che con le nostre ipotesi attuali il punto dati a 1.761 ha molte più probabilità di essere rosso (0,189) rispetto al blu (0,00003).
Possiamo trasformare questi due valori di probabilità in pesi ( passaggio 3 ) in modo che si sommino a 1 come segue:
likelihood_total = likelihood_of_red + likelihood_of_blue
red_weight = likelihood_of_red / likelihood_total
blue_weight = likelihood_of_blue / likelihood_total
Con le nostre stime attuali e i nostri pesi appena calcolati, ora possiamo calcolare nuove stime, probabilmente migliori, per i parametri ( passaggio 4 ). Abbiamo bisogno di una funzione per la media e una funzione per la deviazione standard:
def estimate_mean(data, weight):
return np.sum(data * weight) / np.sum(weight)
def estimate_std(data, weight, mean):
variance = np.sum(weight * (data - mean)**2) / np.sum(weight)
return np.sqrt(variance)
Questi sembrano molto simili alle normali funzioni alla media e alla deviazione standard dei dati. La differenza è l'uso di un weight
parametro che assegna un peso a ciascun punto dati.
Questa ponderazione è la chiave per EM. Maggiore è il peso di un colore su un punto dati, più il punto dati influenza le stime successive per i parametri di quel colore. In definitiva, questo ha l'effetto di tirare ogni parametro nella giusta direzione.
Le nuove ipotesi sono calcolate con queste funzioni:
# 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)
Il processo EM viene quindi ripetuto con queste nuove ipotesi dal passaggio 2 in poi. Possiamo ripetere i passaggi per un determinato numero di iterazioni (diciamo 20), o fino a quando i parametri non convergono.
Dopo cinque iterazioni, vediamo che le nostre ipotesi iniziali iniziano a migliorare:
Dopo 20 iterazioni, il processo EM è più o meno convergente:
Per fare un confronto, ecco i risultati del processo EM rispetto ai valori calcolati in cui le informazioni sul colore non sono nascoste:
| EM guess | Actual
----------+----------+--------
Red mean | 2.910 | 2.802
Red std | 0.854 | 0.871
Blue mean | 6.838 | 6.932
Blue std | 2.227 | 2.195
Nota: questa risposta è stata adattata dalla mia risposta su Stack Overflow qui .