Каково интуитивное объяснение техники максимизации ожидания? [закрыто]

109

Максимизация ожиданий (EM) - это своего рода вероятностный метод классификации данных. Пожалуйста, поправьте меня, если я ошибаюсь, если это не классификатор.

Каково интуитивное объяснение этой техники ЭМ? Что expectationздесь и что происходит maximized?

Лондонский парень
источник
12
Каков алгоритм максимизации ожидания? , Nature Biotechnology 26 , 897–899 (2008) содержит красивую картинку, иллюстрирующую, как работает алгоритм.
chl
@chl В части б о хороших картинах , как же они получают значения распределения вероятностей на Z (т.е. 0.45xA, 0.55xB и т.д.)?
Нуб Сайбот
3
Вы можете посмотреть на этот вопрос math.stackexchange.com/questions/25111/…
v4r
3
Обновлена ​​ссылка на изображение, упомянутое @chl.
n1k31t4 09

Ответы:

120

Примечание: код этого ответа можно найти здесь .


Предположим, у нас есть данные, взятые из двух разных групп, красной и синей:

введите описание изображения здесь

Здесь мы можем увидеть, какая точка данных принадлежит красной или синей группе. Это позволяет легко найти параметры, характеризующие каждую группу. Например, среднее значение для красной группы составляет около 3, а для синей группы - около 7 (и мы могли бы найти точные средние значения, если бы захотели).

Это, вообще говоря, называется оценкой максимального правдоподобия . Учитывая некоторые данные, мы вычисляем значение параметра (или параметров), которое лучше всего объясняет эти данные.

Теперь представьте, что мы не можем видеть, какое значение было выбрано из какой группы. Нам все кажется фиолетовым:

введите описание изображения здесь

Здесь мы знаем, что существует две группы ценностей, но мы не знаем, к какой группе принадлежит конкретная ценность.

Можем ли мы по-прежнему оценить средние значения для красной группы и синей группы, которые лучше всего соответствуют этим данным?

Да, часто можно! Максимизация ожиданий дает нам возможность это сделать. Самая общая идея алгоритма такова:

  1. Начните с первоначальной оценки того, каким может быть каждый параметр.
  2. Вычислите вероятность того, что каждый параметр создает точку данных.
  3. Вычислите веса для каждой точки данных, указав, является ли она более красной или более синей, на основе вероятности того, что она создается параметром. Объедините веса с данными ( ожидание ).
  4. Вычислите лучшую оценку параметров, используя данные с поправкой на вес ( максимизация ).
  5. Повторяйте шаги 2–4 до тех пор, пока оценка параметра не сойдется (процесс перестанет производить другую оценку).

Эти шаги требуют дальнейшего объяснения, поэтому я рассмотрю проблему, описанную выше.

Пример: оценка среднего и стандартного отклонения

В этом примере я буду использовать Python, но код должен быть довольно простым для понимания, если вы не знакомы с этим языком.

Предположим, у нас есть две группы, красная и синяя, со значениями, распределенными, как на изображении выше. В частности, каждая группа содержит значение, полученное из нормального распределения со следующими параметрами:

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...

Вот снова изображение этих красных и синих групп (чтобы избавить вас от необходимости прокручивать вверх):

введите описание изображения здесь

Когда мы видим цвет каждой точки (то есть к какой группе она принадлежит), очень легко оценить среднее значение и стандартное отклонение для каждой группы. Мы просто передаем красные и синие значения встроенным функциям в NumPy. Например:

>>> np.mean(red)
2.802
>>> np.std(red)
0.871
>>> np.mean(blue)
6.932
>>> np.std(blue)
2.195

Но что, если мы не можем видеть цвета точек? То есть вместо красного или синего каждая точка была окрашена в фиолетовый цвет.

Чтобы попытаться восстановить параметры среднего и стандартного отклонения для красной и синей групп, мы можем использовать максимизацию ожидания.

Наш первый шаг ( шаг 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

Эти оценки параметров дают колоколообразные кривые, которые выглядят следующим образом:

введите описание изображения здесь

Это плохие оценки. Оба средства (вертикальные пунктирные линии) выглядят далеко от любой «середины», например, для разумных групп точек. Мы хотим улучшить эти оценки.

Следующим шагом ( шаг 2 ) является вычисление вероятности появления каждой точки данных в предположениях текущего параметра:

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):
    """
    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)

У нас есть новые оценки параметров. Чтобы снова улучшить их, мы можем вернуться к шагу 2 и повторить процесс. Мы делаем это до тех пор, пока оценки не сойдутся, или после того, как будет выполнено некоторое количество итераций ( шаг 5 ).

Для наших данных первые пять итераций этого процесса выглядят следующим образом (последние итерации выглядят сильнее):

введите описание изображения здесь

Мы видим, что средние значения уже сходятся на некоторых значениях, а формы кривых (определяемые стандартным отклонением) также становятся более стабильными.

Если мы продолжим 20 итераций, мы получим следующее:

введите описание изображения здесь

Процесс EM сошелся на следующие значения, которые оказались очень близкими к фактическим значениям (где мы можем видеть цвета - никаких скрытых переменных):

          | 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

В приведенном выше коде вы могли заметить, что новая оценка стандартного отклонения была вычислена с использованием оценки среднего значения предыдущей итерацией. В конечном итоге не имеет значения, вычисляем ли мы сначала новое значение для среднего, поскольку мы просто находим (взвешенную) дисперсию значений вокруг некоторой центральной точки. Мы по-прежнему увидим, что оценки параметров сходятся.

Алекс Райли
источник
что, если мы даже не знаем количество нормальных распределений, из которых это происходит? Здесь вы взяли пример k = 2 распределений, можем ли мы также оценить k и наборы параметров k?
stackit
1
@stackit: я не уверен, что существует простой общий способ вычислить наиболее вероятное значение k как часть процесса EM в этом случае. Основная проблема заключается в том, что нам нужно будет начать EM с оценок для каждого из параметров, которые мы хотим найти, и это влечет за собой, что нам нужно знать / оценивать k, прежде чем мы начнем. Однако здесь можно оценить долю точек, принадлежащих группе, через EM. Может быть, если мы переоценим k, доля всех групп, кроме двух, упадет почти до нуля. Я не экспериментировал с этим, поэтому не знаю, насколько хорошо это будет работать на практике.
Alex Riley
1
@AlexRiley Не могли бы вы рассказать немного больше о формулах для вычисления новых оценок среднего и стандартного отклонения?
Lemon
2
@AlexRiley Спасибо за объяснение. Почему новые оценки стандартного отклонения рассчитываются с использованием старого предположения о среднем значении? Что, если сначала будут найдены новые оценки среднего?
GoodDeeds 02
1
@Lemon GoodDeeds Kaushal - извиняюсь за поздний ответ на ваши вопросы. Я попытался отредактировать ответ, чтобы учесть поднятые вами вопросы. Кроме того, я сделал весь код , используемым в этом ответе , доступном в записной книжке здесь (который также включает в себя больше деталей объяснения некоторых моментов , которые я затрагивал).
Alex Riley
36

EM - это алгоритм для максимизации функции правдоподобия, когда некоторые переменные в вашей модели не наблюдаются (т.е. когда у вас есть скрытые переменные).

Вы можете справедливо спросить, если мы просто пытаемся максимизировать функцию, почему бы нам просто не использовать существующий механизм для максимизации функции. Что ж, если вы попытаетесь максимизировать это, взяв производные и установив их в ноль, вы обнаружите, что во многих случаях условия первого порядка не имеют решения. Проблема курицы и яйца состоит в том, что для решения параметров вашей модели вам необходимо знать распределение ваших ненаблюдаемых данных; но распределение ваших ненаблюдаемых данных является функцией параметров вашей модели.

EM пытается обойти это, итеративно угадывая распределение ненаблюдаемых данных, затем оценивая параметры модели, максимизируя то, что является нижней границей фактической функции правдоподобия, и повторяя до сходимости:

EM алгоритм

Начните с предположения значений параметров вашей модели

Шаг E: для каждой точки данных, в которой отсутствуют значения, используйте уравнение модели, чтобы найти распределение недостающих данных с учетом вашего текущего предположения о параметрах модели и наблюдаемых данных (обратите внимание, что вы решаете распределение для каждого отсутствующего значение, а не ожидаемое значение). Теперь, когда у нас есть распределение для каждого пропущенного значения, мы можем вычислить математическое ожидание функции правдоподобия по отношению к ненаблюдаемым переменным. Если наше предположение для параметра модели было правильным, эта ожидаемая вероятность будет фактической вероятностью наших наблюдаемых данных; если параметры были неправильными, это будет просто нижняя граница.

M-шаг: Теперь, когда у нас есть ожидаемая функция правдоподобия без ненаблюдаемых переменных в ней, максимизируйте функцию, как в полностью наблюдаемом случае, чтобы получить новую оценку параметров вашей модели.

Повторяйте до схождения.

Марк Шиверс
источник
5
Я не понимаю ваш E-step. Отчасти проблема в том, что пока я изучаю этот материал, я не могу найти людей, которые используют ту же терминологию. Так что вы подразумеваете под модельным уравнением? Я не знаю, что вы имеете в виду, говоря о распределении вероятностей?
user678392
27

Вот простой рецепт, чтобы понять алгоритм максимизации ожидания:

1- Прочтите этот учебник по EM от До и Бацоглу.

2- У вас могут быть вопросительные знаки в голове, взгляните на пояснения на этой странице обмена математическим стеком .

3- Взгляните на этот код, который я написал на Python, который объясняет пример из учебного документа EM, пункт 1:

Предупреждение: код может быть беспорядочным / неоптимальным, поскольку я не разработчик Python. Но это работает.

import numpy as np
import math

#### E-M Coin Toss Example as given in the EM tutorial paper by Do and Batzoglou* #### 

def get_mn_log_likelihood(obs,probs):
    """ Return the (log)likelihood of obs, given the probs"""
    # Multinomial Distribution Log PMF
    # ln (pdf)      =             multinomial coeff            *   product of probabilities
    # ln[f(x|n, p)] = [ln(n!) - (ln(x1!)+ln(x2!)+...+ln(xk!))] + [x1*ln(p1)+x2*ln(p2)+...+xk*ln(pk)]     

    multinomial_coeff_denom= 0
    prod_probs = 0
    for x in range(0,len(obs)): # loop through state counts in each observation
        multinomial_coeff_denom = multinomial_coeff_denom + math.log(math.factorial(obs[x]))
        prod_probs = prod_probs + obs[x]*math.log(probs[x])

    multinomial_coeff = math.log(math.factorial(sum(obs))) -  multinomial_coeff_denom
    likelihood = multinomial_coeff + prod_probs
    return likelihood

# 1st:  Coin B, {HTTTHHTHTH}, 5H,5T
# 2nd:  Coin A, {HHHHTHHHHH}, 9H,1T
# 3rd:  Coin A, {HTHHHHHTHH}, 8H,2T
# 4th:  Coin B, {HTHTTTHHTT}, 4H,6T
# 5th:  Coin A, {THHHTHHHTH}, 7H,3T
# so, from MLE: pA(heads) = 0.80 and pB(heads)=0.45

# represent the experiments
head_counts = np.array([5,9,8,4,7])
tail_counts = 10-head_counts
experiments = zip(head_counts,tail_counts)

# initialise the pA(heads) and pB(heads)
pA_heads = np.zeros(100); pA_heads[0] = 0.60
pB_heads = np.zeros(100); pB_heads[0] = 0.50

# E-M begins!
delta = 0.001  
j = 0 # iteration counter
improvement = float('inf')
while (improvement>delta):
    expectation_A = np.zeros((5,2), dtype=float) 
    expectation_B = np.zeros((5,2), dtype=float)
    for i in range(0,len(experiments)):
        e = experiments[i] # i'th experiment
        ll_A = get_mn_log_likelihood(e,np.array([pA_heads[j],1-pA_heads[j]])) # loglikelihood of e given coin A
        ll_B = get_mn_log_likelihood(e,np.array([pB_heads[j],1-pB_heads[j]])) # loglikelihood of e given coin B

        weightA = math.exp(ll_A) / ( math.exp(ll_A) + math.exp(ll_B) ) # corresponding weight of A proportional to likelihood of A 
        weightB = math.exp(ll_B) / ( math.exp(ll_A) + math.exp(ll_B) ) # corresponding weight of B proportional to likelihood of B                            

        expectation_A[i] = np.dot(weightA, e) 
        expectation_B[i] = np.dot(weightB, e)

    pA_heads[j+1] = sum(expectation_A)[0] / sum(sum(expectation_A)); 
    pB_heads[j+1] = sum(expectation_B)[0] / sum(sum(expectation_B)); 

    improvement = max( abs(np.array([pA_heads[j+1],pB_heads[j+1]]) - np.array([pA_heads[j],pB_heads[j]]) ))
    j = j+1
Zhubarb
источник
Я обнаружил, что ваша программа приведет к 0,66 как для A, так и для B, я также реализую его с помощью scala, также обнаружил, что результат будет 0,66, вы можете помочь проверить это?
zjffdu 09
Используя электронную таблицу, я нахожу ваш результат 0,66 только в том случае, если мои первоначальные предположения совпадают. В противном случае я могу воспроизвести вывод учебника.
soakley
@zjffdu, сколько итераций выполняет EM, прежде чем вернет вам 0,66? Если вы инициализируете с равными значениями, он может застрять на локальном максимуме, и вы увидите, что количество итераций чрезвычайно мало (поскольку улучшения нет).
Жубарб
Вы также можете посмотреть этот слайд Эндрю Нг и заметку о курсе Гарварда
Минь Фан
16

Технически термин "EM" немного недооценен, но я предполагаю, что вы имеете в виду метод кластерного анализа Gaussian Mixture Modeling, который является примером общего принципа EM.

Собственно, кластерный анализ EM - это не классификатор . Я знаю, что некоторые люди считают кластеризацию «классификацией без учителя», но на самом деле кластерный анализ - это совсем другое.

Ключевое различие и большое непонимание классификации, которое люди всегда имеют с кластерным анализом, заключается в том, что при кластерном анализе нет «правильного решения» . Это метод открытия знаний , он на самом деле предназначен для поиска чего-то нового ! Это делает оценку очень сложной. Он часто оценивается с использованием известной классификации в качестве справочной, но это не всегда уместно: имеющаяся у вас классификация может или не может отражать то, что содержится в данных.

Приведу пример: у вас есть большой набор данных о клиентах, включая данные о поле. Метод, который разделяет этот набор данных на «мужской» и «женский», является оптимальным при сравнении его с существующими классами. С точки зрения «предсказания» это хорошо, поскольку для новых пользователей теперь можно предсказать их пол. С точки зрения «открытия знаний» это на самом деле плохо, потому что вы хотели обнаружить какую-то новую структуру в данных. Метод, который, например, разделил бы данные на пожилых людей и детей, тем не менее, будет иметь худшие результаты по сравнению с классом мужчин / женщин. Однако это был бы отличный результат кластеризации (если бы возраст не был указан).

А теперь вернемся к EM. По сути, он предполагает, что ваши данные состоят из нескольких многомерных нормальных распределений (обратите внимание, что это очень сильное предположение, особенно когда вы фиксируете количество кластеров!). Затем он пытается найти для этого локальную оптимальную модель, поочередно улучшая модель и назначение объекта модели .

Для достижения наилучших результатов в контексте классификации выберите количество кластеров, превышающее количество классов, или даже примените кластеризацию только к отдельным классам (чтобы узнать, есть ли какая-то структура внутри класса!).

Допустим, вы хотите научить классификатор различать «автомобили», «мотоциклы» и «грузовики». Мало смысла предполагать, что данные состоят ровно из трех нормальных распределений. Однако вы можете предположить, что существует более одного типа автомобилей (а также грузовиков и мотоциклов). Таким образом, вместо обучения классификатора для этих трех классов вы группируете автомобили, грузовики и мотоциклы в 10 кластеров каждый (или, может быть, 10 автомобилей, 3 грузовика и 3 велосипеда, что угодно), затем обучаете классификатора различать эти 30 классов, а затем объединить результат класса с исходными классами. Вы также можете обнаружить, что есть один кластер, который особенно трудно классифицировать, например Trikes. Это что-то вроде автомобилей и в некотором роде мотоциклов. Или грузовики для доставки, которые больше похожи на негабаритные автомобили, чем на грузовики.

ВЫЙТИ - Anony-Mousse
источник
как ЭМ недооценивается?
Sam Boosalis
Существует более чем одна его версия. Технически вы также можете назвать стиль Ллойда "EM". Вам нужно указать, какую модель вы используете.
ВЫЙТИ - Anony-Mousse
2

Если другие ответы хороши, я попытаюсь представить другую точку зрения и заняться интуитивной частью вопроса.

Алгоритм EM (Expectation-Maximization) - это вариант класса итерационных алгоритмов, использующих двойственность

Отрывок (выделено мной):

В математике двойственность, вообще говоря, переводит концепции, теоремы или математические структуры в другие концепции, теоремы или структуры взаимно однозначным образом, часто (но не всегда) посредством операции инволюции: если двойственное A - это B, то двойственным к B является A. Такие инволюции иногда имеют неподвижные точки , так что двойственным к A является сам A.

Обычно двойник B объекта A связан с A каким-либо образом, который сохраняет некоторую симметрию или совместимость . Например AB = const

Примеры итерационных алгоритмов, использующих двойственность (в предыдущем смысле):

  1. Алгоритм Евклида для наибольшего общего делителя и его варианты
  2. Алгоритм векторного базиса Грама – Шмидта и варианты
  3. Среднее арифметическое - неравенство среднего геометрического и его варианты
  4. Алгоритм ожидания-максимизации и его варианты (см. Также здесь для информационно-геометрического представления )
  5. (.. другие подобные алгоритмы ..)

Аналогичным образом алгоритм EM можно рассматривать как два двойных шага максимизации :

.. [EM] рассматривается как максимизация совместной функции параметров и распределения по ненаблюдаемым переменным. E-шаг максимизирует эту функцию по отношению к распределению по ненаблюдаемым переменным; шаг M по параметрам ..

В итеративном алгоритме, использующем двойственность, существует явное (или неявное) предположение о равновесной (или фиксированной) точке сходимости (для EM это доказывается с помощью неравенства Йенсена)

Итак, схема таких алгоритмов такова:

  1. E-подобный шаг: найти лучшее решение x относительно заданного y, которое остается постоянным.
  2. M-подобный шаг (двойной): найти лучшее решение y относительно x (вычисленного на предыдущем шаге), которое остается постоянным.
  3. Критерий завершения / шаг сходимости: повторите шаги 1, 2 с обновленными значениями x , y до сходимости (или до достижения указанного количества итераций)

Обратите внимание, что когда такой алгоритм сходится к (глобальному) оптимуму, он обнаруживает конфигурацию, которая является наилучшей в обоих смыслах (то есть как в области / параметрах x, так и в области / параметрах y ). Однако алгоритм может просто найти локальный оптимум, а не глобальный оптимум.

я бы сказал, что это интуитивное описание схемы алгоритма

Что касается статистических аргументов и приложений, другие ответы дали хорошие объяснения (также проверьте ссылки в этом ответе)

Никос М.
источник
2

Принятый ответ ссылается на статью Chuong EM Paper , которая неплохо объясняет EM. Существует также видео на YouTube , в котором статья объясняется более подробно.

Напомним, вот сценарий:

1st:  {H,T,T,T,H,H,T,H,T,H} 5 Heads, 5 Tails; Did coin A or B generate me?
2nd:  {H,H,H,H,T,H,H,H,H,H} 9 Heads, 1 Tails
3rd:  {H,T,H,H,H,H,H,T,H,H} 8 Heads, 2 Tails
4th:  {H,T,H,T,T,T,H,H,T,T} 4 Heads, 6 Tails
5th:  {T,H,H,H,T,H,H,H,T,H} 7 Heads, 3 Tails

Two possible coins, A & B are used to generate these distributions.
A & B have an unknown parameter: their bias towards heads.

We don't know the biases, but we can simply start with a guess: A=60% heads, B=50% heads.

В случае вопроса первого испытания интуитивно мы могли бы подумать, что B сгенерировал его, поскольку доля орлов очень хорошо соответствует смещению B ... но это значение было всего лишь предположением, поэтому мы не можем быть уверены.

Имея это в виду, мне нравится думать о решении EM следующим образом:

  • Каждая попытка флипов позволяет «проголосовать» за ту монету, которая ему больше всего нравится.
    • Это зависит от того, насколько хорошо каждая монета соответствует своему распределению.
    • ИЛИ, с точки зрения монеты, есть большие ожидания увидеть это испытание относительно другой монеты (на основе логарифмических вероятностей ).
  • В зависимости от того, насколько каждая проба любит каждую монету, она может обновлять предположение о параметре этой монеты (смещение).
    • Чем больше проба любит монету, тем больше у нее появляется возможность обновить смещение монеты, чтобы отразить его собственное!
    • По сути, смещения монеты обновляются путем объединения этих взвешенных обновлений по всем испытаниям, процесс, называемый ( максимизация ), который относится к попытке получить наилучшие предположения для смещения каждой монеты с учетом набора испытаний.

Это может быть чрезмерным упрощением (или даже в корне неверным на некоторых уровнях), но я надеюсь, что это поможет на интуитивном уровне!

lucidv01d
источник
1

EM используется, чтобы максимизировать вероятность модели Q со скрытыми переменными Z.

Это итеративная оптимизация.

theta <- initial guess for hidden parameters
while not converged:
    #e-step
    Q(theta'|theta) = E[log L(theta|Z)]
    #m-step
    theta <- argmax_theta' Q(theta'|theta)

e-step: с учетом текущей оценки Z вычислить ожидаемую функцию логарифмического правдоподобия

m-шаг: найдите тета, которая максимизирует этот Q

Пример GMM:

Электронный шаг: оценка присвоений меток для каждой точки данных с учетом текущей оценки gmm-параметра

m-шаг: максимизировать новую тэту с учетом новых присвоений метки

K-means также является алгоритмом EM, и есть много поясняющих анимаций на K-means.

SlimJim
источник
1

Используя ту же статью До и Бацоглу, процитированную в ответе Жубарба, я реализовал EM для этой проблемы на Java . Комментарии к его ответу показывают, что алгоритм застревает на локальном оптимуме, что также происходит с моей реализацией, если параметры thetaA и thetaB совпадают.

Ниже приведен стандартный вывод моего кода, показывающий сходимость параметров.

thetaA = 0.71301, thetaB = 0.58134
thetaA = 0.74529, thetaB = 0.56926
thetaA = 0.76810, thetaB = 0.54954
thetaA = 0.78316, thetaB = 0.53462
thetaA = 0.79106, thetaB = 0.52628
thetaA = 0.79453, thetaB = 0.52239
thetaA = 0.79593, thetaB = 0.52073
thetaA = 0.79647, thetaB = 0.52005
thetaA = 0.79667, thetaB = 0.51977
thetaA = 0.79674, thetaB = 0.51966
thetaA = 0.79677, thetaB = 0.51961
thetaA = 0.79678, thetaB = 0.51960
thetaA = 0.79679, thetaB = 0.51959
Final result:
thetaA = 0.79678, thetaB = 0.51960

Ниже представлена ​​моя Java-реализация EM для решения проблемы в (Do and Batzoglou, 2008). Основная часть реализации - это цикл для запуска EM до тех пор, пока параметры не сойдутся.

private Parameters _parameters;

public Parameters run()
{
    while (true)
    {
        expectation();

        Parameters estimatedParameters = maximization();

        if (_parameters.converged(estimatedParameters)) {
            break;
        }

        _parameters = estimatedParameters;
    }

    return _parameters;
}

Ниже представлен весь код.

import java.util.*;

/*****************************************************************************
This class encapsulates the parameters of the problem. For this problem posed
in the article by (Do and Batzoglou, 2008), the parameters are thetaA and
thetaB, the probability of a coin coming up heads for the two coins A and B,
respectively.
*****************************************************************************/
class Parameters
{
    double _thetaA = 0.0; // Probability of heads for coin A.
    double _thetaB = 0.0; // Probability of heads for coin B.

    double _delta = 0.00001;

    public Parameters(double thetaA, double thetaB)
    {
        _thetaA = thetaA;
        _thetaB = thetaB;
    }

    /*************************************************************************
    Returns true if this parameter is close enough to another parameter
    (typically the estimated parameter coming from the maximization step).
    *************************************************************************/
    public boolean converged(Parameters other)
    {
        if (Math.abs(_thetaA - other._thetaA) < _delta &&
            Math.abs(_thetaB - other._thetaB) < _delta)
        {
            return true;
        }

        return false;
    }

    public double getThetaA()
    {
        return _thetaA;
    }

    public double getThetaB()
    {
        return _thetaB;
    }

    public String toString()
    {
        return String.format("thetaA = %.5f, thetaB = %.5f", _thetaA, _thetaB);
    }

}


/*****************************************************************************
This class encapsulates an observation, that is the number of heads
and tails in a trial. The observation can be either (1) one of the
experimental observations, or (2) an estimated observation resulting from
the expectation step.
*****************************************************************************/
class Observation
{
    double _numHeads = 0;
    double _numTails = 0;

    public Observation(String s)
    {
        for (int i = 0; i < s.length(); i++)
        {
            char c = s.charAt(i);

            if (c == 'H')
            {
                _numHeads++;
            }
            else if (c == 'T')
            {
                _numTails++;
            }
            else
            {
                throw new RuntimeException("Unknown character: " + c);
            }
        }
    }

    public Observation(double numHeads, double numTails)
    {
        _numHeads = numHeads;
        _numTails = numTails;
    }

    public double getNumHeads()
    {
        return _numHeads;
    }

    public double getNumTails()
    {
        return _numTails;
    }

    public String toString()
    {
        return String.format("heads: %.1f, tails: %.1f", _numHeads, _numTails);
    }

}

/*****************************************************************************
This class runs expectation-maximization for the problem posed by the article
from (Do and Batzoglou, 2008).
*****************************************************************************/
public class EM
{
    // Current estimated parameters.
    private Parameters _parameters;

    // Observations from the trials. These observations are set once.
    private final List<Observation> _observations;

    // Estimated observations per coin. These observations are the output
    // of the expectation step.
    private List<Observation> _expectedObservationsForCoinA;
    private List<Observation> _expectedObservationsForCoinB;

    private static java.io.PrintStream o = System.out;

    /*************************************************************************
    Principal constructor.
    @param observations The observations from the trial.
    @param parameters The initial guessed parameters.
    *************************************************************************/
    public EM(List<Observation> observations, Parameters parameters)
    {
        _observations = observations;
        _parameters = parameters;
    }

    /*************************************************************************
    Run EM until parameters converge.
    *************************************************************************/
    public Parameters run()
    {

        while (true)
        {
            expectation();

            Parameters estimatedParameters = maximization();

            o.printf("%s\n", estimatedParameters);

            if (_parameters.converged(estimatedParameters)) {
                break;
            }

            _parameters = estimatedParameters;
        }

        return _parameters;

    }

    /*************************************************************************
    Given the observations and current estimated parameters, compute new
    estimated completions (distribution over the classes) and observations.
    *************************************************************************/
    private void expectation()
    {

        _expectedObservationsForCoinA = new ArrayList<Observation>();
        _expectedObservationsForCoinB = new ArrayList<Observation>();

        for (Observation observation : _observations)
        {
            int numHeads = (int)observation.getNumHeads();
            int numTails = (int)observation.getNumTails();

            double probabilityOfObservationForCoinA=
                binomialProbability(10, numHeads, _parameters.getThetaA());

            double probabilityOfObservationForCoinB=
                binomialProbability(10, numHeads, _parameters.getThetaB());

            double normalizer = probabilityOfObservationForCoinA +
                                probabilityOfObservationForCoinB;

            // Compute the completions for coin A and B (i.e. the probability
            // distribution of the two classes, summed to 1.0).

            double completionCoinA = probabilityOfObservationForCoinA /
                                     normalizer;
            double completionCoinB = probabilityOfObservationForCoinB /
                                     normalizer;

            // Compute new expected observations for the two coins.

            Observation expectedObservationForCoinA =
                new Observation(numHeads * completionCoinA,
                                numTails * completionCoinA);

            Observation expectedObservationForCoinB =
                new Observation(numHeads * completionCoinB,
                                numTails * completionCoinB);

            _expectedObservationsForCoinA.add(expectedObservationForCoinA);
            _expectedObservationsForCoinB.add(expectedObservationForCoinB);
        }
    }

    /*************************************************************************
    Given new estimated observations, compute new estimated parameters.
    *************************************************************************/
    private Parameters maximization()
    {

        double sumCoinAHeads = 0.0;
        double sumCoinATails = 0.0;
        double sumCoinBHeads = 0.0;
        double sumCoinBTails = 0.0;

        for (Observation observation : _expectedObservationsForCoinA)
        {
            sumCoinAHeads += observation.getNumHeads();
            sumCoinATails += observation.getNumTails();
        }

        for (Observation observation : _expectedObservationsForCoinB)
        {
            sumCoinBHeads += observation.getNumHeads();
            sumCoinBTails += observation.getNumTails();
        }

        return new Parameters(sumCoinAHeads / (sumCoinAHeads + sumCoinATails),
                              sumCoinBHeads / (sumCoinBHeads + sumCoinBTails));

        //o.printf("parameters: %s\n", _parameters);

    }

    /*************************************************************************
    Since the coin-toss experiment posed in this article is a Bernoulli trial,
    use a binomial probability Pr(X=k; n,p) = (n choose k) * p^k * (1-p)^(n-k).
    *************************************************************************/
    private static double binomialProbability(int n, int k, double p)
    {
        double q = 1.0 - p;
        return nChooseK(n, k) * Math.pow(p, k) * Math.pow(q, n-k);
    }

    private static long nChooseK(int n, int k)
    {
        long numerator = 1;

        for (int i = 0; i < k; i++)
        {
            numerator = numerator * n;
            n--;
        }

        long denominator = factorial(k);

        return (long)(numerator / denominator);
    }

    private static long factorial(int n)
    {
        long result = 1;
        for (; n >0; n--)
        {
            result = result * n;
        }

        return result;
    }

    /*************************************************************************
    Entry point into the program.
    *************************************************************************/
    public static void main(String argv[])
    {
        // Create the observations and initial parameter guess
        // from the (Do and Batzoglou, 2008) article.

        List<Observation> observations = new ArrayList<Observation>();
        observations.add(new Observation("HTTTHHTHTH"));
        observations.add(new Observation("HHHHTHHHHH"));
        observations.add(new Observation("HTHHHHHTHH"));
        observations.add(new Observation("HTHTTTHHTT"));
        observations.add(new Observation("THHHTHHHTH"));

        Parameters initialParameters = new Parameters(0.6, 0.5);

        EM em = new EM(observations, initialParameters);

        Parameters finalParameters = em.run();

        o.printf("Final result:\n%s\n", finalParameters);
    }
}
stackoverflowuser2010
источник