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

Generative modeling-এর মানচিত্র — likelihood, latent আর sampling

A Map of Generative Modeling — Likelihood, Latent Variables and Sampling, Measured in Bits per Dimension

Taught in Bangla, this lesson opens the generative-model module by contrasting p(x) with p(y | x) and mapping autoregressive models, flows, VAEs, GANs, diffusion and energy-based models by how they handle likelihood. It shows numerically why maximum likelihood equals minimising KL divergence, samples with the chain rule in NumPy, converts test log-likelihood to bits per dimension, and compares four models on binarised Fashion-MNIST and MNIST in a real run, with in-page Python.

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

  • Generative আর discriminative model-এর পার্থক্য, আর generative model-এর তিন কাজ: নমুনা তোলা, density মাপা, representation শেখা
  • মডিউল ১২-এর পুরো মানচিত্র: কোন পরিবার likelihood কীভাবে সামলায়, আর কোন পর্বে কোনটি আসছে
  • Maximum likelihood কেন KL divergence কমানোর সমান, ছোট সংখ্যায় হাতে-কলমে
  • Autoregressive model-এর chain rule আর ancestral sampling, NumPy-তে
  • Test log-likelihood থেকে bits per dimension, আর মডেল তুলনার শর্তগুলো

১ · Generative model কী — p(x) বনাম p(y | x)

বাজারঘরের গুদামে প্রতিদিন হাজারো পণ্যের ছবি তোলা হয়। দুই রকম প্রশ্ন করা যায়। প্রথমটি: “এই ছবিটা জুতার না ব্যাগের?” দ্বিতীয়টি: “জুতার ছবি দেখতে সাধারণত কেমন হয়, আর একটা নতুন, বিশ্বাসযোগ্য জুতার ছবি এঁকে দাও।” প্রথম প্রশ্নের উত্তর দেয় discriminative model, দ্বিতীয়টির generative model.

একজন অভিজ্ঞ দর্জির কথা ভাবুন। কেউ একটা জামা হাতে দিলে তিনি এক নজরে বলে দেন এটা পাঞ্জাবি না শার্ট — এটা discriminative কাজ। কিন্তু তিনি নিজেও মাপ ছাড়াই একটা নতুন পাঞ্জাবি কেটে দিতে পারেন, আর কেউ অদ্ভুত হাতাওয়ালা কিছু আনলে বলতে পারেন “এমন পাঞ্জাবি সচরাচর হয় না।” এই দ্বিতীয় দক্ষতার জন্য তাঁকে পাঞ্জাবির পুরো বণ্টন মাথায় রাখতে হয়েছে — কোন মাপ কত সাধারণ, কোন নকশার সঙ্গে কোন কলার যায়। Generative model ঠিক এই বণ্টনটাই শেখে।

গণিতের ভাষায়: ডেটা x (যেমন ৭৮৪টি pixel-এর একটি ছবি) একটি অজানা বণ্টন pdata(x) থেকে এসেছে। Discriminative model শেখে শর্তসাপেক্ষ probability p(y | x) — x দেওয়া থাকলে লেবেল y. Generative model শেখে p(x) নিজেই, অথবা লেবেলসহ p(x, y). AI-এর ভিত্তি কোর্সের পর্ব ৩৫-এ conditional আর joint probability দেখেছেন; এখানে সেই ধারণাকে ৭৮৪ মাত্রায় নিয়ে যাওয়া হচ্ছে। আর AI-এর ভিত্তি কোর্সের পর্ব ৪-এ যে “Generative AI”-এর নাম শুনেছিলেন, তার গাণিতিক কেন্দ্র এই p(x).

একটি generative model-এর তিন কাজ

১. নমুনা তোলা (sampling)

p(x) থেকে নতুন x তোলা: নতুন পণ্যের ছবি, নতুন বাক্য, নতুন সুর। “Generative AI” বলতে বেশিরভাগ মানুষ এটাই বোঝেন।

GAN, diffusion, LLM

২. Density মাপা (evaluation)

একটি নির্দিষ্ট x কতটা সম্ভব, p(x) কত? খুব কম হলে x অস্বাভাবিক — জাল ছবি, ভুল সেন্সর রিডিং, প্রতারণার লেনদেন।

anomaly detection, compression

৩. Representation শেখা

ছবির পেছনের লুকানো কারণ (রং, আকার, ধরন) একটি ছোট vector z-এ ধরা: latent variable. পরে অন্য কাজে লাগে।

autoencoder, VAE

সব মডেল তিনটি কাজ সমান ভালো পারে না। কোনোটি দ্রুত নমুনা দেয় কিন্তু p(x) বলতে পারে না; কোনোটি p(x) একদম নিখুঁত বলে কিন্তু নমুনা তুলতে ধীর। এই পর্বের মানচিত্র মূলত এই ভাগাভাগিরই মানচিত্র।

মূল ধারণা

Generative model = ডেটার বণ্টনের একটি মডেল qθ(x), যার কাছ থেকে আমরা চাই দুটি জিনিস: qθ যেন pdata-র কাছাকাছি হয় (শেখা), আর qθ থেকে নমুনা তোলা বা qθ(x) হিসাব করা যেন সম্ভব হয় (ব্যবহার)। বাকি সব নকশা এই দুই চাহিদার আপস।

২ · মানচিত্র — likelihood কে কীভাবে সামলায়

সবচেয়ে কাজের প্রশ্ন একটি: “এই মডেল কি একটি x-এর জন্য qθ(x) একটি সংখ্যা হিসেবে দিতে পারে?” উত্তর অনুযায়ী পরিবারগুলো সাজানো যায়। নিচের ছবিটি ওপর থেকে নিচে পড়ুন।

Generative model-এর পরিবার

Families of generative models, grouped by how they treat the likelihood

Map of generative model families Generative model q(x) শেখা q(x) হিসাব হয় explicit density (বা তার সীমা) q(x) হিসাব নেই implicit / অন্য পথ শুধু নমুনা বা score হুবহু likelihood autoregressive normalizing flow likelihood ছাড়া GAN: generator + discriminator সীমা বা ধ্রুবক বাদে VAE: ELBO energy-based: Z অজানা noise সরানো শেখা diffusion / score ধাপে ধাপে নমুনা এই মডিউলের পথ ৭৫ autoencoder · ৭৬ VAE ৭৭ GAN · ৭৮ GAN-এর রূপ ৭৯ diffusion · ৮০ latent diffusion ৮১ মূল্যায়ন আর নিরাপত্তা
ছবি ৭৪.১ — বাঁ দিকের পরিবারগুলো একটি x-এর জন্য q(x) (বা তার নিম্নসীমা) সংখ্যা হিসেবে দেয়; ডান দিকেরগুলো সরাসরি দেয় না।
পরিবারমূল কৌশলq(x)নমুনা তোলাকোথায় পড়বেন
Autoregressivechain rule: এক pixel বা token করেহুবহুধীর, একটার পর একটাএই পর্ব; পর্ব ৪৬, ৪৯
Normalizing flowউল্টানো যায় এমন রূপান্তর, Jacobian-এর হিসাবহুবহুএক ধাপেশুধু নাম; “আরও গভীরে” দেখুন
VAElatent z, encoder আর decoderনিম্নসীমা (ELBO)এক ধাপেপর্ব ৭৬
Energy-basedq(x) ∝ e−E(x)ধ্রুবক Z বাদেধীর (MCMC)শুধু নাম
GANgenerator আর discriminator-এর খেলানেইএক ধাপে, দ্রুতপর্ব ৭৭, ৭৮
Diffusion / scorenoise যোগ, তারপর ধাপে ধাপে সরানো শেখাসীমা বা আলাদা হিসাবেঅনেক ধাপপর্ব ৭৯, ৮০

এই মডিউলের পরের পর্বগুলো শুধু এক লাইনে: পর্ব ৭৫ (Autoencoder — compression, denoising আর representation) দেখাবে কীভাবে ছবি ছোট কোডে চাপা যায়; পর্ব ৭৬ (Variational autoencoder) সেই কোডকে সম্ভাবনার ভাষা দেবে; পর্ব ৭৭ (GAN) আর ৭৮ (GAN-এর রূপ) likelihood ছাড়া শেখার খেলা; পর্ব ৭৯ (Diffusion model) আর ৮০ (Latent diffusion আর guidance) আজকের text-to-image-এর ভিত্তি; পর্ব ৮১ (Generative model-এর মূল্যায়ন আর নিরাপত্তা) মাপা আর অপব্যবহার ঠেকানো। এই পর্বে সেগুলোর কোনোটি শেখানো হবে না; এখানে শুধু সাধারণ ভাষাটা তৈরি হচ্ছে, যা সবগুলোতে লাগবে।

নামগুলোর পেছনের প্রবন্ধ, সাল প্রথম প্রকাশ ধরে (arXiv আর সম্মেলনের সালে এক বছর এদিক-ওদিক হতে পারে): NADE, Larochelle ও Murray, ২০১১; VAE, Kingma ও Welling, ২০১৩–১৪; GAN, Goodfellow ও সহকর্মীরা, ২০১৪; MADE, Germain ও সহকর্মীরা, ২০১৫; flow-ভিত্তিক variational inference, Rezende ও Mohamed, ২০১৫; diffusion-এর প্রথম রূপ, Sohl-Dickstein ও সহকর্মীরা, ২০১৫; PixelCNN, van den Oord ও সহকর্মীরা, ২০১৬; DDPM, Ho ও সহকর্মীরা, ২০২০. Energy-based ধারণা আরও পুরোনো, Hopfield network আর Boltzmann machine-এর (১৯৮০-র দশক) উত্তরসূরি।

৩ · Maximum likelihood মানে KL কমানো

শেখার নিয়মটা প্রথমে। আমাদের হাতে N টি নমুনা x1, …, xN. Maximum likelihood (মেশিন লার্নিং কোর্সের পর্ব ১৮) বলে: এমন θ বাছুন, যাতে দেখা ডেটার গড় log-likelihood সবচেয়ে বড় হয়:

θ* = argmaxθ (১/N) ∑i log qθ(xi)

এখন ডেটার নিজস্ব “অভিজ্ঞতামূলক” বণ্টন p̂ ধরুন: প্রতিটি দেখা x-এর ওপর ১/N ওজন। KL divergence (AI-এর ভিত্তি কোর্সের পর্ব ৩৮) খুলে লিখলে:

KL(p̂ ‖ qθ) = ∑x p̂(x) log p̂(x) − ∑x p̂(x) log qθ(x) = −H(p̂) − ℓ(θ)

এখানে H(p̂) হলো ডেটার entropy, যা θ-এর ওপর নির্ভর করে না; আর ℓ(θ) = ∑ p̂(x) log qθ(x) হুবহু সেই গড় log-likelihood. তাই ℓ বাড়ানো আর KL কমানো একই কাজ। −ℓ(θ)-কে বলা হয় cross-entropy, যা পর্ব ১৩ (Deep learning-এর loss)-এ classification-এর loss হিসেবে দেখেছেন। এবার ছোট একটি উদাহরণে সংখ্যা দিয়ে দেখুন।

১০টি অর্ডার: চা ৫, চিনি ৩, বিস্কুট ২ (ln, nats)
ধাপ
হিসাব
  1. ডেটা. বাজারঘরের এক দোকানে ১০টি অর্ডার। অভিজ্ঞতামূলক বণ্টন p̂ = (০.৫, ০.৩, ০.২)। এর entropy H(p̂) = −(০.৫ ln ০.৫ + ০.৩ ln ০.৩ + ০.২ ln ০.২) = ১.০২৯৭ nats. এই সংখ্যা মডেলের ওপর নির্ভর করে না।
  2. মডেল পরিবার. ইচ্ছে করে একটি সীমিত পরিবার নেওয়া হলো: একটিমাত্র parameter θ, আর চিনি ও বিস্কুটের probability সবসময় সমান। বাস্তবেও মডেল পরিবার প্রায় কখনো আসল বণ্টনকে হুবহু ধরতে পারে না।
  3. গড় log-likelihood. ০.৫ ln θ চায়ের অংশ; চিনি (০.৩) আর বিস্কুট (০.২) দুটোরই probability (১−θ)/২, তাই তাদের মোট ওজন ০.৫.
  4. অভেদ. KL = −H(p̂) − ℓ(θ). প্রথম পদ ধ্রুবক, তাই ℓ যত বাড়ে, KL তত কমে। নিচের প্রতিটি ধাপে KL + ℓ = −১.০২৯৭ মিলিয়ে দেখুন।
  5. সমান ভাগ. θ = ১/৩ মানে মডেল কিছুই শেখেনি। ℓ = ln(১/৩) = −১.০৯৮৬; KL = ১.০৯৮৬ − ১.০২৯৭ = ০.০৬৯০.
  6. দুই পাশ. θ = ০.৪ আর θ = ০.৬, দুটিতেই ℓ = −১.০৬০১, KL = ০.০৩০৫. একই মান, কারণ ℓ(θ) এই উদাহরণে θ = ০.৫-এর দুই পাশে প্রতিসম। তাই সর্বোচ্চ বিন্দু মাঝখানে কোথাও।
  7. সর্বোচ্চ. dℓ/dθ = ০.৫/θ − ০.৫/(১−θ) = ০ থেকে θ = ০.৫. এখানে ℓ সবচেয়ে বড়, KL সবচেয়ে ছোট — একই বিন্দু। MLE আর KL কমানো একই উত্তর দিল।
  8. শূন্য নয় কেন. সবচেয়ে ভালো q-ও চিনি আর বিস্কুটকে সমান ধরে, অথচ ডেটায় ০.৩ আর ০.২। বাকি ০.০১০১ nats হলো পরিবারের সীমাবদ্ধতার দাম। বড় neural network এই দাম কমায়, কিন্তু শূন্যে নামার নিশ্চয়তা দেয় না।

এই ছোট হিসাবের একটা বড় পরিণতি আছে। KL(p̂ ‖ q) অসমমিত। যেখানে ডেটা আছে (p̂ > ০) অথচ q প্রায় শূন্য, সেখানে log q-এর দণ্ড অসীমের দিকে যায়। তাই maximum likelihood মডেলকে বাধ্য করে সব ডেটা-অঞ্চলে কিছু না কিছু probability রাখতে: একে বলে mode-covering. দাম হলো, দুই চূড়ার মাঝখানের ফাঁকা জায়গাতেও কিছু probability ছড়িয়ে পড়ে, ফলে নমুনা ঝাপসা হতে পারে। উল্টো দিকের KL(q ‖ p) কমালে আচরণ হয় mode-seeking (মেশিন লার্নিং কোর্সের পর্ব ৪৮): একটি চূড়া ধরে বাকিগুলো ছেড়ে দেওয়া। GAN-এর প্রশিক্ষণ (পর্ব ৭৭) কোন divergence কমায় আর তাতে কী হয়, সেটি তখন বিস্তারিত দেখবেন।

Train ডেটায় ℓ বাড়ানো মানেই ভালো মডেল নয়। p̂ হলো শুধু দেখা নমুনার বণ্টন; একটি মডেল প্রতিটি train ছবিকে মুখস্থ করে তার ওপর সব probability রাখলে train-এ ℓ বিশাল হবে, কিন্তু নতুন ছবিতে q(x) প্রায় শূন্য। তাই likelihood সবসময় মাপতে হয় আলাদা test অংশে, ঠিক পর্ব ৪৬-এর held-out perplexity-র মতো।

৪ · এক মাত্রায় শুরু — histogram, Gaussian mixture আর নমুনা

৭৮৪ মাত্রার আগে এক মাত্রা। বাজারঘরের অর্ডার পৌঁছাতে কত ঘণ্টা লাগে, তার ১,০০০টি বানানো (seeded) রেকর্ড: ঢাকার ভেতরে প্রায় ২০ ঘণ্টা, বাইরে প্রায় ৬০ ঘণ্টা। দুটি density estimation পদ্ধতি পাশাপাশি: একটি histogram, আর একটি Gaussian mixture model (মেশিন লার্নিং কোর্সের পর্ব ৩৯ আর পর্ব ৪০-এ বিস্তারিত)। দুটোই একই প্রশ্নের উত্তর দেয়: test-এর ২০০টি অর্ডারে গড় log p কত? যার বেশি, সে ডেটাকে ভালো বোঝে। scikit-learn প্রথমবার লোড হতে প্রায় ১০ সেকেন্ড লাগতে পারে।

import numpy as np
from sklearn.mixture import GaussianMixture
import matplotlib.pyplot as plt

rng = np.random.default_rng(42)            # বাজারঘর: ডেলিভারির সময় (ঘণ্টা)
x = np.concatenate([rng.normal(20, 5, 700),     # ঢাকার ভেতরে
                    rng.normal(60, 12, 300)])   # ঢাকার বাইরে
rng.shuffle(x)
tr, te = x[:800], x[800:]

edges = np.linspace(0, 120, 25)                 # ২৪টি bin, প্রতিটি ৫ ঘণ্টা
cnt, _ = np.histogram(tr, edges)
dens = (cnt + 1) / ((len(tr) + len(cnt)) * 5)   # add-one, তারপর প্রস্থ দিয়ে ভাগ
def hist_logp(v):
    i = np.clip(np.searchsorted(edges, v) - 1, 0, len(cnt) - 1)
    return np.log(dens[i])
print("histogram   test avg log p =", round(hist_logp(te).mean(), 3))

for K in (1, 2, 3):
    g = GaussianMixture(K, init_params="random_from_data",
                        random_state=0).fit(tr[:, None])
    print(f"GMM K={K}     test avg log p = {g.score(te[:, None]):.3f}")

g = GaussianMixture(2, init_params="random_from_data", random_state=0).fit(tr[:, None])
print("weights", g.weights_.round(2), "means", g.means_.ravel().round(1))
new, comp = g.sample(5)
print("৫টি নতুন নমুনা:", new.ravel().round(1), "component:", comp)

grid = np.linspace(0, 120, 400)
plt.hist(tr, edges, density=True, alpha=0.4, label="train histogram")
plt.plot(grid, np.exp(g.score_samples(grid[:, None])), label="GMM K=2")
plt.xlabel("delivery time (hours)"); plt.ylabel("density"); plt.legend()
plt.show()

Histogram-এর test গড় log p হলো −৩.৮২৮, আর একটি Gaussian (K = ১)-এর −৪.৪০২: দুই চূড়ার ডেটায় একটি ঘণ্টা-আকৃতি খাপ খায় না। K = ২-এ −৩.৭৯৮, histogram-এর চেয়ে ভালো, আর মডেল আসল ভাগ (ওজন ০.৭ আর ০.৩) ও গড় (১৯.৯ আর ৬০.২ ঘণ্টা) প্রায় হুবহু ফিরে পেয়েছে। K = ৩-ও তিন দশমিক পর্যন্ত একই −৩.৭৯৮; বাড়তি component কোনো লাভ দেয়নি। নমুনা তোলা নিজেই ancestral: আগে ওজন অনুযায়ী একটি component (এখানে ০ মানে ঢাকার বাইরে, ১ মানে ভেতরে), তারপর সেই Gaussian থেকে একটি মান; scikit-learn ফলগুলো component ধরে সাজিয়ে দেয়, তাই ৬৮.৬ আর ৭৭.৮ আগে এসেছে। দুটি সতর্কতা: Gaussian ঋণাত্মক সময়ও দিতে পারে, যা এই মডেলের একটি সীমা; আর log p এখানে density-র log, তাই এর মান এককের (ঘণ্টা) ওপর নির্ভর করে, শুধু একই ডেটায় তুলনা চলে।

এক মাত্রায় histogram বেশ চলে। কিন্তু ২৮×২৮ ছবিতে প্রতি pixel-এ মাত্র দুটি bin (০ আর ১) ধরলেও মোট ঘর ২৭৮৪ — মহাবিশ্বের পরমাণুর আনুমানিক সংখ্যার চেয়েও অনেক বড়। প্রতিটি ঘর গুনে probability বসানো অসম্ভব। উচ্চ মাত্রায় তাই দুটি পথ: (১) ডেটাকে আগে কম মাত্রায় নামিয়ে সেখানে mixture বসানো (PCA + GMM, সেকশন ৭-এ সত্যিকারের run), অথবা (২) probability-কে টুকরো করে গুণফল হিসেবে লেখা — autoregressive পথ, যা পরের সেকশনের বিষয়।

৫ · Autoregressive model — chain rule আর ancestral sampling

Probability-র chain rule কোনো অনুমান ছাড়াই সব সময় সত্য:

p(x1, …, xD) = p(x1) · p(x2 | x1) · p(x3 | x1, x2) ⋯ p(xD | x<D)

এখানে x<d মানে d-তম pixel-এর আগের সব pixel, ওপর থেকে নিচে, বাঁ থেকে ডানে পড়ার ক্রমে। Autoregressive model প্রতিটি শর্তসাপেক্ষ অংশকে একটি ছোট classifier দিয়ে শেখে। পর্ব ৪৬ (Language modeling)-এ ঠিক এটাই করেছেন অক্ষরের জন্য, আর পর্ব ৪৯ (GPT আর decoder-only model)-এ token-এর জন্য। ছবিতে token-এর জায়গায় pixel, এটুকুই পার্থক্য।

নমুনা তোলাও এই ক্রম মেনে: আগে x1 তুলুন, সেটি জেনে x2, তারপর x3… একে বলে ancestral sampling — প্রতিটি চলক তার “পূর্বপুরুষদের” মান জেনে তোলা হয়। তিন pixel-এর একটি খেলনা মডেলে ধাপে ধাপে দেখুন। প্রতিটি ধাপে একটি uniform সংখ্যা u (০ থেকে ১) তোলা হয়েছে; u যদি p-এর চেয়ে ছোট হয়, pixel = ১.

u = (০.২৫, ০.৬২, ০.৮১)
ধাপ
হিসাব
  1. মডেল. তিনটি টেবিল। x1-এর একটি সংখ্যা; x2-এর দুটি, x1 = ০ বা ১ অনুযায়ী; x3-এর চারটি, (x1, x2) = (০,০), (০,১), (১,০), (১,১) ক্রমে।
  2. প্রথম pixel. কোনো শর্ত নেই। u1 = ০.২৫, যা ০.৩-এর চেয়ে ছোট, তাই x1 = ১. এই তুলনাই একটি Bernoulli নমুনা তোলার সহজতম উপায়।
  3. দ্বিতীয় pixel. এখন x1 = ১ জানা, তাই টেবিলের দ্বিতীয় সংখ্যা ০.৭ কাজে লাগে। u2 = ০.৬২ < ০.৭, তাই x2 = ১. x1 শূন্য হলে একই u-তে x2 হতো ০, কারণ ০.৬২ > ০.২.
  4. তৃতীয় pixel. (১, ১) ঘরের ০.৯ ব্যবহার হলো। u3 = ০.৮১ < ০.৯, তাই x3 = ১.
  5. নমুনা. (১, ১, ১). তিনটি ধাপ পরপর চালাতে হলো; দ্বিতীয়টি প্রথমটির ফল ছাড়া শুরুই করা যায় না। ৭৮৪ pixel-এ ৭৮৪টি ধাপ — autoregressive নমুনা ধীর হওয়ার আসল কারণ এটাই।
  6. Probability. একই chain rule উল্টো দিকে: ০.৩ × ০.৭ × ০.৯ = ০.১৮৯. Density মাপতে কিন্তু ক্রম লাগে না: x পুরোটা জানা থাকলে তিনটি গুণনীয়ক একসাথে হিসাব করা যায়। তাই training দ্রুত, নমুনা ধীর।
  7. পুরো টেবিল. আটটি সম্ভাব্য ছবির probability যোগ করলে ঠিক ১। টেবিলে মোট ৭টি সংখ্যা, যা যেকোনো ৩-bit বণ্টন ধরতে যথেষ্ট (২৩ − ১)। ৭৮৪ pixel-এ এই টেবিল অসম্ভব, তাই প্রতিটি শর্তসাপেক্ষ অংশ একটি neural network দিয়ে লেখা হয়, যার weight সব pixel ভাগ করে নেয়।

এবার একই মডেল NumPy-তে। প্রথমে ধাপে ধাপে দেখা নমুনাটি আবার তোলা হয়েছে, তারপর ১,০০,০০০টি নমুনা একসাথে (vectorised), আর আটটি ঘরের ভাগ হুবহু probability-র সঙ্গে মেলানো হয়েছে।

import numpy as np
from itertools import product

P1 = 0.3                                    # p(x1 = 1)
P2 = np.array([0.2, 0.7])                   # p(x2 = 1 | x1)
P3 = np.array([[0.1, 0.5], [0.4, 0.9]])     # p(x3 = 1 | x1, x2)

def ancestral(U):                           # U: (n, 3) uniform সংখ্যা
    a = (U[:, 0] < P1).astype(int)
    b = (U[:, 1] < P2[a]).astype(int)       # a জানার পরেই b
    c = (U[:, 2] < P3[a, b]).astype(int)    # a, b জানার পরেই c
    return np.stack([a, b, c], axis=1)

def joint(a, b, c):                         # chain rule: তিনটি গুণ
    pa = P1 if a else 1 - P1
    pb = P2[a] if b else 1 - P2[a]
    pc = P3[a, b] if c else 1 - P3[a, b]
    return pa * pb * pc

print("u = (0.25, 0.62, 0.81) থেকে:", ancestral(np.array([[0.25, 0.62, 0.81]]))[0])

rng = np.random.default_rng(7)
S = ancestral(rng.random((100_000, 3)))
freq = np.bincount(S @ np.array([4, 2, 1]), minlength=8) / len(S)

print("x1x2x3  exact p   sampled")
for k, (a, b, c) in enumerate(product((0, 1), repeat=3)):
    print(f"  {a}{b}{c}    {joint(a, b, c):.4f}   {freq[k]:.4f}")
print("exact যোগফল =", round(sum(joint(*t) for t in product((0, 1), repeat=3)), 6))

ধাপে ধাপে দেখা u থেকে একই নমুনা [1 1 1] এসেছে। ১,০০,০০০টি নমুনায় প্রতিটি ঘরের ভাগ হুবহু probability থেকে বড়জোর প্রায় ০.০০১৫ দূরে (১১১ ঘরে ০.১৮৯০ বনাম ০.১৮৭৫), যা এত নমুনায় স্বাভাবিক এলোমেলো ওঠানামা। অর্থাৎ পরপর তিনটি শর্তসাপেক্ষ টস ঠিক যৌথ বণ্টনটাই তৈরি করে। খেয়াল করুন, vectorised কোডেও ক্রম ভাঙা যায়নি: b-এর লাইন a-র পরে, c-এর লাইন b-এর পরে। এক লাখ ছবি একসাথে চললেও প্রতিটি ছবির ভেতরে pixel-গুলো পরপর আসে।

Neural network এই টেবিলের জায়গা নেয় দুই রকমভাবে। সবচেয়ে সরল রূপে প্রতিটি pixel-এর জন্য একটি logistic regression, যার ইনপুট আগের সব pixel: একে বলে fully visible sigmoid belief network (FVSBN; নব্বইয়ের দশকে Neal আর Frey-র কাজে পাওয়া যায়)। এর weight সংখ্যা ৭৮৪ × ৭৮৩ / ২ + ৭৮৪ = ৩,০৭,৭২০, টেবিলের ২৭৮৪-এর তুলনায় নগণ্য। আরেক রূপ MADE: একটি সাধারণ MLP-এর weight-এ ০/১ mask বসিয়ে নিশ্চিত করা হয় যে output d কখনো ইনপুট d বা তার পরের কোনো pixel “দেখতে” না পায়। তখন এক forward pass-এই ৭৮৪টি শর্তসাপেক্ষ probability একসাথে বেরিয়ে আসে। Training-এ তাই একবারেই পুরো ছবির likelihood; নমুনায় অবশ্য সেই ৭৮৪টি ধাপ থেকেই যায়।

৬ · Bits per dimension — likelihood-কে তুলনাযোগ্য করা

একটি ছবির NLL (negative log-likelihood) ধরুন ১২০ nats. এটা ভালো না খারাপ? সংখ্যাটা ছবির আকারের ওপর নির্ভর করে: বড় ছবিতে বেশি pixel, বেশি “অবাক হওয়া”। তাই প্রতি মাত্রায় ভাগ করে, log-এর ভিত্তি ২ নিয়ে লেখা হয় bits per dimension:

bits/dim = NLLnats ÷ (D × ln ২)

এর অর্থ সরাসরি compression-এর ভাষায়: একটি ভালো entropy coder এই মডেল দিয়ে test ছবিকে গড়ে প্রতি pixel-এ প্রায় এত bit-এ চাপতে পারত। ০/১ ছবিতে মডেল ছাড়াই প্রতি pixel-এ ১ bit লাগে, তাই ১-এর নিচের যেকোনো সংখ্যা মানে মডেল কিছু শিখেছে। পর্ব ৪৬-এর bits per token আর perplexity এই একই ধারণা, শুধু ভাগ হয় token-এর সংখ্যায়।

০/১ Fashion-MNIST, D = ২৮ × ২৮ = ৭৮৪
ধাপ
হিসাব
  1. মডেলের ফল. সেকশন ৭-এর সত্যিকারের run থেকে, ছোট autoregressive model (MADE), seed ৪২: ১০,০০০টি test ছবির গড় NLL. সাধারণত এই সংখ্যাটাই training কোড ছাপে।
  2. Bits. ln থেকে log2-এ যেতে ln ২ ≈ ০.৬৯৩১ দিয়ে ভাগ। এখন সংখ্যাটির মানে: “এক ছবি লিখতে মোট কত bit”.
  3. প্রতি pixel. ৭৮৪ দিয়ে ভাগ। এখন ২৮×২৮ আর ৬৪×৬৪ ছবির মডেলকেও একই মাপে রাখা যায়, যদিও তুলনা তবুও সাবধানে করতে হবে।
  4. মাপকাঠি. মডেল ছাড়া ০/১ pixel-এ ১ bit. প্রতি pixel-এর আলাদা গড় জানলেই (স্বাধীন Bernoulli) ০.৭০৫-এ নামে। আগের pixel-গুলো দেখলে আরও অনেক নিচে।
  5. দোটানা. ২ ঘাত bits/dim হলো প্রতি pixel-এর perplexity: মডেল গড়ে যেন এতগুলো সমান-সম্ভাব্য বিকল্পের মধ্যে দোটানায়। ০/১ pixel-এ সর্বোচ্চ ২.
  6. ৮-bit ছবি. এই সংখ্যাগুলো উদাহরণ (নমুনা), কোনো run নয়। ০–২৫৫ মানকে ২৫৬ দিয়ে ভাগ করে [০, ১)-এ density মডেল বসালে, density একের বেশি হতে পারে বলে NLL ঋণাত্মক হতে পারে। তখন প্রতি মাত্রায় log2 ২৫৬ = ৮ bit যোগ করে আসল bits/dim পাওয়া যায়।
  7. শর্ত. একটি শর্ত না মিললেই তুলনা অর্থহীন। যেমন, এই পর্বে ০/১ করা হয়েছে নির্দিষ্ট সীমা (১২৮) দিয়ে; অনেক প্রবন্ধে ০/১ করা হয় এলোমেলোভাবে (প্রতি pixel-এর উজ্জ্বলতাকে probability ধরে), তাদের সংখ্যার সঙ্গে আমাদের সংখ্যা মেলানো যাবে না।

এবার পাতার ভেতরেই একটি সত্যিকারের তুলনা। scikit-learn-এর সঙ্গে আসা ৮×৮ হাতের লেখা সংখ্যার ১,৭৯৭টি ছবি (digits), ৮-এর বেশি মানকে ১ ধরে ০/১ করা। মডেল ১: প্রতিটি pixel স্বাধীন। মডেল ২: autoregressive, যেখানে প্রতিটি pixel-এর জন্য আগের সব pixel থেকে একটি logistic regression (AI-এর ভিত্তি কোর্সের পর্ব ৫৯) — অর্থাৎ ছোট একটি FVSBN. ৬৪টি মডেল fit হয়, তাই কয়েক সেকেন্ড লাগতে পারে।

import numpy as np
from sklearn.datasets import load_digits
from sklearn.linear_model import LogisticRegression

X = (load_digits().data >= 8).astype(int)       # 8×8 ছবি, 0/1 করা
rng = np.random.default_rng(0)
idx = rng.permutation(len(X))
tr, te = X[idx[:1400]], X[idx[1400:]]
D = X.shape[1]

# মডেল ১: প্রতিটি pixel স্বাধীন Bernoulli
p = (tr.sum(0) + 1) / (len(tr) + 2)
ll_ind = (te * np.log(p) + (1 - te) * np.log(1 - p)).sum(1)

# মডেল ২: autoregressive, p(x_d | x_1..x_(d-1)) = logistic regression
ll_ar = np.zeros(len(te))
for d in range(D):
    y = tr[:, d]
    if d == 0 or y.min() == y.max():             # আগে কিছু নেই বা সব একই
        q = (y.sum() + 1) / (len(y) + 2)
        pd_ = np.full(len(te), q)
    else:
        lr = LogisticRegression(C=1.0, max_iter=500).fit(tr[:, :d], y)
        pd_ = lr.predict_proba(te[:, :d])[:, 1]
    ll_ar += np.where(te[:, d] == 1, np.log(pd_), np.log(1 - pd_))

for name, ll in [("independent", ll_ind), ("autoregressive", ll_ar)]:
    nats = -ll.mean()
    print(f"{name:15s} test NLL = {nats:6.2f} nats/image"
          f" = {nats/np.log(2):6.2f} bits = {nats/np.log(2)/D:.3f} bits/dim")
print("প্রথম test ছবি, স্বাধীন মডেলে log p =", round(ll_ind[0], 2),
      "nats; AR মডেলে =", round(ll_ar[0], 2), "nats")

স্বাধীন মডেলের test NLL ২৫.২৬ nats, অর্থাৎ ০.৫৬৯ bits/dim; autoregressive মডেলের ১৮.১২ nats, অর্থাৎ ০.৪০৮ bits/dim, প্রায় ২৮% কম। প্রথম test ছবিতে পার্থক্য আরও বড়: log p −২৯.১২ থেকে −১৪.২২, অর্থাৎ AR মডেল ছবিটিকে প্রায় e১৪.৯ ≈ ৩০ লক্ষ গুণ বেশি সম্ভব মনে করে, কারণ সে জানে কালির একটি দাগের পাশে আরেকটি দাগ আসার সম্ভাবনা বেশি। এখানে ১,৪০০টি ছবিতে শেখা, ৩৯৭টিতে মাপা, আর ছবি ৬৪ মাত্রার; তাই এই bits/dim ২৮×২৮ ছবির সংখ্যার সঙ্গে সরাসরি তুলনীয় নয়।

৭ · সত্যিকারের run — চার মডেল, Fashion-MNIST আর MNIST

এবার আসল আকারে। বাজারঘরের পণ্যের ছবির বদলে ব্যবহার হয়েছে Fashion-MNIST (Zalando Research, MIT licence): জামা, জুতা, ব্যাগের ২৮×২৮ ধূসর ছবি, train ৬০,০০০, test ১০,০০০. এগুলো আসল দোকানের ছবি নয়, একটি পরিচিত গবেষণা-ডেটাসেট। তুলনার জন্য MNIST-এর হাতের লেখা সংখ্যাও (এর licence সাধারণত CC BY-SA 3.0 বলা হয়; নিজের কাজে ব্যবহারের আগে উৎস দেখে নিন)। প্রতিটি pixel-কে ১২৮ সীমায় ০/১ করা হয়েছে। Train-এর শেষ ৫,০০০টি ছবি আলাদা রাখা হয়েছে val হিসেবে: hyperparameter আর সেরা epoch শুধু তা দিয়ে বাছা, test মাপা হয়েছে সবশেষে একবার।

মডেলনকশাlikelihood কীভাবে
স্বাধীন Bernoulliপ্রতি pixel-এ একটি probability, add-one smoothingহুবহু, ৭৮৪টি গুণ
PCA + GMMPCA-তে k মাত্রা, সেখানে K component-এর full-covariance GMM, বাকি মাত্রায় একটি সাধারণ Gaussian noise; k ∈ {১৬, ৩২, ৬৪}, K ∈ {১, ১০, ৩০} val দিয়ে বাছাcontinuous density, তাই ০/১ মানে uniform noise যোগ করে (uniform dequantization); ফলটি আসল NLL-এর একটি ঊর্ধ্বসীমা
FVSBN (linear AR)প্রতি pixel-এ আগের সব pixel থেকে logistic regressionহুবহু, chain rule
MADE (AR)৫০০ hidden unit-এর এক layer, ReLU, mask, সঙ্গে সরাসরি masked skipহুবহু, chain rule

Python 3.14.4, numpy 2.5.4, scikit-learn 1.9.1, torch 2.15.0 (CPU build), সব CPU-তে, ৩টি thread-এ (লেখার সময়ের version). AR মডেল দুটি Adam (lr ০.০০১), batch ১০০, ৩০ epoch, তিনটি seed-এ। MADE-এর মূল অংশ (genmap.py থেকে ছাঁটা):

deg = torch.randint(1, D, (h,), generator=g)        # hidden unit-এর "ক্রম" 1..D-1
inp = torch.arange(1, D + 1)
m1 = (deg[:, None] >= inp[None, :]).float()          # hidden k দেখে pixel 1..deg[k]
m2 = (inp[:, None] > deg[None, :]).float()           # output d দেখে deg < d এমন hidden
m  = torch.tril(torch.ones(D, D), -1)                # skip: output d দেখে pixel < d

def forward(x):                                      # logit of p(x_d = 1 | x_<d)
    hid = torch.relu(x @ (l1.weight * m1).T + l1.bias)
    return hid @ (l2.weight * m2).T + l2.bias + x @ (skip.weight * m).T

loss = F.binary_cross_entropy_with_logits(forward(b), b, reduction="none").sum(1).mean()

PCA + GMM-এর val খোঁজ (Fashion-MNIST, seed ৪২; সত্যিকারের run):

$ uv run python genmap.py fashion gmm 42
k= 16 K=  1 val_bpd=0.6686 params=13481 fit_s=0.5
k= 16 K= 10 val_bpd=0.6528 params=14858 fit_s=2.7
k= 16 K= 30 val_bpd=0.6500 params=17918 fit_s=7.8
k= 32 K=  1 val_bpd=0.6413 params=26433 fit_s=0.6
k= 32 K= 10 val_bpd=0.6165 params=31482 fit_s=3.3
k= 32 K= 30 val_bpd=0.6112 params=42702 fit_s=24.6
k= 64 K=  1 val_bpd=0.6216 params=53105 fit_s=0.6
k= 64 K= 10 val_bpd=0.5843 params=72410 fit_s=7.4
k= 64 K= 30 val_bpd=0.5768 params=115310 fit_s=61.4
… (scikit-learn-এর একটি RuntimeWarning-এর দুই লাইন বাদ)
{'data': 'fashion', 'model': 'gmm', 'seed': 42, 'k': 64, 'K': 30, 'params': 115310, 'sigma2': 0.12059533596038818, 'val_bpd': 0.5768246054649353, 'test_bpd': 0.5789995193481445, 'test_nats': 314.6441955566406, 'fit_s': 61.4, 'sample100_s': 0.0317}

সেরাটি খোঁজের একদম কোণে (k = ৬৪, K = ৩০), তাই আরও বড় k বা K-তে হয়তো আরেকটু কমত; সময় বাঁচাতে খোঁজ এখানেই থামানো হয়েছে। নমুনা তোলার সময় scikit-learn একটি RuntimeWarning: covariance is not symmetric positive-semidefinite দিয়েছিল (float-এর ছোট গোলমাল); নমুনা তবুও তোলা হয়েছে। MADE-এর একটি run-এর লগ (ছাঁটা):

$ uv run python genmap.py fashion ar 42 30 500
fashion ar h=500 seed=42 params=1399940
epoch  1 val_bpd 0.2674 (3 s)
epoch  5 val_bpd 0.2043 (17 s)
epoch 10 val_bpd 0.1938 (36 s)
epoch 15 val_bpd 0.1918 (54 s)
epoch 19 val_bpd 0.1915 (68 s)
epoch 25 val_bpd 0.1921 (91 s)
epoch 30 val_bpd 0.1929 (110 s)
{'data': 'fashion', 'model': 'ar_h500', 'seed': 42, 'params': 1399940, 'best_epoch': 19, 'val_bpd': 0.19152139127254486, 'test_bpd': 0.19357165694236755, 'test_nats': 105.192138671875, 'fit_s': 110.4, 'sample100_s': 1.338}
মডেলকার্যকর parameterFashion test bits/dimMNIST test bits/dimFit সময় (s, Fashion)১০০ নমুনার সময় (s)
স্বাধীন Bernoulli৭৮৪০.৭০৫০ (৩৮৩.১৩ nats)০.৩৭৮৮ (২০৫.৮৪ nats)০.০১০.০০০৫
PCA + GMM, k = ৬৪, K = ৩০ (ঊর্ধ্বসীমা)১,১৫,৩১০০.৫৭৯০ (৩১৪.৬৪ nats)০.৪৯৪০ (২৬৮.৪৬ nats)৬১.৪ (শুধু বাছা মডেল)০.০৩
FVSBN৩,০৭,৭২০০.২০৩১–০.২০৩২ (১১০.৩৮–১১০.৪৩ nats)০.১৪০১ (৭৬.১৩–৭৬.১৫ nats)৪৪–৪৫০.৪৪–০.৪৮
MADE৭,০০,২২০০.১৯৩৫–০.১৯৩৬ (১০৫.১৫–১০৫.১৯ nats)০.১২৭৩–০.১২৭৬ (৬৯.১৭–৬৯.৩৪ nats)১১০–১৫৫১.১২–১.৭৬

সব সংখ্যা এই যন্ত্রের সত্যিকারের run, একটি ভাগে; AR দুটির ঘরে seed ৪২, ৪৩, ৪৪-এর পরিসর, বাকি দুটি seed ৪২. Test অংশ ১০,০০০টি ছবি। দুই ডেটাসেটেই GMM-এর val খোঁজ একই k = ৬৪, K = ৩০ বেছেছে। “কার্যকর parameter” মানে mask-এ শূন্য না হওয়া weight; PyTorch-এ রাখা মোট সংখ্যা FVSBN-এ ৬,১৫,৪৪০ আর MADE-এ ১৩,৯৯,৯৪০, যার প্রায় অর্ধেক mask-এ সবসময় শূন্য। GMM-এর সংখ্যায় PCA-র ৬৪ × ৭৮৪ weight-ও ধরা। সময় CPU-তে, অন্য কাজের সঙ্গে ভাগ করা যন্ত্রে মাপা; শুধু মাত্রা হিসেবে পড়ুন।

টেবিল থেকে চারটি কথা, প্রতিটির সীমাসহ।

  • প্রতিবেশী pixel জানাই আসল লাভ। স্বাধীন মডেল থেকে MADE-এ Fashion-MNIST-এ ০.৭০৫ থেকে ০.১৯৪, প্রায় ৭২% কম; MNIST-এ ০.৩৭৯ থেকে ০.১২৮. Hidden layer ছাড়া FVSBN-ই এই পথের বেশিরভাগটা পেরিয়েছে; MADE-এর hidden layer যোগ করেছে Fashion-এ প্রায় ৫%, MNIST-এ প্রায় ৯%. তবে এটি একটি ভাগ আর একটি আকারের ফল; বড় নকশায় (যেমন PixelCNN) ব্যবধান অন্যরকম হতে পারে।
  • Seed-এর প্রভাব এখানে খুব ছোট। তিন seed-এর পার্থক্য ০.০০০৩ bits/dim-এর বেশি নয়। এই সরল মডেলগুলোর জন্য তা স্বাভাবিক, কিন্তু GAN-এর মতো দুই খেলোয়াড়ের প্রশিক্ষণে (পর্ব ৭৭) এমন আশা করবেন না। Fashion-এ MADE-এর সেরা val epoch ছিল ১৭–১৯, তারপর val খারাপ হতে শুরু করে (overfitting). MNIST-এ দুটি AR মডেলেরই সেরা epoch ছিল শেষটি, ৩০; আরও epoch দিলে হয়তো সামান্য কমত, যা আমরা পরীক্ষা করিনি।
  • GMM-এর ফল দুই দিকে। Fashion-MNIST-এ স্বাধীন মডেলের চেয়ে ভালো (০.৫৭৯ বনাম ০.৭০৫), MNIST-এ খারাপ (০.৪৯৪ বনাম ০.৩৭৯)। মনে রাখবেন, GMM-এর সংখ্যা uniform noise যোগ করে মাপা ঊর্ধ্বসীমা; continuous Gaussian-কে ০/১ ঘরে বসানোর একটা বাড়তি জরিমানা আছে, যা ভাবনার প্রশ্ন ৪-এ হিসাব করা হয়েছে। আর খোঁজের সেরা মান কোণে পড়েছে, তাই এটি GMM-এর সেরা সম্ভাব্য ফল নয়।
  • বাইরের সংখ্যার সঙ্গে মেলাবেন না। অনেক প্রবন্ধ MNIST-কে এলোমেলোভাবে ০/১ করে (প্রতিবার pixel-এর উজ্জ্বলতা অনুযায়ী তুলে)। আমাদের নির্দিষ্ট সীমার ০/১ ছবি অনেক বেশি নিয়মিত, তাই NLL-ও আলাদা মাত্রার। শুধু এই টেবিলের ভেতরের তুলনা অর্থবহ।

সংখ্যার পাশে চোখও লাগে। নিচের ব্লকে চারটি মডেলের সত্যিকারের নমুনা আঁকা হয় (Fashion-MNIST, seed ৪২, AR মডেলের সেরা epoch-এর weight; প্রথম সারিতে তুলনার জন্য আসল test ছবি)। প্রতিটি ছবি ৭৮৪টি bit, ৯৮ byte-এ চেপে hex আকারে পাতায় রাখা।

import numpy as np
import matplotlib.pyplot as plt

rows = []
for line in open("samples.txt"):
    name, hexes = line.strip().split(":")
    imgs = [np.unpackbits(np.frombuffer(bytes.fromhex(h), np.uint8))[:784]
            for h in hexes.split(",")]
    rows.append((name, np.array(imgs).reshape(-1, 28, 28)))

fig, axes = plt.subplots(len(rows), 8, figsize=(8, 1.15 * len(rows)))
for r, (name, imgs) in enumerate(rows):
    for c in range(8):
        ax = axes[r, c]
        ax.imshow(imgs[c], cmap="gray_r", vmin=0, vmax=1)
        ax.set_xticks([]); ax.set_yticks([])
    axes[r, 0].set_ylabel(name, rotation=0, ha="right", va="center", fontsize=9)
    on = np.array(imgs).mean()
    print(f"{name:12s} ৮টি ছবিতে কালো pixel-এর ভাগ = {on:.3f}")
fig.subplots_adjust(left=0.2, wspace=0.05, hspace=0.05)
plt.show()

আঁকা ছবিতে চারটি মডেলের চরিত্র স্পষ্ট। স্বাধীন Bernoulli-র নমুনা গড় আকৃতির একটি দানাদার মেঘ: প্রতিটি pixel আলাদা টস, তাই কোনো রেখা বা কিনারা নেই। PCA + GMM-এ জামা বা কোটের মোটা আকার আসে, কিন্তু বাকি মাত্রার Gaussian noise ছবিজুড়ে বিন্দু ছড়িয়ে দেয়; তাই তার কালো pixel-এর ভাগ সবচেয়ে বেশি, ০.৪১৪. FVSBN আর MADE-এর নমুনায় চেনা আকৃতি আসে (প্যান্ট, কোট, জুতা), সঙ্গে কিছু ভাঙা টুকরোও। আসল ৮টি test ছবিতে কালো ভাগ ০.১৮৭, FVSBN ০.২৫৮, MADE ০.৩৩৫; কিন্তু ৮টি ছবি কোনো মূল্যায়ন নয়, শুধু চোখের পরীক্ষা। মান মাপার নিয়ম পর্ব ৮১-এ আসবে।

৮ · মান, বৈচিত্র্য আর গতি — trilemma আর মূল্যায়নের প্রথম ধারণা

সেকশন ৭-এর টেবিলে একটি টানাপোড়েন দেখা গেছে: সবচেয়ে ভালো likelihood-এর মডেলটি নমুনা তুলতে সবচেয়ে ধীর। Xiao, Kreis আর Vahdat ২০২২ সালের ICLR-এর একটি প্রবন্ধে (“Tackling the Generative Learning Trilemma with Denoising Diffusion GANs”) এই টানাপোড়েনকে নাম দেন generative learning trilemma: একটি generative model-এর কাছে আমরা চাই উঁচু মানের নমুনা, বৈচিত্র্য (ডেটার সব ধরন ঢাকা, mode coverage), আর দ্রুত নমুনা — আর তাঁদের পর্যবেক্ষণ অনুযায়ী তখনকার প্রচলিত পরিবারগুলো সাধারণত তিনটির দুটিতে ভালো ছিল। নিচের টেবিল তাঁদের ঢালাও বর্ণনার সরলীকরণ, কোনো মাপা ফল নয়; প্রতিটি পরিবারের ভেতরে বহু ব্যতিক্রম আছে, আর পরে অনেক কাজ এই সীমা ঠেলেছে।

পরিবারমানবৈচিত্র্যনমুনার গতি
GANসাধারণত উঁচুদুর্বল হতে পারে (mode collapse)দ্রুত, এক ধাপ
VAE, flowছবিতে প্রায়ই ঝাপসা বা কমভালো (likelihood-এ শেখা)দ্রুত
Diffusionউঁচুভালোধীর, বহু ধাপ
Autoregressive (pixel)likelihood ভালো, নমুনা মাঝারি থেকে ভালোভালোধীর, প্রতি pixel এক ধাপ

আমাদের নিজের সংখ্যায় গতির অক্ষটি স্পষ্ট: ১০০টি নমুনা তুলতে স্বাধীন Bernoulli-র লেগেছে প্রায় ০.০০০৫ সেকেন্ড, PCA + GMM-এর প্রায় ০.০৩ সেকেন্ড, আর MADE-এর ১.১ থেকে ১.৮ সেকেন্ড, কারণ তাকে ৭৮৪টি forward pass পরপর চালাতে হয়েছে। ছোট মডেলে পার্থক্যটা সামান্য মনে হয়, কিন্তু ৬৪×৬৪ রঙিন ছবিতে মাত্রা ১২,২৮৮, তখন ধাপও ততগুলো। মান আর বৈচিত্র্য সংখ্যায় মাপা কঠিন; সেজন্যই পর্ব ৮১-তে FID, precision/recall আর CLIP score আসবে।

Likelihood বনাম নমুনার মান

এই পর্বের মাপকাঠি ছিল likelihood. কিন্তু Theis, van den Oord আর Bethge (ICLR ২০১৬, “A note on the evaluation of generative models”) দেখান, উচ্চ মাত্রায় likelihood আর নমুনার চোখে-দেখা মান অনেকটাই আলাদা জিনিস। তাঁদের একটি যুক্তি হাতে মেলানো যায়: একটি ভালো মডেল q-কে ১% ওজন দিয়ে ৯৯% আজেবাজে noise-এর সঙ্গে মেশালে নমুনার ৯৯টিই noise হবে, অথচ log-likelihood কমবে বড়জোর ln ১০০ ≈ ৪.৬ nats প্রতি ছবিতে — ৭৮৪ মাত্রায় তা মাত্র প্রায় ০.০০৮৫ bits/dim. উল্টো দিকে, train ছবি হুবহু ফেরত দেওয়া একটি “মডেল” চমৎকার নমুনা দেখাবে, অথচ test likelihood হবে ভয়ানক। তাই:

  • Likelihood (bits/dim) ভালো মাপে মডেল ডেটার সব অঞ্চল ঢেকেছে কি না, আর compression-এ কাজে লাগে। শুধু explicit মডেলেই মাপা যায়।
  • নমুনার মান মাপতে লাগে অন্য মাপকাঠি, আর শেষ পর্যন্ত মানুষের চোখ। একটি মাপকাঠিতে জেতা মানে অন্যটিতে জেতা নয়।
  • মুখস্থ আলাদা করে পরীক্ষা করতে হয়: নমুনার সবচেয়ে কাছের train ছবি খুঁজে দেখা — পর্ব ৮১-এর একটি কাজ।

আরও গভীরে: Normalizing flow (Dinh ও সহকর্মীদের RealNVP, Kingma আর Dhariwal-এর Glow) উল্টানো যায় এমন রূপান্তর z = f(x) ব্যবহার করে, আর change-of-variables সূত্রে log q(x) = log p(z) + log |det ∂f/∂x| হুবহু হিসাব করে; শর্ত হলো Jacobian-এর determinant সস্তায় বের করার মতো নকশা। Discrete ডেটায় continuous density বসালে uniform noise যোগ করে মাপা NLL আসল discrete NLL-এর গড়ে একটি ঊর্ধ্বসীমা — Theis ও সহকর্মীদের ২০১৬-র প্রবন্ধে এর প্রমাণ আছে, আর Ho ও সহকর্মীদের Flow++ (২০১৯) একটি শেখা noise দিয়ে এই ফাঁক কমানোর চেষ্টা করে। Energy-based model-এর কঠিন অংশ normalising ধ্রুবক Z, যা না জেনে শেখার জন্য contrastive divergence বা score matching-এর মতো কৌশল লাগে; score matching-ই diffusion-এর একটি দৃষ্টিভঙ্গির ভিত্তি (পর্ব ৭৯)।

পরীক্ষা ও ইন্টারভিউতে:

  • Generative আর discriminative model-এর পার্থক্য? প্রথমটি p(x) বা p(x, y) শেখে, দ্বিতীয়টি শুধু p(y | x).
  • MLE কেন KL কমানোর সমান? KL(p̂ ‖ q) = −H(p̂) − গড় log q; H(p̂) θ-এর ওপর নির্ভর করে না।
  • একটি ২৮×২৮ ০/১ ছবির test NLL ৯৫ nats হলে bits/dim কত? ৯৫ ÷ ০.৬৯৩১ ÷ ৭৮৪ ≈ ০.১৭৫.
  • GAN-এর likelihood কেন মাপা যায় না? এটি implicit: শুধু z থেকে x বানায়, q(x)-এর সূত্র দেয় না।
  • Autoregressive মডেলে training দ্রুত কিন্তু নমুনা ধীর কেন? Training-এ পুরো x জানা, সব শর্তসাপেক্ষ অংশ একসাথে; নমুনায় প্রতিটি মাত্রা আগেরটির ওপর নির্ভর করে, তাই D টি ধাপ পরপর।

৯ · AI-এর যুগে — generative model-এর কাজে AI সহকারী

AI সহকারী একটি MADE বা GMM-এর কোড কয়েক সেকেন্ডে লিখে দেবে। এই বিষয়ে তার ভুলগুলোও চেনা: পুরোনো বা বানানো API (যেমন torch-এর সরিয়ে দেওয়া function বা scikit-learn-এর বাতিল argument), mask-এ একঘর সরে যাওয়া (output d নিজের ইনপুট d দেখে ফেলছে, ফলে likelihood অবিশ্বাস্য রকম ভালো), nats আর bits গুলিয়ে ফেলা, ln ২ দিয়ে ভাগ ভুলে যাওয়া, শুধু train-এ likelihood মাপা, model.eval() আর torch.no_grad() ভুলে যাওয়া, একটি ভাগ্যবান seed-এর ফল সাধারণ সত্য বলে লেখা, আর বিশ্বাসযোগ্য কিন্তু ভুল derivation বা প্রবন্ধের নাম। আর মনে রাখবেন, ভাষার মডেল নিজেও একটি autoregressive generative model; তার উত্তর “বোঝা” থেকে আসে, এমন দাবি প্রমাণিত নয়।

নিচের prompt-টি কোড চাওয়ার পাশাপাশি যাচাইয়ের পথও চেয়ে নেয়:

আমি PyTorch-এ (version: [আপনার version]) ০/১ করা ২৮×২৮ ছবির জন্য একটি MADE মডেল লিখছি। তুমি mask তৈরির কোড দাও, তারপর: ১. একটি পরীক্ষা লেখো যা প্রমাণ করে output d কখনো ইনপুট d বা তার পরের pixel-এর ওপর নির্ভর করে না (একটি pixel বদলে আগের output-গুলো না বদলানো দেখাও)। ২. test NLL nats থেকে bits/dim-এ রূপান্তরের সূত্রটি সংখ্যাসহ দেখাও। ৩. যে API ব্যবহার করছ তার নাম আর কোন version-এর docs দেখে লিখেছ, তা বলো; নিশ্চিত না হলে স্পষ্ট বলো। ৪. স্বাধীন Bernoulli baseline-ও লেখো, যাতে আমি ফল তুলনা করতে পারি।

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

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

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

প্র ০১Likelihood বেশি মানেই কি নমুনা ভালো? কোন মডেল কোন কাজে, তা ঠিক করবেন কীভাবে?

না, এবং এটি generative modeling-এর সবচেয়ে পুরোনো ভুল বোঝাবুঝিগুলোর একটি। Theis, van den Oord আর Bethge ২০১৬ সালে ICLR-এ প্রকাশিত “A note on the evaluation of generative models” প্রবন্ধে দেখান যে উচ্চ মাত্রায় তিনটি মাপকাঠি — log-likelihood, নমুনার চোখে-দেখা মান, আর Parzen window-এর মতো আনুমানিক মাপ — প্রায় স্বাধীনভাবে আচরণ করতে পারে।

দুটি চরম উদাহরণ ভাবুন। প্রথমটি: একটি ভালো মডেলের সঙ্গে ৯৯% ওজনের noise মেশানো। নমুনার ১০০টির মধ্যে ৯৯টি অর্থহীন, তবু log-likelihood কমে বড়জোর ln ১০০ ≈ ৪.৬ nats প্রতি ছবিতে। একটি ২৮×২৮ ছবির NLL যখন শত শত nats, এই পার্থক্য প্রায় চোখেই পড়ে না। দ্বিতীয়টি: train ছবি হুবহু ফেরত দেওয়া একটি “মডেল”। নমুনা নিখুঁত, কিন্তু একটি নতুন ছবির probability শূন্য, ফলে test NLL অসীম।

তাহলে কোনটি কোথায়? Compression, anomaly detection বা ডেটার সম্ভাব্যতা যাচাইয়ে likelihood-ই আসল প্রশ্ন, কারণ সেখানে আপনার দরকার q(x)-এর একটি নির্ভরযোগ্য সংখ্যা। বাজারঘরের বিজ্ঞাপনের জন্য ছবি বানাতে আসল প্রশ্ন নমুনার মান আর বৈচিত্র্য, যা মাপতে লাগে FID-এর মতো feature-ভিত্তিক মাপ আর শেষে মানুষের মূল্যায়ন। একটি ভালো অভ্যাস হলো, যে কাজে মডেল লাগবে, সেই কাজের মাপকাঠি আগে ঠিক করা, তারপর মডেল বাছা। এ কারণেই পর্ব ৮১-তে মূল্যায়নের জন্য একটি পুরো পর্ব রাখা হয়েছে।

মূল উপলব্ধি: একটি সংখ্যা দিয়ে generative model বিচার করা যায় না; কাজ ঠিক করে, কোন সংখ্যাটি গুরুত্বপূর্ণ।

প্র ০২GAN likelihood ছাড়াই কীভাবে শেখে, আর তাতে কী হারায়?

২০১৪ সালে Ian Goodfellow ও সহকর্মীরা একটি ভিন্ন প্রশ্ন করেন: q(x) না লিখেই কি q-কে pdata-র কাছে আনা যায়? উত্তর ছিল দুটি network-এর প্রতিযোগিতা। Generator এলোমেলো z থেকে একটি x বানায়; discriminator নামের একটি classifier বলার চেষ্টা করে x আসল না বানানো। Generator শেখে discriminator-কে ধোঁকা দিতে। এখানে কোথাও q(x)-এর সূত্র নেই, শুধু নমুনা তোলার একটি যন্ত্র — তাই একে বলে implicit মডেল।

লাভটা বড়। প্রথমত, মডেলের আকার নিয়ে কোনো বাধা নেই: chain rule-এর ক্রম, উল্টানো যায় এমন রূপান্তর বা Jacobian-এর হিসাব — কিছুই লাগে না। দ্বিতীয়ত, নমুনা এক ধাপে আসে, তাই দ্রুত। তৃতীয়ত, discriminator নিজেই একটি শেখা মাপকাঠি, যা pixel-ভিত্তিক loss-এর মতো সব ভুলকে সমান ধরে না; এজন্য GAN-এর ছবি প্রায়ই ঝকঝকে দেখায়।

দামও আছে। (১) q(x) জানা নেই, তাই একটি নির্দিষ্ট ছবি কতটা সম্ভব তা বলা যায় না, likelihood-ভিত্তিক anomaly detection বা compression-এ সরাসরি কাজে লাগে না। (২) Maximum likelihood-এর mode-covering চাপ নেই; generator কয়েকটি “নিরাপদ” ধরন শিখে বাকিগুলো বাদ দিতে পারে — mode collapse. বাজারঘরের ক্ষেত্রে, মডেল হয়তো শুধু শার্ট আর জুতা বানায়, ব্যাগ কখনো নয়। (৩) প্রশিক্ষণ দুই খেলোয়াড়ের খেলা, একটি loss কমানো নয়; তাই অস্থিরতা, দোলাচল আর seed-নির্ভরতা বেশি। এগুলো পর্ব ৭৭ আর ৭৮-এ মাপা হবে।

মূল উপলব্ধি: Likelihood ছেড়ে দিলে নকশার স্বাধীনতা আর গতি আসে, কিন্তু মাপার একটি নির্ভরযোগ্য সংখ্যা আর সব ধরন ঢাকার নিশ্চয়তা হারায়।

প্র ০৩Autoregressive মডেলে pixel-এর ক্রম কি গুরুত্বপূর্ণ? আর এত ধীর হয়েও কেন এই পরিবার আজ এত বিস্তৃত?

গাণিতিকভাবে ক্রম গুরুত্বহীন: chain rule যেকোনো ক্রমে সত্য, তাই যথেষ্ট শক্তিশালী মডেল যেকোনো ক্রমে একই বণ্টন ধরতে পারে। বাস্তবে মডেল সীমিত, আর ক্রম ঠিক করে কোন শর্তসাপেক্ষ অংশ শেখা সহজ। ছবিতে সারি ধরে পড়ার (raster) ক্রমে একটি pixel-এর ওপরের আর বাঁয়ের প্রতিবেশী জানা থাকে, যা অনুমানে সাহায্য করে। ২০১৫ সালে MADE-এর প্রবন্ধে Germain ও সহকর্মীরা একাধিক এলোমেলো ক্রমে একই network শেখানোর কৌশলও দেখান, যাকে order-agnostic training বলা হয়।

ধীর গতির হিসাব এই পর্বের run-এ মাপা: ১০০টি ২৮×২৮ নমুনা তুলতে MADE-এর লেগেছে ১.১ থেকে ১.৮ সেকেন্ড, PCA + GMM-এর প্রায় ০.০৩ সেকেন্ড, যদিও MADE-এর likelihood অনেক ভালো। পার্থক্যের কারণ ৭৮৪টি pixel-এর জন্য ৭৮৪টি পরপর forward pass চালানো। ২০১৬ সালে DeepMind-এর PixelCNN আর WaveNet একই ধারণা ছবি আর শব্দে নিয়ে যায়; WaveNet-এর প্রথম রূপে প্রতি সেকেন্ড অডিওর জন্য হাজার হাজার ধারাবাহিক ধাপ লাগত, পরে দ্রুত করার আলাদা কৌশল আসে।

তবু পরিবারটি বিস্তৃত, কারণ তিনটি সুবিধা একসাথে খুব কম মডেলে আছে: হুবহু likelihood (মাপা আর তুলনা সহজ), সরল ও স্থিতিশীল প্রশিক্ষণ (শুধু cross-entropy), আর ডেটা ও মডেল বাড়ালে নিয়মিত উন্নতি। Text-এ D ছবির তুলনায় ছোট আর token-এ ভাগ করা স্বাভাবিক, তাই আজকের বড় ভাষা মডেলগুলো মূলত autoregressive (পর্ব ৪৯)। ধীর গতির সমাধানে KV cache, ছোট মডেল দিয়ে আগাম অনুমান (speculative decoding) — এগুলো আগের মডিউলে এসেছে।

মূল উপলব্ধি: Autoregressive পরিবার গতি বিক্রি করে সরলতা আর হুবহু likelihood কেনে; কোন ডেটায় সেই দাম সহনীয়, সেটিই আসল প্রশ্ন।

প্র ০৪MNIST-এ Gaussian mixture স্বাধীন Bernoulli-র চেয়েও খারাপ এলো, অথচ Fashion-MNIST-এ ভালো। কেন? এই সংখ্যাগুলো কি একই মাপে?

প্রায় একই মাপে, কিন্তু পুরোপুরি নয়, আর সেখানেই উত্তর। Bernoulli আর AR মডেল discrete: তারা প্রতিটি ০/১ ছবির probability সরাসরি দেয়। GMM continuous: সে একটি density দেয়, যা একটি বিন্দুতে একের বেশিও হতে পারে। দুটিকে মেলাতে ০/১ মানে [০, ১) থেকে uniform noise যোগ করা হয়েছে, যাতে প্রতিটি ছবি একক আয়তনের একটি ঘর হয়ে যায়। Theis ও সহকর্মীদের ২০১৬-র প্রবন্ধ অনুযায়ী এভাবে মাপা NLL ঘরের ওপর continuous মডেলের সমাকলন থেকে পাওয়া discrete মডেলের আসল NLL-এর একটি ঊর্ধ্বসীমা (গড়ে)। অর্থাৎ GMM-এর সংখ্যা তার পক্ষে সবচেয়ে খারাপ দিকে ঝুঁকে আছে।

কতটা খারাপ দিকে? একটি কোণের pixel ভাবুন যা সব ছবিতে ০. Noise যোগের পরে সেটি [০, ১)-এ uniform, যার আসল discrete NLL ০ bit. কিন্তু একটি Gaussian এই uniform-কে সবচেয়ে ভালো যা ধরতে পারে, তা variance ১/১২-এর Gaussian, যার গড় NLL ½ ln(২πe/১২) ≈ ০.১৭৬ nats ≈ ০.২৫ bits. কেবল আকারের অমিলের জন্য প্রতি এমন pixel-এ প্রায় ০.২৫ bit জরিমানা। MNIST-এর বেশিরভাগ pixel পটভূমি, যেখানে স্বাধীন Bernoulli প্রায় শূন্য খরচে কাজ সারে; তাই সেখানে এই জরিমানা GMM-কে পেছনে ফেলে। Fashion-MNIST-এ জামা আর জুতার বড় ভরাট অংশ আছে, pixel-গুলোর মধ্যে সম্পর্ক অনেক বেশি; PCA + GMM সেই সম্পর্ক ধরে জরিমানা পুষিয়ে নেয়।

শিক্ষাটা সাধারণ: ভিন্ন পরিবারের মডেলের likelihood তুলনায় আগে দেখতে হয় তারা একই “ঘর”-এর ওপর probability দিচ্ছে কি না। ৮-bit ছবির flow আর diffusion মডেলের প্রবন্ধে এজন্যই dequantization-এর নিয়ম আলাদা করে লেখা থাকে।

মূল উপলব্ধি: একই একক (bits/dim) মানেই একই মাপ নয়; ডেটার রূপ আর মডেলের ধরন মিলিয়ে তবেই তুলনা।

প্র ০৫শুধু classification করতে হলেও কি generative model কখনো ভালো? Discriminative-এর চেয়ে কখন এগিয়ে?

এই প্রশ্নের একটি বিখ্যাত উত্তর দেন Andrew Ng আর Michael Jordan, ২০০১ সালের NeurIPS (তখন NIPS)-এ প্রকাশিত প্রবন্ধে: তাঁরা Naive Bayes (generative, AI-এর ভিত্তি কোর্সের পর্ব ৫১) আর logistic regression (discriminative)-কে তুলনা করেন। তাঁদের বিশ্লেষণ আর পরীক্ষা অনুযায়ী, খুব কম ডেটায় generative মডেল প্রায়ই দ্রুত তার সেরা অবস্থায় পৌঁছে আগে ভালো করে, কিন্তু ডেটা বাড়লে discriminative মডেল সাধারণত কম ভুলে থামে। কারণ generative মডেল p(x | y)-এর ওপর একটি শক্ত অনুমান (যেমন বৈশিষ্ট্যগুলো স্বাধীন) চাপায়; ডেটা কম থাকলে সেই অনুমান সাহায্য করে, বেশি থাকলে তা-ই সীমা হয়ে দাঁড়ায়। এটি একটি নির্দিষ্ট জোড়ার ফল, সব ক্ষেত্রের নিয়ম নয়।

Classification-এর বাইরে generative মডেলের বাড়তি সুবিধাও আছে। (১) Label ছাড়া ডেটা থেকে শেখা যায়, যা semi-supervised শেখায় কাজে লাগে। (২) অচেনা ইনপুট চেনার সম্ভাবনা: discriminative মডেল একটি বাঘের ছবিকেও আত্মবিশ্বাসের সঙ্গে “জুতা” বলে দিতে পারে, অথচ p(x) জানা থাকলে বলা যায় “এমন ছবি আগে দেখিনি”। তবে সাবধান: গবেষণায় দেখা গেছে deep generative মডেলও কখনো কখনো অচেনা ডেটাসেটকে চেনাটির চেয়ে বেশি likelihood দেয় (Nalisnick ও সহকর্মীরা, ICLR ২০১৯-এর প্রবন্ধ “Do Deep Generative Models Know What They Don't Know?”), তাই এই ব্যবহার সরল নয়। (৩) নকল ডেটা বানিয়ে training set বাড়ানো, যদিও তাতে মডেলের নিজস্ব পক্ষপাতও ঢুকে পড়ে।

আজকের বড় মডেলে সীমারেখা ঝাপসা: একটি বড় ভাষা মডেল generative ভাবে শেখে, কিন্তু তাকে দিয়ে classification-ও করানো হয়, prompt-এ প্রশ্ন রেখে।

মূল উপলব্ধি: Generative বনাম discriminative কোনো দল নয়, একটি trade-off: শক্ত অনুমান কম ডেটায় সাহায্য করে, বেশি ডেটায় বাধা হয়; আর p(x) জানা অতিরিক্ত কাজের দরজা খোলে।

অনুশীলন

হাতে হিসাব, আউটপুট অনুমান, একটি নকশার সমালোচনা আর একটি কোড পরিবর্তন।

  1. হাতে হিসাব: একটি মডেল ০/১ করা ২৮×২৮ ছবির test অংশে গড় NLL দেয় ৯৫ nats. (ক) bits/dim কত? (খ) একই ডেটায় স্বাধীন Bernoulli-র NLL ২০৫.৮ nats হলে প্রতি ছবিতে কত bit বাঁচল? (গ) KL উদাহরণে θ = ০.৪৫ হলে গড় log-likelihood আর KL কত (p̂ = (০.৫, ০.৩, ০.২), q = (θ, (১−θ)/২, (১−θ)/২))?

    (ক) ৯৫ ÷ ০.৬৯৩১ = ১৩৭.০৬ bits; ÷ ৭৮৪ = ০.১৭৫ bits/dim.

    (খ) ২০৫.৮ ÷ ০.৬৯৩১ = ২৯৬.৯১ bits. বাঁচল ২৯৬.৯১ − ১৩৭.০৬ ≈ ১৫৯.৯ bits প্রতি ছবি (প্রতি pixel-এ প্রায় ০.২০৪ bit).

    (গ) ℓ = ০.৫ ln ০.৪৫ + ০.৫ ln ০.২৭৫ = ০.৫ × (−০.৭৯৮৫) + ০.৫ × (−১.২৯১০) = −১.০৪৪৮. KL = −H(p̂) − ℓ = −১.০২৯৭ + ১.০৪৪৮ = ০.০১৫১ nats — θ = ০.৫-এর ০.০১০১-এর চেয়ে বেশি, θ = ০.৪-এর ০.০৩০৫-এর চেয়ে কম, যেমনটা হওয়ার কথা।

  2. আউটপুট অনুমান: তিন pixel-এর মডেলটি: p(x1=১) = ০.৩; p(x2=১ | x1) = ০.২ (x1=০) বা ০.৭ (x1=১); p(x3=১ | x1, x2) = ০.১, ০.৫, ০.৪, ০.৯ ((০,০), (০,১), (১,০), (১,১) ক্রমে)। নিয়ম: u < p হলে pixel = ১. নিচের কোড কী ছাপবে?
    import numpy as np
    P1, P2 = 0.3, np.array([0.2, 0.7])
    P3 = np.array([[0.1, 0.5], [0.4, 0.9]])
    u = (0.10, 0.90, 0.30)
    a = int(u[0] < P1); b = int(u[1] < P2[a]); c = int(u[2] < P3[a, b])
    pa = P1 if a else 1 - P1
    pb = P2[a] if b else 1 - P2[a]
    pc = P3[a, b] if c else 1 - P3[a, b]
    print((a, b, c), round(pa * pb * pc, 4))

    ০.১০ < ০.৩, তাই a = ১. তারপর p = ০.৭; ০.৯০ < ০.৭ মিথ্যা, তাই b = ০. এবার (১, ০) ঘরের ০.৪; ০.৩০ < ০.৪, তাই c = ১. Probability = ০.৩ × (১ − ০.৭) × ০.৪ = ০.৩ × ০.৩ × ০.৪ = ০.০৩৬. আউটপুট: (1, 0, 1) 0.036.

  3. নকশা ও সমালোচনা: একটি দল লিখেছে: “আমাদের নতুন মডেলের bits/dim ০.১৪, একটি বিখ্যাত প্রবন্ধে MNIST-এ ০.১২ — আমরা প্রায় সমান। আমাদের GAN-এর likelihood-ও ভালো, কারণ নমুনাগুলো দেখতে খুব বাস্তব।” তিনটি সমস্যা চিহ্নিত করুন আর সঠিক পদ্ধতি লিখুন।
    • ডেটা আর ০/১ নিয়ম: প্রবন্ধে MNIST কীভাবে ০/১ করা হয়েছে (নির্দিষ্ট সীমা, না এলোমেলো) আর কোন test ভাগ, তা মিলিয়ে না দেখলে দুটি সংখ্যা তুলনাযোগ্য নয়। সম্ভব হলে প্রবন্ধের মডেলটি নিজের pipeline-এ আবার চালানো।
    • GAN-এর likelihood: সাধারণ GAN implicit; q(x)-এর সূত্র নেই, তাই “likelihood ভালো” দাবির কোনো ভিত্তি নেই। নমুনা বাস্তব দেখানো likelihood-এর প্রমাণ নয় (Theis ও সহকর্মী, ২০১৬)।
    • একটি run: seed, val-এ বাছাই আর অনিশ্চয়তা উল্লেখ নেই। সঠিক পদ্ধতি: একই ডেটা ও ভাগে কয়েকটি seed, val দিয়ে বাছা, test একবার, সঙ্গে স্বাধীন Bernoulli-র মতো baseline; নমুনার মান আলাদা মাপকাঠিতে (পর্ব ৮১), আর মুখস্থ যাচাই।
  4. কোড পরিবর্তন: digits-এর bits/dim ব্লকে pixel-এর ক্রম উল্টে দিন (শেষ pixel আগে)। কোথায় কী বদলাবেন? Autoregressive মডেলের bits/dim কি একই থাকবে? স্বাধীন মডেলেরটি?

    ভাগ করার পরেই এক লাইন যথেষ্ট: tr, te = tr[:, ::-1], te[:, ::-1]. বাকি কোড অপরিবর্তিত, কারণ লুপ সবসময় “আগের d টি column” দেখে।

    স্বাধীন মডেলের ফল হুবহু একই থাকবে, কারণ গুণফলের ক্রম বদলালে গুণফল বদলায় না। Autoregressive মডেলের ফল সামান্য বদলাবে: chain rule যেকোনো ক্রমে সত্য, কিন্তু logistic regression একটি সীমিত পরিবার, আর ক্রম বদলালে প্রতিটি শর্তসাপেক্ষ অংশ শেখার কঠিনতা বদলায়। আমরা ডেস্কটপে (scikit-learn 1.9.1, পাতার 1.8.0 নয়) চালিয়ে দেখেছি: autoregressive মডেলের test NLL ১৮.১২ থেকে ১৮.০৮ nats, অর্থাৎ ০.৪০৮ থেকে ০.৪০৭ bits/dim; স্বাধীন মডেলের ২৫.২৬ nats অপরিবর্তিত। এখানে পার্থক্য খুব ছোট, কিন্তু অন্য ডেটায় বা অন্য মডেলে বেশি হতে পারে।

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

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

ক্রমএকটি likelihood-ভিত্তিক generative model শেখানো আর মাপার ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. ডেটাকে train, val আর test-এ ভাগ করা
  2. প্রক্রিয়াকরণের নিয়ম (যেমন ০/১ করার সীমা) একবার ঠিক করে সব ভাগে একইভাবে প্রয়োগ
  3. Train অংশে গড় log-likelihood বাড়িয়ে parameter শেখা
  4. Val অংশ দিয়ে hyperparameter আর সেরা epoch বাছা
  5. সবশেষে test অংশে NLL মেপে bits/dim-এ রূপান্তর

একটি মডেল শুধু p(y | x) শেখে, যেখানে x একটি পণ্যের ছবি আর y তার শ্রেণি। এটি কোন ধরনের মডেল?

  • Generative model
  • Discriminative model
  • Implicit generative model
  • Energy-based model

p̂ হলো train ডেটার অভিজ্ঞতামূলক বণ্টন আর qθ মডেল। গড় log-likelihood সর্বোচ্চ করা কোনটির সমান?

  • KL(qθ ‖ p̂) কমানো
  • H(p̂) বাড়ানো
  • KL(p̂ ‖ qθ) কমানো
  • qθ-এর entropy বাড়ানো

একটি মডেলের ২৮×২৮ ০/১ ছবিতে গড় test NLL ১০৮.৭ nats. Bits per dimension প্রায় কত? (ln ২ ≈ ০.৬৯৩১)

  • ০.২০০
  • ০.১৩৯
  • ১৫৬.৮
  • ০.০৯৬

৭৮৪ pixel-এর autoregressive মডেলে training-এ একটি forward pass-এ সব শর্তসাপেক্ষ probability পাওয়া যায়, কিন্তু একটি নমুনা তুলতে সাধারণত ৭৮৪টি ধাপ লাগে। কেন?

  • নমুনার সময় mask খুলে ফেলতে হয়
  • Bernoulli নমুনা তোলা ব্যয়বহুল
  • Pixel-এর ক্রম raster না হলে এমন হয়
  • নমুনায় প্রতিটি pixel আগের pixel-গুলোর মানের ওপর নির্ভর করে, যা তখনো তোলা হয়নি

কোন পরিবারের সাধারণ রূপ কোনো x-এর জন্য q(x) বা তার কোনো সীমা সরাসরি দেয় না?

  • Normalizing flow
  • GAN
  • VAE
  • Autoregressive model

একটি ভালো মডেল q-কে ১% ওজন আর পুরোপুরি noise-কে ৯৯% ওজন দিয়ে মেশানো হলো। D = ৭৮৪ মাত্রার ছবিতে প্রতি ছবির log-likelihood সর্বোচ্চ কতটা কমতে পারে, আর এর শিক্ষা কী?

  • অসীম; likelihood সবসময় নমুনার মান ধরে
  • ৭৮৪ × ln ১০০ nats; likelihood খুব সংবেদনশীল
  • বড়জোর ln ১০০ ≈ ৪.৬ nats; likelihood আর নমুনার মান আলাদা জিনিস
  • একটুও কমবে না, বরং বাড়বে

শব্দকোষ

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

মনে রাখুন

  1. Generative model শেখে p(x); তার তিন কাজ: নমুনা, density মাপা, representation.
  2. পরিবার ভাগ হয় likelihood দিয়ে: হুবহু (autoregressive, flow), সীমা (VAE), নেই (GAN), noise সরানো (diffusion), Z অজানা (energy-based).
  3. Maximum likelihood = KL(p̂ ‖ q) কমানো; এর ফলে মডেল সব ডেটা-অঞ্চল ঢাকতে চায় (mode-covering).
  4. bits/dim = NLLnats ÷ (D ln ২); তুলনা শুধু একই ডেটা, একই প্রক্রিয়াকরণ আর একই test ভাগে।
  5. Autoregressive: training দ্রুত, নমুনা ধীর; আর ভালো likelihood মানেই ভালো নমুনা নয়।

পরের পর্বে: ছবিকে একটি ছোট কোডে চেপে আবার ফিরিয়ে আনা — autoencoder. সেখান থেকেই latent space-এর যাত্রা শুরু।

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

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