PyMC для непараметричної кластеризації: Процес Діріхле для оцінки параметрів суміші Гаусса не вдається кластеризувати


10

Налаштування проблеми

Однією з перших іграшкових проблем, до якої я хотів застосувати PyMC, є непараметричне кластеризація: давши деякі дані, моделюйте її як гауссову суміш та дізнайтеся кількість кластерів та середнє значення та коеваріантність кожного кластеру. Більшість того, що я знаю про цей метод, походить з відео-лекцій Майкла Джордана та Йе-Уу-Тех, близько 2007 року (до того, як рідкість стала люттю), і останні кілька днів читаючи підручники доктора Фоннесбека та Е. Чена [fn1], [ fn2]. Але проблема добре вивчена і має деякі надійні реалізації [fn3].

У цій проблемі з іграшкою я генерую десять малюнків з одновимірного гауссового і сорок малюнків із . Як ви бачите нижче, я не перетасовував малюнки, щоб було легко визначити, які зразки вийшли з якого компонента суміші.N(μ=0,σ=1)N(μ=4,σ=2)

Дані моделі суміші Гаусса

Я моделюю кожен зразок даних , для і де вказує кластер для цієї ї точки даних: . ось тривалість урізаного процесу Діріхле: для мене .yiN(μzi,σzi)i=1,...,50ziizi[1,...,NDP]NDPNDP=50

Розширюючи інфраструктуру процесу Діріхле, кожен ідентифікатор кластера являє собою малюнок із категоричної випадкової величини, функція масової ймовірності якої задана конструкцією розбиття палиці: з для параметр концентрації . Конструкти розбиття -довгий вектор , який повинен дорівнювати 1, спочатку отримуючи iid розподілених бета-чертежів, які залежать від , див. [Fn1]. І оскільки я хотів би, щоб дані повідомили про своє незнання , я слідую за [fn1] і припускаю .ziziCategorical(p)pStick(α)αNDPpNDPαααUniform(0.3,100)

Це визначає, як формується ідентифікатор кластера кожного зразка даних. Кожен з кластерів має пов'язане середнє та стандартне відхилення та . Тоді і .NDPμziσziμziN(μ=0,σ=50)σziUniform(0,100)

(Раніше я нескінченно слідкував за [fn1] і розміщував гіперпріор на , тобто з сам жеребкуванням нормальний розподіл з фіксованим параметром і від рівномірного. Але за https://stats.stackexchange.com/a/71932/31187 мої дані не підтримують такого типу ієрархічного гіперприору.)μziμziN(μ0,σ0)μ0σ0

Підсумовуючи, моя модель:

yiN(μzi,σzi) де працює від 1 до 50 (кількість зразків даних).i

ziCategorical(p) і може приймати значення між 0 і ; , вектор ; і , скаляр. (Я зараз трохи шкодую, що кількість вибірок даних дорівнювала усіченій довжині Діріхле до цього, але я сподіваюся, що це зрозуміло.)NDP1=49pStick(α)NDPαUniform(0.3,100)

μziN(μ=0,σ=50) та . Існує цих засобів і стандартних відхилень (по одному для кожного з можливих кластерів .)σziUniform(0,100)NDPNDP

Ось графічна модель: імена - це назви змінних, див. Розділ коду нижче.

Графік

Постановка проблеми

Незважаючи на кілька виправлень та помилок виправлень, вивчені параметри зовсім не схожі на справжні значення, які генерували дані.

В даний час я ініціалізую більшість випадкових змінних до фіксованих значень. Середні і стандартні змінні відхилення ініціалізуються до їх очікуваних значень (тобто 0 для нормальних, середина їх підтримки для рівномірних). Я форматувати все кластера ідентифікатори 0. І я ініціалізувати параметр концентрації .ziα=5

З такою ініціалізацією 100 000 ітерацій MCMC просто не можуть знайти другий кластер. Перший елемент близький до 1, і майже всі для всіх зразків даних є однаковими, приблизно 3,5. Я показую тут кожен 100-й малюнок для перших двадцяти зразків даних, тобто для :pμziiμzii=1,...,20

Значить з ініціалізованим нулем ідентифікатором кластера

Нагадуючи, що перші десять вибірок даних були з одного режиму, а решта - з другого, вищенаведений результат явно не вдається зафіксувати це.

Якщо я дозволяю випадкову ініціалізацію ідентифікаторів кластера, я отримую більше одного кластера, але кластер означає, що всі блукають приблизно на одному рівні 3,5:

Значить із випадково ініціалізованим ідентифікатором кластера

Це підказує мені, що це звичайна проблема з MCMC, що він не може досягти іншого режиму заднього від того, на якому він знаходиться: пригадайте, що ці різні результати трапляються після зміни ініціалізації ідентифікаторів кластера , а не їх пріорів чи будь-що інше.zi

Чи роблю я якісь помилки моделювання? Подібне запитання: https://stackoverflow.com/q/19114790/500207 хоче використовувати розподіл Диріхле і підходити до 3-х елементної суміші Гаусса і стикається з дещо подібними проблемами. Чи варто розглянути можливість створення повністю сполученої моделі та використання вибірки Гіббса для такого типу кластеризації? (Я впровадив вибірку Гіббса для випадку параметричного розподілу Диріхле , за винятком використання фіксованої концентрації , і він працював добре, тому очікуйте, що PyMC зможе вирішити принаймні цю проблему.)α

Додаток: код

import pymc
import numpy as np

### Data generation

# Means and standard deviations of the Gaussian mixture model. The inference
# engine doesn't know these.
means = [0, 4.0]
stdevs = [1, 2.0]

# Rather than randomizing between the mixands, just specify how many
# to draw from each. This makes it really easy to know which draws
# came from which mixands (the first N1 from the first, the rest from
# the secon). The inference engine doesn't know about N1 and N2, only Ndata
N1 = 10
N2 = 40
Ndata = N1+N2

# Seed both the data generator RNG  as well as the global seed (for PyMC)
RNGseed = 123
np.random.seed(RNGseed)

def generate_data(draws_per_mixand):
    """Draw samples from a two-element Gaussian mixture reproducibly.

    Input sequence indicates the number of draws from each mixand. Resulting
    draws are concantenated together.

    """
    RNG = np.random.RandomState(RNGseed)
    values = np.hstack([RNG.normal(means[i], stdevs[i], ndraws)
                        for (i,ndraws) in enumerate(draws_per_mixand)])
    return values

observed_data = generate_data([N1, N2])


### PyMC model setup, step 1: the Dirichlet process and stick-breaking

# Truncation level of the Dirichlet process
Ndp = 50

# "alpha", or the concentration of the stick-breaking construction. There exists
# some interplay between choice of Ndp and concentration: a high concentration
# value implies many clusters, in turn implying low values for the leading
# elements of the probability mass function built by stick-breaking. Since we
# enforce the resulting PMF to sum to one, the probability of the last cluster
# might be then be set artificially high. This may interfere with the Dirichlet
# process' clustering ability.
#
# An example: if Ndp===4, and concentration high enough, stick-breaking might
# yield p===[.1, .1, .1, .7], which isn't desireable. You want to initialize
# concentration so that the last element of the PMF is less than or not much
# more than the a few of the previous ones. So you'd want to initialize at a
# smaller concentration to get something more like, say, p===[.35, .3, .25, .1].
#
# A thought: maybe we can avoid this interdependency by, rather than setting the
# final value of the PMF vector, scale the entire PMF vector to sum to 1? FIXME,
# TODO.
concinit = 5.0
conclo = 0.3
conchi = 100.0
concentration = pymc.Uniform('concentration', lower=conclo, upper=conchi,
                             value=concinit)

# The stick-breaking construction: requires Ndp beta draws dependent on the
# concentration, before the probability mass function is actually constructed.
betas = pymc.Beta('betas', alpha=1, beta=concentration, size=Ndp)

@pymc.deterministic
def pmf(betas=betas):
    "Construct a probability mass function for the truncated Dirichlet process"
    # prod = lambda x: np.exp(np.sum(np.log(x))) # Slow but more accurate(?)
    prod = np.prod
    value = map(lambda (i,u): u * prod(1.0 - betas[:i]), enumerate(betas))
    value[-1] = 1.0 - sum(value[:-1]) # force value to sum to 1
    return value

# The cluster assignments: each data point's estimated cluster ID.
# Remove idinit to allow clusterid to be randomly initialized:
idinit = np.zeros(Ndata, dtype=np.int64)
clusterid = pymc.Categorical('clusterid', p=pmf, size=Ndata, value=idinit)

### PyMC model setup, step 2: clusters' means and stdevs

# An individual data sample is drawn from a Gaussian, whose mean and stdev is
# what we're seeking.

# Hyperprior on clusters' means
mu0_mean = 0.0
mu0_std = 50.0
mu0_prec = 1.0/mu0_std**2
mu0_init = np.zeros(Ndp)
clustermean = pymc.Normal('clustermean', mu=mu0_mean, tau=mu0_prec,
                          size=Ndp, value=mu0_init)

# The cluster's stdev
clustersig_lo = 0.0
clustersig_hi = 100.0
clustersig_init = 50*np.ones(Ndp) # Again, don't really care?
clustersig = pymc.Uniform('clustersig', lower=clustersig_lo,
                          upper=clustersig_hi, size=Ndp, value=clustersig_init)
clusterprec = clustersig ** -2

### PyMC model setup, step 3: data

# So now we have means and stdevs for each of the Ndp clusters. We also have a
# probability mass function over all clusters, and a cluster ID indicating which
# cluster a particular data sample belongs to.

@pymc.deterministic
def data_cluster_mean(clusterid=clusterid, clustermean=clustermean):
    "Converts Ndata cluster IDs and Ndp cluster means to Ndata means."
    return clustermean[clusterid]

@pymc.deterministic
def data_cluster_prec(clusterid=clusterid, clusterprec=clusterprec):
    "Converts Ndata cluster IDs and Ndp cluster precs to Ndata precs."
    return clusterprec[clusterid]

data = pymc.Normal('data', mu=data_cluster_mean, tau=data_cluster_prec,
                   observed=True, value=observed_data)

Список літератури

  1. fn1: http://nbviewer.ipython.org/urls/raw.github.com/fonnesbeck/Bios366/master/notebooks/Section5_2-Dirichlet-Process.ipynb
  2. fn2: http://blog.echen.me/2012/03/20/infinite-mixture-models-with-nonparametric-bayes-and-the-dirichlet-process/
  3. fn3: http://scikit-learn.org/stable/auto_examples/mixture/plot_gmm.html#example-mixture-plot-gmm-py

Ваша попередня відмінність компонентів - це Уніформа (0,100), що може спричинити серйозні проблеми. Лише 2% маси цього попереднього періоду охоплюють справжні відхилення 1 і 2. Очікувана дисперсія ваших компонентів відповідно до цього пріоритету - 50, що є таким широким гауссом, що він може легко враховувати ваші дані за допомогою одного компонента.
jerad

Чи читали ви цей розділ книги про ймовірнісне програмування та байєсівської статистики для хакерів ? У ній є приклад, який може вам допомогти!
Тім

Це здається невеликим стислим для відповіді. Здається, це більше коментарів. Чи можете ви хоча б окреслити, яку інформацію отримає ОП, прочитавши її?
Glen_b -Встановіть Моніку

@TimRich так, я прочитав це, і взяв клас в аспірантурі, працював у галузі, використовуючи прикладну статистику;) це питання, яке стосується PyMC.
Ахмед Фасіх

1
Я б не відхиляв ієрархічну посаду раніше. Загальновідомо, що ви ставите себе в погану позицію, якщо ставите плоскі пріори на компоненти суміші, особливо коли ви одночасно намагаєтеся дізнатися кількість кластерів. Саме це спричиняє дивні сплески у ваших слідових сюжетах. Всі великі імена в NP-Bayes, схоже, встановлюють і або використовують оцінки плагіну або розміщуючи інформаційні попередні дані про ці компоненти, призначені для розмістити їх у відповідних масштабах. μZiN(μ0,σ0)μ0,σ0
хлопець

Відповіді:


4

Я не впевнений, чи хтось більше розглядає це питання, але я поставив ваше запитання в rjags, щоб перевірити пропозицію відбору проб Гімбса Тома, включаючи розуміння Гая про квартиру до стандартного відхилення.

Ця проблема з іграшками може бути складною, оскільки 10 і навіть 40 точок даних недостатньо для оцінки відхилення без попереднього інформаційного попередження. Поточний попередній σzi∼Uniform (0,100) не є інформативним. Це може пояснити, чому майже всі намалювання μzi - це очікуване середнє значення для двох розподілів. Якщо це занадто не змінить ваше запитання, я використовуватиму 100 та 400 точок даних відповідно.

Я також не використовував процес злому палиці безпосередньо в своєму коді. Сторінка вікіпедії для процесу диріхлету змусила мене думати, що p ~ Dir (a / k) буде добре.

Нарешті, це лише напівпараметрична реалізація, оскільки вона все ще займає ряд кластерів k. Я не знаю, як зробити нескінченну модель суміші в штангах.

markov ланцюг mu кластер 1

markov ланцюг mu кластер 2

library("rjags")

set1 <- rnorm(100, 0, 1)
set2 <- rnorm(400, 4, 1)
data <- c(set1, set2)

plot(data, type='l', col='blue', lwd=3,
     main='gaussian mixture model data',
     xlab='data sample #', ylab='data value')
points(data, col='blue')

cpd.model.str <- 'model {
  a ~ dunif(0.3, 100)
  for (i in 1:k){
    alpha[i] <- a/k
    mu[i] ~ dnorm(0.0, 0.001)
    sigma[i] ~ dunif(0, 100)
  }
  p[1:k] ~ ddirich(alpha[1:k])
  for (i in 1:n){
    z[i] ~ dcat(p)
    y[i] ~ dnorm(mu[z[i]], pow(sigma[z[i]], -2))
  }
}' 


cpd.model <- jags.model(textConnection(cpd.model.str),
                        data=list(y=data,
                                  n=length(data),
                                  k=5))
update(cpd.model, 1000)
chain <- coda.samples(model = cpd.model, n.iter = 1000,
                      variable.names = c('p', 'mu', 'sigma'))
rchain <- as.matrix(chain)
apply(rchain, 2, mean)

1
Що стосується використання кінцевої кількості кластерів , ви можете просто взяти великий (і встановити , як у вас є) і отримати модель, яка фактично ідентична процесу Діріхле. Зближення цього до процесу Діріхле дуже швидке; для спостережень чогось типу повинно бути більше ніж достатньо. Це також розумно, при використанні , щоб НЕ використовувати палиці-ламати конструкцію, тому що не виявлять оновлення блоку на ; що ви тут зробили, краще. KKαi=a/K500K=25JAGSJAGSp
хлопець

1

Погане змішування, яке ви бачите, швидше за все, через те, як PyMC малює зразки. Як пояснено в розділі 5.8.1 документації PyMC, всі елементи змінної масиву оновлюються разом. У вашому випадку це означає, що він спробує оновити весь clustermeanмасив за один крок, і так само для clusterid. PyMC не робить вибірки Гіббса; це робить Метрополіс, де пропозицію обирають деякі прості евристики. Це робить малоймовірним запропонувати хороше значення для всього масиву.


Як тільки ви сказали, "це спробує оновити весь масив за один крок", я зрозумів недоліки Metropolis (в даному випадку) проти Гіббса. Чи є щось особливе в STAN або JAGS, що може зробити це краще для цього? У будь-якому випадку я збираюся витратити деякий час на реалізацію Gibbs в PyMC. Дякую! (Я був прихильником вашої роботи з моменту блискавки, тому подвійно дякую!)
Ахмед Фасіх

1
STAN не обробляє дискретні змінні, але JAGS варто спробувати.
Тома Мінька
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.