การอนุมานแบบผสม 2-Gaussian ด้วย MCMC และ PyMC


10

ปัญหา

ฉันต้องการให้พอดีกับพารามิเตอร์แบบจำลองของประชากร 2-Gaussian แบบเรียบง่าย ให้ทุก hype รอบวิธี Bayesian ฉันต้องการเข้าใจว่าสำหรับปัญหานี้อนุมาน Bayesian เป็นเครื่องมือที่ดีกว่าวิธีการกระชับแบบดั้งเดิม

จนถึงตอนนี้ MCMC ทำงานได้แย่มากในตัวอย่างของเล่นนี้ แต่บางทีฉันอาจมองข้ามบางสิ่งบางอย่าง ดังนั้นเรามาดูรหัส

เครื่องมือ

ฉันจะใช้ python (2.7) + scipy stack, lmfit 0.8 และ PyMC 2.3

สมุดบันทึกเพื่อทำซ้ำการวิเคราะห์สามารถพบได้ที่นี่

สร้างข้อมูล

ก่อนอื่นให้สร้างข้อมูล:

from scipy.stats import distributions

# Sample parameters
nsamples = 1000
mu1_true = 0.3
mu2_true = 0.55
sig1_true = 0.08
sig2_true = 0.12
a_true = 0.4

# Samples generation
np.random.seed(3)  # for repeatability
s1 = distributions.norm.rvs(mu1_true, sig1_true, size=round(a_true*nsamples))
s2 = distributions.norm.rvs(mu2_true, sig2_true, size=round((1-a_true)*nsamples))
samples = np.hstack([s1, s2])

ฮิสโตแกรมของsamplesมีลักษณะดังนี้:

กราฟข้อมูล

"ยอดเขากว้าง" ส่วนประกอบต่าง ๆ ยากต่อการมองเห็น

วิธีคลาสสิค: พอดีกับฮิสโตแกรม

ลองวิธีการคลาสสิคก่อน การใช้lmfitเป็นเรื่องง่ายในการกำหนดรูปแบบ 2-peaks:

import lmfit

peak1 = lmfit.models.GaussianModel(prefix='p1_')
peak2 = lmfit.models.GaussianModel(prefix='p2_')
model = peak1 + peak2

model.set_param_hint('p1_center', value=0.2, min=-1, max=2)
model.set_param_hint('p2_center', value=0.5, min=-1, max=2)
model.set_param_hint('p1_sigma', value=0.1, min=0.01, max=0.3)
model.set_param_hint('p2_sigma', value=0.1, min=0.01, max=0.3)
model.set_param_hint('p1_amplitude', value=1, min=0.0, max=1)
model.set_param_hint('p2_amplitude', expr='1 - p1_amplitude')
name = '2-gaussians'

ในที่สุดเราก็พอดีกับโมเดลด้วยอัลกอริธึมเริม:

fit_res = model.fit(data, x=x_data, method='nelder')
print fit_res.fit_report()

ผลที่ได้คือภาพต่อไปนี้ (เส้นประสีแดงเป็นจุดศูนย์กลางพอดี):

NLS เหมาะสมกับผลลัพธ์

แม้ว่าปัญหาจะเป็นเรื่องยาก แต่ด้วยค่าเริ่มต้นและข้อ จำกัด ที่เหมาะสมโมเดลก็แปรเปลี่ยนเป็นค่าประมาณที่สมเหตุสมผล

วิธี Bayesian: MCMC

ฉันกำหนดโมเดลใน PyMC ตามลำดับชั้น centersและsigmasเป็นการแจกแจงของไฮเปอร์สำหรับไฮเปอร์พารามิเตอร์ที่เป็นตัวแทนของ 2 ศูนย์และ 2 sigmas ของ 2 Gaussians alphaคือเศษส่วนของประชากรแรกและการกระจายก่อนหน้านี้อยู่ที่นี่เบต้า

ตัวแปรเด็ดขาดเลือกระหว่างประชากรสองคน ฉันเข้าใจว่าตัวแปรนี้ต้องมีขนาดเท่ากันกับข้อมูล ( samples)

ในที่สุดmuและtauเป็นตัวแปรกำหนดที่กำหนดพารามิเตอร์ของการกระจายปกติ (พวกเขาขึ้นอยู่กับcategoryตัวแปรดังนั้นพวกเขาสุ่มสลับระหว่างค่าสองค่าสำหรับประชากรทั้งสอง)

sigmas = pm.Normal('sigmas', mu=0.1, tau=1000, size=2)
centers = pm.Normal('centers', [0.3, 0.7], [1/(0.1)**2, 1/(0.1)**2], size=2)
#centers = pm.Uniform('centers', 0, 1, size=2)

alpha  = pm.Beta('alpha', alpha=2, beta=3)
category = pm.Categorical("category", [alpha, 1 - alpha], size=nsamples)

@pm.deterministic
def mu(category=category, centers=centers):
    return centers[category]

@pm.deterministic
def tau(category=category, sigmas=sigmas):
    return 1/(sigmas[category]**2)

observations = pm.Normal('samples_model', mu=mu, tau=tau, value=samples, observed=True)
model = pm.Model([observations, mu, tau, category, alpha, sigmas, centers])

จากนั้นฉันก็รัน MCMC ด้วยการวนซ้ำที่ค่อนข้างนาน (1e5, ~ 60s บนเครื่องของฉัน):

mcmc = pm.MCMC(model)
mcmc.sample(100000, 30000)

α

สรุป MCMC อัลฟ่า

นอกจากนี้ศูนย์กลางของ Gaussians ก็ไม่มาบรรจบกันเช่นกัน ตัวอย่างเช่น:

สรุป MCMC Centers_0

α

แล้วเกิดอะไรขึ้นที่นี่? ฉันกำลังทำอะไรผิดหรือ MCMC ไม่เหมาะสำหรับปัญหานี้หรือไม่?

ฉันเข้าใจว่าวิธีการ MCMC จะช้าลง แต่ฮิสโตแกรมเล็กน้อยนั้นดูเหมือนว่าจะทำงานได้ดีขึ้นอย่างมากในการแก้ปัญหาประชากร

คำตอบ:


6

ปัญหาเกิดจากวิธีที่ PyMC ดึงตัวอย่างสำหรับรุ่นนี้ ดังที่อธิบายไว้ในหัวข้อ 5.8.1ของเอกสารประกอบ PyMC องค์ประกอบทั้งหมดของตัวแปรอาร์เรย์จะได้รับการอัพเดตพร้อมกัน สำหรับอาร์เรย์ขนาดเล็กเช่นcenterนี้ไม่ใช่ปัญหา แต่สำหรับอาร์เรย์ขนาดใหญ่เช่นcategoryนั้นจะนำไปสู่อัตราการตอบรับต่ำ คุณสามารถดูอัตราการยอมรับผ่าน

print mcmc.step_method_dict[category][0].ratio

วิธีแก้ปัญหาที่แนะนำในเอกสารประกอบคือการใช้อาร์เรย์ของตัวแปรที่มีค่าสเกลาร์ นอกจากนี้คุณต้องกำหนดค่าการแจกแจงข้อเสนอบางอย่างเนื่องจากตัวเลือกเริ่มต้นไม่ดี นี่คือรหัสที่เหมาะกับฉัน:

import pymc as pm
sigmas = pm.Normal('sigmas', mu=0.1, tau=1000, size=2)
centers = pm.Normal('centers', [0.3, 0.7], [1/(0.1)**2, 1/(0.1)**2], size=2)
alpha  = pm.Beta('alpha', alpha=2, beta=3)
category = pm.Container([pm.Categorical("category%i" % i, [alpha, 1 - alpha]) 
                         for i in range(nsamples)])
observations = pm.Container([pm.Normal('samples_model%i' % i, 
                   mu=centers[category[i]], tau=1/(sigmas[category[i]]**2), 
                   value=samples[i], observed=True) for i in range(nsamples)])
model = pm.Model([observations, category, alpha, sigmas, centers])
mcmc = pm.MCMC(model)
# initialize in a good place to reduce the number of steps required
centers.value = [mu1_true, mu2_true]
# set a custom proposal for centers, since the default is bad
mcmc.use_step_method(pm.Metropolis, centers, proposal_sd=sig1_true/np.sqrt(nsamples))
# set a custom proposal for category, since the default is bad
for i in range(nsamples):
    mcmc.use_step_method(pm.DiscreteMetropolis, category[i], proposal_distribution='Prior')
mcmc.sample(100)  # beware sampling takes much longer now
# check the acceptance rates
print mcmc.step_method_dict[category[0]][0].ratio
print mcmc.step_method_dict[centers][0].ratio
print mcmc.step_method_dict[alpha][0].ratio

proposal_sdและproposal_distributionตัวเลือกมีการอธิบายในส่วน 5.7.1 สำหรับศูนย์ฉันตั้งข้อเสนอให้ตรงกับค่าเบี่ยงเบนมาตรฐานของคนหลังซึ่งประมาณน้อยกว่าค่าเริ่มต้นเนื่องจากจำนวนข้อมูล PyMC พยายามปรับความกว้างของข้อเสนอ แต่จะใช้งานได้ก็ต่อเมื่ออัตราการตอบรับของคุณสูงพอที่จะเริ่มต้นด้วย สำหรับcategoryค่าเริ่มต้นproposal_distribution = 'Poisson'ซึ่งให้ผลลัพธ์ที่ไม่ดี (ฉันไม่รู้ว่าทำไมถึงเป็นเช่นนี้ แต่ไม่แน่นอนว่าฟังดูเหมือนข้อเสนอที่สมเหตุสมผลสำหรับตัวแปรไบนารี)


ขอบคุณสิ่งนี้มีประโยชน์จริง ๆ แม้ว่ามันจะช้าจนแทบทนไม่ไหว คุณสามารถอธิบายสั้น ๆ ของความหมายของproposal_distributionและproposal_sdและเหตุผลที่ใช้Priorจะดีกว่าสำหรับตัวแปรเด็ดขาด?
2304916

ขอบคุณทอม ฉันยอมรับว่าปัวซองเป็นตัวเลือกที่แปลกที่นี่ ฉันเปิดปัญหา: github.com/pymc-devs/pymc/issues/627
twiecki

2

σ

sigmas = pm.Exponential('sigmas', 0.1, size=2)

อัปเดต:

ฉันได้ใกล้พารามิเตอร์เริ่มต้นของข้อมูลโดยการเปลี่ยนส่วนเหล่านี้ของแบบจำลองของคุณ:

sigmas = pm.Exponential('sigmas', 0.1, size=2)
alpha  = pm.Beta('alpha', alpha=1, beta=1)

และด้วยการเรียกใช้ mcmc ด้วยการทำให้ผอมบาง:

mcmc.sample(200000, 3000, 10)

ผล:

แอลฟา

ศูนย์

sigmas

ผู้โพสต์ไม่ดีมาก ... ในส่วนที่ 11.6 ของหนังสือ BUGSพวกเขาพูดคุยเกี่ยวกับรูปแบบประเภทนี้และระบุว่ามีปัญหาการลู่เข้าโดยไม่มีทางออกชัดเจน ตรวจสอบยังที่นี่


นั่นเป็นประเด็นที่ดีฉันใช้แกมม่าตอนนี้ อย่างไรก็ตามการติดตามอัลฟามักจะมีค่าเป็น 0 (แทนที่จะเป็น 0.4) ฉันสงสัยว่ามีข้อผิดพลาดบางอย่างที่ซุ่มซ่อนอยู่ในตัวอย่างของฉัน
2304916

ฉันลอง Gamma (.1, .1) แต่ Exp (.1) ดูเหมือนว่าจะทำงานได้ดีขึ้น นอกจากนี้เมื่อความสัมพันธ์อัตโนมัติสูงคุณอาจรวมถึงการทำให้ผอมบางเช่นmcmc.sample(60000, 3000, 3)
jpneto

2

นอกจากนี้การไม่ระบุตัวตนเป็นปัญหาใหญ่สำหรับการใช้ MCMC สำหรับโมเดลผสม โดยทั่วไปถ้าคุณสลับเลเบลบนวิธีการคลัสเตอร์และการกำหนดคลัสเตอร์โอกาสจะไม่เปลี่ยนแปลงและสิ่งนี้อาจสร้างความสับสนตัวอย่าง (ระหว่างเชนหรือภายในเชน) สิ่งหนึ่งที่คุณอาจพยายามลดสิ่งนี้คือการใช้Potentialsใน PyMC3 การดำเนินที่ดีของ GMM มีศักยภาพเป็นที่นี่ ด้านหลังของปัญหาประเภทนี้มักจะมีหลายรูปแบบที่สูงซึ่งเป็นปัญหาใหญ่เช่นกัน คุณอาจต้องการใช้ EM (หรือการอนุมานแบบผันแปร)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.