Методы Монте-Карло для марковских цепей (MCMC). Введение

в 18:31, , рубрики: MCMC, python, Алгоритмы, Блог компании Издательский дом «Питер», книги, марковские цепи, машинное обучение, Профессиональная литература, статистика

Привет!

Напоминаем, что ранее мы анонсировали книгу "Машинное обучение без лишних слов" — и теперь она уже в продаже. Притом, что для начинающих специалистов по МО книга действительно может стать настольной, некоторые темы в ней все-таки затронуты не были. Поэтому всем заинтересованным предлагаем перевод статьи Саймона Керстенса о сути алгоритмов MCMC с реализацией такого алгоритма на Python.

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

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

Алгоритмы MCMC – это класс методов, предназначенных именно для этого. Они восходят к эпохальной статье Метрополиса и др.; Метрополис разработал первый MCMC-алгоритм, названный в его честь и предназначенный для вычисления равновесного состояния двумерной системы жестких сфер. Фактически, исследователи искали универсальный метод, который позволил бы вычислять ожидаемые значения, встречающиеся в статистической физике.
В этой статье будут рассмотрены основы выборки по MCMC.

МАРКОВСКИЕ ЦЕПИ

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

Для выборки из распределения Методы Монте-Карло для марковских цепей (MCMC). Введение - 1 алгоритм MCMC создает и имитирует марковскую цепь, чье стационарное распределение равно Методы Монте-Карло для марковских цепей (MCMC). Введение - 2; это означает, что после начального «затравочного» периода, состояния такой марковской цепи распределяются по принципу Методы Монте-Карло для марковских цепей (MCMC). Введение - 3. Следовательно, нам придется всего лишь сохранить состояния, чтобы получить образцы из Методы Монте-Карло для марковских цепей (MCMC). Введение - 4.

В образовательных целях давайте рассмотрим как дискретное пространство состояний, так и дискретное «время». Ключевая величина, характеризующая марковскую цепь – это оператор перехода Методы Монте-Карло для марковских цепей (MCMC). Введение - 5, указывающий вероятность нахождения в состоянии Методы Монте-Карло для марковских цепей (MCMC). Введение - 6 в момент времени Методы Монте-Карло для марковских цепей (MCMC). Введение - 7, при условии, что цепь находится в состоянии Методы Монте-Карло для марковских цепей (MCMC). Введение - 8 во время i.
Теперь просто для интереса (и в качестве демонстрации) давайте по-быстрому сплетем марковскую цепь, имеющую уникальное стационарное распределение. Начнем с некоторых импортов и настроек для графиков:

%matplotlib notebook
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['figure.figsize'] = [10, 6]
np.random.seed(42)

Марковская цепь будет обходить дискретное пространство состояний, образуемое тремя состояниями погоды:

state_space = ("sunny", "cloudy", "rainy")

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

transition_matrix = np.array(((0.6, 0.3, 0.1),
                              (0.3, 0.4, 0.3),
                              (0.2, 0.3, 0.5)))

В строках указаны состояния, в которых в настоящее время может находиться цепь, а в столбцах – состояния, в которые цепи могут перейти. Если взять «временной» шаг марковской цепи за один час, то, при условии, что сейчас солнечно, существует 60% вероятность, что солнечная погода сохранится в течение ближайшего часа. Также есть 30% вероятность, что в следующий час будет облачная погода, и 10%-я вероятность, что сразу же после солнечной погоды пойдет дождь. Это также означает, что значения в каждом ряду в сумме дают единицу.
Давайте немного погоняем нашу марковскую цепь:

n_steps = 20000
states = [0]
for i in range(n_steps):
    states.append(np.random.choice((0, 1, 2), p=transition_matrix[states[-1]]))
states = np.array(states)

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

def despine(ax, spines=('top', 'left', 'right')):
    for spine in spines:
        ax.spines[spine].set_visible(False)

fig, ax = plt.subplots()
width = 1000
offsets = range(1, n_steps, 5)
for i, label in enumerate(state_space):
    ax.plot(offsets, [np.sum(states[:offset] == i) / offset 
            for offset in offsets], label=label)
ax.set_xlabel("number of steps")
ax.set_ylabel("likelihood")
ax.legend(frameon=False)
despine(ax, ('top', 'right'))
plt.show()

Методы Монте-Карло для марковских цепей (MCMC). Введение - 9

РОДОНАЧАЛЬНИК ВСЕХ MCMC: АЛГОРИТМ МЕТРОПОЛИСА-ГАСТИНГСА

Конечно, все это очень интересно, но вернемся к процессу выборки произвольного вероятностного распределения Методы Монте-Карло для марковских цепей (MCMC). Введение - 10. Оно может быть либо дискретным, и в этом случае мы и далее будем говорить о матрице перехода Методы Монте-Карло для марковских цепей (MCMC). Введение - 11, либо непрерывным, и в этом случае Методы Монте-Карло для марковских цепей (MCMC). Введение - 12 будет переходным ядром. Здесь и далее речь пойдет о непрерывных распределениях, но все концепции, которые мы здесь рассмотрим, применимы и к дискретным случаям.

Если бы мы смогли спроектировать переходное ядро таким образом, чтобы следующее состояние уже было выведено из Методы Монте-Карло для марковских цепей (MCMC). Введение - 13, то этим можно было бы и ограничиться, так как наша марковская цепь… непосредственно делала бы выборку из Методы Монте-Карло для марковских цепей (MCMC). Введение - 14. К сожалению, чтобы добиться этого, нам нужна возможность делать выборку из Методы Монте-Карло для марковских цепей (MCMC). Введение - 15, чего мы делать не можем – иначе вы бы этого не читали, верно?

Обходной путь – разбить переходное ядро Методы Монте-Карло для марковских цепей (MCMC). Введение - 16 на две части: шаг проб (proposal step) и шаг приема/отбрасывания (acceptance/rejection step). На шаге проб фигурирует вспомогательное распределение Методы Монте-Карло для марковских цепей (MCMC). Введение - 17, из которого выбираются возможные следующие состояния цепочки. Мы не только можем делать выборку из этого распределения, но и в силах произвольно выбирать само распределение. Однако, при проектировании следует стремиться прийти к такой конфигурации, в которой образцы, взятые из этого распределения, минимально коррелировали бы с актуальным состоянием и одновременно имели хорошие шансы пройти этап приема. Вышеуказанный этап приема/отбрасывания – вторая часть переходного ядра; на данном этапе корректируются ошибки, содержавшиеся в пробных состояниях, выбранных из Методы Монте-Карло для марковских цепей (MCMC). Введение - 18. Здесь вычисляется вероятность успешного приема Методы Монте-Карло для марковских цепей (MCMC). Введение - 19 и принимается проба Методы Монте-Карло для марковских цепей (MCMC). Введение - 20 с такой вероятностью в качестве следующего состояния в цепи. Получение следующего состояния Методы Монте-Карло для марковских цепей (MCMC). Введение - 21 из Методы Монте-Карло для марковских цепей (MCMC). Введение - 22 тогда выполняется следующим образом: сначала пробное состояние Методы Монте-Карло для марковских цепей (MCMC). Введение - 23 берется из Методы Монте-Карло для марковских цепей (MCMC). Введение - 24. Затем оно принимается в качестве следующего состояния с вероятностью Методы Монте-Карло для марковских цепей (MCMC). Введение - 25 или отбрасывается с вероятностью Методы Монте-Карло для марковских цепей (MCMC). Введение - 26, и в последнем случае актуальное состояние копируется и используется в качестве следующего.
Следовательно, имеем

Методы Монте-Карло для марковских цепей (MCMC). Введение - 27

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

Методы Монте-Карло для марковских цепей (MCMC). Введение - 29.

Это означает, что вероятность находиться в состоянии Методы Монте-Карло для марковских цепей (MCMC). Введение - 30 и перейти оттуда в Методы Монте-Карло для марковских цепей (MCMC). Введение - 31 должна быть равна вероятности обратного процесса, то есть, быть в состоянии Методы Монте-Карло для марковских цепей (MCMC). Введение - 32 и перейти в состояние Методы Монте-Карло для марковских цепей (MCMC). Введение - 33. Ядра перехода большинства MCMC-алгоритмов удовлетворяют этому условию.
Для того, чтобы двухчастное ядро перехода подчинялось детальному равновесию, нужно правильно выбрать Методы Монте-Карло для марковских цепей (MCMC). Введение - 34, то есть, добиться, чтобы оно позволяло корректировать любые асимметрии в потоке вероятностей от / до Методы Монте-Карло для марковских цепей (MCMC). Введение - 35 или Методы Монте-Карло для марковских цепей (MCMC). Введение - 36. Алгоритм Метрополиса-Гастингса использует критерий приемлемости Метрополиса:

Методы Монте-Карло для марковских цепей (MCMC). Введение - 37.

А вот здесь начинается магия: Методы Монте-Карло для марковских цепей (MCMC). Введение - 38 известно нам только до константы, но это не имеет значения, поскольку данная неизвестная константа обнуляет выражение для Методы Монте-Карло для марковских цепей (MCMC). Введение - 39! Именно это свойство paccpacc обеспечивает работу алгоритмов, основанных на алгоритме Метрополиса-Гастингса, на ненормированных распределениях. Часто используются симметричные вспомогательные распределения с Методы Монте-Карло для марковских цепей (MCMC). Введение - 40, и в таком случае алгоритм Метрополиса-Гастингса редуцируется до оригинального (менее общего) алгоритма Метрополиса, разработанного в 1953 году. В оригинальном алгоритме

Методы Монте-Карло для марковских цепей (MCMC). Введение - 41.

В таком случае полное переходное ядро Метрополиса-Гастингса можно записать как

Методы Монте-Карло для марковских цепей (MCMC). Введение - 42.

РЕАЛИЗУЕМ АЛГОРИТМ МЕТРОПОЛИСА-ГАСТИНГСА НА PYTHON

Отлично, теперь, когда мы разобрались в принципе работы алгоритма Метрополиса-Гастингса, давайте перейдем к его реализации. Сначала установим логарифмическую вероятность того распределения, из которого собираемся делать выборку – без нормировочных констант; предполагается, что мы их не знаем. Далее работаем со стандартным нормальным распределением:

def log_prob(x):
     return -0.5 * np.sum(x ** 2)

Далее выбираем симметричное вспомогательное распределение. В целом, производительность алгоритма Метрополиса-Гастингса можно повысить, если включить во вспомогательное распределение уже известную вам информацию о том распределении, из которого вы хотите сделать выборку. Упрощенный подход выглядит так: берем текущее состояние x и выбираем пробу из Методы Монте-Карло для марковских цепей (MCMC). Введение - 43, то есть, задаем некоторую величину шага Δ и переходим влево или вправо от нашего текущего состояния не более чем на Методы Монте-Карло для марковских цепей (MCMC). Введение - 44:

def proposal(x, stepsize):
    return np.random.uniform(low=x - 0.5 * stepsize, 
                             high=x + 0.5 * stepsize, 
                             size=x.shape)

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

def p_acc_MH(x_new, x_old, log_prob):
    return min(1, np.exp(log_prob(x_new) - log_prob(x_old)))

Теперь собираем все это вместе в по-настоящему краткую реализацию этапа построения выборки для алгоритма Метрополиса-Гастингса:

def sample_MH(x_old, log_prob, stepsize):
    x_new = proposal(x_old, stepsize)
    # здесь мы определяем, принимается новое состояние или нет:
    # мы равномерно выбираем случайное число из [0,1] и сравниваем
    # его с вероятностью успешного приема
    accept = np.random.random() < p_acc(x_new, x_old, log_prob)
    if accept:
        return accept, x_new
    else:
        return accept, x_old

Кроме следующего состояния в марковской цепи, x_new или x_old, мы также возвращаем информацию о том, был ли принят шаг MCMC. Это позволит нам отслеживать динамику приема проб. В заключение данной реализации напишем функцию, которая будет итеративно вызывать sample_MH и таким образом строить марковскую цепь:

def build_MH_chain(init, stepsize, n_total, log_prob):

    n_accepted = 0
    chain = [init]

    for _ in range(n_total):
        accept, state = sample_MH(chain[-1], log_prob, stepsize)
        chain.append(state)
        n_accepted += accept
    
    acceptance_rate = n_accepted / float(n_total)
    
    return chain, acceptance_rate

ТЕСТИРУЕМ НАШ АЛГОРИТМ МЕТРОПОЛИСА-ГАСТИНГСА И ИССЛЕДУЕМ ЕГО ПОВЕДЕНИЕ

Наверное, теперь вам не терпится увидеть все это в действии. Этим и займемся, примем несколько информированных решений об аргументах stepsize и n_total:

chain, acceptance_rate = build_MH_chain(np.array([2.0]), 3.0, 10000, log_prob)
chain = [state for state, in chain]
print("Acceptance rate: {:.3f}".format(acceptance_rate))
last_states = ", ".join("{:.5f}".format(state) 
                        for state in chain[-10:])
print("Last ten states of chain: " + last_states)
Acceptance rate: 0.722
Last ten states of chain: -0.84962, -0.84962, -0.84962, -0.08692, 0.92728, -0.46215, 0.08655, -0.33841, -0.33841, -0.33841

Все хорошо. Итак, это сработало? Мы добились принятия проб примерно в 71% случаев, и у нас есть цепочка состояний. Следует отбросить несколько первых состояний, в которых цепь еще не сошлась к своему стационарному распределению. Давайте проверим, на самом ли деле у выбранных нами состояний – нормальное распределение:

def plot_samples(chain, log_prob, ax, orientation='vertical', normalize=True,
                 xlims=(-5, 5), legend=True):
    from scipy.integrate import quad
    
    ax.hist(chain, bins=50, density=True, label="MCMC samples",
           orientation=orientation)
    # вычисляем нормировочную константу нашего PDF
    if normalize:
        Z, _ = quad(lambda x: np.exp(log_prob(x)), -np.inf, np.inf)
    else:
        Z = 1.0
    xses = np.linspace(xlims[0], xlims[1], 1000)
    yses = [np.exp(log_prob(x)) / Z for x in xses]
    if orientation == 'horizontal':
        (yses, xses) = (xses, yses)
    ax.plot(xses, yses, label="true distribution")
    if legend:
        ax.legend(frameon=False)
    
fig, ax = plt.subplots()
plot_samples(chain[500:], log_prob, ax)
despine(ax)
ax.set_yticks(())
plt.show()

Методы Монте-Карло для марковских цепей (MCMC). Введение - 45

Выглядит отлично!

Что же с параметрами stepsize и n_total? Обсудим сначала размер шага: он определяет, насколько может быть удалено пробное состояние от текущего состояния цепи. Следовательно, это параметр вспомогательного распределения q, контролирующий, насколько велики будут случайные шаги, совершаемые цепью Маркова. Если размер шага слишком велик, то пробные состояния часто оказываются в хвосте распределения, где значения вероятности низкие. Механизм выборки Метрополиса-Гастингса отбрасывает большинство из этих шагов, вследствие чего темпы приема снижаются, и сходимость значительно замедляется. Убедитесь сами:

def sample_and_display(init_state, stepsize, n_total, n_burnin, log_prob):
    chain, acceptance_rate = build_MH_chain(init_state, stepsize, n_total, log_prob)
    print("Acceptance rate: {:.3f}".format(acceptance_rate))
    fig, ax = plt.subplots()
    plot_samples([state for state, in chain[n_burnin:]], log_prob, ax)
    despine(ax)
    ax.set_yticks(())
    plt.show()
    
sample_and_display(np.array([2.0]), 30, 10000, 500, log_prob)
Acceptance rate: 0.116

Методы Монте-Карло для марковских цепей (MCMC). Введение - 46

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

sample_and_display(np.array([2.0]), 0.1, 10000, 500, log_prob)
Acceptance rate: 0.992

Методы Монте-Карло для марковских цепей (MCMC). Введение - 47

Независимо от того, как вы выберете параметр размера шага, марковская цепь в конце концов сойдется к стационарному распределению. Но на это может потребоваться мноооого времени. Время, в течение которого мы будем симулировать марковскую цепь, задается параметром n_total — он просто определяет, сколько состояний марковской цепи (и, следовательно, выбранных образцов) у нас в итоге будет. Если цепь сходится медленно, то требуется увеличить n_total, чтобы марковская цепь успела «забыть» исходное состояние. Поэтому оставим размер шага крошечным и увеличим количество образцов, нарастив параметр n_total:

sample_and_display(np.array([2.0]), 0.1, 500000, 25000, log_prob)
Acceptance rate: 0.990

Методы Монте-Карло для марковских цепей (MCMC). Введение - 48

Мееееедленно идем к цели…

ВЫВОДЫ

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

Настоятельно рекомендую вам самим поэкспериментировать с кодом, приведенным здесь – так вы освоитесь с поведением алгоритма в различных обстоятельствах и глубже его поймете. Попробуйте несимметричное вспомогательное распределение! Что будет, если не настроить критерий приема как следует? Что произойдет, если попытаться сделать выборку из бимодального распределения? Можете ли придумать способ автоматической настройки размера шага? Какие здесь есть подводные камни? Ответьте на эти вопросы самостоятельно!

Автор: ph_piter

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js