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

Variational inference-এর ধারণা — ELBO আর approximation

Variational Inference — the ELBO and Approximation

Taught in Bangla, this lesson contrasts mode-seeking and mass-covering KL, derives log p(x) = ELBO + KL and checks it by hand, and runs mean-field CAVI on a Normal model against the exact posterior. In-browser runs fit black-box VI with the reparameterisation trick, compare it with MCMC, and test BayesianGaussianMixture priors.

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

  • Posterior কেন প্রায়ই হিসাব করা যায় না, আর inference-কে কীভাবে একটি optimization সমস্যায় বদলানো যায়
  • KL(q ‖ p) আর KL(p ‖ q)-এর পার্থক্য, আর কোন দিক কেমন approximation দেয়
  • log p(x) = ELBO + KL-এর derivation আর সংখ্যায় যাচাই, আর EM-এর সঙ্গে সম্পর্ক
  • Mean-field ধারণা, Normal model-এ CAVI নিজে লেখা, আর আসল posterior-এর সঙ্গে তুলনা
  • Reparameterisation trick দিয়ে black-box VI, MCMC-র সঙ্গে গতি আর নির্ভুলতার তুলনা, আর BayesianGaussianMixture-এ VI-এর ব্যবহার

AI-এর ভিত্তি কোর্সের পর্ব ৫০-এ prior, likelihood, evidence আর posterior এসেছে, আর পর্ব ৩৮-এ KL divergence. এই কোর্সের পর্ব ৩৯ EM-এর প্রমাণে ELBO ব্যবহার করেছে। পর্ব ৪৫ (Bayesian linear regression — posterior আর predictive অনিশ্চয়তা) আর পর্ব ৪৬ (Gaussian process — kernel দিয়ে অনিশ্চয়তাসহ অনুমান) এমন model দেখায় যেখানে posterior সূত্রে লেখা যায়। পর্ব ৪৭ (MCMC — Metropolis-Hastings আর Gibbs sampling) দেখায় সূত্র না থাকলে নমুনা দিয়ে কীভাবে চলে। এই পর্ব তৃতীয় পথ: posterior-কে একটি সহজ বণ্টন দিয়ে আসন্ন করা, আর সেই খোঁজকে optimization বানানো। চারটি ব্লক পাতাতেই চলে (ব্রাউজারের Python: NumPy, SciPy, matplotlib আর scikit-learn সহ)। প্রথমবার প্যাকেজ নামতে কয়েক সেকেন্ড লাগে, scikit-learn-এ প্রায় ১০ সেকেন্ড।

১ · Inference-এর সমস্যা — posterior কেন সরাসরি পাওয়া যায় না

দর্জির কাছে মাপ দিয়ে বানানো শার্ট শরীরে হুবহু বসে, কিন্তু সময় লাগে সপ্তাহখানেক। রেডিমেড দোকানে S, M, L, XL থেকে সবচেয়ে কাছের মাপটি বেছে নিলে দশ মিনিটে কাজ শেষ। শার্ট একটু ঢিলা বা আঁটসাঁট হতে পারে, তবে দাম আর সময় দুটোই কম। MCMC দর্জির মতো: যথেষ্ট সময় দিলে আসল posterior-এর কাছে পৌঁছায়। VI রেডিমেডের মতো: আগে থেকে ঠিক করা একটি পরিবারের মধ্যে সবচেয়ে কাছের বণ্টন খোঁজে। কোন মাপ “সবচেয়ে কাছের”, সেটি মাপার ফিতাই এই পর্বের KL divergence.

Bayesian model-এ অজানা জিনিসগুলোকে (parameter বা লুকানো চলক) একসঙ্গে z বলি। Bayes-এর নিয়মে:

p(z | x) = p(x | z) p(z) ÷ p(x), p(x) = ∫ p(x | z) p(z) dz

লবটি সহজ: likelihood আর prior গুণ করলেই হয়। সমস্যা হরে। p(x)-কে বলে evidence বা marginal likelihood, আর এটি সব সম্ভাব্য z-এর ওপর একটি integral. Conjugate prior থাকলে integral-টি সূত্রে আসে, যেমন পর্ব ৪৫-এর Gaussian prior আর Gaussian noise. কিন্তু logistic regression-এর likelihood-এ sigmoid আছে, তাই Gaussian prior-এর সঙ্গে গুণ করলে চেনা কোনো বণ্টন আসে না। Mixture model-এ লুকানো দল-পরিচয়ের সম্ভাব্য বিন্যাস Kn-টি; ৩টি দল আর ৩৬০ জন ক্রেতায় সংখ্যাটি প্রায় ১০১৭২. দশ মাত্রার parameter-এ প্রতি অক্ষে ১০০টি grid বিন্দু নিলে integral-এ ১০২০-টি বিন্দু লাগে।

Posterior হাতে না থাকলে তিনটি পথ খোলা থাকে:

Laplace approximation

Posterior-এর চূড়া (MAP, পর্ব ১৮) খুঁজে সেখানে বক্রতা দিয়ে একটি Gaussian বসানো। দ্রুত, তবে শুধু চূড়ার আশেপাশের আকার দেখে।

MCMC

Posterior থেকে নমুনা তোলা, p(x) না জেনেই। সময় দিলে নির্ভুল, তবে ধীর, আর convergence যাচাই করতে হয় (পর্ব ৪৭)।

Variational inference

সহজ বণ্টনের একটি পরিবার Q বেছে, তার মধ্যে posterior-এর সবচেয়ে কাছেরটি optimization দিয়ে খোঁজা। দ্রুত আর বড় ডেটায় চলে, কিন্তু Q-এর বাইরের আকার ধরতে পারে না।

VI-তে প্রশ্নটি দাঁড়ায় এভাবে: q* = arg minq ∈ Q KL(q(z) ‖ p(z | x)). Q-কে বলে variational family, আর q-এর parameter-গুলো (যেমন একটি Gaussian-এর গড় আর sd) variational parameter. “Variational” নামটি এসেছে calculus of variations থেকে, যেখানে একটি সংখ্যা নয়, একটি পুরো function বদলে কিছু সর্বনিম্ন করা হয়। এখানে সেই function হলো বণ্টন q.

২ · KL-এর দুই দিক — mode-seeking বনাম mass-covering

AI-এর ভিত্তি কোর্সের পর্ব ৩৮ বলেছে: KL divergence কখনো ঋণাত্মক নয়, শূন্য শুধু দুটি বণ্টন এক হলে, আর এটি প্রতিসম নয়। প্রতিসম না হওয়াই এখানে মূল কথা:

KL(q ‖ p) = ∑ q(z) log [q(z) / p(z)], KL(p ‖ q) = ∑ p(z) log [p(z) / q(z)]

প্রথমটিতে গড় নেওয়া হয় q-এর ওপর। যেখানে q কিছু ভর রাখে কিন্তু p প্রায় শূন্য, সেখানে log(q/p) বিশাল। তাই KL(q ‖ p) কমাতে q এমন জায়গা এড়ায় যেখানে p নেই। একে বলে zero-forcing: p শূন্য হলে q-কেও শূন্য হতে হয়। দ্বিতীয়টিতে গড় নেওয়া হয় p-এর ওপর। যেখানে p ভর রাখে কিন্তু q প্রায় শূন্য, সেখানে log(p/q) বিশাল। তাই KL(p ‖ q) কমাতে q-কে p-এর সব ভর ঢাকতে হয়, প্রয়োজনে ফাঁকা জায়গাতেও ছড়িয়ে। নিচের stepper তিনটি অবস্থার একটি ছোট উদাহরণে দুটি দিক হাতে হিসাব করে।

p = (0.48, 0.04, 0.48); qA = (0.80, 0.15, 0.05) এক চূড়ায়; qB = (0.25, 0.50, 0.25) মাঝে ছড়ানো; ln স্বাভাবিক log
ধাপ
হিসাব
পদগুলো
যোগফল
  1. লক্ষ্য। p-এর দুটি চূড়া, দুই প্রান্তে ০.৪৮ করে, মাঝে মাত্র ০.০৪. qA একটি চূড়ায় বসে অন্যটি প্রায় উপেক্ষা করে। qB মাঝখানে, যেখানে p প্রায় নেই, অর্ধেক ভর রাখে।
  2. KL(qA ‖ p). qA-র ভর যেখানে, সেখানে p যথেষ্ট আছে। সবচেয়ে বড় পদ ০.৮০ × ln(০.৮০/০.৪৮) = ০.৪০৮৭. ডান প্রান্তে qA ছোট বলে পদটি ঋণাত্মক। যোগফল ০.৪৯৩৮.
  3. KL(qB ‖ p). মাঝের পদ ০.৫০ × ln(০.৫০/০.০৪) = ১.২৬২৯: p যেখানে প্রায় শূন্য, সেখানে ভর রাখার শাস্তি। যোগফল ০.৯৩৬৭, তাই এই দিকে qA জেতে।
  4. KL(p ‖ qA). এবার গড় p-এর ওপর। ডান প্রান্তে p = ০.৪৮ অথচ qA = ০.০৫, পদ ০.৪৮ × ln(০.৪৮/০.০৫) = ১.০৮৫৬. একটি চূড়া ছেড়ে দেওয়ার শাস্তি। যোগফল ০.৭৮৭৬.
  5. KL(p ‖ qB). qB দুই প্রান্তেই ০.২৫ রাখে, তাই কোনো পদ বিশাল নয়: ০.৩১৩১ + (−০.১০১০) + ০.৩১৩১ = ০.৫২৫২. এই দিকে qB জেতে।
  6. রায়। KL(q ‖ p) একটি চূড়া বেছে নেয়, অর্থাৎ mode-seeking. KL(p ‖ q) সব ভর ঢাকে, ফাঁকা মাঝখানেও, অর্থাৎ mass-covering. প্রশ্নে “কোন দিক কোন fit” এলে এই দুটি বড় পদই যুক্তি।

এবার অবিরাম রূপ। বাজারঘরের অর্ডার-মূল্যের একটি কাল্পনিক দুই-চূড়ার বণ্টন p (শত টাকায়): ৬০% সাধারণ দিনের অর্ডার, গড় ৯, sd ২; ৪০% উৎসবের, গড় ২৪, sd ৩. Q হলো সব এক-চূড়ার Gaussian. নিচের ব্লক একটি grid-এ integral-কে যোগফল দিয়ে আসন্ন করে, আর scipy.optimize.minimize দিয়ে (m, log s) বদলে দুই দিকের KL আলাদা করে সর্বনিম্ন করে। KL(q ‖ p)-কে তিনটি ভিন্ন শুরু থেকে চালানো হয়েছে।

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
from scipy.optimize import minimize
x = np.linspace(-10, 45, 4001); dx = x[1] - x[0]   # অর্ডারের মূল্য, শত টাকায়
p = 0.6 * norm.pdf(x, 9, 2) + 0.4 * norm.pdf(x, 24, 3)  # দুই চূড়ার লক্ষ্য p
def kl(a, b):                              # grid-এ ∫ a log(a/b) dx
    m = a > 1e-300
    return np.sum(a[m] * (np.log(a[m]) - np.log(b[m] + 1e-300))) * dx
def q_of(t):                               # t = (m, log s)
    return norm.pdf(x, t[0], np.exp(t[1]))
fits = {}
for start in (5.0, 15.0, 28.0):            # KL(q‖p): শুরু বদলালে ফল বদলায়
    r = minimize(lambda t: kl(q_of(t), p), [start, 1.0], method="Nelder-Mead")
    fits[f"KL(q||p), start {start:g}"] = r
r = minimize(lambda t: kl(p, q_of(t)), [15.0, 1.0], method="Nelder-Mead")
fits["KL(p||q)"] = r
for name, r in fits.items():
    print(f"{name:20s} m = {r.x[0]:6.2f}  s = {np.exp(r.x[1]):5.2f}  KL = {r.fun:.3f}")
mean = np.sum(x * p) * dx; sd = np.sqrt(np.sum((x - mean) ** 2 * p) * dx)
print(f"mean and sd of p:    m = {mean:6.2f}  s = {sd:5.2f}")
fig, ax = plt.subplots(figsize=(6, 3.6), layout="constrained")
ax.fill_between(x, p, alpha=0.3, color="gray", label="target p (bimodal)")
for name, st in (("KL(q||p), start 5", "-"), ("KL(q||p), start 28", "-"), ("KL(p||q)", "--")):
    ax.plot(x, q_of(fits[name].x), st, label="q from " + name)
ax.set_xlim(0, 40); ax.set_xlabel("order value (100 taka)"); ax.set_ylabel("density")
ax.set_title("Mode-seeking KL(q||p) vs mass-covering KL(p||q)"); ax.legend(fontsize=8)
plt.show()

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

KL(q||p), start 5    m =   9.02  s =  2.03  KL = 0.508
KL(q||p), start 15   m =   9.02  s =  2.03  KL = 0.508
KL(q||p), start 28   m =  23.92  s =  3.12  KL = 0.910
KL(p||q)             m =  15.00  s =  7.75  KL = 0.523
mean and sd of p:    m =  15.00  s =  7.75

KL(q ‖ p): শুরু ৫ বা ১৫ হলে q বসে বাঁ চূড়ায় (m = ৯.০২, s = ২.০৩), প্রায় হুবহু সাধারণ দিনের component. শুরু ২৮ হলে বসে ডান চূড়ায়। দুটিই local minimum, তাই VI-এর objective-ও non-convex (পর্ব ১২)। KL-এর মানগুলোও অর্থবহ: q একটি component-এর সমান হলে KL ≈ −ln(সেই component-এর ওজন)। −ln ০.৬ = ০.৫১১ আর −ln ০.৪ = ০.৯১৬, ছাপা ০.৫০৮ আর ০.৯১০-এর খুব কাছে। বড় চূড়াটি তাই ভালো local minimum. KL(p ‖ q): q-এর গড় ১৫.০০ আর sd ৭.৭৫, হুবহু p-এর গড় আর sd. Q যখন Gaussian, তখন KL(p ‖ q) কমানো মানে moment মেলানো। ছবিতে সবুজ ড্যাশ রেখার চূড়া দুই চূড়ার মাঝখানে, যেখানে আসল অর্ডার প্রায় নেই। “সাধারণ অর্ডার কত টাকার?” প্রশ্নে এই q বলবে ১,৫০০ টাকা, যা প্রায় কোনো ক্রেতাই খরচ করেন না।

VI কেন KL(q ‖ p) নেয়? কারণ ব্যবহারিক। KL(q ‖ p)-তে প্রত্যাশা q-এর ওপর, আর q আমাদের বানানো, তাই তার থেকে নমুনা তোলা আর গড় নেওয়া সহজ। অংশ ৩ দেখাবে যে এই দিকে অজানা p(x) একটি ধ্রুবক হয়ে আলাদা হয়ে যায়। KL(p ‖ q)-তে প্রত্যাশা posterior-এর ওপর, যা থেকে নমুনা তোলাই আসল সমস্যা। মূল্যটা হলো: VI-এর উত্তর একটি চূড়ায় আটকাতে পারে, আর অনিশ্চয়তা কম দেখায় (অংশ ৪ আর ৫)। Expectation propagation (Thomas Minka, ২০০১) নামের আরেকটি পদ্ধতি স্থানীয়ভাবে KL(p ‖ q)-ধরনের মাপ ব্যবহার করে, তাই তার উত্তর বেশি ছড়ানো হয়।

৩ · ELBO — log evidence = ELBO + KL

KL(q ‖ p(z | x)) সরাসরি হিসাব করা যায় না, কারণ তার ভেতরে অজানা posterior. সংজ্ঞা খুলে p(z | x) = p(x, z) / p(x) বসালে:

KL(q ‖ p(z | x)) = Eq[log q(z)] − Eq[log p(x, z)] + log p(x)

log p(x) z-এর ওপর নির্ভর করে না, তাই প্রত্যাশার বাইরে চলে আসে। সাজিয়ে লিখলে পর্বের কেন্দ্রীয় সমীকরণ:

log p(x) = ELBO(q) + KL(q ‖ p(z | x)), ELBO(q) = Eq[log p(x, z)] − Eq[log q(z)]

বাঁ দিক q-এর ওপর নির্ভর করে না, এটি ডেটা আর model-এর একটি স্থির সংখ্যা। তাই q বদলে ELBO বাড়ালে KL ঠিক ততটাই কমে। ELBO-তে আছে শুধু যৌথ p(x, z) = p(x | z)p(z), যা আমরা লিখতে পারি, আর q, যা আমাদের বানানো। KL ≥ ০ বলে ELBO ≤ log p(x), তাই নাম evidence lower bound. পর্ব ৩৯ একই নিম্নসীমা Jensen's inequality দিয়ে পেয়েছিল। এখানের পথে ফাঁকটির পরিচয়ও পাওয়া যায়: ফাঁক = KL.

ELBO-কে আরেকভাবে লেখা যায়: ELBO = Eq[log p(x, z)] + H(q), যেখানে H(q) হলো q-এর entropy. প্রথম পদ q-কে টানে সেই z-গুলোর দিকে যেখানে যৌথ সম্ভাবনা বেশি, অর্থাৎ চূড়ার দিকে। একা থাকলে q সংকুচিত হয়ে MAP বিন্দুতে বসত। দ্বিতীয় পদ q-কে ছড়াতে উৎসাহ দেয়। দুইয়ের টানাটানিতে q চূড়ার চারপাশে একটি যুক্তিসংগত চওড়ায় থামে।

VI-এর দুই ছবি: পরিবারের মধ্যে সবচেয়ে কাছের q, আর log p(x)-এর ভাগ

বাঁয়ে: Q-এর ভেতরে q*, বাইরে আসল posterior. ডানে: স্থির log p(x) = ELBO + KL

Variational family with the closest q, and log evidence split into ELBO plus KL পরিবার Q-এর ভেতরে খোঁজ Q q1q2 q* optimization আসল posterior q* থেকে আসল posterior: সবচেয়ে কম KL log p(x) স্থির KLELBOELBO KL শুরুর qশেষের q* ELBO বাড়ে = KL কমে
ছবি ৪৮.১ — বাঁয়ে: VI শুধু Q-এর ভেতরে খোঁজে; আসল posterior বাইরে থাকলে সবচেয়ে কাছের q*-ও তার থেকে কিছুটা দূরে থাকে। ডানে: দুটি স্তম্ভের মোট উচ্চতা একই log p(x). q ভালো হলে নীল ELBO বাড়ে আর কমলা KL ঠিক ততটাই কমে।

এবার সংখ্যায়। বাজারঘরের একটি অর্ডার ৩ দিন দেরিতে পৌঁছেছে (x). কোন কুরিয়ার পাঠিয়েছিল, তা রেকর্ডে নেই (লুকানো z). দ্রুত কুরিয়ার অর্ডারের ৭০% নেয়, আর তার ১০% অর্ডার এমন দেরি করে; ধীর কুরিয়ার ৩০% নেয়, তার ৪০% দেরি করে। এখানে posterior হাতেই বের করা যায়, তাই সমীকরণটি হুবহু যাচাই করা যায়।

p(z) = (0.7, 0.3); p(x | z) = (0.1, 0.4); ln স্বাভাবিক log; চার দশমিকে গোল
ধাপ
হিসাব
সংখ্যায়
যোগফল
  1. যৌথ আর evidence. p(x, দ্রুত) = ০.৭ × ০.১ = ০.০৭, p(x, ধীর) = ০.৩ × ০.৪ = ০.১২. যোগ করে p(x) = ০.১৯, আর ln ০.১৯ = −১.৬৬০৭. q যা-ই হোক, এই সংখ্যা বদলাবে না।
  2. Posterior. ০.০৭/০.১৯ = ০.৩৬৮৪ আর ০.১২/০.১৯ = ০.৬৩১৬. Prior-এ দ্রুত কুরিয়ার এগিয়ে ছিল, দেরি দেখে পাল্লা ঘুরেছে।
  3. q = (০.৫, ০.৫)-এ ELBO. প্রত্যাশিত log যৌথ: ০.৫ × ln ০.০৭ + ০.৫ × ln ০.১২ = −১.৩২৯৬ − ১.০৬০১ = −২.৩৮৯৭. Entropy H(q) = ln ২ = ০.৬৯৩১. ELBO = −১.৬৯৬৬, log p(x)-এর নিচে।
  4. একই q-এ KL. ০.৫ × ln(০.৫/০.৩৬৮৪) + ০.৫ × ln(০.৫/০.৬৩১৬) = ০.০৩৫৯. যোগ করলে −১.৬৯৬৬ + ০.০৩৫৯ = −১.৬৬০৭, হুবহু log p(x).
  5. খারাপ q. q = (০.৯, ০.১) ভুল দিকে ঝুঁকে আছে। ELBO নেমে −২.২৮০৩, KL উঠে ০.৬১৯৫, যোগফল আবার −১.৬৬০৭. ELBO-র পতন আর KL-এর বৃদ্ধি সমান।
  6. q = posterior. KL = ০, ELBO = log p(x). পর্ব ৩৯-এর EM-এর E-step ঠিক এটাই করে। VI-তে Q ছোট বলে সাধারণত এখানে পৌঁছানো যায় না, তাই ফাঁকটুকু থেকে যায়।

EM আর VI-এর সম্পর্ক। পর্ব ৩৯-এ θ ছিল একটি সংখ্যা (point estimate), আর লুকানো z-এর posterior হুবহু হিসাব করা যেত, তাই E-step-এ KL শূন্য হতো। VI-তে parameter-ও অনিশ্চিত, তাই সেগুলোও z-এর অংশ, আর posterior হুবহু পাওয়া যায় না। Q-এর ভেতরে KL সবচেয়ে কম করাই তখন সেরা চেষ্টা। দুটির মাঝামাঝি পদ্ধতির নাম variational EM: লুকানো চলকের জন্য VI, parameter-এর জন্য point estimate. Model বাছাইয়ে ELBO-কে প্রায়ই log p(x)-এর বদলে ব্যবহার করা হয়, তবে সতর্কতা দরকার: দুই model-এর ফাঁক (KL) আলাদা হতে পারে, তাই ELBO-র তুলনা আর evidence-এর তুলনা এক জিনিস নয়।

৪ · Mean-field আর CAVI — অজানা গড় আর precision-এর Normal model

Q কতটা বড় হবে, তা-ই VI-এর প্রধান নকশা-সিদ্ধান্ত। সবচেয়ে প্রচলিত পছন্দ mean-field approximation: অজানাগুলো q-এর অধীনে পরস্পর স্বাধীন, q(z) = q1(z1) q2(z2) ⋯ qm(zm). নামটি পদার্থবিদ্যা থেকে এসেছে, যেখানে প্রতিটি কণা বাকিদের প্রভাবকে একটি গড় “ক্ষেত্র” হিসেবে দেখে। এই গঠনের একটি বড় সুবিধা আছে। বাকি সব অংশ স্থির রাখলে একটি অংশের সেরা রূপ সরাসরি লেখা যায়:

log qj*(zj) = E−j[log p(x, z)] + ধ্রুবক

E−j মানে zj ছাড়া বাকি সব অংশের বর্তমান q দিয়ে প্রত্যাশা। প্রতিটি অংশ পালাক্রমে এভাবে হালনাগাদ করাকে বলে CAVI (coordinate ascent variational inference). প্রতিটি ধাপ নিজের অংশে ELBO-কে হুবহু সর্বোচ্চ করে, তাই ELBO কখনো কমে না। এটি পর্ব ১৭-এর lasso-তে দেখা coordinate descent-এর ভাই, শুধু চলকগুলো এখানে পুরো বণ্টন। Conjugate model-এ প্রতিটি qj* চেনা পরিবারের হয়, তাই হালনাগাদ মানে কয়েকটি সংখ্যা বদলানো।

উদাহরণ: অর্ডার-মূল্যের গড় আর ছড়ানো, দুটিই অজানা। বাজারঘরের ১০টি নতুন অর্ডার (হাজার টাকায়) xi ~ N(μ, 1/τ), যেখানে τ = 1/σ² হলো precision. Prior: τ ~ Gamma(a0, b0) (Gamma distribution, b0 হলো rate), আর μ | τ ~ N(μ0, 1/(λ0τ)). এই prior-এর আসল posterior জানা আছে (Normal-Gamma), তাই VI-এর ভুল সরাসরি মাপা যায়। এর ভেতরে μ আর τ নির্ভরশীল: τ বড় হলে (ছড়ানো কম) μ-এর অনিশ্চয়তাও কমে। Mean-field নেয় q(μ, τ) = q(μ)q(τ), আর উপরের সূত্র থেকে আসে (Bishop-এর ২০০৬ সালের বইয়ের অধ্যায় ১০-এর পরিচিত উদাহরণ):

q(μ) = N(mN, 1/λN)

mN = (λ0μ0 + N x̄) / (λ0 + N), আর λN = (λ0 + N)·E[τ]. গড় একবারেই স্থির হয়; শুধু চওড়া নির্ভর করে q(τ)-এর ওপর।

q(τ) = Gamma(aN, bN)

aN = a0 + (N + 1)/2, আর bN = b0 + ½·Eμ[∑(xi − μ)² + λ0(μ − μ0)²]. Eμ[(x − μ)²] = (x − mN)² + 1/λN, তাই এখানে q(μ)-এর চওড়া ঢোকে।

দুটি একে অপরের ওপর নির্ভর করে, তাই পালাক্রমে হালনাগাদ। নিচের ব্লক q(τ)-কে ইচ্ছা করে খারাপ জায়গায় শুরু করায় (E[τ] = ০.১৫, অর্থাৎ sd প্রায় ২.৬ হাজার টাকা), প্রতি পাকে ELBO ছাপে, শেষে আসল log p(x)-এর সঙ্গে ফাঁক মাপে, আর দুই posterior-এর contour আঁকে। ELBO-র পদগুলো Gaussian আর Gamma-র পরিচিত প্রত্যাশা থেকে আসে; E[log τ] = ψ(aN) − log bN, যেখানে ψ হলো digamma.

import numpy as np
import matplotlib.pyplot as plt
from scipy.special import gammaln, digamma
from scipy.stats import norm, gamma
rng = np.random.default_rng(42)
x = rng.normal(1.2, 0.4, 10); N, xb = len(x), x.mean()   # ১০টি অর্ডার, হাজার টাকায়
m0, l0, a0, b0 = 1.0, 1.0, 2.0, 0.2       # prior: μ|τ ~ N(m0, 1/(l0·τ)), τ ~ Gamma(a0, b0)
mN = (l0 * m0 + N * xb) / (l0 + N)         # q(μ)-এর গড় প্রথম ধাপেই স্থির
aN, bN = a0 + (N + 1) / 2, 50.0            # q(τ)-এর শুরু: ইচ্ছা করে খারাপ, E[τ] = 0.15
def elbo(lN, aN, bN):
    Et, Elt = aN / bN, digamma(aN) - np.log(bN)
    S = np.sum((x - mN) ** 2) + N / lN; P = (mN - m0) ** 2 + 1 / lN
    return (0.5 * (N + 1) * (Elt - np.log(2 * np.pi)) + 0.5 * np.log(l0) - 0.5 * Et * (S + l0 * P)
            + a0 * np.log(b0) - gammaln(a0) + (a0 - 1) * Elt - b0 * Et
            + 0.5 * np.log(2 * np.pi * np.e / lN) + aN - np.log(bN) + gammaln(aN) + (1 - aN) * digamma(aN))
for it in range(1, 7):                     # CAVI: পালাক্রমে q(μ), তারপর q(τ)
    lN = (l0 + N) * aN / bN
    bN = b0 + 0.5 * (np.sum((x - mN) ** 2) + N / lN + l0 * ((mN - m0) ** 2 + 1 / lN))
    print(f"iter {it}: E[tau] = {aN / bN:6.3f}  ELBO = {elbo((l0 + N) * aN / bN, aN, bN):.4f}")
lN = (l0 + N) * aN / bN
A, B = a0 + N / 2, b0 + 0.5 * np.sum((x - xb) ** 2) + l0 * N * (xb - m0) ** 2 / (2 * (l0 + N))
logpx = (gammaln(A) - gammaln(a0) + a0 * np.log(b0) - A * np.log(B)
         + 0.5 * np.log(l0 / (l0 + N)) - N / 2 * np.log(2 * np.pi))       # আসল log evidence
print(f"exact log p(x) = {logpx:.4f}   gap (= KL) = {logpx - elbo(lN, aN, bN):.4f}")
print(f"sd of mu : exact {np.sqrt(B / ((l0 + N) * (A - 1))):.4f}  VI {1 / np.sqrt(lN):.4f}")
print(f"sd of tau: exact {np.sqrt(A) / B:.3f}   VI {np.sqrt(aN) / bN:.3f}")
mu, t = np.meshgrid(np.linspace(0.7, 1.45, 200), np.linspace(1, 19, 200))
exact = norm.pdf(mu, mN, 1 / np.sqrt((l0 + N) * t)) * gamma.pdf(t, A, scale=1 / B)
vi = norm.pdf(mu, mN, 1 / np.sqrt(lN)) * gamma.pdf(t, aN, scale=1 / bN)
fig, ax = plt.subplots(figsize=(6, 4), layout="constrained")
lv = exact.max() * np.array([0.1, 0.3, 0.6, 0.9])      # দুটিতে একই উচ্চতার রেখা
ax.contour(mu, t, exact, lv, colors="k"); ax.contour(mu, t, vi, lv, colors="r", linestyles="--")
ax.set_xlabel("mean mu (1000 taka)"); ax.set_ylabel("precision tau")
ax.set_title("Exact posterior (black) vs mean-field q (red dashed)"); plt.show()

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

iter 1: E[tau] =  1.802  ELBO = -11.0578
iter 2: E[tau] =  6.776  ELBO = -5.9109
iter 3: E[tau] =  8.304  ELBO = -5.7547
iter 4: E[tau] =  8.431  ELBO = -5.7537
iter 5: E[tau] =  8.439  ELBO = -5.7537
iter 6: E[tau] =  8.440  ELBO = -5.7537
exact log p(x) = -5.7184   gap (= KL) = 0.0353
sd of mu : exact 0.1121  VI 0.1038
sd of tau: exact 3.190   VI 3.082

ELBO একটানা বাড়ে: −১১.০৬ থেকে −৫.৯১, তারপর −৫.৭৫৪৭, আর চতুর্থ পাক থেকে চার দশমিকে স্থির −৫.৭৫৩৭. E[τ] ০.১৫-এর খারাপ শুরু থেকে ৮.৪৪-এ পৌঁছায়, অর্থাৎ sd প্রায় ১/√৮.৪৪ ≈ ০.৩৪ হাজার টাকা (ডেটা বানানো হয়েছিল sd ০.৪ দিয়ে, মাত্র ১০টি নমুনায়)। ফাঁক: আসল log p(x) = −৫.৭১৮৪, শেষ ELBO তার ০.০৩৫৩ নিচে। অংশ ৩-এর সমীকরণ বলে এই ফাঁকই KL(q* ‖ posterior). আলাদা একটি scratch হিসাবে grid-এ KL সরাসরি integral করে ০.০৩৫৩ পাওয়া গেছে, তাই ELBO-র সূত্রও যাচাই হলো। ফাঁকটি শূন্য নয়, কারণ আসল posterior ভাঙা গুণফল নয়। Sd কম: μ-এর আসল posterior sd ০.১১২১, VI বলে ০.১০৩৮, প্রায় ৭% কম; τ-এর বেলায় ৩.১৯০ বনাম ৩.০৮২. ছবি: কালো contour-গুলো ত্রিভুজের মতো, নিচে চওড়া আর ওপরে সরু: τ ছোট (ছড়ানো বেশি) হলে μ নিয়ে অনিশ্চয়তা বেশি। লাল ড্যাশ contour-গুলো খাড়া-শোয়ানো উপবৃত্ত, কারণ গুণফল q(μ)q(τ) এই নির্ভরতা প্রকাশ করতে পারে না।

কেন অনিশ্চয়তা কম দেখায়। একটি দুই-মাত্রার Gaussian লক্ষ্য নিন, যার দুই চলকের sd σ1, σ2 আর correlation ρ. KL(q ‖ p)-তে mean-field Gaussian-এর গড় ঠিক আসে, কিন্তু প্রতিটি অক্ষের sd হয় 1/√Λii, যেখানে Λ = Σ−1 হলো precision matrix. হিসাব করলে এটি σi√(1 − ρ²), অর্থাৎ শর্তসাপেক্ষ sd, প্রান্তিক sd নয় (পর্ব ১৯-এর শর্তসাপেক্ষ বণ্টন)। ρ = ০.৯ হলে √(১ − ০.৮১) = ০.৪৩৬: VI-এর sd আসলের অর্ধেকেরও কম। Zero-forcing-এর যুক্তিতে q হেলানো উপবৃত্তের বাইরে যেতে পারে না, তাই সে উপবৃত্তের ভেতরের সবচেয়ে বড় অক্ষ-বরাবর উপবৃত্তটি নেয়। পরের অংশের ব্লকে এই সূত্রটি সংখ্যায় মিলে যাবে।

৫ · Black-box VI আর reparameterisation trick

CAVI-এর জন্য প্রতিটি model-এ আলাদা derivation লাগে, আর conjugacy না থাকলে E−j[…] সূত্রে আসে না। বাজারঘরের প্রশ্নটি নিন: ডেলিভারি যত দিন দেরি, ফেরতের সম্ভাবনা কি তত বাড়ে? Model: P(ফেরত) = σ(w0 + w1·দেরি), prior w ~ N(0, 3²I). এখানে ELBO-র প্রত্যাশা Eq[log p(y, w)]-এ sigmoid-এর log আছে, যার Gaussian গড় সূত্রে আসে না। সমাধান: প্রত্যাশাকে Monte Carlo নমুনার গড় দিয়ে আসন্ন করা, আর তার gradient দিয়ে পর্ব ১৩-এর মতো stochastic ধাপ নেওয়া। Model থেকে শুধু log p(x, z) আর তার gradient লাগে, তাই একে বলে black-box VI.

কঠিন অংশ হলো q-এর parameter-এর সাপেক্ষে gradient, কারণ নমুনাগুলো q থেকেই আসে। দুটি পরিচিত কৌশল আছে। Score-function estimator (REINFORCE নামেও পরিচিত) ∇ log q ব্যবহার করে। এটি যেকোনো q-এ চলে, কিন্তু gradient-এর variance খুব বেশি। Reparameterisation trick নমুনাকে একটি নির্দিষ্ট রূপান্তর হিসেবে লেখে: ε ~ N(0, 1) তুলে w = m + s·ε. এখন এলোমেলোতা ε-এ, যা m বা s-এর ওপর নির্ভর করে না। তাই chain rule সরাসরি চলে: ∂/∂m = ∇wlog p, আর ∂/∂s = ∇wlog p · ε. Entropy-র অংশ Gaussian-এ সূত্রে আসে, H = log s + ধ্রুবক, তাই ∂H/∂s = 1/s. নিচের stepper একটি নমুনায় পুরো হিসাব দেখায়।

log p(w) = −(w − 2)²/2 + ধ্রুবক; শুরু m = 0.5, s = 0.8; একটি নমুনা ε = 1.2; ধাপের মাপ 0.1
ধাপ
হিসাব
সংখ্যায়
মানে
  1. নমুনা। ε = ১.২ তুলে w = ০.৫ + ০.৮ × ১.২ = ১.৪৬. একই ε রেখে m বা s বদলালে w মসৃণভাবে বদলায়; এটাই কৌশলের মূল।
  2. লক্ষ্যের ঢাল। log p(w) = −(w − ২)²/২ হলে ঢাল −(w − ২) = ০.৫৪. ধনাত্মক মানে w বাড়ালে log p বাড়ে।
  3. m-এর gradient. ∂w/∂m = ১, তাই ∂/∂m ≈ ০.৫৪. Entropy m-এর ওপর নির্ভর করে না।
  4. s-এর gradient. ∂w/∂s = ε = ১.২, তাই প্রথম পদ ০.৫৪ × ১.২ = ০.৬৪৮. Entropy থেকে আসে ১/s = ১.২৫. মোট ১.৮৯৮.
  5. আসল gradient. সব ε-এর ওপর গড় নিলে m-এ ১.৫ আর s-এ −০.৮ + ১.২৫ = ০.৪৫. এক নমুনার মান (০.৫৪, ১.৮৯৮) আসল থেকে দূরে, কিন্তু গড়ে সঠিক, ঠিক SGD-র mini-batch gradient-এর মতো। তাই কয়েকটি নমুনার গড় আর ছোট হতে থাকা ধাপ লাগে।
  6. এক ধাপ। s ধনাত্মক রাখতে log s-এ ধাপ নেওয়া হয়; সেখানে gradient g·ε·s + ১ = ১.৫১৮৪. নতুন m = ০.৫৫৪, s = e−০.২২৩১ + ০.১৫১৮ = ০.৯৩১. দুটিই সঠিক উত্তর (m = ২, s = ১)-এর দিকে সরেছে।

এবার আসল কাজ। ৪০টি কাল্পনিক অর্ডারের দেরি (০ থেকে ৮ দিন) আর ফেরত (১৯টি ফেরত), q(w) = N(m, diag(s²)), অর্থাৎ mean-field Gaussian. প্রতি ধাপে ১০টি ε, ৮০০টি ধাপ, আর পর্ব ১৪-এর Adam (নিজে লেখা, ছোট হতে থাকা ধাপসহ)। তুলনার জন্য একই posterior-এ ২০,০০০ ধাপের random-walk Metropolis, যার বিস্তারিত পর্ব ৪৭-এর বিষয়; প্রথম ২,০০০টি নমুনা বাদ।

import numpy as np, time
rng = np.random.default_rng(42)            # ৪০টি অর্ডার: ডেলিভারি কত দিন দেরি, ফেরত এল কি না
d = rng.integers(0, 9, 40); y = (rng.random(40) < 1 / (1 + np.exp(-(-2.5 + 0.5 * d)))).astype(float)
X = np.column_stack([np.ones(40), d])
def logp(w):                               # log prior N(0, 3²I) + log likelihood
    z = X @ w; return y @ z - np.logaddexp(0, z).sum() - w @ w / 18
def grad(W):                               # প্রতিটি সারি একটি w-এর জন্য gradient
    return (y - 1 / (1 + np.exp(-W @ X.T))) @ X - W / 9
t0 = time.perf_counter()
m, rho = np.zeros(2), np.full(2, -1.0)     # q(w) = N(m, diag(s²)), s = exp(rho)
gm, gr, vm, vr = (np.zeros(2) for _ in range(4))
for step in range(1, 801):                 # Adam দিয়ে Monte Carlo ELBO বাড়ানো
    eps = rng.standard_normal((10, 2)); s = np.exp(rho)
    G = grad(m + s * eps)                  # reparameterisation: w = m + s·ε
    dm, dr = G.mean(0), (G * eps * s).mean(0) + 1     # +1 আসে entropy-র log s থেকে
    for g, a, v in ((dm, gm, vm), (dr, gr, vr)):
        a[:] = 0.9 * a + 0.1 * g; v[:] = 0.999 * v + 0.001 * g ** 2
    lr = 0.05 / np.sqrt(step / 100 + 1)
    m += lr * (gm / (1 - 0.9 ** step)) / (np.sqrt(vm / (1 - 0.999 ** step)) + 1e-8)
    rho += lr * (gr / (1 - 0.9 ** step)) / (np.sqrt(vr / (1 - 0.999 ** step)) + 1e-8)
t_vi = time.perf_counter() - t0
t0 = time.perf_counter()
w, lw, chain, acc = np.zeros(2), logp(np.zeros(2)), [], 0
for i in range(20000):                     # random-walk Metropolis, একই posterior
    prop = w + 0.25 * rng.standard_normal(2); lp = logp(prop)
    if np.log(rng.random()) < lp - lw: w, lw, acc = prop, lp, acc + 1
    chain.append(w)
C = np.array(chain[2000:]); t_mh = time.perf_counter() - t0
print(f"VI  : mean {m.round(3)} sd {np.exp(rho).round(3)} corr 0 (mean-field)  {t_vi:.2f} s")
print(f"MCMC: mean {C.mean(0).round(3)} sd {C.std(0).round(3)} corr {np.corrcoef(C.T)[0, 1]:.2f}"
      f"  {t_mh:.2f} s, accept {acc / 20000:.2f}")

আসল আউটপুট (ব্রাউজারের Python; সময় আপনার যন্ত্রে আলাদা হবে):

VI  : mean [-2.541  0.581] sd [0.379 0.077] corr 0 (mean-field)  0.13 s
MCMC: mean [-2.698  0.605] sd [0.915 0.191] corr -0.90  0.67 s, accept 0.35

গড়: দুই পদ্ধতির গড় কাছাকাছি: w1 ০.৫৮১ বনাম ০.৬০৫, অর্থাৎ প্রতি দিনের দেরিতে ফেরতের log-odds প্রায় ০.৬ বাড়ে। w0-এ পার্থক্য একটু বেশি (−২.৫৪ বনাম −২.৭০), কারণ posterior পুরো প্রতিসম নয়। Sd: এখানেই বড় পার্থক্য। MCMC-তে w0-এর sd ০.৯১৫, VI-তে ০.৩৭৯; w1-এ ০.১৯১ বনাম ০.০৭৭. কারণ correlation: MCMC দেখায় দুই weight-এর correlation −০.৯০. দেরি সবসময় ধনাত্মক, তাই ঢাল একটু বাড়ালে intercept একটু কমিয়ে প্রায় একই fit পাওয়া যায়। অংশ ৪-এর সূত্রে ০.৯১৫ × √(১ − ০.৯০²) ≈ ০.৩৯৯, VI-এর ০.৩৭৯-এর কাছে। সময়: এই ছোট সমস্যায় দুটিই এক সেকেন্ডের কম (০.১৩ বনাম ০.৬৭ সেকেন্ড), আর এখানে VI প্রায় পাঁচ গুণ দ্রুত। একটি run থেকে সাধারণ সিদ্ধান্ত টানবেন না: বড় ডেটা আর বেশি মাত্রায় পার্থক্য সাধারণত অনেক বড় হয়, কিন্তু কতটা, তা সমস্যা আর বাস্তবায়নের ওপর নির্ভর করে। Acceptance rate ০.৩৫ একটি যুক্তিসংগত মান (পর্ব ৪৭)।

এই ফলের ব্যবসায়িক বিপদ। VI-এর sd দিয়ে w1-এর ৯৫% credible interval মোটামুটি ০.৫৮ ± ২ × ০.০৭৭, অর্থাৎ ০.৪৩ থেকে ০.৭৩. MCMC দিয়ে প্রায় ০.৬০ ± ২ × ০.১৯, অর্থাৎ ০.২২ থেকে ০.৯৮. প্রথমটি দেখে কেউ বলতে পারেন “দেরির প্রভাব নিশ্চিতভাবে বড়”, অথচ ডেটা ততটা নিশ্চয়তা দেয় না। প্রতিকার: (১) feature-কে centre করা (গড় বাদ দেওয়া), যাতে correlation কমে; (২) full-rank Gaussian, q = N(m, LLᵀ), যেখানে L একটি নিম্ন-ত্রিভুজ matrix (পর্ব ১০-এর Cholesky), আর নমুনা w = m + Lε; (৩) গুরুত্বপূর্ণ সিদ্ধান্তের আগে MCMC দিয়ে মিলিয়ে নেওয়া।

৬ · VI বনাম MCMC — কখন কোনটি

দুটি পদ্ধতির লক্ষ্য একই posterior, কিন্তু ভুলের ধরন আলাদা। MCMC-র ভুল আসে সীমিত নমুনা থেকে, আর সময় বাড়ালে কমে। VI-এর ভুল আসে Q-এর আকার থেকে, আর optimization যত ভালোই হোক, Q-এর বাইরের আকার ধরা যায় না। এই পার্থক্যকে বলে variance বনাম bias.

বিষয়Variational inferenceMCMC
মূল কাজoptimization: ELBO বাড়ানোsampling: Markov chain চালানো
সময় বাড়ালেQ-এর সেরা q-তে থামে; ফাঁক থেকে যায়আসল posterior-এর দিকে যায়
সাধারণ ভুলঅনিশ্চয়তা কম দেখানো, নির্ভরতা হারানো, একটি চূড়ায় আটকানোchain যথেষ্ট না মেশা, ধীর হওয়া
বড় ডেটাmini-batch দিয়ে stochastic VI সহজপ্রতি ধাপে সাধারণত সব ডেটা লাগে
অগ্রগতি মাপাELBO-র বক্ররেখা; তবে ফাঁক কত, তা বলে নাR-hat, effective sample size, trace plot
ফলের রূপএকটি সূত্র (গড়, sd), দ্রুত নতুন নমুনাহাজার হাজার নমুনা
ভালো ক্ষেত্রবড় ডেটা, অনেক model দ্রুত তুলনা, topic model, deep learning-এর ভেতরেছোট-মাঝারি ডেটা, নির্ভুল অনিশ্চয়তা দরকার, চূড়ান্ত বিশ্লেষণ

সম্প্রদায়ে এ নিয়ে মতভেদ আছে। অনেক পরিসংখ্যানবিদ চূড়ান্ত বিশ্লেষণে MCMC (বিশেষ করে HMC/NUTS) পছন্দ করেন আর VI-কে দ্রুত খসড়া মনে করেন। বড় মাপের ML-এ, যেমন লাখ লাখ নথির topic model বা neural network-এর ভেতরের লুকানো চলক, MCMC প্রায়ই ব্যবহারিক নয়, তাই VI-ই সাধারণ পছন্দ। মাঝামাঝি একটি অভ্যাস হলো ছোট নমুনায় দুটি চালিয়ে মিলিয়ে দেখা, ঠিক অংশ ৫-এর ব্লকের মতো। VI-এর ফল কতটা বিশ্বাসযোগ্য, তা মাপার কিছু diagnostic-ও প্রস্তাব করা হয়েছে, যেমন Yao ও সহকর্মীদের (২০১৮) Pareto-smoothed importance sampling-ভিত্তিক মাপ।

৭ · VI কোথায় চলে — BayesianGaussianMixture, LDA আর VAE

আপনি হয়তো আগেই VI ব্যবহার করেছেন, না জেনে। scikit-learn-এর BayesianGaussianMixture ওজন, গড় আর covariance-এর ওপর prior বসায় আর mean-field VI দিয়ে fit হয়। গঠনটি পর্ব ৩৯-এর EM-এর মতো পালাক্রমে চলে, শুধু point estimate-এর বদলে প্রতিটি parameter-এর একটি q. গুরুত্বপূর্ণ সেটিং weight_concentration_prior: ছোট মান prior-কে বলে “অল্প কয়েকটি component-এ ভার জমুক”, বড় মান বলে “সবাইকে ভাগ দাও”। weight_concentration_prior_type ডিফল্টে "dirichlet_process" (Dirichlet process-এর stick-breaking রূপ, n_components পর্যন্ত কাটা); বিকল্প "dirichlet_distribution", যা সাধারণ Dirichlet distribution. নিচের ব্লক পর্ব ৩৯-এর তিন দলের কাল্পনিক ক্রেতা-ডেটায় ৮টি component দিয়ে শুরু করে তিনটি সেটিং তুলনা করে। আগের পর্বের মতোই init_params="random_from_data", কারণ ডিফল্ট "kmeans" ব্রাউজারের Python-এ একটি অপ্রাসঙ্গিক warning ছাপে।

import numpy as np
import matplotlib.pyplot as plt
from sklearn.mixture import BayesianGaussianMixture
rng = np.random.default_rng(42)            # পর্ব ৩৯-এর কাল্পনিক ক্রেতা-ডেটা, ৩টি আসল দল
X = np.vstack([rng.multivariate_normal([6, 3], [[4, 1], [1, 1]], 150),
               rng.multivariate_normal([15, 7], [[9, 4], [4, 4]], 150),
               rng.multivariate_normal([28, 4.5], [[30, -6], [-6, 3]], 60)])
fig, ax = plt.subplots(figsize=(6, 3.4), layout="constrained")
for j, (kind, c) in enumerate([("dirichlet_process", 0.001), ("dirichlet_distribution", 0.001),
                               ("dirichlet_distribution", 100.0)]):
    bgm = BayesianGaussianMixture(n_components=8, weight_concentration_prior_type=kind,
                                  weight_concentration_prior=c, init_params="random_from_data",
                                  max_iter=1000, random_state=0).fit(X)
    w = np.sort(bgm.weights_)[::-1]
    print(f"{kind[10:]:12s} prior {c:g}: weights {w.round(3)}")
    print(f"   components above 1%: {(w > 0.01).sum()}, iterations {bgm.n_iter_}")
    ax.bar(np.arange(8) + 0.27 * (j - 1), w, width=0.27, label=f"{kind[10:]}, {c:g}")
ax.set_xlabel("component (sorted by weight)"); ax.set_ylabel("weight")
ax.set_title("BayesianGaussianMixture with 8 components"); ax.legend(fontsize=8)
plt.show()

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

process      prior 0.001: weights [0.428 0.394 0.166 0.003 0.003 0.003 0.003 0.   ]
   components above 1%: 3, iterations 46
distribution prior 0.001: weights [0.426 0.409 0.165 0.    0.    0.    0.    0.   ]
   components above 1%: 3, iterations 37
distribution prior 100: weights [0.135 0.128 0.128 0.125 0.122 0.122 0.122 0.118]
   components above 1%: 8, iterations 116

ছোট prior (০.০০১): দুই ধরনের prior-ই ৮টির মধ্যে ৩টি component রাখে, ওজন প্রায় ০.৪৩, ০.৪০ আর ০.১৭, আসল অনুপাত ০.৪১৭, ০.৪১৭, ০.১৬৭-এর কাছে। বাকি ৫টির ওজন ০.৩%-এর নিচে, কার্যত বন্ধ। এখানে K আলাদা করে বাছাই করতে হয়নি; পর্ব ৩৯-এ একই ডেটায় BIC-ও ৩ বেছেছিল। বড় prior (১০০, Dirichlet distribution): prior জোর দিয়ে সমান ভাগ চায়, তাই ৮টি component-ই প্রায় ১২.৫% করে পায়। আসল দলগুলো টুকরো হয়ে কয়েকটি component-এ ভাগ হয়, আর থামতে ১১৬টি পাক লাগে। শিক্ষা: “Bayesian GMM নিজেই K খুঁজে নেয়” কথাটি অর্ধেক সত্য। উত্তর prior-এর ওপর নির্ভর করে, তাই কয়েকটি মান চালিয়ে দেখা দরকার।

LDA. পর্ব ৪৪-এর scikit-learn LatentDirichletAllocation variational Bayes দিয়ে শেখে (Blei, Ng আর Jordan, ২০০৩-এর মূল প্রবন্ধের পদ্ধতি, আর learning_method="online"-এ Hoffman, Blei আর Bach-এর ২০১০ সালের online রূপ)। প্রতিটি রিভিউয়ের topic-অনুপাত θ-এর জন্য একটি Dirichlet q, আর প্রতিটি শব্দের topic-এর জন্য একটি categorical q, mean-field-এ আলাদা। সেখানে দেখা perplexity এই ELBO থেকেই আসে, তাই সেটি আসন্ন মান। Stochastic VI: Hoffman ও সহকর্মীরা (২০১৩) দেখান যে ELBO-র gradient mini-batch থেকে অনুমান করা যায়, ফলে কোটি নথিতেও VI চলে।

Variational autoencoder (VAE). Kingma আর Welling (২০১৩ সালে প্রকাশিত, ২০১৪-এর ICLR) আর প্রায় একই সময়ে Rezende, Mohamed আর Wierstra (২০১৪) অংশ ৫-এর reparameterisation trick-কে neural network-এর সঙ্গে জোড়েন। প্রতিটি ডেটা-বিন্দুর জন্য আলাদা q-এর parameter optimize না করে একটি network (encoder) x থেকে সরাসরি q(z | x)-এর গড় আর sd দেয়। একে বলে amortized inference. আরেকটি network (decoder) z থেকে x ফিরিয়ে আনে, আর দুটিকে একসঙ্গে ELBO বাড়িয়ে শেখানো হয়। পরের deep learning কোর্স এটি বিস্তারিত দেখাবে। এখানে মনে রাখার কথা: VAE-র loss-এর “reconstruction + KL” দুই অংশ ঠিক অংশ ৩-এর ELBO.

আরও গভীরে: Graphical model-এ variational পদ্ধতির পরিচিত সূচনা-প্রবন্ধ Jordan, Ghahramani, Jaakkola আর Saul-এর (১৯৯৯)। Blei, Kucukelbir আর McAuliffe-এর “Variational Inference: A Review for Statisticians” (২০১৭) CAVI নামটি জনপ্রিয় করে আর VI বনাম MCMC-র ভারসাম্য আলোচনা করে। Black-box VI-এর score-function রূপ Ranganath, Gerrish আর Blei (২০১৪), আর automatic differentiation variational inference (ADVI) Kucukelbir ও সহকর্মীদের (২০১৭)। Mean-field-এর বাইরে যেতে normalizing flow দিয়ে আরও নমনীয় q বানানো হয় (Rezende আর Mohamed, ২০১৫)।

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

  • ELBO-কে log p(x)-এর নিম্নসীমা বলা হয় কেন? log p(x) = ELBO + KL(q ‖ p(z | x)) আর KL ≥ ০.
  • VI কোন KL কমায়, আর তার ফল কী? KL(q ‖ p); zero-forcing, তাই mode-seeking আর অনিশ্চয়তা কম দেখানো।
  • log p(x) = −৫.০, ELBO = −৫.৩ হলে KL? ০.৩.
  • Correlation ০.৮, sd ১-এর দুই-মাত্রার Gaussian-এ mean-field sd? √(১ − ০.৬৪) = ০.৬.
  • Reparameterisation trick কী সমস্যা সমাধান করে? নমুনাকে w = m + s·ε লিখে gradient-কে প্রত্যাশার ভেতরে নেয়, ফলে কম variance-এর Monte Carlo gradient.

৮ · AI-এর যুগে — VI-এর derivation আর কোড যাচাই

VI-এর কোডে ভুল সহজে লুকায়, কারণ একটি ভুল ELBO-ও মসৃণভাবে বাড়তে পারে। AI assistant-এর উত্তরে সাধারণ ভুলগুলো:

AI প্রায়ই যা করেকেন ভুলকীভাবে ধরবেন
KL-এর দিক উল্টে লেখে, বা “VI KL(p ‖ q) কমায়” বলেVI KL(q ‖ p) কমায়; দিক বদলালে ফলের চরিত্র বদলায়অংশ ২-এর মতো দুই চূড়ার লক্ষ্যে দুটি চালিয়ে দেখুন
ELBO-র derivation-এ চিহ্ন বা entropy পদ বাদ দেয়Entropy ছাড়া q সংকুচিত হয়ে MAP-এ বসেছোট discrete উদাহরণে ELBO + KL = log p(x) হুবহু মেলান
VI-এর sd সরাসরি credible interval হিসেবে রিপোর্ট করেMean-field অনিশ্চয়তা কম দেখায়, বিশেষ করে correlation থাকলেছোট নমুনায় MCMC-র সঙ্গে মেলান; full-rank বা centring চেষ্টা করুন
বানানো parameter দেয়, যেমন BayesianGaussianMixture(n_clusters=…) বা prior_type="dp"আসল নাম n_components, weight_concentration_prior_type="dirichlet_process"নিজের scikit-learn version-এর docs পড়ুন, চালিয়ে দেখুন
দাবি করে “Bayesian GMM নিজেই সঠিক K খুঁজে দেয়”ফল weight_concentration_prior-এর ওপর নির্ভর করেকয়েকটি prior আর seed-এ চালিয়ে ওজন তুলনা করুন
ভিন্ন model-এর ELBO তুলনা করে বলে “এটির evidence বেশি”প্রতিটি model-এর ফাঁক (KL) আলাদা হতে পারেসম্ভব হলে আসল evidence, held-out likelihood বা MCMC দিয়ে যাচাই
Reparameterisation-এ s-এর বদলে variance-কে সরাসরি optimize করেধাপে ঋণাত্মক হয়ে NaN আসেlog s-এ ধাপ নিন; gradient finite difference দিয়ে মিলিয়ে নিন

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

আমি বাজারঘরের ডেলিভারি-দেরি আর ফেরতের ডেটায় একটি Bayesian logistic regression-এর posterior variational inference দিয়ে আনুমানিক করতে চাই। ১. log p(x) = ELBO + KL(q ‖ p(z | x))-এর derivation ধাপে ধাপে লেখো, প্রতিটি ধাপে কোন নিয়ম ব্যবহার করলে তা বলো। ২. NumPy-তে reparameterisation trick দিয়ে mean-field Gaussian q-এর একটি ছোট black-box VI লেখো, আর ELBO-র gradient-কে finite difference দিয়ে যাচাই করার কোডও দাও। ৩. একই posterior-এ একটি ছোট random-walk Metropolis লেখো, আর গড়, sd আর correlation পাশাপাশি তুলনা করার কোড দাও। ৪. কোন অবস্থায় mean-field অনিশ্চয়তা কম দেখাবে, আর full-rank Gaussian কীভাবে লিখব, বলো। ৫. তোমার উত্তরের কোন অংশে ভুল হওয়ার সম্ভাবনা বেশি, সেটিও জানাও।

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

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

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

প্র ০১Variational পদ্ধতি পদার্থবিদ্যা থেকে ML-এ এল কীভাবে, আর ১৯৯০-এর দশকে কেন এটি দরকার হয়ে পড়ল?

Mean-field ধারণার শিকড় পরিসংখ্যানিক পদার্থবিদ্যায়। চুম্বকের মতো ব্যবস্থায় লক্ষ লক্ষ কণা একে অপরকে প্রভাবিত করে, আর সব কণার যৌথ বণ্টন হিসাব করা অসম্ভব। পদার্থবিদরা প্রতিটি কণাকে আলাদা করে দেখেন, বাকিদের প্রভাবকে একটি গড় “ক্ষেত্র” হিসেবে ধরে। আধুনিক ভাষায় এটি যৌথ বণ্টনকে একটি গুণফল দিয়ে আসন্ন করা, আর free energy নামের একটি রাশি সর্বনিম্ন করা। এই free energy ঋণাত্মক ELBO-র সঙ্গে গাণিতিকভাবে সম্পর্কিত।

নিউরাল নেটওয়ার্কে এই ধারণা ১৯৮০-এর দশকের শেষে আসে। Peterson আর Anderson (১৯৮৭) Boltzmann machine শেখাতে mean-field theory ব্যবহার করেন, কারণ সেখানে নমুনা তোলা খুব ধীর ছিল। ১৯৯০-এর দশকে graphical model জনপ্রিয় হয়। Bayesian network আর লুকানো চলকের বড় model-এ হুবহু inference অনেক ক্ষেত্রে computationally অসম্ভব হয়ে দাঁড়ায়, বিশেষ করে যখন গ্রাফে অনেক সংযোগ থাকে। Hinton আর van Camp (১৯৯৩) neural network-এর weight-এর ওপর একটি Gaussian q রেখে description length-এর যুক্তিতে একধরনের VI প্রস্তাব করেন। Jordan, Ghahramani, Jaakkola আর Saul-এর ১৯৯৯ সালের পরিচিতিমূলক প্রবন্ধ এই ধারণাগুলোকে graphical model-এর একটি সাধারণ কাঠামোয় আনে।

সময়টা গুরুত্বপূর্ণ ছিল। ডেটা বাড়ছিল, আর MCMC-র তখনকার রূপগুলো বড় model-এ ধীর ছিল। VI সমস্যাকে optimization-এ বদলায়, আর optimization-এর জন্য তখন অনেক পরিচিত যন্ত্র ছিল। ২০০৩ সালে LDA-র মূল প্রবন্ধে variational EM ব্যবহার করা হয়। ২০১০-এর দশকে stochastic VI আর reparameterisation trick এটিকে কোটি ডেটা-বিন্দু আর neural network-এ নিয়ে যায়।

তবে পদার্থবিদ্যার উত্তরাধিকারের একটি সীমাও আছে। Mean-field-এর নির্ভরতা-হারানো দুর্বলতা পদার্থবিদ্যাতেও জানা ছিল: শক্তিশালী পারস্পরিক প্রভাবের ব্যবস্থায় এটি ভুল উত্তর দিতে পারে। ML-এ এটিই অনিশ্চয়তা কম দেখানো হিসেবে ফিরে আসে।

মূল উপলব্ধি: VI এসেছে একটি পুরোনো কৌশল থেকে, যার ভিত্তি “কঠিন যৌথ বণ্টনের বদলে সহজ গুণফল”; বড় model আর বড় ডেটায় optimization-এর গতি একে জনপ্রিয় করেছে, আর নির্ভরতা হারানোর সীমাটিও একই উৎস থেকে এসেছে।

প্র ০২VI-এর KL(q ‖ p) “ভুল দিকের” KL বলে কেউ কেউ সমালোচনা করেন। তাহলে কেন এই দিকটিই প্রচলিত, আর অন্য দিকের পদ্ধতিগুলো কী দেয়?

পরিসংখ্যানের স্বাভাবিক লক্ষ্য হলো আসল posterior p-এর প্রত্যাশা, যেমন একটি parameter-এর গড় বা কোনো ঘটনার সম্ভাবনা, সঠিকভাবে পাওয়া। KL(p ‖ q) কমালে q, p-এর ভর ঢাকে আর moment মেলায় (অংশ ২-এর ব্লকে q-এর গড় আর sd হুবহু p-এর)। এই অর্থে KL(p ‖ q) “সঠিক দিক”। সমস্যা হলো, এটি কমাতে p-এর ওপর প্রত্যাশা লাগে, যা ঠিক সেই কঠিন কাজ যা আমরা এড়াতে চাই।

KL(q ‖ p)-তে প্রত্যাশা q-এর ওপর। q আমাদের বানানো, তাই তার থেকে নমুনা তোলা বা সূত্রে প্রত্যাশা নেওয়া সহজ। আর p(z | x)-এর অজানা হর p(x) একটি ধ্রুবক হয়ে বেরিয়ে যায়। এই দুই সুবিধা ছাড়া VI চলতই না। এর মূল্য zero-forcing: q চূড়া বেছে নেয়, লেজ কাটে, অনিশ্চয়তা কম দেখায়।

অন্য দিকের পথও আছে। Thomas Minka-র expectation propagation (২০০১) posterior-কে কয়েকটি অংশের গুণফল হিসেবে দেখে, প্রতিটি অংশকে স্থানীয়ভাবে KL(p ‖ q)-ধরনের moment মিলিয়ে হালনাগাদ করে। ফল সাধারণত বেশি ছড়ানো, যা অনেক ক্ষেত্রে বাস্তব অনিশ্চয়তার কাছে। কিন্তু EP-র convergence-এর নিশ্চয়তা দুর্বল, আর এটি সবসময় থামে না। আরেকটি পরিবার α-divergence আর Rényi divergence, যেখানে একটি সংখ্যা α বদলে দুই দিকের মাঝামাঝি আচরণ বেছে নেওয়া যায় (যেমন Li আর Turner, ২০১৬)। আবার importance-weighted ELBO (Burda ও সহকর্মীরা, ২০১৬) কয়েকটি নমুনা মিলিয়ে log p(x)-এর আরও কাছের নিম্নসীমা দেয়।

ব্যবহারিক সিদ্ধান্ত লক্ষ্যের ওপর নির্ভর করে। শুধু একটি ভালো অনুমান চাইলে, বা দ্রুত অনেক model তুলনা করতে হলে, mode-seeking দোষের নয়। নির্ভুল ঝুঁকির হিসাব দরকার হলে, যেমন ফেরতের সম্ভাবনার ওপর ভিত্তি করে বীমা বা বড় স্টক-সিদ্ধান্ত, অনিশ্চয়তা কম দেখানো বিপজ্জনক। তখন MCMC বা মাঝামাঝি পদ্ধতি দিয়ে যাচাই জরুরি।

মূল উপলব্ধি: KL(q ‖ p) গাণিতিকভাবে “সুবিধাজনক দিক”, পরিসংখ্যানিকভাবে “আদর্শ দিক” নয়; এর দুর্বলতা জেনে, লক্ষ্য অনুযায়ী অন্য divergence বা MCMC দিয়ে যাচাই করাই পরিণত অভ্যাস।

প্র ০৩Reparameterisation trick কেন score-function estimator-এর চেয়ে সাধারণত ভালো, আর কখন এটি ব্যবহার করা যায় না?

দুটি estimator-ই একই gradient-এর নিরপেক্ষ অনুমান দেয়, অর্থাৎ গড়ে সঠিক। পার্থক্য variance-এ। Score-function estimator লেখে ∇Eq[f(z)] = Eq[f(z)·∇log q(z)]. এখানে f-এর মানটি একটি গুণক হিসেবে আসে, তার ঢাল নয়। f-এর মান বড় হলে, যেমন অনেক ডেটা-বিন্দুর log-likelihood-এর যোগফল, প্রতিটি নমুনার gradient বিশাল হয় আর চিহ্ন এদিক-ওদিক ঘোরে। এর পদ্ধতি Ronald Williams-এর ১৯৯২ সালের REINFORCE অ্যালগরিদমের সঙ্গে সম্পর্কিত, যা reinforcement learning-এ পরিচিত।

Reparameterisation trick f-এর ঢাল ব্যবহার করে। w = m + s·ε লিখলে ∇mf = f′(w), যা লক্ষ্যের আকার সম্পর্কে সরাসরি তথ্য দেয়। অংশ ৫-এর stepper-এ এক নমুনার gradient ছিল ০.৫৪, আসল ১.৫; দূরে, কিন্তু চিহ্ন ঠিক। অনুশীলনে reparameterisation gradient-এর variance প্রায়ই কয়েক ক্রম কম পাওয়া যায়, যদিও এটি কোনো নিশ্চয়তা নয়; কিছু বিশেষ ক্ষেত্রে উল্টোও হতে পারে। তাই একই ফলের জন্য অনেক কম নমুনা লাগে, আর optimization স্থির থাকে। Ranganath ও সহকর্মীদের ২০১৪ সালের black-box VI score-function পথ নিয়েছিল আর variance কমাতে control variate ও Rao-Blackwellisation-এর মতো কৌশল যোগ করেছিল।

সীমা দুটি। প্রথমত, f-কে z-এর সাপেক্ষে differentiable হতে হবে। Discrete লুকানো চলক, যেমন GMM-এর দল-পরিচয় বা LDA-র শব্দের topic, ধারাবাহিক রূপান্তর দিয়ে লেখা যায় না। সেখানে score-function, বা Gumbel-softmax-এর মতো ধারাবাহিক আসন্ন রূপ (Jang ও সহকর্মীরা, আর Maddison ও সহকর্মীরা, ২০১৭) ব্যবহার করা হয়। দ্বিতীয়ত, q-কে একটি সহজ রূপান্তর দিয়ে লিখতে পারতে হবে। Gaussian-এ এটি সহজ, Gamma বা Dirichlet-এ সরাসরি নয়; সেখানে বিশেষ কৌশল (যেমন implicit reparameterisation) লাগে।

আধুনিক probabilistic programming library-গুলো প্রায়ই parameter-কে প্রথমে সীমাহীন বাস্তব সংখ্যায় রূপান্তর করে (যেমন ধনাত্মক σ-এর বদলে log σ, ঠিক যেমন অংশ ৫-এর ব্লকে rho), তারপর সেখানে Gaussian q বসিয়ে reparameterisation চালায়। ADVI-র মূল ধারণা এটাই।

মূল উপলব্ধি: Reparameterisation এলোমেলোতাকে parameter থেকে আলাদা করে লক্ষ্যের ঢাল কাজে লাগায়, তাই কম variance; কিন্তু তার জন্য ধারাবাহিক, differentiable চলক আর রূপান্তরযোগ্য q দরকার।

প্র ০৪ELBO বাড়ছে, তবু VI-এর ফল ভুল হতে পারে কেন? VI-এর ফল কতটা বিশ্বাসযোগ্য, তা কীভাবে যাচাই করবেন?

ELBO-র বৃদ্ধি শুধু বলে যে Q-এর ভেতরে q ভালো হচ্ছে। ফাঁক KL কত, তা বলে না, কারণ log p(x) সাধারণত অজানা। অংশ ৪-এ আসল log p(x) জানা ছিল বলে ফাঁক ০.০৩৫৩ মাপা গেছে; বাস্তব model-এ এই সুযোগ প্রায় থাকে না। ভুলের উৎস কয়েকটি। (১) Q খুব ছোট: mean-field correlation হারায় (অংশ ৫-এ sd অর্ধেকেরও কম)। (২) Local optimum: দুই চূড়ার posterior-এ q একটি চূড়ায় বসে (অংশ ২-এ শুরু ২৮ থেকে ডান চূড়া)। (৩) Stochastic optimization থামেনি: Monte Carlo ELBO কোলাহলপূর্ণ, তাই “স্থির হয়েছে” মনে হলেও এখনো সরছে। (৪) কোডের ভুল: ভুল চিহ্নের entropy-ও একটি মসৃণ বক্ররেখা দিতে পারে।

যাচাইয়ের কয়েকটি স্তর আছে। প্রথম স্তর, সিন্থেটিক ডেটা: জানা parameter দিয়ে ডেটা বানিয়ে দেখুন posterior তাদের ঢাকে কি না, অনেকবার করলে coverage মাপা যায় (simulation-based calibration-এর ধারণা, Talts ও সহকর্মীরা, ২০১৮)। দ্বিতীয় স্তর, ছোট নমুনায় MCMC-র সঙ্গে তুলনা, যেমন অংশ ৫-এর ব্লক। তৃতীয় স্তর, importance sampling-ভিত্তিক diagnostic: q থেকে নমুনা তুলে p(x, z)/q(z) ওজনের বণ্টন দেখা। ওজন খুব অসমান হলে q-এর লেজ p-এর লেজের চেয়ে হালকা। Yao, Vehtari, Simpson আর Gelman (২০১৮) এই ওজনের লেজে একটি Pareto বণ্টন বসিয়ে k̂ নামের মাপ প্রস্তাব করেন; তাঁদের পরামর্শ অনুযায়ী k̂ মোটামুটি ০.৭-এর বেশি হলে VI-এর ফল অবিশ্বস্ত।

চতুর্থ স্তর, সিদ্ধান্তের প্রতি সংবেদনশীলতা: VI আর MCMC দুটির ফলে বাজারঘরের সিদ্ধান্ত (যেমন কোন কুরিয়ার বাদ দেবেন) কি বদলায়? না বদলালে VI-এর দ্রুততা নিরাপদে কাজে লাগানো যায়। এ ছাড়া কয়েকটি seed আর শুরুতে চালানো, ELBO-র বক্ররেখা চলমান গড় দিয়ে দেখা, আর gradient-কে finite difference দিয়ে মিলিয়ে নেওয়া মৌলিক অভ্যাস।

মূল উপলব্ধি: ELBO-র বৃদ্ধি optimization-এর অগ্রগতি মাপে, approximation-এর মান নয়; সিন্থেটিক ডেটা, MCMC-র সঙ্গে তুলনা আর importance-weight diagnostic দিয়ে সেই মান আলাদা করে যাচাই করতে হয়।

প্র ০৫বাজারঘর প্রতি রাতে লাখ লাখ অর্ডারে একটি Bayesian model হালনাগাদ করতে চায়। VI আর MCMC-র মধ্যে কীভাবে বাছবেন, আর প্রোডাকশনে কী কী নজরে রাখবেন?

প্রথম প্রশ্ন: ফলটা কী কাজে লাগবে? যদি লক্ষ্য একটি ভালো point prediction, যেমন কোন পণ্য কত বিক্রি হবে, তাহলে VI-এর গড় সাধারণত যথেষ্ট, আর তার গতি বড় সুবিধা। যদি লক্ষ্য ঝুঁকির হিসাব, যেমন “ফেরতের হার ১০%-এর বেশি হওয়ার সম্ভাবনা কত”, তাহলে লেজের সম্ভাবনা গুরুত্বপূর্ণ। সেখানে mean-field-এর কম দেখানো অনিশ্চয়তা ভুল সিদ্ধান্তে নিতে পারে।

দ্বিতীয় প্রশ্ন: ডেটার আকার আর সময়ের বাজেট। লাখ লাখ অর্ডারে প্রতি রাতে পূর্ণ MCMC চালানো প্রায়ই ব্যবহারিক নয়, কারণ প্রতি ধাপে সব ডেটা লাগে। Stochastic VI mini-batch নিয়ে চলে, আর আগের রাতের q থেকে শুরু করা যায় (warm start). একটি মাঝামাঝি নকশা: প্রতি রাতে VI, আর সপ্তাহে একবার একটি নমুনায় MCMC চালিয়ে VI-এর গড়, sd আর গুরুত্বপূর্ণ সম্ভাবনা মিলিয়ে দেখা। পার্থক্য একটি সীমা ছাড়ালে সতর্কবার্তা।

তৃতীয়: Q-এর নকশা। Feature centre আর scale করুন, যাতে correlation কমে আর mean-field কম ক্ষতি করে। দরকার হলে গুরুত্বপূর্ণ parameter-গুলোর জন্য full-rank অংশ রাখুন। চতুর্থ: নজরদারি। প্রতি রাতের ELBO, তার পরিবর্তন, আর optimization কতটা থেমেছে তা লগ করুন। ELBO হঠাৎ লাফালে ডেটায় পরিবর্তন বা কোডে সমস্যা থাকতে পারে। ডেটার বণ্টন বদলালে, যেমন উৎসবের মৌসুমে, posterior-ও বদলায়; এ নিয়ে পর্ব ৫৯ (Distribution shift) আর পর্ব ৬৯ (ML system-এর test আর নজরদারি) বিস্তারিত বলবে।

পঞ্চম: পুনরুৎপাদনযোগ্যতা। Stochastic VI-এর ফল seed-নির্ভর। Seed, library version আর optimizer সেটিং লিখে রাখুন (পর্ব ৪)। আর ব্যবসার কাছে রিপোর্টে স্পষ্ট লিখুন যে sd বা interval একটি approximation থেকে এসেছে, আর শেষ MCMC যাচাই কবে হয়েছিল।

মূল উপলব্ধি: বাছাই নির্ভর করে লক্ষ্য (গড় না লেজ), ডেটার আকার আর সময়ের বাজেটের ওপর; প্রোডাকশনে VI-এর গতি আর MCMC-র নির্ভুলতা মিলিয়ে নিয়মিত যাচাইয়ের একটি নকশাই সবচেয়ে টেকসই।

অনুশীলন

হিসাবগুলো খাতায় করুন, তারপর উত্তর মেলান। একটির জন্য কোড চালানো ঐচ্ছিক।

  1. হাতের হিসাব: (ক) একটি model-এ log p(x) = −৪.২. একটি q-এর ELBO = −৪.৫, আরেকটির −৪.৩. প্রতিটির KL(q ‖ posterior) কত, আর কোন q ভালো? (খ) একটি দুই-মাত্রার Gaussian posterior-এর দুই চলকের sd ১ আর ২, correlation ০.৮. Mean-field VI প্রতিটি চলকের কত sd দেবে, আর KL(p ‖ q) কমানো একটি গুণফল-Gaussian কত দেবে?

    (ক) KL = log p(x) − ELBO. প্রথম q-এ −৪.২ − (−৪.৫) = ০.৩, দ্বিতীয়টিতে ০.১. দ্বিতীয় q ভালো, কারণ ELBO বেশি মানেই KL কম। (খ) Mean-field (KL(q ‖ p)) দেয় শর্তসাপেক্ষ sd, σi√(১ − ρ²). √(১ − ০.৬৪) = ০.৬, তাই sd ০.৬ আর ১.২. KL(p ‖ q) moment মেলায়, তাই প্রান্তিক sd ১ আর ২ হুবহু আসে। প্রথমটি অনিশ্চয়তা ৪০% কম দেখায়, দ্বিতীয়টি sd ঠিক রাখে কিন্তু correlation-এর তথ্য দুটিই হারায়।

  2. সমালোচনা ও নকশা: একজন বিশ্লেষক বাজারঘরের ২০টি জেলার ফেরতের হারের একটি Bayesian model mean-field VI দিয়ে fit করলেন। ELBO-র বক্ররেখা ২০০ ধাপে সমতল দেখে থামালেন, আর q-এর গড় ± ২sd-কে ৯৫% credible interval বলে রিপোর্ট দিলেন: “৩টি জেলার ফেরতের হার নিশ্চিতভাবে গড়ের বেশি, ওই জেলায় ক্যাশ-অন-ডেলিভারি বন্ধ হোক।” কোথায় কোথায় সমস্যা, আর কীভাবে নতুন করে সাজাবেন?

    সমস্যা: (১) Mean-field interval সাধারণত সরু, বিশেষ করে জেলাগুলোর parameter যখন একটি সাধারণ গড়ের মাধ্যমে পরস্পর-নির্ভর। “নিশ্চিতভাবে” দাবিটি তাই দুর্বল ভিত্তির ওপর। (২) Stochastic ELBO কোলাহলপূর্ণ; ২০০ ধাপে “সমতল” চোখে দেখা যথেষ্ট নয়। (৩) একটি seed, একটি শুরু: local optimum বাদ দেওয়া হয়নি। (৪) MCMC বা অন্য কোনো যাচাই নেই। (৫) ২০টি জেলায় একসঙ্গে খোঁজায় কয়েকটি জেলা কাকতালীয়ভাবে বেশি দেখাতে পারে (পর্ব ২২-এর একাধিক তুলনার সমস্যা)। (৬) একটি নীতিগত সিদ্ধান্ত, যা ওই জেলার গ্রাহকদের সরাসরি প্রভাবিত করে, শুধু একটি approximation-এর ওপর।

    নতুন নকশা: একই model MCMC (যেমন NUTS) দিয়ে চালিয়ে interval তুলনা; ELBO-র চলমান গড় আর কয়েকটি seed; দরকার হলে full-rank বা low-rank q. সিদ্ধান্তের আগে প্রতিটি জেলায় posterior সম্ভাবনা P(হার > গড় + নির্দিষ্ট মাত্রা) হিসাব, আর সেই জেলায় ফেরতের কারণ (কুরিয়ার, পণ্য) আলাদা করে দেখা। নীতি বদলানোর আগে ছোট পরীক্ষা (পর্ব ৬২-এর A/B test-এর ধারণা)।

  3. আউটপুট অনুমান করুন: চালানোর আগে লিখে ফেলুন নিচের কোড কী ছাপবে।
    import numpy as np
    p = np.array([0.5, 0.5])
    q = np.array([0.9, 0.1])
    print(round(np.sum(q * np.log(q / p)), 3), round(np.sum(p * np.log(p / q)), 3))
    m, s = np.array([1.0, -2.0]), np.array([0.5, 2.0])
    eps = np.array([[1.0, 0.0], [-2.0, 0.5]])
    print(m + s * eps)
    0.368 0.511
    [[ 1.5 -2. ]
     [ 0.  -1. ]]

    KL(q ‖ p) = ০.৯ ln(০.৯/০.৫) + ০.১ ln(০.১/০.৫) = ০.৫২৯০ − ০.১৬০৯ = ০.৩৬৮. KL(p ‖ q) = ০.৫ ln(০.৫/০.৯) + ০.৫ ln(০.৫/০.১) = −০.২৯৩৯ + ০.৮০৪৭ = ০.৫১১. দুই দিক আলাদা, কারণ KL প্রতিসম নয়। দ্বিতীয় লাইন reparameterisation: broadcasting-এ প্রতিটি সারি একটি নমুনা। প্রথম সারি (১ + ০.৫ × ১, −২ + ২ × ০) = (১.৫, −২), দ্বিতীয় সারি (১ + ০.৫ × −২, −২ + ২ × ০.৫) = (০, −১)। ছাপা ফরম্যাট একটি ডেস্কটপ run-এ যাচাই করা।

  4. CAVI-এর একটি পাক হাতে: চারটি অর্ডার (হাজার টাকায়) x = (১.০, ১.৪, ০.৮, ১.২)। Prior μ0 = ১, λ0 = ১, a0 = ২, b0 = ০.২. বর্তমান q(τ)-এ E[τ] = ৫. অংশ ৪-এর সূত্র দিয়ে mN, λN, aN, bN আর নতুন E[τ] বের করুন। (ঐচ্ছিক: অংশ ৪-এর ব্লকে x বদলে মিলিয়ে নিন।)

    x̄ = ৪.৪/৪ = ১.১. mN = (১ × ১ + ৪ × ১.১)/(১ + ৪) = ৫.৪/৫ = ১.০৮. λN = ৫ × ৫ = ২৫, অর্থাৎ q(μ)-এর sd ০.২. aN = ২ + ৫/২ = ৪.৫. bN-এর জন্য: ∑(x − ১.০৮)² = ০.০০৬৪ + ০.১০২৪ + ০.০৭৮৪ + ০.০১৪৪ = ০.২০১৬; N/λN = ০.১৬; λ0((mN − μ0)² + ১/λN) = ০.০০৬৪ + ০.০৪ = ০.০৪৬৪. মোট ০.৪০৮, অর্ধেক ০.২০৪, তাই bN = ০.২ + ০.২০৪ = ০.৪০৪. নতুন E[τ] = ৪.৫/০.৪০৪ ≈ ১১.১৪, আর পরের পাকে λN = ৫ × ১১.১৪ ≈ ৫৫.৭, sd ≈ ০.১৩৪. খারাপ শুরু (E[τ] = ৫) এক পাকেই ডেটার ছড়ানোর দিকে সরেছে।

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

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

ক্রমReparameterisation trick দিয়ে black-box VI-এর একটি পূর্ণ চক্র সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. Model-এর log p(x, z) আর তার gradient লেখা, আর variational family বাছাই
  2. q-এর parameter (m, log s) শুরুর মানে বসানো
  3. কয়েকটি ε ~ N(0, 1) তুলে z = m + s·ε বানানো
  4. প্রতিটি নমুনায় gradient হিসাব করে গড় নেওয়া, entropy-র অংশ যোগ করা
  5. Adam বা SGD দিয়ে m আর log s-এ একটি ধাপ নেওয়া, তারপর আবার নমুনা
  6. ELBO স্থির হলে থেমে ছোট নমুনায় MCMC-র সঙ্গে ফল মিলিয়ে দেখা

একটি model-এ log p(x) = −10.0 আর একটি variational বণ্টন q-এর ELBO = −10.4. KL(q ‖ p(z | x)) কত?

  • −0.4
  • 0.4
  • 20.4
  • 0

দুই চূড়ার একটি লক্ষ্য p-কে একটি এক-চূড়ার Gaussian q দিয়ে আসন্ন করা হলো। KL(q ‖ p) কমালে সাধারণত কী পাওয়া যায়?

  • দুই চূড়ার মাঝখানে বসা একটি চওড়া Gaussian
  • দুই চূড়াই হুবহু ধরা একটি বণ্টন
  • একটি চূড়ার ওপর বসা Gaussian, কোনটি তা শুরুর ওপর নির্ভর করে
  • প্রায় সমান ছড়ানো একটি খুব চওড়া বণ্টন

একটি দুই-মাত্রার Gaussian posterior-এর দুই চলকের sd 2 আর correlation 0.6. Mean-field VI (KL(q ‖ p)) প্রথম চলকের কত sd দেবে?

  • 1.6
  • 2
  • 1.2
  • 1.28

q = N(m, s²) থেকে Monte Carlo দিয়ে ELBO-র gradient নিতে reparameterisation trick কী করে?

  • ELBO-র integral-কে বন্ধ সূত্রে বদলে দেয়
  • নমুনা ছাড়াই হুবহু gradient দেয়
  • posterior-এর হর p(x) হিসাব করে
  • নমুনাকে z = m + s·ε লিখে এলোমেলোতাকে parameter থেকে আলাদা করে

Variational inference আর MCMC সম্পর্কে কোন বক্তব্যটি সঠিক?

  • যথেষ্ট সময় দিলে VI সবসময় আসল posterior-এ পৌঁছায়
  • VI-এর ভুল মূলত পরিবারের সীমা থেকে, MCMC-র ভুল সীমিত নমুনা থেকে
  • Mean-field VI সাধারণত posterior-এর অনিশ্চয়তা বাড়িয়ে দেখায়
  • ELBO-র মান দেখেই approximation কতটা নির্ভুল তা বলা যায়

scikit-learn-এর BayesianGaussianMixture-এ n_components = 8 দিয়ে তিন দলের ডেটায় fit করলে কোন সেটিং অপ্রয়োজনীয় component-এর ওজন প্রায় শূন্যে নামাতে সবচেয়ে বেশি সাহায্য করে?

  • weight_concentration_prior-এর খুব বড় মান, যেমন 100
  • max_iter কমিয়ে 10 করা
  • weight_concentration_prior-এর ছোট মান, যেমন 0.001
  • covariance_type="spherical"

Mean-field CAVI সম্পর্কে কোনটি সঠিক?

  • প্রতিটি অংশ পালাক্রমে হালনাগাদ হয়, আর ELBO কখনো কমে না
  • প্রতিটি ধাপে একটি learning rate বাছতে হয়
  • CAVI posterior-এর correlation হুবহু ধরে রাখে
  • CAVI সবসময় global optimum-এ পৌঁছায়

শব্দকোষ

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

মনে রাখুন

  1. VI = inference-কে optimization বানানো: q* = arg minq ∈ Q KL(q ‖ p(z | x)); Q-এর বাইরের আকার ধরা যায় না।
  2. KL(q ‖ p) mode-seeking (zero-forcing, এক চূড়া, অনিশ্চয়তা কম); KL(p ‖ q) mass-covering (moment মেলানো, ফাঁকা জায়গাতেও ভর)।
  3. log p(x) = ELBO + KL; ELBO = Eq[log p(x, z)] + H(q). ELBO বাড়ানো মানে KL কমানো, p(x) না জেনেই।
  4. Mean-field CAVI: log qj* = E−j[log p(x, z)] + ধ্রুবক, পালাক্রমে; ELBO কমে না, কিন্তু নির্ভরতা হারায়। Correlation ρ থাকলে sd হয় σ√(১ − ρ²).
  5. Reparameterisation: z = m + s·ε দিয়ে Monte Carlo gradient, যেকোনো differentiable model-এ VI; ফল MCMC-র সঙ্গে মিলিয়ে নিন। BayesianGaussianMixture, LDA আর VAE এই পদ্ধতিতে শেখে।

পরের পর্বে: পর্ব ৪৯ (Bayesian optimization — অল্প চেষ্টায় hyperparameter খোঁজা) Bayesian চিন্তাকে আরেকটি কাজে লাগাবে: একটি দামি function, যেমন model-এর cross-validation score, কয়েকবার মেপে Gaussian process দিয়ে তার অনিশ্চয়তা আঁকা, আর acquisition function দিয়ে পরের চেষ্টার জায়গা বাছা।

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

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