PyMC cho phân cụm không theo tỷ lệ: Quá trình Dirichlet để ước tính các tham số của hỗn hợp Gaussian không thành cụm


10

Vấn đề thiết lập

Một trong những vấn đề đồ chơi đầu tiên tôi muốn áp dụng PyMC là phân cụm không theo tỷ lệ: đưa ra một số dữ liệu, mô hình hóa nó như một hỗn hợp Gaussian, và tìm hiểu số lượng cụm và ý nghĩa và hiệp phương sai của từng cụm. Hầu hết những gì tôi biết về phương pháp này đến từ các bài giảng video của Michael Jordan và Yee Whye Teh, vào khoảng năm 2007 (trước khi thưa thớt trở thành cơn thịnh nộ), và vài ngày cuối cùng đọc bài hướng dẫn của Tiến sĩ Fonnesbeck và E. Chen [fn1], [ fn2]. Nhưng vấn đề được nghiên cứu kỹ và có một số triển khai đáng tin cậy [fn3].

Trong bài toán đồ chơi này, tôi tạo ra mười lần rút từ một Gaussian và bốn mươi lần rút từ . Như bạn có thể thấy bên dưới, tôi đã không xáo trộn các bản vẽ, để dễ dàng biết mẫu nào đến từ thành phần hỗn hợp nào.N ( μ = 4 , σ = 2 )N(μ=0,σ=1)N(μ=4,σ=2)

Dữ liệu mô hình hỗn hợp Gaussian

Tôi mô hình hóa từng mẫu dữ liệu , cho và trong đó chỉ ra cụm cho điểm dữ liệu thứ này: . ở đây là độ dài của quá trình Dirichlet bị cắt ngắn được sử dụng: đối với tôi, .i = 1 , . . . , 50 z i i z i[ 1 , . . . , N D P ] N D P N D P = 50yiN(μzi,σzi)i=1,...,50ziizi[1,...,NDP]NDPNDP=50

Mở rộng cơ sở hạ tầng quy trình Dirichlet, mỗi ID cụm được rút ra từ một biến ngẫu nhiên phân loại, có chức năng khối lượng xác suất được đưa ra bởi cấu trúc phá vỡ: với cho a tham số nồng độ . Stick phá cấu trúc các vector -long , mà phải tổng hợp tới 1, bằng cách đầu tiên lấy IID Beta-phân phối thu hút phụ thuộc vào , xem [ct1]. Và vì tôi muốn dữ liệu thông báo cho sự thiếu hiểu biết của mình về , tôi theo dõi [fn1] và giả sử .ziziCategorical(p)pStick(α)αNDPpNDPαααUniform(0.3,100)

Điều này xác định cách tạo ID cụm của mỗi mẫu dữ liệu. Mỗi cụm có độ lệch chuẩn và trung bình liên quan, và . Sau đó, và .NDPμziσziμziN(μ=0,σ=50)σziUniform(0,100)

(Trước đây tôi đã sau [ct1] không suy nghĩ và đặt một hyperprior trên , có nghĩa là, với bản thân một trận hòa từ một phân phối bình thường tham số cố định và từ đồng phục. Nhưng theo https://stats.stackexchange.com/a/71932/31187 , dữ liệu của tôi không hỗ trợ loại siêu nhân phân cấp này.)μziμziN(μ0,σ0)μ0σ0

Tóm lại, mô hình của tôi là:

yiN(μzi,σzi) trong đó chạy từ 1 đến 50 (số lượng mẫu dữ liệu).i

ziCategorical(p) và có thể nhận các giá trị trong khoảng từ 0 đến ; , một vectơ dài ; và , vô hướng. (Bây giờ tôi hơi hối hận khi làm cho số lượng mẫu dữ liệu bằng với độ dài bị cắt của Dirichlet trước đó, nhưng tôi hy vọng nó rõ ràng.)NDP1=49pStick(α)NDPαUniform(0.3,100)

σ z i ~ U n i f o r m ( 0 , 100 ) N D P N D PμziN(μ=0,σ=50) và . Có về các phương tiện và độ lệch chuẩn này (một cho mỗi cụm có thể.)σziUniform(0,100)NDPNDP

Đây là mô hình đồ họa: tên là tên biến, xem phần mã bên dưới.

Đồ thị

Báo cáo vấn đề

Mặc dù có một số điều chỉnh và sửa lỗi không thành công, các tham số đã học hoàn toàn không giống với các giá trị thực đã tạo ra dữ liệu.

Hiện tại, tôi đang khởi tạo hầu hết các biến ngẫu nhiên thành các giá trị cố định. Các biến trung bình và độ lệch chuẩn được khởi tạo cho các giá trị dự kiến ​​của chúng (nghĩa là 0 đối với các giá trị bình thường, ở giữa hỗ trợ của chúng cho các giá trị đồng nhất). Tôi khởi tạo tất cả ID cụm thành 0. Và tôi khởi tạo tham số nồng độ . α = 5ziα=5

Với các khởi tạo như vậy, 100.000 lần lặp MCMC đơn giản là không thể tìm thấy cụm thứ hai. Phần tử đầu tiên của gần bằng 1 và gần như tất cả các lần rút cho tất cả các mẫu dữ liệu đều giống nhau, khoảng 3,5. Tôi hiển thị mỗi lần rút thứ 100 ở đây cho hai mươi mẫu dữ liệu đầu tiên, nghĩa là cho :μ z i i μ z i i = 1 , . . . , 20pμziiμzii=1,...,20

Có nghĩa là với ID cụm khởi tạo bằng không

Nhắc lại rằng mười mẫu dữ liệu đầu tiên là từ một chế độ và phần còn lại là từ chế độ khác, kết quả trên rõ ràng không thể nắm bắt được điều đó.

Nếu tôi cho phép khởi tạo ngẫu nhiên các ID cụm, thì tôi nhận được nhiều hơn một cụm nhưng cụm có nghĩa là tất cả đi lang thang xung quanh cùng một mức 3,5:

Có nghĩa là với ID cụm khởi tạo ngẫu nhiên

Điều này gợi ý cho tôi rằng đó là vấn đề thường gặp với MCMC, rằng nó không thể đạt đến một chế độ khác của hậu thế từ đó: nhớ lại rằng những kết quả khác nhau này xảy ra sau khi thay đổi khởi tạo ID cụm , không phải là linh mục của chúng hoặc còn gì nữa không.zi

Tôi có phạm sai lầm nào trong mô hình không? Câu hỏi tương tự: https://stackoverflow.com/q/19114790/500207 muốn sử dụng phân phối Dirichlet và phù hợp với hỗn hợp Gaussian 3 yếu tố và đang gặp vấn đề tương tự. Tôi có nên xem xét việc thiết lập một mô hình liên hợp hoàn toàn và sử dụng lấy mẫu Gibbs cho kiểu phân cụm này không? (Tôi đã triển khai bộ lấy mẫu Gibbs cho trường hợp phân phối Dirichlet tham số, ngoại trừ sử dụng nồng độ cố định , trở lại trong ngày và nó hoạt động tốt, vì vậy mong PyMC có thể giải quyết vấn đề đó ít nhất một cách cẩn thận.)α

Phụ lục: mã

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)

Người giới thiệu

  1. fn1: http://nbviewer.ipython.org/urls/raw.github.com/fonnesbeck/Bios366/master/notebooks/Section5_2-Dirichlet-Processes.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/urdy/auto_examples/mixture/plot_gmm.html#example-mixture-plot-gmm-py

Ưu tiên của bạn đối với phương sai thành phần là Đồng nhất (0,100) có thể gây ra sự cố lớn cho bạn. Chỉ có 2% khối lượng của ưu tiên này bao gồm các phương sai thực sự của 1 và 2. Phương sai dự kiến ​​của các thành phần của bạn theo trước này là 50, đó là một Gaussian rộng đến mức có thể dễ dàng tính toán dữ liệu của bạn với một thành phần duy nhất.
jerad

Bạn đã đọc qua chương này của cuốn sách Lập trình xác suất và Bayesian cho tin tặc chưa? Nó có một ví dụ có thể giúp bạn!
Tim

Điều này có vẻ một chút ngắn gọn cho một câu trả lời. Nó dường như là một bình luận nhiều hơn. Bạn có thể ít nhất phác thảo những thông tin mà OP sẽ có được bằng cách đọc nó không?
Glen_b -Reinstate Monica

@TimRich vâng, tôi đã đọc nó, và tham gia lớp học ở trường đại học, và làm việc trong ngành công nghiệp về thống kê ứng dụng;) đây là một câu hỏi dành riêng cho PyMC.
Ahmed Fasih

1
Tôi sẽ không loại bỏ thứ bậc trước. Điều nổi tiếng là bạn tự đặt mình vào vị trí xấu nếu bạn đặt các linh mục phẳng vào các thành phần của hỗn hợp - đặc biệt là khi bạn đang cố gắng tìm hiểu số lượng cụm. Tôi nghĩ đây là nguyên nhân gây ra những đột biến kỳ lạ trong âm mưu dấu vết của bạn. Tất cả các tên tuổi lớn trong NP-Bayes dường như đặt và sử dụng ước tính trình cắm của hoặc bằng cách đặt thông tin trước cho các thành phần này được thiết kế để đặt chúng trên quy mô thích hợp. μ 0 , σ 0μZiN(μ0,σ0)μ0,σ0
anh chàng

Câu trả lời:


4

Tôi không chắc liệu có ai đang xem câu hỏi này nữa không nhưng tôi đặt câu hỏi của bạn vào rjags để kiểm tra đề xuất lấy mẫu Gibbs của Tom trong khi kết hợp cái nhìn sâu sắc từ Guy về căn hộ trước cho độ lệch chuẩn.

Vấn đề đồ chơi này có thể khó khăn vì 10 và thậm chí 40 điểm dữ liệu không đủ để ước tính phương sai mà không có thông tin trước. Σzi∼Uniform (0,100) trước đây không có thông tin. Điều này có thể giải thích tại sao gần như tất cả các lần rút μzi là giá trị trung bình dự kiến ​​của hai bản phân phối. Nếu nó không làm thay đổi câu hỏi của bạn quá nhiều, tôi sẽ sử dụng 100 và 400 điểm dữ liệu tương ứng.

Tôi cũng không sử dụng quy trình phá vỡ thanh trực tiếp trong mã của mình. Các wikipedia trang cho quá trình Dirichlet khiến tôi suy nghĩ p ~ Dir (a / k) sẽ là ok.

Cuối cùng, nó chỉ là một triển khai bán tham số vì nó vẫn cần một số cụm k. Tôi không biết làm thế nào để tạo một mô hình hỗn hợp vô hạn trong rjags.

chuỗi markov mu cụm 1

chuỗi markov mu cụm 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
Đối với việc sử dụng số lượng cụm hữu hạn , bạn chỉ cần lấy lớn (và đặt , như bạn có) và có được một mô hình giống hệt với quy trình Dirichlet. Sự hội tụ của điều này trước quá trình Dirichlet trước là rất nhanh; đối với quan sát, một cái gì đó như là quá đủ. Thật là thông minh, khi sử dụng , không sử dụng cấu trúc chống dính, vì sẽ không phát hiện cập nhật khối trên ; những gì bạn đã làm ở đây là tốt hơn. K α i = a / K 500 K = 25 pKKαi=a/K500K=25JAGSJAGSp
anh chàng

1

Sự pha trộn kém mà bạn đang thấy rất có thể là do cách PyMC vẽ mẫu. Như đã giải thích trong phần 5.8.1 của tài liệu PyMC, tất cả các yếu tố của một biến mảng được cập nhật cùng nhau. Trong trường hợp của bạn, điều đó có nghĩa là nó sẽ cố gắng cập nhật toàn bộ clustermeanmảng trong một bước và tương tự cho clusterid. PyMC không làm mẫu Gibbs; đó là đô thị nơi đề xuất được lựa chọn bởi một số phương pháp phỏng đoán đơn giản. Điều này làm cho nó không có khả năng đề xuất một giá trị tốt cho toàn bộ một mảng.


Ngay khi bạn nói, "nó sẽ cố gắng cập nhật toàn bộ mảng trong một bước", tôi đã hiểu những hạn chế của Metropolis (trong trường hợp này) so với Gibbs. Có điều gì đặc biệt về STAN hoặc JAGS có thể cho phép họ làm tốt hơn về điều này không? Trong cả hai trường hợp, tôi sẽ dành thời gian để thực hiện Gibbs ở PyMC. Cảm ơn bạn! (Tôi là một người hâm mộ công việc của bạn kể từ đèn chớp, vì vậy hãy cảm ơn gấp đôi!)
Ahmed Fasih

1
STAN không xử lý các biến rời rạc, nhưng JAGS đáng để thử.
Tom Minka
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.