সরাসরি পর্বে যান
পর্ব ৪৭ · ৭৭-এর মধ্যে · মডিউল ৯

MCMC — Metropolis-Hastings আর Gibbs sampling

MCMC — Metropolis–Hastings and Gibbs Sampling

Taught in Bangla, this lesson explains why posteriors need sampling, proves stationarity through detailed balance, and writes random-walk Metropolis in NumPy checked against a conjugate Gamma-Poisson answer. It measures ESS, thinning and R-hat in the browser, shows Gibbs slowing under correlation, and compares a real desktop PyMC NUTS run.

এই পর্বে যা শিখবেন

  • Posterior-এর প্রত্যাশা কেন integral, আর অজানা normalising constant থাকলেও কীভাবে নমুনা নেওয়া যায়
  • Stationary distribution আর detailed balance, আর Metropolis-Hastings-এর গ্রহণের নিয়ম কেন কাজ করে
  • NumPy-তে এক আর দুই মাত্রার sampler নিজে লেখা, আর conjugate উত্তর ও MLE-র সঙ্গে মেলানো
  • Proposal-এর মাপ, autocorrelation, ESS, warm-up, thinning আর R-hat নিজে হিসাব করে chain-এর মান বিচার
  • Gibbs sampling-এর শর্তসাপেক্ষ সূত্র, তার দুর্বলতা, আর HMC ও NUTS-এর মূল ধারণা

AI-এর ভিত্তি কোর্সের পর্ব ৫০ শিখিয়েছে prior, likelihood আর posterior, আর Beta–Binomial-এ posterior থেকে নমুনা নেওয়া। সেখানে posterior-এর সূত্র হাতে ছিল। এই কোর্সের পর্ব ১৮ posterior-এর সবচেয়ে উঁচু বিন্দু (MAP) খুঁজেছে। কিন্তু একটি বিন্দু অনিশ্চয়তার কথা বলে না। এই পর্বের প্রশ্ন: সূত্র হাতে না থাকলেও পুরো posterior থেকে নমুনা কীভাবে পাব? চালানো যায় এমন চারটি ব্লক পাতাতেই চলে (ব্রাউজারের Python: NumPy, SciPy, matplotlib আর scikit-learn সহ)। প্রথমবার প্যাকেজ নামতে কয়েক সেকেন্ড লাগে, scikit-learn-এ প্রায় ১০ সেকেন্ড।

১ · কেন নমুনা লাগে — posterior-এর integral আর অজানা হর

Bayes-এর সূত্রে posterior হলো

p(θ | D) = p(D | θ)·p(θ) ÷ Z, Z = ∫ p(D | θ)·p(θ) dθ

এখানে θ হলো parameter (যেমন দিনে ফেরতের গড় হার), D ডেটা, p(D | θ) likelihood, p(θ) prior, আর Z হলো normalising constant, যার আরেক নাম evidence. লব হিসাব করা সহজ: যেকোনো θ-তে দুটি সংখ্যা গুণ। হর Z কঠিন, কারণ সব সম্ভাব্য θ-এর ওপর integral লাগে। আমরা আসলে যা চাই, তা-ও integral:

E[f(θ) | D] = ∫ f(θ)·p(θ | D) dθ ≈ (১/S)·∑s f(θ(s))

f(θ) = θ নিলে posterior গড়। f হলো “θ > ৪.৫ হলে ১, নইলে ০” নিলে একটি সম্ভাবনা। Percentile থেকে credible interval. ডান দিকের গড় হলো Monte Carlo অনুমান: posterior থেকে S-টি নমুনা থাকলে সব প্রশ্নের উত্তর গড় নিয়ে মেলে, আর স্বাধীন নমুনায় ভুল কমে ১/√S হারে (AI-এর ভিত্তি কোর্সের পর্ব ৩৬)।

Grid কেন চলে না? এক মাত্রায় ১০০টি বিন্দুতে লব হিসাব করে যোগ করলেই Z মেলে। দুই মাত্রায় ১০০² = ১০,০০০টি বিন্দু, দশ মাত্রায় ১০০১০ = ১০২০টি। একটি ছোট logistic regression-এও ২০টি feature থাকতে পারে। তাই grid-এর পথ বন্ধ। আবার posterior থেকে সরাসরি স্বাধীন নমুনা নেওয়ার উপায়ও সাধারণত নেই। ব্যতিক্রম conjugate prior: Poisson likelihood আর Gamma prior হলে posterior আবার Gamma. অংশ ৪-এ এই বন্ধ উত্তরটিকেই আমরা মাপকাঠি বানাব।

ঢাকার নিউমার্কেটে কোন গলিতে ভিড় বেশি, জানতে মোট লোকসংখ্যা জানার দরকার নেই। একজন হাঁটতে থাকুন। প্রতিবার পাশের একটি গলিতে যাওয়ার কথা ভাবুন। সেখানে ভিড় বেশি হলে চলে যান। কম হলে মাঝে মাঝে যান, ভিড়ের অনুপাতের সমান সম্ভাবনায়। অনেকক্ষণ পর হিসাব করলে দেখা যাবে, প্রতিটি গলিতে কাটানো সময় সেখানকার ভিড়ের অনুপাতে। দুই গলির ভিড়ের অনুপাত লেগেছে, মোট ভিড় লাগেনি। MCMC ঠিক এই কাজটি করে।

২ · Markov chain দিয়ে নমুনা — stationary distribution আর detailed balance

AI-এর ভিত্তি কোর্সের পর্ব ৫৩-এ Markov chain এসেছে: পরের অবস্থা শুধু এখনকার অবস্থার ওপর নির্ভর করে, transition matrix P দিয়ে। আর stationary distribution π সেই বণ্টন, যা একটি ধাপের পরেও বদলায় না: πP = π. সাধারণ Markov chain-এ P জানা, π খুঁজতে হয়। MCMC উল্টো কাজ করে: π (posterior) জানা, এমন P বানাতে হবে যার stationary distribution ঠিক π.

সবচেয়ে সহজ শর্তটি হলো detailed balance:

π(i)·P(i → j) = π(j)·P(j → i) প্রতিটি জোড়া i, j-এর জন্য

বাঁ দিক হলো “i-তে থাকা আর i থেকে j-তে যাওয়া”-র প্রবাহ, ডান দিক উল্টো প্রবাহ। প্রতিটি জোড়ায় দুই দিকের প্রবাহ সমান হলে কোনো অবস্থায় সম্ভাবনা জমে না বা কমে না। গাণিতিকভাবে দুই দিকে i-এর ওপর যোগ করলে ∑i π(i)P(i → j) = π(j)∑i P(j → i) = π(j), অর্থাৎ πP = π. Detailed balance তাই stationarity-র জন্য যথেষ্ট (তবে আবশ্যক নয়)। আরও দুটি শর্ত লাগে, যাতে যেকোনো শুরু থেকে chain π-তে পৌঁছায়: সব অবস্থায় পৌঁছানো যায় (irreducible), আর chain একটি নির্দিষ্ট ছন্দে আটকে থাকে না (aperiodic). নিচের stepper দুই অবস্থার ছোট একটি chain-এ সব হিসাব হাতে করে।

অবস্থা A = শান্ত দিন, B = ব্যস্ত দিন; প্রস্তাব সবসময় অন্য অবস্থাটি
ধাপ
হিসাব
সংখ্যায়
মানে
  1. লক্ষ্য। বাজারঘরের দিন দুই রকম: শান্ত (A) আর ব্যস্ত (B), লক্ষ্য π = (০.৭৫, ০.২৫)। প্রস্তাব সবসময় অন্য অবস্থায় যাওয়া, যা প্রতিসম। গ্রহণের নিয়মে শুধু অনুপাত ০.২৫/০.৭৫ লাগে, তাই π-কে যেকোনো ধ্রুবক দিয়ে গুণ করলেও কিছু বদলায় না।
  2. Transition matrix. A থেকে B-তে যাওয়ার প্রস্তাব গ্রহণ হয় ১/৩ সম্ভাবনায়, তাই A-তে থেকে যাওয়া ২/৩. B থেকে A-তে প্রস্তাব উঁচুতে যাওয়া, তাই সবসময় গ্রহণ: P(B→A) = ১, P(B→B) = ০.
  3. Detailed balance. A→B প্রবাহ ০.৭৫ × ১/৩ = ০.২৫, B→A প্রবাহ ০.২৫ × ১ = ০.২৫. সমান, তাই শর্ত মানা হলো।
  4. Stationarity. πP-এর প্রথম উপাদান ০.৭৫ × ২/৩ + ০.২৫ × ১ = ০.৭৫, দ্বিতীয় ০.৭৫ × ১/৩ = ০.২৫. π একটি ধাপের পরেও একই।
  5. শুরু থেকে। নিশ্চিত শান্ত দিন (১, ০) থেকে শুরু করলে তিন ধাপে (০.৬৬৬৭, ০.৩৩৩৩), (০.৭৭৭৮, ০.২২২২), (০.৭৪০৭, ০.২৫৯৩)। লক্ষ্যের দুই পাশে দুলে দুলে কাছে আসছে।
  6. দীর্ঘমেয়াদে। pₜ → (০.৭৫, ০.২৫), শুরু যা-ই হোক। MCMC-র শুরুর অংশ বাদ দেওয়ার (warm-up) কারণ এটিই: শুরুর দিকের মানে তখনও শুরুর বিন্দুর ছাপ থাকে।

৩ · Metropolis-Hastings — প্রস্তাব, গ্রহণ আর বাতিল

অবিচ্ছিন্ন θ-তে একই কৌশল। ধরা যাক π̃(θ) = p(D | θ)p(θ), অর্থাৎ Z ছাড়া posterior. বর্তমান মান θ থেকে প্রতিটি ধাপে:

  1. একটি proposal distribution q(θ′ | θ) থেকে নতুন মান θ′ আঁকা।
  2. গ্রহণের সম্ভাবনা হিসাব: α = min(১, π̃(θ′)·q(θ | θ′) ÷ [π̃(θ)·q(θ′ | θ)]).
  3. ০ থেকে ১-এর একটি uniform সংখ্যা u আঁকা। u < α হলে θ′ গ্রহণ, নইলে θ-ই থাকে। দুই ক্ষেত্রেই chain-এ এই ধাপের মান লেখা হয়।

অনুপাতে Z উপরে-নিচে থাকে, তাই কেটে যায়। এই কারণেই MCMC অজানা হরের posterior-এ কাজ করে। q প্রতিসম হলে (যেমন θ′ = θ + σprop·z, z ~ N(০, ১)) q-এর অংশও কাটে, আর থাকে শুধু π̃(θ′)/π̃(θ). এটিই random-walk Metropolis. প্রস্তাব অসমমিত হলে q-এর অনুপাতটি হলো Hastings-এর সংশোধন। যেমন ধনাত্মক λ-এ গুণ আকারের লাফ (λ′ = λ·eσz) দিলে সংশোধনটি λ′/λ. Metropolis-Hastings কেন detailed balance মানে, এক লাইনে দেখা যায়: θ ≠ θ′ হলে π(θ)q(θ′ | θ)α(θ → θ′) = min(π(θ)q(θ′ | θ), π(θ′)q(θ | θ′)), যা θ আর θ′-এ প্রতিসম।

Random-walk Metropolis-এর একটি ধাপ

উঁচুতে প্রস্তাব সবসময় গ্রহণ, নিচুতে density-র অনুপাতের সমান সম্ভাবনায়

One random-walk Metropolis step on a posterior density লক্ষ্য: π̃(θ) = likelihood × prior q(θ′ │ θ) θ θ′ উঁচুতে: সবসময় গ্রহণ θ″ নিচুতে: অনুপাতের সম্ভাবনায় গ্রহণ α = min(1, π̃(θ′) ÷ π̃(θ)); Z কেটে যায় বাতিল হলে θ-ই আবার chain-এ লেখা হয় রেখার উচ্চতা = π̃, শুধু অনুপাত লাগে
ছবি ৪৭.১ — নীল θ বর্তমান মান, ড্যাশ দেওয়া ঘণ্টা হলো প্রস্তাবের বণ্টন। সবুজ θ′-এ π̃ বেশি, তাই অনুপাত ১-এর বেশি আর গ্রহণ নিশ্চিত। লাল θ″-এ π̃ অনেক কম, তাই গ্রহণ কালেভদ্রে। এভাবে chain উঁচু অঞ্চলে বেশি সময় কাটায়, কিন্তু লেজেও মাঝে মাঝে যায়। সেজন্যই লেজের সম্ভাবনাও ঠিক অনুপাতে আসে।

এবার অংশ ৪-এর আসল লক্ষ্যে এক-দুটি ধাপ হাতে করা যাক। সেখানে log π̃(λ) = ১৪৮·ln λ − ৩০.৫·λ (সংখ্যাগুলো কোথা থেকে, অংশ ৪-এ আসবে)। বাস্তব কোডে সবসময় log-এ কাজ হয়, কারণ likelihood-এর গুণফল সহজেই underflow করে।

Random-walk প্রস্তাব, তাই q কেটে যায়; u আগে থেকে দেওয়া
ধাপ
হিসাব
সংখ্যায়
সিদ্ধান্ত
  1. লক্ষ্য। π̃(λ) = λ১৪৮e−৩০.৫λ, যার চূড়া λ = ১৪৮/৩০.৫ ≈ ৪.৮৫২-এ। Z জানা নেই, দরকারও নেই। শুরু λ₀ = ৪.২.
  2. উঁচুতে প্রস্তাব। λ′ = ৪.৮. log অনুপাত ১৯.৭৬২৬ − ১৮.৩ = ১.৪৬২৬, অর্থাৎ π̃ প্রায় ৪.৩২ গুণ বড়। α = ১, তাই u না দেখেই গ্রহণ।
  3. নিচুতে প্রস্তাব। λ′ = ৬.০, ডান লেজে। log অনুপাত −৩.৫৭৪৮, α = e−৩.৫৭৪৮ ≈ ০.০২৮. u = ০.৪০ বড়, তাই বাতিল। Chain-এ ৪.৮ আবার লেখা হয়।
  4. মাঝারি প্রস্তাব। λ′ = ৫.৩. log অনুপাত −০.৫৮৪৫, α ≈ ০.৫৫৭. u = ০.৩১ ছোট, তাই গ্রহণ।
  5. ফল। Chain: ৪.২, ৪.৮, ৪.৮, ৫.৩. বাতিল হলে মানটি বাদ দেওয়া একটি পরিচিত ভুল। তাতে chain উঁচু অঞ্চলে কম সময় দেখায় আর বণ্টন ভুল হয়।

ইতিহাস: পদ্ধতিটি প্রকাশিত হয় ১৯৫৩ সালে, Metropolis, Arianna ও Marshall Rosenbluth, আর Augusta ও Edward Teller-এর প্রবন্ধে (Journal of Chemical Physics). কাজটি হয়েছিল Los Alamos-এর MANIAC কম্পিউটারে, তরলের অণুর অবস্থা অনুকরণের জন্য। কার অবদান কতটা, তা নিয়ে পরে মতভেদ হয়েছে; অনেক বিবরণে বলা হয়, কোডটি মূলত Arianna Rosenbluth লিখেছিলেন। W. K. Hastings ১৯৭০ সালে Biometrika-য় অসমমিত প্রস্তাবের সাধারণ রূপ দেন। AI-এর ভিত্তি কোর্সের পর্ব ৪৪-এর simulated annealing একই গ্রহণের নিয়ম ব্যবহার করে, তবে সেখানে লক্ষ্য নমুনা নয়, সর্বনিম্ন খরচ।

৪ · এক মাত্রায় হাতে-কলমে — ফেরতের হার, Gamma prior আর আসল উত্তর

বাজারঘরে ৩০ দিনে প্রতিদিন কতগুলো অর্ডার ফেরত এসেছে, সেই কাল্পনিক গণনা নিন। Model: প্রতিদিনের গণনা yi ~ Poisson(λ), prior λ ~ Gamma(shape ২, rate ০.৫), যার গড় ৪. Gamma–Poisson conjugate, তাই posterior Gamma(২ + ∑y, ০.৫ + n). এখানে ∑y = ১৪৭, n = ৩০, তাই posterior Gamma(১৪৯, ৩০.৫), আর log π̃(λ) = ১৪৮·ln λ − ৩০.৫·λ. নিচের sampler এই সূত্র ব্যবহার করে না। সে শুধু log π̃ জানে; শেষে আসল উত্তরের সঙ্গে মেলানো হয়।

import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
rng = np.random.default_rng(47)            # কাল্পনিক বাজারঘর: ৩০ দিনের ফেরত অর্ডার
y = rng.poisson(4.2, size=30)
a0, b0 = 2.0, 0.5                          # Gamma(shape 2, rate 0.5) prior, গড় 4
S, n = y.sum(), len(y)
def log_post(lam):                         # Z ছাড়া log posterior
    return (a0 - 1 + S) * np.log(lam) - (b0 + n) * lam if lam > 0 else -np.inf
lam, step, draws, acc = 1.0, 0.6, [], 0    # শুরু ইচ্ছা করে দূরে
for t in range(6000):
    prop = lam + step * rng.normal()       # random-walk প্রস্তাব
    if np.log(rng.uniform()) < log_post(prop) - log_post(lam):
        lam, acc = prop, acc + 1           # গ্রহণ
    draws.append(lam)                      # বাতিল হলে পুরোনো মানই আবার
draws = np.array(draws[1000:])             # প্রথম 1000 বাদ (warm-up)
exact = stats.gamma(a0 + S, scale=1 / (b0 + n))   # conjugate উত্তর
print("returns per day:", y[:10], "... sum", S, "n", n)
print(f"acceptance rate: {acc / 6000:.2f}")
print(f"posterior mean  MCMC {draws.mean():.3f}  exact {exact.mean():.3f}")
print(f"posterior sd    MCMC {draws.std():.3f}  exact {exact.std():.3f}")
print("95% interval MCMC", np.percentile(draws, [2.5, 97.5]).round(2),
      " exact", np.array(exact.interval(0.95)).round(2))
print(f"P(lambda > 4.5): MCMC {np.mean(draws > 4.5):.3f}  exact {exact.sf(4.5):.3f}")
g = np.linspace(2.5, 6, 300)
fig, ax = plt.subplots(figsize=(6, 3.4), layout="constrained")
ax.hist(draws, bins=40, density=True, alpha=0.5, label="Metropolis draws")
ax.plot(g, exact.pdf(g), "k", label="exact Gamma posterior")
ax.set_xlabel("lambda (returns per day)"); ax.set_ylabel("density")
ax.set_title("Random-walk Metropolis vs conjugate posterior"); ax.legend(fontsize=8)
plt.show()

আসল আউটপুট (ব্রাউজারের Python):

returns per day: [ 5  4  8  3 11  6  2  3  6  4] ... sum 147 n 30
acceptance rate: 0.60
posterior mean  MCMC 4.876  exact 4.885
posterior sd    MCMC 0.396  exact 0.400
95% interval MCMC [4.14 5.67]  exact [4.13 5.7 ]
P(lambda > 4.5): MCMC 0.824  exact 0.832

৩০ দিনে মোট ১৪৭টি ফেরত, দিনে গড় ৪.৯. Sampler-এর গড় ৪.৮৭৬, আসল ৪.৮৮৫; sd ০.৩৯৬ বনাম ০.৪০০; ৯৫% পরিসর [৪.১৪, ৫.৬৭] বনাম [৪.১৩, ৫.৭০]। “দিনে গড় ফেরত ৪.৫-এর বেশি”-র posterior সম্ভাবনা ০.৮২৪, আসল ০.৮৩২. ছবিতে histogram কালো density রেখার চারপাশে ঘোরে। পার্থক্যগুলো ছোট, কিন্তু শূন্য নয়: ৫,০০০টি নমুনা পরস্পর-নির্ভর, তাই তাদের কার্যকর সংখ্যা অনেক কম (পরের অংশ)। শুরু ১.০ ইচ্ছা করে দূরে রাখা হয়েছে। প্রথম ১,০০০টি ধাপ বাদ দেওয়ায় সেই দূরত্বের ছাপ গড়ে ঢোকেনি। np.log(lam) শুধু λ > ০ হলেই ডাকা হয়; ঋণাত্মক প্রস্তাবে log π̃ = −∞, তাই সেগুলো সবসময় বাতিল।

৫ · লাফের মাপ, autocorrelation আর effective sample size

Proposal-এর মাপ σprop একমাত্র “নব”, আর এটি দুই দিকেই ভুল হতে পারে। খুব ছোট লাফ প্রায় সবই গ্রহণ হয়, কিন্তু chain পিঁপড়ের মতো হাঁটে। খুব বড় লাফ প্রায় সবই লেজে পড়ে বাতিল হয়, chain একই জায়গায় দাঁড়িয়ে থাকে। মাঝের মাপটি খোঁজার জন্য দুটি মাপকাঠি লাগে।

Autocorrelation ρk হলো chain-এর মান আর k ধাপ পরের মানের correlation. ρk ধীরে কমলে পাশাপাশি নমুনাগুলো প্রায় একই তথ্য বহন করে। Effective sample size:

ESS = N ÷ (১ + ২∑k ≥ ১ ρk)

হরের রাশিটিকে বলে integrated autocorrelation time τ. স্বাধীন নমুনায় সব ρk = ০, তাই ESS = N. যদি ρk = ০.৮k হয়, তাহলে ∑ρk = ০.৮/০.২ = ৪, τ = ৯, আর ১০,০০০টি নমুনা কাজ করে প্রায় ১,১১১টি স্বাধীন নমুনার মতো। গড়ের Monte Carlo ভুল তাই MCSE ≈ sd ÷ √ESS, √N নয়। বাস্তবে দূরের lag-এর ρk শব্দে ভরা, তাই যোগফল কোথাও কেটে দিতে হয়। নিচের ব্লক সবচেয়ে সরল নিয়ম নেয়: প্রথম ঋণাত্মক ρk পর্যন্ত যোগ। ArviZ-এর মতো টুল Geyer-এর আরও সাবধানী নিয়ম আর কয়েকটি chain একসঙ্গে ব্যবহার করে, তাই তাদের সংখ্যা একটু আলাদা হবে।

import math
import numpy as np
import matplotlib.pyplot as plt
A, B = 149.0, 30.5                         # Gamma(149, rate 30.5): আগের ব্লকের posterior
def log_post(lam):
    return (A - 1) * math.log(lam) - B * lam if lam > 0 else -math.inf
def metropolis(step, n=5000, lam=4.0, seed=1):
    rng = np.random.default_rng(seed)
    z, u, out, acc = rng.normal(size=n), rng.uniform(size=n), np.empty(n), 0
    for t in range(n):
        prop = lam + step * z[t]
        if math.log(u[t]) < log_post(prop) - log_post(lam):
            lam, acc = prop, acc + 1
        out[t] = lam
    return out, acc / n
def acf(x):                                # autocorrelation, FFT দিয়ে
    x = x - x.mean(); f = np.fft.rfft(x, 2 * len(x))
    r = np.fft.irfft(f * np.conj(f))[:len(x)]
    return r / r[0]
def ess(x):                                # প্রথম ঋণাত্মক lag পর্যন্ত যোগ
    rho = acf(x); k = np.argmax(rho < 0)
    return len(x) / (1 + 2 * rho[1:k].sum())
fig, axes = plt.subplots(3, 1, figsize=(6, 5), sharex=True, layout="constrained")
for ax, step in zip(axes, [0.03, 1.0, 8.0]):
    d, rate = metropolis(step)
    rho = acf(d)
    print(f"step {step:5}: accept {rate:.2f}  lag1 {rho[1]:.3f}  lag10 {rho[10]:.3f}"
          f"  ESS {ess(d):6.0f} of {len(d)}  mean {d.mean():.3f}")
    ax.plot(d, lw=0.6); ax.set_ylabel(f"step {step}")
axes[0].set_title("Trace plots: proposal too small / about right / too large")
axes[2].set_xlabel("iteration"); plt.show()
d, _ = metropolis(1.0, n=20000)
for k in (1, 5, 10):                       # thinning: প্রতি k-তম রাখা
    print(f"thin {k:2d}: kept {len(d[::k]):5d}  ESS {ess(d[::k]):6.0f}")

আসল আউটপুট (ব্রাউজারের Python):

step  0.03: accept 0.97  lag1 0.998  lag10 0.977  ESS      6 of 5000  mean 4.734
step   1.0: accept 0.43  lag1 0.608  lag10 0.000  ESS   1125 of 5000  mean 4.864
step   8.0: accept 0.07  lag1 0.916  lag10 0.444  ESS    175 of 5000  mean 4.876
thin  1: kept 20000  ESS   4969
thin  5: kept  4000  ESS   3442
thin 10: kept  2000  ESS   2000

খুব ছোট লাফ (০.০৩): ৯৭% গ্রহণ, শুনতে ভালো। কিন্তু lag ১-এ autocorrelation ০.৯৯৮, আর ৫,০০০ ধাপে ESS মাত্র ৬. গড় ৪.৭৩৪, আসল ৪.৮৮৫ থেকে বেশ দূরে, কারণ chain পুরো posterior ঘুরেই আসেনি। ঠিক মাপে (১.০): ৪৩% গ্রহণ, lag ১০-এ autocorrelation প্রায় শূন্য, ESS ১,১২৫. খুব বড় লাফ (৮.০): মাত্র ৭% গ্রহণ। Chain লম্বা সময় একই মানে দাঁড়িয়ে থাকে, lag ১০-এও ০.৪৪৪, ESS ১৭৫. তিনটি trace plot-এর ছবিতে এই তিন আচরণ চোখে দেখা যায়। পরের stepper-এ এগুলো পড়ার নিয়ম আছে।

Thinning: ২০,০০০ ধাপের chain-এর ESS ৪,৯৬৯. প্রতি ৫ম মান রাখলে ৪,০০০টি থাকে, ESS ৩,৪৪২; প্রতি ১০ম রাখলে ২,০০০, ESS ২,০০০. অর্থাৎ ফেলে দেওয়া নমুনার সঙ্গে তথ্যও গেছে। পাতলা করা chain-এর autocorrelation কম দেখায়, কিন্তু অনুমান ভালো হয় না।

কোন গ্রহণের হার লক্ষ্য? Gaussian লক্ষ্যে random-walk Metropolis-এর তাত্ত্বিক ফল: এক মাত্রায় প্রায় ০.৪৪, আর অনেক মাত্রায় প্রায় ০.২৩৪ (Gelman, Roberts আর Gilks ১৯৯৬; Roberts, Gelman আর Gilks ১৯৯৭)। এগুলো আদর্শ অবস্থার নির্দেশিকা, আইন নয়। এক মাত্রায় ভালো লাফের মাপ মোটামুটি ২.৪ × posterior sd. এখানে ২.৪ × ০.৪ ≈ ১.০, ঠিক ব্লকের মাঝের মাপ। তাই অনেক sampler warm-up-এ গ্রহণের হার দেখে লাফের মাপ নিজেই মানিয়ে নেয়। Warm-up শেষ হলে মাপ স্থির করতে হয়; নইলে chain আর Markov chain থাকে না, আর নিশ্চয়তাগুলো ভাঙে।

Thinning নিয়ে প্রচলিত ভুল ধারণা। “Autocorrelation কমাতে প্রতি ১০ম নমুনা রাখুন” পরামর্শটি অনেক পুরোনো টিউটোরিয়ালে আছে। একটি স্থির chain থেকে মান ফেলে দিলে গড়ের ভুল কমে না, সাধারণত বাড়ে; ব্লকে ESS ৪,৯৬৯ থেকে ২,০০০-এ নেমেছে (Link আর Eaton ২০১২ এই বিষয়ে একটি পরিচিত আলোচনা)। Thinning-এর বৈধ কারণ মেমরি বা ডিস্ক বাঁচানো, বা প্রতিটি নমুনার ওপর পরের ধাপের হিসাব খুব দামি হলে।

৬ · কয়েকটি chain, warm-up আর R-hat — দুই মাত্রার logistic posterior

একটি chain কখন “যথেষ্ট” চলেছে, তা সেই chain দেখে বলা কঠিন। সমাধান: কয়েকটি chain, একে অপরের থেকে দূরের শুরু থেকে। সবগুলো যদি একই বণ্টন দেখায়, তবেই ভরসা। এই তুলনার মাপ R-hat (Gelman আর Rubin ১৯৯২)। m-টি chain, প্রতিটিতে n-টি মান। W = প্রতিটি chain-এর ভেতরের variance-এর গড়; B/n = chain-গুলোর গড়ের variance. তারপর

V̂ = ((n − ১)/n)·W + B/n, R̂ = √(V̂ ÷ W)

Chain-গুলো একই বণ্টনে থাকলে তাদের গড় প্রায় এক, B/n ছোট, আর R̂ ≈ ১. কোনো chain অন্য জায়গায় আটকে থাকলে B/n বড়, R̂ > ১. Vehtari ও সহকর্মীরা (২০২১) rank-normalised, split রূপ প্রস্তাব করেন, আর R̂ < ১.০১ ও ESS > ৪০০-কে নির্দেশিকা বলেন; ArviZ সেই রূপটি দেয়। নিচের ব্লক সহজ ক্লাসিক রূপটি নিজে হিসাব করে।

Model: বাজারঘরের ২০০টি কাল্পনিক অর্ডার, অর্ডারের মূল্য x (হাজার টাকায়), ফেরত হলে y = ১. Logistic regression: P(y = ১) = σ(a + b·x), prior a, b ~ N(০, ৫²)। চারটি chain চার কোণ থেকে শুরু, ৫,০০০ ধাপ, প্রথম ১,০০০টি warm-up. চারটি chain একসঙ্গে array-তে চলে, তাই loop একটিই। শেষে posterior গড় মেলানো হয় scikit-learn-এর penalty ছাড়া fit-এর (C = 1e10) MLE-র সঙ্গে।

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression
rng = np.random.default_rng(7)             # কাল্পনিক বাজারঘর: অর্ডার মূল্য (হাজার টাকা), ফেরত?
x = rng.uniform(0.3, 5.0, 200)
y = (rng.uniform(size=200) < 1 / (1 + np.exp(-(-2.5 + 0.6 * x)))).astype(float)
X = np.column_stack([np.ones(200), x])
def log_post(th):                          # th: (chain, 2); prior N(0, 5²) দুটিতেই
    eta = X @ th.T                         # 200 × chain
    ll = (y[:, None] * eta - np.logaddexp(0, eta)).sum(axis=0)
    return ll - (th ** 2).sum(axis=1) / (2 * 25)
th = np.array([[-6.0, 2.0], [3.0, -1.5], [-6.0, -1.5], [3.0, 2.0]])  # ছড়ানো শুরু
lp, T, keep, acc = log_post(th), 5000, [], 0
for t in range(T):
    prop = th + rng.normal(size=th.shape) * [0.5, 0.15]
    lp_new = log_post(prop)
    ok = np.log(rng.uniform(size=4)) < lp_new - lp       # প্রতিটি chain আলাদা সিদ্ধান্ত
    th[ok], lp[ok] = prop[ok], lp_new[ok]
    keep.append(th.copy()); acc += ok.sum()
draws = np.array(keep)                     # (T, 4 chain, 2)
def rhat(c):                               # c: (n, m chain); Gelman-Rubin
    n = len(c); W = c.var(axis=0, ddof=1).mean()
    Bn = c.mean(axis=0).var(ddof=1)        # B/n
    return np.sqrt(((n - 1) / n * W + Bn) / W)
for name, d in [("first 100 (warm-up)", draws[:100]), ("after warm-up", draws[1000:])]:
    print(f"R-hat {name:20s}: a {rhat(d[:, :, 0]):.3f}  b {rhat(d[:, :, 1]):.3f}")
post = draws[1000:].reshape(-1, 2); mle = LogisticRegression(C=1e10).fit(x[:, None], y)
print(f"acceptance rate: {acc / (4 * T):.2f}")
print("posterior mean (a, b):", post.mean(axis=0).round(3), " sd:", post.std(axis=0).round(3))
print("MLE sklearn    (a, b):", np.round([mle.intercept_[0], mle.coef_[0, 0]], 3))
print("95% interval b:", np.percentile(post[:, 1], [2.5, 97.5]).round(3))
fig, ax = plt.subplots(figsize=(6, 3.2), layout="constrained")
ax.plot(draws[:, :, 1], lw=0.5); ax.axvspan(0, 1000, color="grey", alpha=0.15)
ax.set_xlabel("iteration (shaded: warm-up)"); ax.set_ylabel("slope b")
ax.set_title("Four chains from dispersed starts"); plt.show()

আসল আউটপুট (ব্রাউজারের Python):

R-hat first 100 (warm-up) : a 1.317  b 1.548
R-hat after warm-up       : a 1.001  b 1.001
acceptance rate: 0.24
posterior mean (a, b): [-2.592  0.621]  sd: [0.431 0.131]
MLE sklearn    (a, b): [-2.563  0.613]
95% interval b: [0.377 0.892]

প্রথম ১০০ ধাপে R-hat a-তে ১.৩১৭, b-তে ১.৫৪৮: চারটি chain তখনও চার কোণ থেকে হেঁটে আসছে। ছবির ধূসর অংশে চারটি রেখা আলাদা উচ্চতা থেকে নেমে বা উঠে একটি পট্টিতে মেশে। Warm-up বাদে দুটিই ১.০০১. Posterior গড় (−২.৫৯২, ০.৬২১), MLE (−২.৫৬৩, ০.৬১৩)। N(০, ৫²) prior প্রায় তথ্যহীন আর n = ২০০, তাই দুটি কাছাকাছি। ছোট পার্থক্যের কারণ: MLE posterior-এর চূড়ার কাছে, আর গড় একটু হেলানো posterior-এর ভরকেন্দ্রে। বাড়তি লাভ অনিশ্চয়তা: b-এর ৯৫% credible interval [০.৩৭৭, ০.৮৯২]। অর্থাৎ বড় অর্ডারে ফেরতের ঝোঁক আছে, এটি model আর prior অনুযায়ী প্রায় নিশ্চিত, কিন্তু মাত্রা অনেকখানি অনিশ্চিত। গ্রহণের হার ০.২৪, দুই মাত্রার জন্য যুক্তিসংগত।

Warm-up বনাম burn-in. “Burn-in” পুরোনো নাম: শুরুর অংশ ফেলে দেওয়া। আধুনিক টুলে এই সময়ে লাফের মাপ আর দিকও মানিয়ে নেওয়া হয়, তাই নাম warm-up বা tuning. কতটা বাদ দেবেন, তার কোনো জাদু সংখ্যা নেই। Trace plot আর R-hat দেখে ঠিক করুন। নিচের stepper অংশ ৫ আর ৬-এর আসল chain থেকে বানানো ছোট sparkline দিয়ে trace plot পড়া শেখায়।

পরপর ২৪টি মান, উচ্চতা = মানসংখ্যা আসল run থেকে

            
  1. ভালো chain. মানগুলো দ্রুত ওঠানামা করে, কিন্তু একটি স্থির পট্টির ভেতরে। লোকে একে বলে “লোমশ শুঁয়োপোকা”। গ্রহণ ৪৩%, ESS ৫,০০০-এ ১,১২৫.
  2. খুব ছোট লাফ। প্রায় সব প্রস্তাব গ্রহণ, কিন্তু প্রতিটি লাফ ক্ষুদ্র। ২৪টি ধাপে মান ৫.৩ থেকে ৫.৫৯-এর মধ্যে, পুরো chain-এ ধীর ঢেউ। উচ্চ গ্রহণের হার এখানে ভালো খবর নয়।
  3. খুব বড় লাফ। বেশিরভাগ প্রস্তাব বাতিল, তাই একই মান বারবার লেখা হয়: ২৪টি ধাপে মাত্র ৩টি আলাদা মান। Trace সিঁড়ির মতো সমতল।
  4. না-মেশা chain. চারটি chain চার কোণ থেকে শুরু। প্রথম ৭২ ধাপে chain ১ আর ৪ ওপর থেকে নামছে, ২ আর ৩ নিচ থেকে উঠছে। এক chain দেখে এটি ধরা যেত না; প্রথম ১০০ ধাপের R-hat ১.৫৪৮ তা ধরেছে।
  5. Warm-up-এর পরে। চারটি chain-ই একই পট্টিতে এলোমেলো ঘুরছে, কোনোটির আলাদা ঝোঁক নেই। R-hat ১.০০১.
  6. তালিকা। R-hat ১.০১-এর নিচে, ESS যথেষ্ট, trace ঘন, সব chain-এর পট্টি এক, NUTS-এ divergence শূন্য। এগুলো সমস্যা ধরার পরীক্ষা, convergence-এর প্রমাণ নয়: সব chain যদি একই ভুল চূড়ায় থাকে, কোনো মাপই তা বলবে না।

৭ · Gibbs sampling — একবারে একটি চলক

অনেক model-এ পুরো posterior জটিল, কিন্তু বাকি সব স্থির রাখলে একটি parameter-এর বণ্টন পরিচিত। এই বণ্টনকে বলে full conditional. Gibbs sampling পালাক্রমে প্রতিটি parameter তার full conditional থেকে আঁকে। এটি Metropolis-Hastings-এরই বিশেষ রূপ: প্রস্তাব যখন ঠিক full conditional, তখন α = ১, তাই কোনো বাতিল নেই আর লাফের মাপও বাছতে হয় না।

বাজারঘরের ডেলিভারি সময়। ৪০টি কাল্পনিক ডেলিভারির সময় yi (ঘণ্টা), model yi ~ N(μ, σ²)। Prior: μ ~ N(৪৮, ২০²), আর σ² ~ Inverse-Gamma(২, ২০০)। দুটি prior আলাদা, তাই যুক্ত posterior-এর বন্ধ রূপ সহজ নয়। কিন্তু full conditional দুটি পরিচিত:

μ | σ², y : Normal

vn = ১ ÷ (১/s0² + n/σ²), mn = vn·(m0/s0² + nȳ/σ²)। অর্থাৎ prior আর ডেটার গড়ের precision দিয়ে ওজন দেওয়া গড়।

σ² | μ, y : Inverse-Gamma

shape a0 + n/২, scale b0 + ½∑(yi − μ)²। NumPy-তে Inverse-Gamma নেই, তাই scale ÷ Gamma(shape) আঁকা হয়।

ব্লকের দ্বিতীয় অংশ Gibbs-এর দুর্বলতা দেখায়। লক্ষ্য একটি দুই মাত্রার standard Gaussian, correlation ρ. এখানে a | b ~ N(ρb, ১ − ρ²), আর b | a একইভাবে। ρ = ০.৯৯ হলে প্রতিটি শর্তসাপেক্ষ বণ্টনের sd মাত্র √(১ − ০.৯৮০১) ≈ ০.১৪, অথচ লক্ষ্যের ছড়ানো ১. Chain তাই সরু উপত্যকায় অক্ষ-বরাবর ছোট ছোট সিঁড়ি-ধাপে চলে। তত্ত্ব বলে, a-এর lag-১ autocorrelation ঠিক ρ².

import numpy as np
import matplotlib.pyplot as plt
rng = np.random.default_rng(11)            # কাল্পনিক বাজারঘর: ৪০টি ডেলিভারির সময় (ঘণ্টা)
y = rng.normal(52, 14, 40); n, ybar = len(y), y.mean()
m0, s0sq, a0, b0 = 48.0, 20.0 ** 2, 2.0, 200.0   # μ ~ N(48, 20²), σ² ~ Inv-Gamma(2, 200)
mu, sig2, out = 30.0, 100.0, []
for t in range(4000):
    v = 1 / (1 / s0sq + n / sig2)                       # μ | σ², y : Normal
    mu = rng.normal(v * (m0 / s0sq + n * ybar / sig2), np.sqrt(v))
    rate = b0 + 0.5 * np.sum((y - mu) ** 2)             # σ² | μ, y : Inv-Gamma
    sig2 = rate / rng.gamma(a0 + n / 2)                 # 1/Gamma = Inv-Gamma
    out.append((mu, np.sqrt(sig2)))
d = np.array(out[500:])
print(f"data: mean {ybar:.2f} h, sd {y.std(ddof=1):.2f} h")
print("posterior mean (mu, sigma):", d.mean(axis=0).round(2))
print("95% interval mu:", np.percentile(d[:, 0], [2.5, 97.5]).round(2))
def gibbs_2d(rho, T=2000):                 # bivariate Normal, corr rho
    a, b, path = 3.0, -3.0, []
    for t in range(T):
        a = rng.normal(rho * b, np.sqrt(1 - rho ** 2))  # a | b
        b = rng.normal(rho * a, np.sqrt(1 - rho ** 2))  # b | a
        path.append((a, b))
    return np.array(path)
fig, axes = plt.subplots(1, 2, figsize=(6.4, 3.2), layout="constrained")
for ax, rho in zip(axes, [0.5, 0.99]):
    p = gibbs_2d(rho); lag1 = np.corrcoef(p[:-1, 0], p[1:, 0])[0, 1]
    print(f"rho {rho}: lag-1 autocorr of a {lag1:.3f}, mean of a {p[:, 0].mean():.3f}")
    ax.plot(p[:40, 0], p[:40, 1], "o-", ms=2, lw=0.7)
    ax.set_xlim(-4, 4); ax.set_ylim(-4, 4); ax.set_title(f"Gibbs, rho = {rho}, 40 steps")
    ax.set_xlabel("a"); ax.set_ylabel("b")
plt.show()

আসল আউটপুট (ব্রাউজারের Python):

data: mean 50.85 h, sd 12.00 h
posterior mean (mu, sigma): [50.8  12.03]
95% interval mu: [47.05 54.39]
rho 0.5: lag-1 autocorr of a 0.204, mean of a -0.010
rho 0.99: lag-1 autocorr of a 0.981, mean of a -0.177

ডেলিভারি: ডেটার গড় ৫০.৮৫ ঘণ্টা, sd ১২.০০. Posterior গড় μ ≈ ৫০.৮০, σ ≈ ১২.০৩, আর μ-এর ৯৫% পরিসর [৪৭.০৫, ৫৪.৩৯] ঘণ্টা। Prior-এর গড় ৪৮ μ-কে সামান্য টেনেছে, কারণ ৪০টি মানের তথ্য prior-এর চেয়ে অনেক বেশি। শুরু (৩০, √১০০) ইচ্ছা করে দূরে, তবু প্রথম ৫০০টি বাদ দেওয়ায় তার ছাপ নেই। Correlation: ρ = ০.৫-এ lag-১ autocorrelation ০.২০৪ (তত্ত্বে ০.২৫), ρ = ০.৯৯-এ ০.৯৮১ (তত্ত্বে ০.৯৮)। ২,০০০ ধাপেও ρ = ০.৯৯-এ a-এর গড় −০.১৭৭, যেখানে আসল গড় ০. ছবির ডান অংশে (৩, −৩) থেকে শুরু করা ৪০টি ধাপ সরু কর্ণ-বরাবর ধীরে এগোয়; বাঁ অংশে ৪০ ধাপেই পুরো মেঘ ঘোরা হয়ে যায়।

প্রতিকার। Correlated চলকগুলো একসঙ্গে আঁকা (blocked Gibbs), parameter বদলে নেওয়া (যেমন x-কে centre করলে intercept আর slope-এর correlation কমে), বা gradient-ভিত্তিক পদ্ধতি (অংশ ৮)। Topic model-এর সাহিত্যে collapsed Gibbs sampling খুব পরিচিত (Griffiths আর Steyvers ২০০৪); পর্ব ৪৪-এ ব্যবহার করা scikit-learn-এর LDA অবশ্য variational পদ্ধতিতে চলে, যার ধারণা পর্ব ৪৮ (Variational inference-এর ধারণা — ELBO আর approximation)-এ আসবে।

৮ · HMC আর NUTS — ঢাল দেখে লম্বা লাফ, আর PyMC-র একটি সত্যিকারের run

Random walk-এর সমস্যা মাত্রার সঙ্গে বাড়ে। d মাত্রায় গ্রহণের হার ধরে রাখতে লাফ ছোট করতে হয়, আর এক প্রান্ত থেকে অন্য প্রান্তে যেতে মোটামুটি d-এর অনুপাতে বেশি ধাপ লাগে। Hamiltonian Monte Carlo (HMC) একটি ভৌত তুলনা ব্যবহার করে। θ-কে একটি কণার অবস্থান ধরুন, −log π̃(θ)-কে পাহাড়ি উপত্যকার উচ্চতা। কণাকে একটি এলোমেলো ধাক্কা (momentum) দিয়ে কিছুক্ষণ গড়াতে দিন। Gradient কণাকে উপত্যকার আকার মেনে বাঁকায়, তাই সে সরু, হেলানো উপত্যকা ধরেও অনেক দূর যায়। গতিপথ সংখ্যাগতভাবে (leapfrog ধাপে) হিসাব হয়। ভুল জমে, তাই শেষে Metropolis-এর একটি গ্রহণ-পরীক্ষা দিয়ে সংশোধন হয়। ফল: দূরের প্রস্তাব, তবু উচ্চ গ্রহণের হার।

HMC-তে দুটি নব আছে: leapfrog-এর ধাপের মাপ আর ধাপের সংখ্যা। Hoffman আর Gelman-এর No-U-Turn Sampler (NUTS; প্রবন্ধ ২০১৪ সালে Journal of Machine Learning Research-এ) দ্বিতীয়টি নিজে ঠিক করে: গতিপথ যখন নিজের দিকে ফিরতে শুরু করে, তখন থামে। প্রথমটি warm-up-এ মানিয়ে নেয়। Stan আর PyMC-র ডিফল্ট sampler এটিই। একটি সীমা মনে রাখুন: gradient লাগে বলে বিচ্ছিন্ন (discrete) parameter, যেমন mixture-এর দল-নম্বর, সরাসরি চলে না; সেগুলো যোগ করে বাদ দিতে (marginalise) হয়।

নিচে অংশ ৬-এর একই ডেটা আর একই prior, এবার PyMC দিয়ে। এটি ব্রাউজারে চলে না, তাই একটি আলাদা uv project-এ ডেস্কটপে চালানো হয়েছে (সত্যিকারের run; লেখার সময়ের সর্বশেষ stable version). একে বলে probabilistic programming: model লিখে দিলে inference টুল নিজে চালায়।

~$ uv init --vcs none --author-from none bazarghar-ml
~$ cd bazarghar-ml && uv add pymc
 + pymc==6.4.0
 + pytensor==3.3.7
 …
~/bazarghar-ml$ cat nuts_pymc.py
import sys
import numpy as np
import pymc as pm
import arviz as az
import pandas as pd
pd.set_option("display.width", 120); pd.set_option("display.max_columns", None)

rng = np.random.default_rng(7)             # same fictional data as the in-page block
x = rng.uniform(0.3, 5.0, 200)
y = (rng.uniform(size=200) < 1 / (1 + np.exp(-(-2.5 + 0.6 * x)))).astype(int)

print("python", sys.version.split()[0], "| pymc", pm.__version__, "| arviz", az.__version__)
with pm.Model() as model:
    a = pm.Normal("a", 0, 5)
    b = pm.Normal("b", 0, 5)
    pm.Bernoulli("returned", logit_p=a + b * x, observed=y)
    idata = pm.sample(draws=1000, tune=1000, chains=4, cores=1,
                      random_seed=47, progressbar=False)
print(az.summary(idata, var_names=["a", "b"], round_to=3))
print("divergences:", int(idata.sample_stats["diverging"].sum()))
~/bazarghar-ml$ uv run python nuts_pymc.py
g++ not available, if using conda: `conda install gxx`
Initializing NUTS using jitter+adapt_diag...
Sequential sampling (4 chains in 1 job)
NUTS: [a, b]
Sampling 4 chains for 1_000 tune and 1_000 draw iterations (4_000 + 4_000 draws total) took 2 seconds.
python 3.14.4 | pymc 6.4.0 | arviz 1.3.0
    mean    sd  eti89_lb  eti89_ub  ess_bulk  ess_tail  r_hat  mcse_mean  mcse_sd
a -2.569  0.43    -3.262    -1.879   915.576   888.047  1.010      0.014    0.010
b  0.613  0.13     0.407     0.817   918.595   929.604  1.009      0.004    0.003
divergences: 0

পড়া যাক। প্রথম লাইন একটি সতর্কবার্তা: এই Windows যন্ত্রে C++ compiler (g++) নেই, তাই PyMC-র ভেতরের PyTensor দ্রুততম পথে চলে না। তবু ১,০০০ tune আর ১,০০০ draw-এর চারটি chain শেষ হয়েছে ২ সেকেন্ডে। গড় (−২.৫৬৯, ০.৬১৩), sd (০.৪৩০, ০.১৩০), নিজের sampler-এর (−২.৫৯২, ০.৬২১) আর (০.৪৩১, ০.১৩১)-এর খুব কাছে। ArviZ 1.x ডিফল্টে ৮৯% equal-tailed interval (eti89) দেখায়, ৯৫% নয়। r_hat ১.০১০ আর ১.০০৯, নির্দেশিকার একেবারে সীমায়; আরও draw নিলে কমার কথা। ESS: ৪,০০০টি draw থেকে bulk ESS প্রায় ৯১৬ আর ৯১৯. একই ডেস্কটপে ArviZ-এর az.ess দিয়ে অংশ ৬-এর random-walk chain-গুলো মাপলে ১৬,০০০টি draw থেকে প্রায় ৫৮৬ (এক local run). প্রতি draw-এ NUTS প্রায় ছয় গুণ বেশি তথ্য দিয়েছে, যদিও প্রতি draw-এ সে কয়েকবার gradient হিসাব করে। এখানে posterior-এ a আর b-এর correlation প্রায় −০.৯২, তাই NUTS-ও পুরো দক্ষ নয়; x-কে centre করলে দুটিরই উন্নতি হওয়ার কথা। Divergence ০, অর্থাৎ গতিপথ কোথাও ভেঙে পড়েনি।

Mixture-এ label switching. পর্ব ৩৯-এর GMM-এ দল ১ আর ২ অদলবদল করলে likelihood একই, তাই posterior-এ K!-টি হুবহু এক চূড়া থাকে। একটি chain কখনো এক চূড়ায় থাকে, কখনো চূড়া বদলায়, আর chain-ভেদে আলাদা চূড়ায় থাকতে পারে। তখন “দল ১-এর গড়”-এর posterior গড় অর্থহীন, R-hat বড় দেখায় যদিও কিছু ভুল নেই। প্রচলিত প্রতিকার: ক্রমের শর্ত বসানো (যেমন μ1 < μ2), বা নমুনা নেওয়ার পরে দলগুলো সাজানো।

আরও গভীরে: HMC এসেছিল lattice QCD পদার্থবিজ্ঞান থেকে, Duane, Kennedy, Pendleton আর Roweth-এর ১৯৮৭ সালের “Hybrid Monte Carlo” প্রবন্ধে। Radford Neal ১৯৯০-এর দশকে একে পরিসংখ্যান আর Bayesian neural network-এ আনেন, আর তাঁর “MCMC using Hamiltonian dynamics” (২০১১) অধ্যায়টি এখনো সবচেয়ে পরিচিত পরিচিতি। Betancourt-এর “A Conceptual Introduction to Hamiltonian Monte Carlo” (২০১৭) জ্যামিতির দিক থেকে ব্যাখ্যা করে কেন divergence সমস্যার সংকেত। Vehtari ও সহকর্মীদের ২০২১ সালের প্রবন্ধে আধুনিক R-hat আর bulk/tail ESS-এর সংজ্ঞা আছে।

পরীক্ষা ও ইন্টারভিউতে: এই ধরনের প্রশ্ন বিশ্ববিদ্যালয়, চাকরি বা ইন্টারভিউর পরীক্ষায় প্রায়ই আসে।

  • MCMC-তে normalising constant লাগে না কেন? গ্রহণের নিয়মে শুধু π̃(θ′)/π̃(θ), যেখানে Z উপরে-নিচে কেটে যায়।
  • বর্তমান π̃ = ৮, প্রস্তাবিত π̃ = ২, প্রতিসম প্রস্তাব; α কত? min(১, ২/৮) = ০.২৫.
  • N = ৮,০০০, τ = ২০; ESS? ৮,০০০/২০ = ৪০০; MCSE ≈ sd/২০.
  • দুই মাত্রার standard Gaussian, correlation ρ; Gibbs-এর শর্ত? a | b ~ N(ρb, ১ − ρ²); lag-১ autocorrelation ρ², তাই ρ বেশি হলে ধীর।
  • R-hat ১.৩ মানে? Chain-গুলো এখনো একই বণ্টন দেখছে না; ফল ব্যবহার করা চলবে না, আরও চালান বা model বদলান।

৯ · AI-এর যুগে — MCMC কোড আর ফল যাচাই

AI assistant দ্রুত sampler লিখে দেয়, কিন্তু MCMC-র ভুল প্রায়ই কোনো error ছাড়াই ভুল সংখ্যা দেয়:

AI প্রায়ই যা করেকেন ভুলকীভাবে ধরবেন
বাতিল হলে মানটি chain-এ লেখে নাউঁচু অঞ্চলের ওজন কমে, বণ্টন ভুল হয়conjugate উদাহরণে আসল উত্তরের সঙ্গে মেলান
density সরাসরি গুণ করে, log নয়বড় ডেটায় underflow, 0/0log π̃ ব্যবহার, ছোট আর বড় n-এ চালিয়ে দেখা
অসমমিত প্রস্তাবে Hastings সংশোধন বাদ দেয়detailed balance ভাঙে, posterior সরে যায়q(θ′ | θ) প্রতিসম কি না লিখে যাচাই
একটি chain, warm-up ছাড়া, “trace ভালো দেখাচ্ছে” বলেশুরুর ছাপ আর না-দেখা অঞ্চল ধরা পড়ে না৪টি ছড়ানো শুরু, R-hat আর ESS
পুরোনো বা বানানো API দেয় (যেমন PyMC 3-এর pymc3 import, sample(njobs=...))নাম আর ডিফল্ট version-ভেদে বদলেছেনিজের version-এর docs পড়ুন, চালিয়ে দেখুন
autocorrelation কমাতে thinning-এর পরামর্শ দেয়তথ্য ফেলে দেয়, ESS কমেESS তুলনা করুন, মেমরির কারণ ছাড়া রাখুন সব
divergence-এর সতর্কবার্তা উপেক্ষা করতে বলেposterior-এর কিছু অংশ দেখা হয়নিreparameterise, target_accept বাড়িয়ে আবার চালান

উত্তরের সঙ্গে যাচাইয়ের কোড চেয়ে নিন; নিজে চালানো অংশটিই আসল পরীক্ষা।

আমি বাজারঘরের ফেরতের ডেটায় একটি Bayesian logistic regression চালাতে চাই (feature: অর্ডারের মূল্য, ক্যাটাগরি, জেলা)। ১. NumPy দিয়ে log-space-এ একটি random-walk Metropolis লেখো, যা ৪টি ছড়ানো শুরু থেকে চলে, warm-up বাদ দেয়, আর R-hat ও ESS নিজে হিসাব করে। ২. একই model PyMC দিয়ে লেখো। আমার PyMC আর ArviZ version-এ pm.sample আর az.summary-র কোন parameter-এর ডিফল্ট কী, বলো। ৩. একটি Gamma–Poisson উদাহরণ দাও, যেখানে আসল posterior জানা, যাতে আমার sampler যাচাই করতে পারি। ৪. কোন সংখ্যা দেখে বুঝব chain ভালো নয় (R-hat, ESS, divergence), আর তখন কী করব, বলো। ৫. তোমার উত্তরের কোন অংশে ভুল হওয়ার সম্ভাবনা বেশি, সেটিও জানাও।

AI-এর দেওয়া MCMC কোড হাতে পেলে — যাচাইয়ের চেকলিস্ট

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।

প্র ০১Metropolis-এর পদ্ধতি এসেছিল ১৯৫৩ সালে, কিন্তু Bayesian পরিসংখ্যানে MCMC ব্যাপক হলো প্রায় ১৯৯০ সালে। এত দেরি কেন?

১৯৫৩ সালের প্রবন্ধটি ছিল পদার্থবিজ্ঞানের। লক্ষ্য ছিল তরলের অণুগুলোর অবস্থার একটি বণ্টন থেকে নমুনা নিয়ে চাপ বা শক্তির মতো গড় হিসাব করা। সেই বণ্টনের হর (partition function) পদার্থবিজ্ঞানেও অজানা, তাই অনুপাত-নির্ভর পদ্ধতিটি সেখানে স্বাভাবিক সমাধান ছিল। পরের কয়েক দশক পদ্ধতিটি মূলত পদার্থবিজ্ঞান আর রসায়নে থেকে যায়। Hastings ১৯৭০ সালে Biometrika-য় সাধারণ রূপ দিলেও পরিসংখ্যানবিদদের মূলধারা তখনও তেমন সাড়া দেয়নি বলে সাধারণত বলা হয়।

কয়েকটি কারণ একসঙ্গে কাজ করেছে। প্রথমত কম্পিউটার। হাজার হাজার ধাপের chain চালানো ১৯৭০-এর দশকে গবেষণাগারের বড় যন্ত্র ছাড়া কঠিন ছিল; ১৯৮০-এর দশকের শেষে workstation আর ব্যক্তিগত কম্পিউটার তা সহজ করে। দ্বিতীয়ত পরিসংখ্যানের ভাষা। Geman আর Geman ১৯৮৪ সালে ছবি পুনরুদ্ধারের সমস্যায় Gibbs sampling ব্যবহার করেন। Gelfand আর Smith ১৯৯০ সালে Journal of the American Statistical Association-এ দেখান যে সাধারণ Bayesian model-এর posterior এভাবে হিসাব করা যায়। এই প্রবন্ধকে প্রায়ই Bayesian গণনার মোড় ঘোরানো কাজ বলা হয়। তৃতীয়ত সফটওয়্যার। Cambridge-এর BUGS প্রকল্প ১৯৯০-এর দশকের শুরু থেকে model লিখে দিলেই Gibbs sampling চালানোর সুযোগ দেয়, পরে এল JAGS, Stan আর PyMC.

এর আগে Bayesian পদ্ধতি মূলত conjugate model বা আসন্ন হিসাবে আটকে ছিল। MCMC সেই সীমা তুলে দিল: hierarchical model, missing data আর জটিল likelihood-এর posterior-ও নমুনা দিয়ে পাওয়া গেল। Frequentist বনাম Bayesian বিতর্ক তাতে শেষ হয়নি, কিন্তু Bayesian পদ্ধতি ব্যবহারিক বিকল্প হয়ে উঠল।

মূল উপলব্ধি: একটি গাণিতিক ধারণা ব্যাপক হতে কম্পিউটারের ক্ষমতা, নিজের ক্ষেত্রের ভাষায় পরিচিতি আর সহজ সফটওয়্যার, তিনটিই লাগে।

প্র ০২MCMC, Laplace approximation আর variational inference — একই posterior-এর জন্য কোনটি কখন বেছে নেবেন?

তিনটিই একই প্রশ্নের উত্তর দেয়: posterior-এর গড়, ছড়ানো আর সম্ভাবনা। পার্থক্য খরচ আর সঠিকতার বিনিময়ে। Laplace approximation posterior-এর চূড়ায় (MAP) একটি Gaussian বসায়, যার covariance আসে log posterior-এর Hessian থেকে (পর্ব ১৮-এর Fisher information-এর মতো)। এটি দ্রুত, একটি optimisation আর একটি Hessian মাত্র। কিন্তু posterior হেলানো, বহু-চূড়ার বা সীমানার কাছে হলে ভুল। পর্ব ৪৫ (Bayesian linear regression)-এ logistic model-এর জন্য এটি একটি নোট হিসেবে এসেছে।

Variational inference (পর্ব ৪৮) একটি সরল বণ্টন পরিবার থেকে সবচেয়ে কাছেরটি খোঁজে, optimisation দিয়ে। এটি বড় ডেটা আর বড় model-এ চলে, mini-batch-এ চলে, আর একটি নির্দিষ্ট সময়ে শেষ হয়। দুর্বলতা পরিচিত: mean-field রূপ সাধারণত posterior-এর variance কম দেখায়, আর পরিবারটি ভুল হলে যত সময়ই দিন, ভুলটি থেকে যায়।

MCMC-র নিশ্চয়তা আলাদা ধরনের: যথেষ্ট সময় দিলে chain-এর গড় আসল posterior গড়ের দিকে যায়, কোনো পরিবার ধরে নিতে হয় না। দাম হলো সময়, আর “যথেষ্ট” হয়েছে কি না তা নিশ্চিত হওয়ার কঠিনতা। অংশ ৬-এর R-hat আর ESS এই দুশ্চিন্তার জন্যই। লাখো সারির ডেটায় প্রতিটি ধাপে পুরো likelihood লাগে বলে MCMC ধীর; stochastic-gradient রূপের MCMC আছে, তবে তাদের নিজস্ব bias আছে।

বাস্তবে সিদ্ধান্তটি এমন হতে পারে। হাজারখানেক সারি আর কয়েক ডজন parameter, আর ফল কোনো বড় সিদ্ধান্তে যাবে (যেমন ফেরত-নীতির খরচ অনুমান): NUTS. লাখো সারি, ঘনঘন হালনাগাদ, আর মোটামুটি অনিশ্চয়তাই যথেষ্ট: VI বা Laplace. সন্দেহ হলে একটি ছোট নমুনায় দুটি চালিয়ে তুলনা করুন; বড় পার্থক্য নিজেই একটি সতর্ক সংকেত।

মূল উপলব্ধি: Laplace আর VI দ্রুত কিন্তু ধরে-নেওয়া আকৃতির মধ্যে আটকে থাকে; MCMC ধীর কিন্তু সময়ের সঙ্গে সঠিক হয়, আর সেজন্য অন্য দুটির মাপকাঠি হিসেবেও কাজে লাগে।

প্র ০৩R-hat ১.০০ আর ESS অনেক বড়, তবু posterior ভুল হতে পারে কি? কীভাবে?

পারে। এই মাপগুলো একটি প্রশ্নের উত্তর দেয়: chain-গুলো একে অপরের সঙ্গে মিলছে কি না, আর প্রতিটির ভেতরে নমুনা কতটা পরস্পর-নির্ভর। Posterior-এর এমন অংশ যদি থাকে যেখানে কোনো chain যায়নি, কোনো মাপই তা জানবে না। সবচেয়ে পরিচিত উদাহরণ বহু-চূড়ার posterior. দুটি চূড়ার মাঝে খুব নিচু উপত্যকা থাকলে random-walk বা NUTS কোনোটিই সহজে পার হয় না। চারটি chain-ই একই চূড়া থেকে শুরু হলে (যেমন সবাই MAP-এর কাছ থেকে), তারা নিখুঁতভাবে মিলবে, R-hat ১.০০, অথচ অন্য চূড়ার ভর পুরো বাদ।

Mixture model-এ এটি আরও জটিল। পর্ব ৩৯-এর label switching-এর জন্য K!-টি হুবহু এক চূড়া থাকে। এখানে উল্টো সমস্যাও হয়: chain-গুলো আলাদা চূড়ায় থাকলে R-hat বড় দেখায়, যদিও প্রতিটি chain “ঠিক” আছে। তাই mixture-এ R-hat পড়ার আগে দলগুলো সাজাতে বা ক্রমের শর্ত দিতে হয়।

দ্বিতীয় উৎস হলো ভারী লেজ আর সরু অঞ্চল। Neal-এর “funnel” নামে পরিচিত hierarchical posterior-এ একটি সরু গলা থাকে। Sampler সেখানে ঢুকতে পারে না, কিন্তু চওড়া অংশে ভালো mixing দেখায়। NUTS এই ক্ষেত্রে divergence-এর সতর্কবার্তা দেয়, আর তার প্রতিকার সাধারণত model-কে নতুন করে লেখা (non-centred parameterisation). তৃতীয় উৎস কোডের ভুল: log posterior-এ prior-এর একটি পদ বাদ পড়লে sampler নিখুঁতভাবে একটি ভুল বণ্টন থেকে নমুনা নেবে।

প্রতিরক্ষা কয়েক স্তরের। ছড়ানো শুরু, আরও লম্বা run, ভিন্ন sampler দিয়ে একই উত্তর, আর simulation-based calibration (জানা parameter থেকে ডেটা বানিয়ে দেখা যে posterior তা ঠিক অনুপাতে ধরে)। অংশ ৪-এর মতো conjugate মাপকাঠিও একটি সস্তা পরীক্ষা।

মূল উপলব্ধি: Convergence-এর মাপগুলো সমস্যা ধরার পরীক্ষা, প্রমাণ নয়; না-দেখা অঞ্চল আর ভুল model সম্পর্কে তারা নীরব।

প্র ০৪Gibbs sampling-এ লাফের মাপ বাছতে হয় না, বাতিলও নেই। তাহলে আধুনিক টুলগুলো কেন NUTS-কে ডিফল্ট বানিয়েছে?

Gibbs-এর আকর্ষণ সত্যি। Conjugate full conditional থাকলে প্রতিটি ধাপ একটি পরিচিত বণ্টন থেকে আঁকা, কোনো নব নেই, আর কোডও ছোট। BUGS এই সুবিধার ওপর দাঁড়িয়েছিল, আর ১৯৯০ ও ২০০০-এর দশকে অসংখ্য প্রয়োগ এভাবেই হয়েছে। Topic model-এর collapsed Gibbs sampler-ও এই পরিবারের।

দুর্বলতা দুটি। প্রথমত, conjugate কাঠামো সবসময় থাকে না। Logistic regression-এর slope-এর full conditional কোনো পরিচিত বণ্টন নয়; তখন Gibbs-এর ভেতরে Metropolis ধাপ বা অন্য কৌশল লাগে, আর সরলতা হারিয়ে যায়। দ্বিতীয়ত, correlation. অংশ ৭-এর ব্লকে ρ = ০.৯৯-এ lag-১ autocorrelation ০.৯৮১, তত্ত্বে ρ². অক্ষ-বরাবর ধাপগুলো হেলানো উপত্যকায় সিঁড়ির মতো এগোয়। Hierarchical model-এ গ্রুপের গড় আর তাদের ছড়ানোর মধ্যে ঠিক এমন শক্ত নির্ভরতা থাকে, তাই Gibbs সেখানে খুব ধীর হতে পারে।

NUTS gradient থেকে উপত্যকার দিক জানে, আর সব চলক একসঙ্গে সরায়। ফলে correlation-এ তার ক্ষতি অনেক কম, আর মাত্রা বাড়লে দক্ষতা random walk বা Gibbs-এর চেয়ে ধীরে কমে। Automatic differentiation (যা deep learning-এর টুলেও আছে) থাকায় gradient হাতে লিখতে হয় না। Stan ২০১২ সালের দিকে NUTS-কে ডিফল্ট করার পর PyMC, NumPyro-ও একই পথে গেছে। তবে দাম আছে: প্রতি ধাপে কয়েকটি থেকে শতাধিক gradient হিসাব, আর বিচ্ছিন্ন parameter সরাসরি চলে না। তাই আধুনিক টুলগুলো বিচ্ছিন্ন অংশে Gibbs বা Metropolis আর অবিচ্ছিন্ন অংশে NUTS মিশিয়ে চালাতে পারে; PyMC এটি নিজে থেকেই করে।

মূল উপলব্ধি: Gibbs সরল কিন্তু correlation আর non-conjugate model-এ ধীর; NUTS প্রতি ধাপে দামি কিন্তু প্রতি ESS-এ সাধারণত সস্তা, আর automatic differentiation তাকে সহজলভ্য করেছে।

প্র ০৫বাজারঘরের ব্যবস্থাপক জানতে চান “বড় অর্ডারে ফেরতের ঝুঁকি কি সত্যিই বেশি?” Posterior দিয়ে উত্তর দেবেন, না p-value দিয়ে? কী কী সাবধানতা লাগবে?

দুই পথেই উত্তর দেওয়া যায়, আর এখানে সংখ্যায় তারা প্রায় একই দিকে যাবে। Frequentist পথে logistic regression-এর slope-এর p-value আর confidence interval. Bayesian পথে অংশ ৬-এর মতো posterior: b-এর ৯৫% credible interval [০.৩৭৭, ০.৮৯২], আর P(b > ০ | ডেটা) প্রায় ১. Posterior-এর সুবিধা হলো ব্যবস্থাপকের ভাষায় সরাসরি বাক্য: “model আর prior অনুযায়ী বড় অর্ডারে ফেরতের ঝোঁক থাকার সম্ভাবনা প্রায় নিশ্চিত, আর প্রতি হাজার টাকায় log-odds ০.৩৮ থেকে ০.৮৯ বাড়ে”। আরও কাজের: যেকোনো ব্যবসায়িক পরিমাণের posterior সরাসরি, যেমন ৫,০০০ টাকার অর্ডারে ফেরতের সম্ভাবনা, বা একটি নতুন ফেরত-নীতিতে প্রত্যাশিত খরচ। প্রতিটি নমুনায় হিসাব করে গড় নিলেই হয়।

সাবধানতা কয়েকটি। প্রথমত, prior একটি সিদ্ধান্ত। এখানে N(০, ৫²) প্রায় তথ্যহীন, কিন্তু অল্প ডেটায় prior ফল বদলায়; কয়েকটি prior-এ চালিয়ে দেখানো (sensitivity analysis) সৎ পথ। দ্বিতীয়ত, credible interval model-নির্ভর। Logit-এ রৈখিক সম্পর্কের অনুমান ভুল হলে নিখুঁত sampler-ও ভুল প্রশ্নের নিখুঁত উত্তর দেবে। Posterior predictive check, অর্থাৎ posterior থেকে নতুন ডেটা বানিয়ে আসল ডেটার সঙ্গে মেলানো, এটি ধরে। তৃতীয়ত, কারণ নয়, সম্পর্ক: দামি অর্ডারে হয়তো ইলেকট্রনিক্স বেশি, আর ফেরতের কারণ পণ্যের ধরন।

চতুর্থত, পুনরুৎপাদন। Seed, টুলের version, chain-এর সংখ্যা আর diagnostics রিপোর্টে লিখুন। ভিন্ন seed-এ ফল সামান্য বদলায়; MCSE বলে দেয় কতটা।

মূল উপলব্ধি: Posterior ব্যবসার প্রশ্নকে সরাসরি সম্ভাবনায় উত্তর দেয়, কিন্তু সেই উত্তর prior, model-এর আকৃতি আর ডেটার সীমার ওপর দাঁড়িয়ে; এগুলো খোলাখুলি বলাই পেশাদারি।

অনুশীলন

হিসাবগুলো খাতায় করুন, তারপর উত্তর মেলান।

  1. হাতের হিসাব: (ক) লক্ষ্য π̃(θ) = θ³e−২θ (θ > ০), প্রতিসম প্রস্তাব। θ = ১ থেকে θ′ = ২ প্রস্তাবে α কত? তারপর θ = ২ থেকে θ′ = ৩ প্রস্তাবে α কত, আর u = ০.৬ হলে chain-এর পরের মান কী? (খ) একটি chain-এর autocorrelation ρk = ০.৮k, N = ১০,০০০. ESS কত?

    (ক) অনুপাত = (২³e−৪) ÷ (১³e−২) = ৮e−২ ≈ ১.০৮৩ > ১, তাই α = ১, গ্রহণ। পরের ধাপে অনুপাত = (২৭/৮)e−২ ≈ ৩.৩৭৫ × ০.১৩৫৩ ≈ ০.৪৫৭, তাই α ≈ ০.৪৫৭. u = ০.৬ বড়, বাতিল; chain-এর পরের মান আবার ২. (খ) ∑ρk = ০.৮ ÷ (১ − ০.৮) = ৪, τ = ১ + ২ × ৪ = ৯, ESS ≈ ১০,০০০/৯ ≈ ১,১১১. সাধারণ সূত্র: ρk = φk হলে τ = (১ + φ)/(১ − φ)।

  2. সমালোচনা ও নকশা: একজন বিশ্লেষক বাজারঘরের ২৫টি parameter-এর একটি model-এ নিজের লেখা random-walk Metropolis চালালেন: একটি chain, ২,০০০ ধাপ, MAP থেকে শুরু, warm-up নেই, গ্রহণের হার ০.৯৫. “Autocorrelation কমাতে” প্রতি ১০ম মান রেখে ২০০টি নমুনার গড় রিপোর্ট করলেন, আর বললেন “trace দেখতে স্থির, তাই converge করেছে”। কোথায় সমস্যা, আর কীভাবে নতুন করে সাজাবেন?

    সমস্যা: (১) ০.৯৫ গ্রহণ মানে লাফ খুব ছোট; ২৫ মাত্রায় লক্ষ্য মোটামুটি ০.২-০.৩. Trace “স্থির” দেখাচ্ছে কারণ chain নড়ছেই কম। (২) একটি chain, আর শুরু MAP থেকে: অন্য chain-এর সঙ্গে তুলনা নেই, অন্য অঞ্চল বা চূড়া ধরা পড়বে না। (৩) Warm-up নেই। (৪) Thinning তথ্য ফেলেছে; ESS দেখানো হয়নি, তাই ২০০টি নমুনার গড়ের ভুল জানা নেই। (৫) Correlation থাকলে random walk ২৫ মাত্রায় খুব ধীর।

    নতুন নকশা: PyMC বা Stan-এ NUTS, ৪টি chain ছড়ানো শুরু থেকে, ১,০০০ warm-up আর ১,০০০ draw; সব রাখা। R-hat < ১.০১, bulk আর tail ESS > ৪০০, divergence ০ যাচাই। সমস্যা হলে feature centre/scale বা reparameterise. রিপোর্টে গড়ের সঙ্গে credible interval আর MCSE, আর একটি সরল baseline (MLE) এর সঙ্গে তুলনা।

  3. আউটপুট অনুমান করুন: চালানোর আগে লিখে ফেলুন নিচের কোড কী ছাপবে। এখানে প্রতিটি জোড়ায় প্রস্তাবিত মান আর u আগে থেকে দেওয়া।
    import math
    logp = lambda t: -abs(t)                   # π(θ) ∝ exp(−|θ|)
    theta, chain, acc = 0.0, [], 0
    for prop, u in [(1.0, 0.5), (0.5, 0.9), (-1.5, 0.1), (3.0, 0.2)]:
        a = min(1.0, math.exp(logp(prop) - logp(theta)))
        if u < a:
            theta, acc = prop, acc + 1
        chain.append(theta)
    print(chain, acc / 4)
    print(round(sum(chain) / len(chain), 3))
    [0.0, 0.0, -1.5, 3.0] 0.5
    0.375

    θ = ০ থেকে ১.০: α = e−১ ≈ ০.৩৬৮, u = ০.৫ বড়, বাতিল, chain-এ ০.০. তারপর ০.৫: α = e−০.৫ ≈ ০.৬০৭, u = ০.৯ বড়, বাতিল, আবার ০.০. তারপর −১.৫: α = e−১.৫ ≈ ০.২২৩, u = ০.১ ছোট, গ্রহণ। শেষে ৩.০: log অনুপাত −৩ − (−১.৫) = −১.৫, α ≈ ০.২২৩, u = ০.২ ছোট, গ্রহণ। চারটির দুটি গ্রহণ, তাই ০.৫; গড় (০ + ০ − ১.৫ + ৩)/৪ = ০.৩৭৫. ছাপা ফরম্যাট একটি ডেস্কটপ run-এ যাচাই করা।

  4. Gibbs হাতে: দুই মাত্রার standard Gaussian, correlation ρ = ০.৮. (ক) a | b আর b | a-এর বণ্টন লিখুন। (খ) b = ১ থেকে শুরু করে একটি পুরো Gibbs ধাপ করুন, যেখানে standard normal সংখ্যা দুটি z₁ = ০.৫ আর z₂ = −১. (গ) a-এর lag-১ autocorrelation কত হওয়ার কথা?

    (ক) a | b ~ N(০.৮b, ১ − ০.৬৪) = N(০.৮b, ০.৩৬), sd ০.৬; b | a ~ N(০.৮a, ০.৩৬)। (খ) a = ০.৮ × ১ + ০.৬ × ০.৫ = ১.১. তারপর b = ০.৮ × ১.১ + ০.৬ × (−১) = ০.৮৮ − ০.৬ = ০.২৮. নতুন অবস্থা (১.১, ০.২৮)। (গ) ρ² = ০.৬৪. ρ = ০.৯৯ হলে ০.৯৮, তাই সেখানে Gibbs অনেক ধীর।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমএকটি MCMC বিশ্লেষণের ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. Normalising constant ছাড়া log posterior লেখা (likelihood + prior)
  2. কয়েকটি chain-এর জন্য একে অপরের থেকে দূরের শুরুর মান বাছাই
  3. Warm-up চালানো, দরকার হলে লাফের মাপ মানিয়ে নেওয়া
  4. লাফের মাপ স্থির রেখে নমুনা নেওয়া, বাতিল ধাপেও মান লেখা
  5. R-hat, ESS আর trace plot দিয়ে chain যাচাই
  6. নমুনা থেকে posterior গড়, credible interval আর সম্ভাবনা হিসাব

Random-walk Metropolis-এ বর্তমান মানে Z ছাড়া posterior π̃ = 8, প্রস্তাবিত মানে π̃ = 2. প্রস্তাবটি গ্রহণের সম্ভাবনা কত?

  • 1
  • 0.25
  • 4
  • 0

Metropolis-Hastings অজানা normalising constant Z-সহ posterior থেকে নমুনা নিতে পারে। মূল কারণ কোনটি?

  • Warm-up-এ Z অনুমান করা হয়
  • Sampler প্রথমে grid দিয়ে Z হিসাব করে
  • গ্রহণের সম্ভাবনায় শুধু দুটি density-র অনুপাত লাগে
  • Prior সবসময় conjugate হতে হয়

দুই অবস্থার একটি chain-এ লক্ষ্য π = (0.75, 0.25), P(A→B) = 1/3 আর P(B→A) = 1. Detailed balance-এর হিসাব কী দেখায়?

  • দুই দিকের প্রবাহ 0.25 করে সমান, তাই π এই chain-এর stationary distribution
  • P(A→B) আর P(B→A) সমান নয়, তাই শর্ত ভেঙেছে
  • A থেকে প্রবাহ 0.75, B থেকে 0.25, তাই chain A-তে জমে যাবে
  • Detailed balance stationarity সম্পর্কে কিছু বলে না

একটি chain-এ N = 8000টি নমুনা আর integrated autocorrelation time τ = 20. Effective sample size কত?

  • 8000
  • 160000
  • প্রায় 89
  • 400

চারটি chain চালানোর পর একটি parameter-এর R-hat = 1.30. সবচেয়ে যুক্তিসংগত পদক্ষেপ কোনটি?

  • চারটি chain-এর সব নমুনা মিলিয়ে গড় রিপোর্ট করা
  • ফল ব্যবহার না করে আরও লম্বা run বা model-এর সমস্যা খোঁজা
  • প্রতি 10ম নমুনা রেখে thinning করা
  • যে chain আলাদা, সেটি বাদ দিয়ে বাকিগুলো রাখা

দুই মাত্রার একটি Gaussian-এ দুই চলকের correlation 0.99. Gibbs sampling-এ কী আশা করবেন?

  • বেশিরভাগ ধাপ বাতিল হবে
  • Correlation বেশি বলে chain খুব দ্রুত মিশবে
  • ধাপগুলো খুব ছোট হবে, autocorrelation প্রায় 0.98, মিশতে অনেক সময় লাগবে
  • Chain ভুল বণ্টনে থিতু হবে

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. Posterior-এর সব প্রশ্ন integral, আর হর Z বেশি মাত্রায় হিসাব করা যায় না; MCMC এমন chain চালায় যার stationary distribution posterior, আর Z অনুপাতে কেটে যায়।
  2. Metropolis-Hastings: α = min(১, π̃(θ′)q(θ | θ′) ÷ π̃(θ)q(θ′ | θ)); বাতিল হলে পুরোনো মান আবার লেখা। Detailed balance π(i)P(i→j) = π(j)P(j→i) stationarity নিশ্চিত করে।
  3. লাফের মাপ খুব ছোট বা খুব বড় দুটোই খারাপ; মাপুন ESS = N/(১ + ২∑ρk) দিয়ে, গ্রহণের হার দিয়ে নয়। Thinning সাধারণত ESS কমায়।
  4. কয়েকটি ছড়ানো chain, warm-up বাদ, R-hat < ১.০১, যথেষ্ট ESS, trace plot আর divergence দেখুন; এগুলো সমস্যা ধরে, convergence প্রমাণ করে না।
  5. Gibbs full conditional থেকে আঁকে (α = ১), কিন্তু correlation-এ ধীর; HMC/NUTS gradient দিয়ে লম্বা লাফ দেয়, বিচ্ছিন্ন parameter-এ সরাসরি চলে না।

পরের পর্বে: MCMC সময় দিলে সঠিক, কিন্তু ধীর। পর্ব ৪৮ (Variational inference-এর ধারণা — ELBO আর approximation) নমুনার বদলে optimisation দিয়ে posterior-এর কাছের একটি সরল বণ্টন খুঁজবে, আর দেখাবে সেই গতির দাম কোথায় দিতে হয়।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান