Как извлечь неконтролируемые кластеры из процесса Дирихле в PyMC3?

Я только что закончил Байесовский анализ в Python в книге Освальдо Мартин (отличная книга, чтобы понять байесовские концепции и некоторые фантазии индексации).

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

Я понимаю, как назначить priors для Dirichlet distribution, но я не могу понять, как получить кластеры в PyMC3. Похоже, что большинство mus сходятся к центроидам (т.е. Средствам выборок I, отбираемых из), но они все еще являются отдельными components. Я думал об отключении для weights (w в модели), но, похоже, не работает так, как я себе представлял, так как несколько components имеют несколько отличающиеся средние параметры mus, которые сходятся.

Как извлечь кластеры (центроиды) из этой модели PyMC3? Я дал ей максимум 15 компонентов, которые я хочу свести на 3. mus похоже, находится в правильном месте, но весы перепутаны b/c, они распределяются между другими кластерами, поэтому я не могу использовать порог веса (если я не объединю их, но я не думаю, что как это обычно делается).

import pymc3 as pm
import numpy as np
import matplotlib.pyplot as plt
import multiprocessing
import seaborn as sns
import pandas as pd
import theano.tensor as tt
%matplotlib inline

# Clip at 15 components
K = 15

# Create mixture population
centroids = [0, 10, 50]
weights = [(2/5),(2/5),(1/5)]

mix_3 = np.concatenate([np.random.normal(loc=centroids[0], size=int(150*weights[0])), # 60 samples
                        np.random.normal(loc=centroids[1], size=int(150*weights[1])), # 60 samples
                        np.random.normal(loc=centroids[2], size=int(150*weights[2]))])# 30 samples
n = mix_3.size

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

# Create and fit model
with pm.Model() as Mod_dir:
    alpha = pm.Gamma('alpha', 1., 1.)

    beta = pm.Beta('beta', 1., alpha, shape=K)

    w = pm.Deterministic('w', beta * tt.concatenate([[1], tt.extra_ops.cumprod(1 - beta)[:-1]]))

    component = pm.Categorical('component', w, shape=n)

    tau = pm.Gamma("tau", 1.0, 1.0, shape=K)

    mu = pm.Normal('mu', 0, tau=tau, shape=K)

    obs = pm.Normal('obs',
                    mu[component], 
                    tau=tau[component],
                    observed=mix_3)

    step1 = pm.Metropolis(vars=[alpha, beta, w, tau, mu, obs])
#     step2 = pm.CategoricalGibbsMetropolis(vars=[component])
    step2 = pm.ElemwiseCategorical([component], np.arange(K)) # Much, much faster than the above

    tr = pm.sample(1e4, [step1, step2], njobs=multiprocessing.cpu_count())

#burn-in = 1000, thin by grabbing every 5th idx
pm.traceplot(tr[1e3::5])

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

Похожие вопросы ниже

https://stats.stackexchange.com/info/120209/pymc3-dirichlet-distribution для регрессии, а не кластеризации

https://stats.stackexchange.com/info/108251/image-clustering-and-dirichlet-process теория процесса DP

https://stats.stackexchange.com/info/116311/draw-a-multinomial-distribution-from-a-dirichlet-distribution объясняет DP

Процесс Дирихле в PyMC 3 направляет меня в учебник Остина Рочфорда выше

Ответ 1

Использование нескольких новых-новых дополнений к pymc3 поможет сделать это понятным. Я думаю, что я обновил пример Dirichlet Process после их добавления, но, похоже, он был возвращен к старой версии во время очистки документации; Я скоро это исправлю.

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

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

Наконец, с этими усеченными версиями бесконечных моделей при столкновении с вычислительными задачами часто бывает полезно увеличить количество потенциальных компонентов. Я обнаружил, что K = 30 работает лучше для этой модели, чем K = 15.

Следующий код реализует эти изменения и показывает, как можно извлечь "активное" компонент.

from matplotlib import pyplot as plt
import numpy as np
import pymc3 as pm
import seaborn as sns
from theano import tensor as T

blue = sns.color_palette()[0]

np.random.seed(462233) # from random.org

N = 150

CENTROIDS = np.array([0, 10, 50])
WEIGHTS = np.array([0.4, 0.4, 0.2])

x = np.random.normal(CENTROIDS[np.random.choice(3, size=N, p=WEIGHTS)], size=N)
x_std = (x - x.mean()) / x.std()

fig, ax = plt.subplots(figsize=(8, 6))

ax.hist(x_std, bins=30);

Стандартизированные данные

K = 30

with pm.Model() as model:
    alpha = pm.Gamma('alpha', 1., 1.)
    beta = pm.Beta('beta', 1., alpha, shape=K)
    w = pm.Deterministic('w', beta * T.concatenate([[1], T.extra_ops.cumprod(1 - beta)[:-1]]))

    tau = pm.Gamma('tau', 1., 1., shape=K)
    lambda_ = pm.Uniform('lambda', 0, 5, shape=K)
    mu = pm.Normal('mu', 0, tau=lambda_ * tau, shape=K)
    obs = pm.NormalMixture('obs', w, mu, tau=lambda_ * tau,
                           observed=x_std)

with model:
    trace = pm.sample(2000, n_init=100000)

fig, ax = plt.subplots(figsize=(8, 6))

ax.bar(np.arange(K) - 0.4, trace['w'].mean(axis=0));

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

Вес смеси

Наконец, мы видим, что задние ожидаемые средства этих трех компонентов соответствуют истинному (стандартизованному) средству достаточно хорошо.

trace['mu'].mean(axis=0)[:3]

массив ([- 0.73763891, -0.17284594, 2.10423978])

(CENTROIDS - x.mean()) / x.std()

массив ([- 0.73017789, -0.16765707, 2.0824262])