Вот пример максимизации ожидания (EM), используемой для оценки среднего значения и стандартного отклонения. Код написан на Python, но за ним должно быть легко следовать, даже если вы не знакомы с языком.
Мотивация для EM
Красные и синие точки, показанные ниже, взяты из двух разных нормальных распределений, каждое из которых имеет определенное среднее значение и стандартное отклонение:
![введите описание изображения здесь](https://i.stack.imgur.com/JDJ1n.png)
Чтобы вычислить разумные аппроксимации «истинного» среднего значения и параметров стандартного отклонения для красного распределения, мы могли бы очень легко посмотреть на красные точки и записать положение каждой из них, а затем использовать знакомые формулы (и аналогично для синей группы) ,
Теперь рассмотрим случай, когда мы знаем, что есть две группы точек, но мы не можем видеть, какая точка принадлежит какой группе. Другими словами, цвета скрыты:
![введите описание изображения здесь](https://i.stack.imgur.com/uvcRO.png)
Совсем не очевидно, как разделить точки на две группы. Теперь мы не можем просто посмотреть на позиции и вычислить оценки для параметров красного или синего распределения.
Здесь EM может быть использован для решения проблемы.
Использование EM для оценки параметров
Вот код, используемый для генерации точек, показанных выше. Вы можете увидеть фактические средние и стандартные отклонения нормальных распределений, из которых были нарисованы точки. Переменные red
и blue
содержат позиции каждой точки в красной и синей группах соответственно:
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)))
Если бы мы могли видеть цвет каждой точки, мы попытались бы восстановить средние значения и стандартные отклонения, используя библиотечные функции:
>>> np.mean(red)
2.802
>>> np.std(red)
0.871
>>> np.mean(blue)
6.932
>>> np.std(blue)
2.195
Но так как цвета скрыты от нас, мы начнем процесс EM ...
Сначала мы просто угадываем значения для параметров каждой группы ( шаг 1 ). Эти догадки не должны быть хорошими:
# 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
![введите описание изображения здесь](https://i.stack.imgur.com/GxLlr.png)
Довольно плохие догадки - средства выглядят так, будто они далеки от любой «середины» группы точек.
Чтобы продолжить с EM и улучшить эти догадки, мы вычисляем вероятность того, что каждая точка данных (независимо от ее секретного цвета) появится под этими догадками для среднего и стандартного отклонения ( шаг 2 ).
Переменная both_colours
содержит каждую точку данных. Функция stats.norm
вычисляет вероятность точки при нормальном распределении с заданными параметрами:
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)
Это говорит нам, например, что с нашими текущими догадками точка данных в 1.761, скорее всего, будет красной (0.189), чем синей (0.00003).
Мы можем превратить эти два значения правдоподобия в веса ( шаг 3 ), чтобы они суммировали до 1 следующим образом:
likelihood_total = likelihood_of_red + likelihood_of_blue
red_weight = likelihood_of_red / likelihood_total
blue_weight = likelihood_of_blue / likelihood_total
С нашими текущими оценками и нашими недавно вычисленными весами мы можем теперь вычислить новые, возможно, лучшие оценки параметров ( шаг 4 ). Нам нужна функция для среднего значения и функция для стандартного отклонения:
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)
Они выглядят очень похоже на обычные функции для среднего значения и стандартного отклонения данных. Разница заключается в использовании weight
параметра, который присваивает вес каждой точке данных.
Это взвешивание является ключом к EM. Чем больше вес цвета в точке данных, тем больше точка данных влияет на следующие оценки параметров этого цвета. В конечном итоге это приводит к вытягиванию каждого параметра в правильном направлении.
Новые догадки вычисляются с помощью этих функций:
# 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)
Процесс ЭМ затем повторяется с этими новыми догадками, начиная с шага 2 и далее. Мы можем повторять шаги для заданного количества итераций (скажем, 20) или пока мы не увидим, что параметры сходятся.
После пяти итераций мы видим, что наши начальные плохие догадки начинают поправляться:
![введите описание изображения здесь](https://i.stack.imgur.com/QbKmW.png)
После 20 итераций процесс EM более или менее сходится:
![введите описание изображения здесь](https://i.stack.imgur.com/RVFBC.png)
Для сравнения, вот результаты EM-процесса по сравнению со значениями, вычисленными, когда информация о цвете не скрыта:
| 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
Примечание: этот ответ был адаптирован из моего ответа о переполнении стека здесь .