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

Exponential family আর GLM — Poisson, binomial আর link function

Exponential Family and Generalized Linear Models — Poisson Regression with an Exposure Offset, IRLS, Rate Ratios, Deviance and Overdispersion

Taught in Bangla, this lesson writes Bernoulli, Poisson and Gaussian in exponential-family form, explains the three parts of a GLM and canonical links, and fits a Poisson regression with an exposure offset on fictional district data. Hand-written IRLS, statsmodels and scikit-learn agree in in-browser runs; it reads rate ratios with confidence intervals, checks deviance and Pearson dispersion, and handles overdispersion with negative binomial.

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

  • Bernoulli, Poisson আর Gaussian-কে exponential family-র রূপে লেখা, আর log-partition-এর derivative থেকে গড় ও variance বের করা
  • GLM-এর random component, linear predictor আর link আলাদা করে চেনা, আর canonical link কী তা বোঝা
  • Exposure offset-সহ Poisson regression লেখা, IRLS হাতে চালানো, আর statsmodels ও scikit-learn-এর সঙ্গে মেলানো
  • Coefficient-কে rate ratio হিসেবে পড়া, confidence interval-সহ, আর তার সাধারণ ভুল ব্যাখ্যা এড়ানো
  • Deviance, Pearson residual আর Pearson χ²/df দিয়ে model যাচাই, আর overdispersion-এ negative binomial বা অন্য প্রতিকার বাছা

AI-এর ভিত্তি কোর্সের পর্ব ৩৬-এ Bernoulli, binomial আর Poisson বণ্টন দেখেছেন, পর্ব ৫৮-এ linear regression আর পর্ব ৫৯-এ logistic regression. এই কোর্সের পর্ব ১১-এ logistic-এর Hessian XᵀSX বের করেছিলেন, আর পর্ব ১৫-এ শুনেছিলেন যে পরিসংখ্যানের library এই Newton-এর ধাপকে IRLS নামে চালায়। এই পর্ব সেই সুতোগুলো এক জায়গায় বাঁধে: linear, logistic আর Poisson regression আসলে একটিই model-এর তিন রূপ, যার নাম generalized linear model. Likelihood আর MLE-এর মূলনীতি পর্ব ১৮ (Maximum likelihood আর MAP — model fit করার মূলনীতি)-এর বিষয়; এখানে শুধু যতটুকু লাগে ততটুকু। চালানো যায় এমন চারটি ব্লক পাতাতেই চলে (ব্রাউজারের Python, NumPy, SciPy, statsmodels আর scikit-learn); প্রথমবার প্যাকেজ নামতে কয়েক সেকেন্ড লাগে, scikit-learn আর statsmodels-এ প্রায় দশ সেকেন্ড।

১ · গোনা সংখ্যা আর হ্যাঁ/না — সাধারণ regression কোথায় আটকায়

পাড়ার দুটি চায়ের দোকানের কথা ভাবুন। একটি গলির ভেতরে, দিনে গড়ে ১০ কাপ বিক্রি; অন্যটি বাসস্ট্যান্ডে, দিনে গড়ে ২০০ কাপ। গলির দোকানে কোনো দিন ৭ কাপ, কোনো দিন ১৩: ওঠানামা মোটামুটি ±৩. বাসস্ট্যান্ডে ওঠানামা ±১৪-এর মতো। বিক্রি কখনো ঋণাত্মক হয় না, পূর্ণসংখ্যাই হয়, আর গড় যত বড় ওঠানামাও তত বড়। আবার বৃষ্টির দিনে দুই দোকানেই বিক্রি “২০% কমে”, “২০ কাপ কমে” না: প্রভাবটি গুণের, যোগের নয়। আর কোনো দোকান মাসে ১০ দিন বন্ধ থাকলে তার মাসের মোট বিক্রি তুলনা করতে হয় খোলা দিনের হিসাবে। এই চারটি কথাই এই পর্বের পুরো গল্প।

Linear regression ধরে নেয় y = Xβ + ভুল, যেখানে ভুল সব জায়গায় একই মাপের normal বণ্টন। গোনা সংখ্যার (count) লক্ষ্যে তিনটি অনুমানই ভাঙে: অনুমান ঋণাত্মক হতে পারে, ভুলের ছড়ানো গড়ের সঙ্গে বাড়ে (heteroscedasticity), আর feature-এর প্রভাব যোগ নয়, গুণ। হ্যাঁ/না লক্ষ্যে অবস্থা আরও খারাপ: সম্ভাবনা ০ আর ১-এর বাইরে চলে যায়। সমাধান তিনটি আলাদা প্রশ্নে ভাগ করা: y কোন বণ্টন থেকে আসে? feature-গুলো কীভাবে একত্র হয়? সেই যোগফল গড়ের সঙ্গে কীভাবে জোড়া? এই ভাগের নামই generalized linear model (GLM).

লক্ষ্যের ধরনবণ্টনসাধারণ linkবাজারঘরে উদাহরণ
যেকোনো বাস্তব সংখ্যাGaussianidentity: μ = ηদামের ছাড় শতাংশে বদল
হ্যাঁ/না, বা n-এর মধ্যে কয়টিBernoulli / binomiallogit: log(μ/(1 − μ)) = ηঅর্ডার ফেরত এল কি না; জেলার ১০০ অর্ডারে কয়টি ফেরত
গোনা সংখ্যা ০, ১, ২, …Poisson (বা negative binomial)log: log μ = ηজেলায় মাসে কয়টি অর্ডার
ধনাত্মক, ডানে লম্বা লেজGamma, Tweedielogডেলিভারির সময়, ঝুড়ির মোট দাম

টেবিলের প্রথম তিনটি বণ্টন একটি পরিবারের সদস্য। সেই পরিবারটি আগে চিনে নিলে বাকি সব যন্ত্র, অর্থাৎ fit করার পদ্ধতি, residual আর যাচাই, একবারেই সবার জন্য লেখা যায়।

২ · Exponential family — এক ছাঁচে Bernoulli, Poisson আর Gaussian

একটি বণ্টন exponential family-র সদস্য, যদি তার সম্ভাবনা (বা density) এই রূপে লেখা যায়:

p(y | θ) = h(y) · exp( θ · T(y) − A(θ) )

চারটি অংশ। θ হলো natural parameter: বণ্টনের এমন একটি parameter, যা y-এর সঙ্গে শুধু গুণ হয়ে সূচকে বসে। T(y) হলো sufficient statistic: ডেটার যে সারাংশ জানলেই θ সম্পর্কে ডেটার সব তথ্য পাওয়া যায়; এই পর্বের তিনটি বণ্টনেই T(y) = y. A(θ) হলো log-partition function: সম্ভাবনার যোগফল ১ রাখার জন্য যে log-সংশোধন লাগে। আর h(y) θ-মুক্ত একটি ভিত্তি অংশ। Poisson-কে এই রূপে আনার কাজটি ধাপে ধাপে দেখুন।

Poisson(λ): দিনে গড়ে λ = 4টি অর্ডারy = 0, 1, 2, …

            
  1. শুরু পরিচিত সূত্রে। দিনে গড়ে ৪টি অর্ডার এলে ঠিক ২টি আসার সম্ভাবনা ১৬ × e−৪ / ২ ≈ ০.১৪৬৫.
  2. সব exp-এর ভেতরে। λy-কে exp(y·log λ) লিখলে y আর parameter সূচকে শুধু গুণফল হিসেবে থাকে; y! আলাদা হয়ে বাইরে যায়।
  3. ছাঁচে বসানো। natural parameter θ = log λ, অর্থাৎ Poisson-এর “স্বাভাবিক” মাপকাঠি গড়ের log. A(θ) = eθ, আর h(y) = ১/y!.
  4. Derivative-এর জাদু। A(θ)-কে একবার derivative করলে গড়, দুবার করলে variance. Poisson-এ দুটিই λ = ৪, তাই গড় আর variance সমান হতেই হবে।
  5. Sufficient statistic. পাঁচ দিনের likelihood-এ ডেটা ঢোকে শুধু Σy = ২০ হয়ে। দিনগুলোর ক্রম বা আলাদা মান λ সম্পর্কে নতুন কিছু বলে না; MLE λ̂ = ২০/৫ = ৪.

A′(θ) = গড় কেন হয়? সম্ভাবনার যোগফল ১, তাই eA(θ) = Σ h(y)eθy. দুই দিকে θ-এর সাপেক্ষে derivative নিলে A′(θ)eA(θ) = Σ y·h(y)eθy, অর্থাৎ A′(θ) = Σ y·p(y) = E[y]. আরেকবার derivative নিলে A″(θ) = Var(y). Variance কখনো ঋণাত্মক নয়, তাই A″ ≥ ০: A(θ) একটি convex function. এই ছোট তথ্যটিই পরে নিশ্চিত করবে যে canonical link-এর GLM-এ log-likelihood-এর একটিই চূড়া।

বণ্টনθ (natural parameter)A(θ)গড় μ = A′(θ)V(μ)
Bernoulli(p)log(p/(1 − p)), log-oddslog(1 + eθ)১/(১ + e−θ) = pμ(1 − μ)
Poisson(λ)log λeθeθ = λμ
Gaussian(μ, σ² জানা)μθ²/2θ = μ১ (গুণ σ²)

শেষ কলামের V(μ)-কে বলে variance function: গড় μ হলে variance কত। Gaussian-এ σ² আলাদা একটি সংখ্যা, যাকে বলে dispersion parameter φ; সাধারণ রূপে Var(y) = φ·V(μ). Bernoulli আর Poisson-এ φ = ১ ধরা থাকে, কারণ variance পুরোপুরি গড় দিয়েই ঠিক হয়। নিচের ব্লক তিনটি বণ্টনকে ছাঁচের রূপে লিখে SciPy-র নিজস্ব logpmf/logpdf-এর সঙ্গে মেলায়, আর central difference দিয়ে A′ ও A″ মেপে গড় ও variance-এর সঙ্গে তুলনা করে।

import numpy as np
from scipy import stats
from scipy.special import gammaln, expit

# p(x) = h(x)·exp(θ·T(x) − A(θ)); তিনটিতেই T(x) = x
fams = {
    "Bernoulli p=0.3": (np.log(0.3 / 0.7),            # θ = log-odds
                        lambda t: np.log1p(np.exp(t)),  # A(θ)
                        lambda x: 0 * x,                # log h(x)
                        np.array([0, 1]), stats.bernoulli(0.3).logpmf),
    "Poisson lam=4":   (np.log(4.0), np.exp,
                        lambda x: -gammaln(x + 1),
                        np.arange(0, 12), stats.poisson(4).logpmf),
    "Gaussian mu=2":   (2.0, lambda t: t ** 2 / 2,      # σ = 1 জানা
                        lambda x: -x ** 2 / 2 - 0.5 * np.log(2 * np.pi),
                        np.linspace(-2, 6, 9), stats.norm(2, 1).logpdf),
}
h = 1e-4
for name, (t, A, logh, x, ref) in fams.items():
    logp = logh(x) + t * x - A(t)                       # exponential-family রূপ
    gap = np.max(np.abs(logp - ref(x)))                 # scipy-র সঙ্গে পার্থক্য
    dA = (A(t + h) - A(t - h)) / (2 * h)                # A′(θ)
    d2A = (A(t + h) - 2 * A(t) + A(t - h)) / h ** 2     # A″(θ)
    print(f"{name:16s} theta={t:+.4f} A={A(t):.4f} max|gap|={gap:.0e}")
    print(f"{'':16s} A'(theta)={dA:.4f}  A''(theta)={d2A:.4f}")

print("Bernoulli: expit(theta) =", round(expit(np.log(0.3 / 0.7)), 4))
print("Poisson:   exp(theta)   =", round(np.exp(np.log(4.0)), 4))

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

Bernoulli p=0.3  theta=-0.8473 A=0.3567 max|gap|=2e-16
                 A'(theta)=0.3000  A''(theta)=0.2100
Poisson lam=4    theta=+1.3863 A=4.0000 max|gap|=0e+00
                 A'(theta)=4.0000  A''(theta)=4.0000
Gaussian mu=2    theta=+2.0000 A=2.0000 max|gap|=2e-15
                 A'(theta)=2.0000  A''(theta)=1.0000
Bernoulli: expit(theta) = 0.3
Poisson:   exp(theta)   = 4.0

max|gap| কলাম দেখায়, ছাঁচের রূপে লেখা log-সম্ভাবনা আর SciPy-র নিজস্ব হিসাবের সর্বোচ্চ পার্থক্য ১০−১৫-এর কাছাকাছি বা শূন্য: float64-এর rounding মাত্র, অর্থাৎ তিনটি রূপই হুবহু ঠিক। Bernoulli-তে θ = log(০.৩/০.৭) = −০.৮৪৭৩, আর A′(θ) = ০.৩০০০ = p, A″(θ) = ০.২১০০ = p(1 − p). Poisson-এ A, A′ আর A″ তিনটিই ৪. Gaussian-এ σ = ১ ধরায় A″ = ১ = variance. শেষ দুই লাইন একটি গুরুত্বপূর্ণ মিল দেখায়: θ থেকে গড়ে ফেরার function Bernoulli-তে sigmoid (expit), Poisson-এ exp. এই উল্টো function-দুটিই logistic আর Poisson regression-এর link-এর উল্টো রূপ; পরের অংশে এটিই “canonical link”.

৩ · GLM-এর তিন অংশ — random component, linear predictor আর link

John Nelder আর Robert Wedderburn ১৯৭২ সালে একটি প্রবন্ধে দেখান, linear, logistic, probit, Poisson আর আরও কয়েকটি regression একই কাঠামোয় লেখা যায় আর একই algorithm দিয়ে fit করা যায়। কাঠামোর তিনটি অংশ:

১. Random component

yi exponential family-র একটি বণ্টন থেকে আসে, গড় μi. যেমন অর্ডারের সংখ্যা ~ Poisson(μi).

২. Linear predictor

ηi = β0 + β1xi1 + … = xiᵀβ. “Linear” কথাটি এখানেই: β-তে linear, μ-তে নয়।

৩. Link function

g(μi) = ηi, অর্থাৎ μi = g−1(ηi). Link গড়ের সীমিত পরিসরকে (যেমন μ > ০) পুরো বাস্তব রেখায় খুলে দেয়।

Random component ঠিক করে ভুলের আকৃতি, linear predictor ঠিক করে feature-গুলো কীভাবে মেশে, আর link function দুটিকে জোড়ে। Poisson-এ log link নিলে μ = eη সবসময় ধনাত্মক, আর η-তে যোগ মানে μ-তে গুণ: চায়ের দোকানের “২০% কম” ঠিক এভাবেই আসে। নিচের ছবিতে বাজারঘরের একটি জেলার একটি সারি পুরো পথ পেরোয়।

Poisson GLM-এর পথ — feature থেকে দেখা অর্ডার পর্যন্ত

একটি জেলা: promo = 1, dist = 1.11 (×100 km), days = 9; fit হওয়া β = (1.2975, 0.3170, −0.6140)

Path of one row through a Poisson GLM with log link and offset feature x promo = 1, dist = 1.11 linear predictor η β0 + β1·promo + β2·dist = 0.933 + offset log(9) = 2.197 η = 3.130 inverse link: μ = e^η μ = 22.9 (দিনে 2.54 × 9) random component Y ~ Poisson(22.9), Var 22.9 দেখা মান y = 21 β দিয়ে যোগফল link-এর উল্টো বণ্টন থেকে আসে
ছবি ৩.১ — সবুজ বাক্সে যা ঘটে তা সাধারণ linear regression-এর মতোই; নতুন শুধু কমলা (link) আর বেগুনি (বণ্টন) বাক্স। Offset-ও linear predictor-এ ঢোকে, কিন্তু তার coefficient fit হয় না, ১-এ বাঁধা।
সারি: orders = 21, days = 9, promo = 1, dist = 1.11; β = (1.2975, 0.3170, −0.6140)
অংশ
সূত্র
হিসাব
ফল
  1. বণ্টন বাছা। অর্ডার গোনা সংখ্যা, তাই Poisson ধরা হলো; এতে variance = গড়, যা পরে যাচাই করতে হবে।
  2. Linear predictor. ক্যাম্পেইন চলেছে (১) আর hub থেকে দূরত্ব ১১১ km: ১.২৯৭৫ + ০.৩১৭০ − ০.৬১৪০ × ১.১১ = ০.৯৩৩০. এটি প্রতি দিনের অর্ডার-হারের log.
  3. Offset. জেলাটি মাসে মাত্র ৯ দিন খোলা ছিল। log ৯ = ২.১৯৭২ যোগ করলে η = ৩.১৩০২, পুরো মাসের প্রত্যাশিত অর্ডারের log.
  4. Link উল্টানো। μ = e৩.১৩০২ ≈ ২২.৮৮: দিনে ২.৫৪টি হারে ৯ দিনে প্রায় ২৩টি। μ কখনো ঋণাত্মক হতে পারে না।
  5. দেখা মানের সঙ্গে মেলানো। আসলে এসেছে ২১টি। Poisson(২২.৮৮)-এ ঠিক ২১ আসার সম্ভাবনা প্রায় ০.০৮০; ফারাক −১.৮৮, standard deviation √২২.৮৮ ≈ ৪.৭৮ দিয়ে ভাগ করলে Pearson residual −০.৩৯: একদম স্বাভাবিক।

Canonical link আর logistic regression-এর পরিচয়

যে link η-কে সরাসরি natural parameter θ বানায়, অর্থাৎ g(μ) = θ, তাকে বলে canonical link. অংশ ২-এর টেবিল থেকে পড়ে নিন: Gaussian-এর canonical link identity, Poisson-এর log, আর Bernoulli-র logit, log(μ/(1 − μ)). তাই logistic regression হলো Bernoulli random component + linear predictor + logit link-এর GLM; AI-এর ভিত্তি কোর্সের পর্ব ৫৯-এর sigmoid আসলে logit-এর উল্টো। জেলার ১০০টি অর্ডারের মধ্যে কয়টি ফেরত এল, এমন ডেটায় binomial GLM একই logit link নেয়, শুধু প্রতিটি সারির ওজন তার অর্ডারের সংখ্যা।

Canonical link-এর দুটি সুবিধা আছে। প্রথমত, log-likelihood β-তে concave, তাই চূড়া একটিই (ডেটা যথেষ্ট আর X-এর column-গুলো স্বাধীন হলে)। দ্বিতীয়ত, MLE-এর সমীকরণ খুব সরল হয়: Xᵀ(y − μ) = ০. Intercept থাকলে এর প্রথম সারি বলে Σyi = Σμ̂i: fit হওয়া model মোট অর্ডার হুবহু মেলায়। তবে canonical link বাধ্যতামূলক নয়। Binomial-এ probit বা complementary log-log, Gamma-তে canonical inverse link-এর বদলে log link খুব প্রচলিত, কারণ ব্যাখ্যা সহজ আর μ ধনাত্মক থাকার নিশ্চয়তা আছে। Link একটি model-অনুমান; ব্যাখ্যা আর যাচাইয়ের ফল দেখে বাছতে হয়।

৪ · Poisson regression আর exposure — জেলাভিত্তিক অর্ডারের model

এই পর্বের ডেটা: বাজারঘরের ৬৪টি জেলার এক মাসের অর্ডার, সব seed দিয়ে বানানো, কোনো আসল দোকানের নয়। প্রতিটি সারিতে মোট অর্ডার, মাসে কত দিন ডেলিভারি খোলা ছিল (নতুন শাখা বা বন্যার কারণে ৫ থেকে ৩০ দিন), সেই মাসে ছাড়ের ক্যাম্পেইন চলেছে কি না, আর ঢাকার hub থেকে দূরত্ব (১০০ km এককে)। ডেটা বানানোর আসল সূত্রে প্রতি দিনের হার e১.২ + ০.৪·promo − ০.৬·dist; model সেটি জানে না, ডেটা থেকে খুঁজে বের করবে। চারটি ব্লকই নিচের module থেকে ডেটা নেয়।

# বাজারঘর (কাল্পনিক দোকান): ৬৪ জেলার এক মাসের অর্ডার, seed দিয়ে বানানো
import numpy as np
import pandas as pd

def make_orders(seed=20, nb_alpha=None):
    rng = np.random.default_rng(seed)
    n = 64                                     # ৬৪টি জেলা, প্রতিটি এক সারি
    promo = rng.integers(0, 2, n)              # সেই মাসে ছাড়ের ক্যাম্পেইন (1 = হ্যাঁ)
    dist = rng.uniform(0.1, 4.0, n).round(2)   # ঢাকার hub থেকে দূরত্ব, 100 km এককে
    days = rng.integers(5, 31, n)              # মাসে কত দিন ডেলিভারি খোলা ছিল
    rate = np.exp(1.2 + 0.4 * promo - 0.6 * dist)   # প্রতি দিনের আসল গড় অর্ডার
    mu = rate * days
    if nb_alpha:                               # জেলাভেদে অদেখা পার্থক্য: overdispersion
        mu = mu * rng.gamma(1 / nb_alpha, nb_alpha, n)
    return pd.DataFrame({"orders": rng.poisson(mu), "days": days,
                         "promo": promo, "dist": dist})

Poisson regression-এর model:

ordersi ~ Poisson(μi),   log μi = log(daysi) + β0 + β1·promoi + β2·disti

log(days)-কে বলে offset, আর days হলো exposure: ঘটনা ঘটার “সুযোগ” কতটুকু ছিল। দুই দিকে log(days) বিয়োগ করলে log(μi/daysi) = Xβ, অর্থাৎ model আসলে প্রতি দিনের হার নিয়ে, অথচ ডেটা থাকে গোনা সংখ্যায়। তিনটি ভুল বিকল্পের সঙ্গে তুলনা করুন। (ক) days বাদ দিলে ৯ দিন খোলা জেলাকে ৩০ দিনের জেলার সঙ্গে সরাসরি তুলনা করা হয়, আর exposure-এর পার্থক্য promo বা dist-এর ঘাড়ে চাপে। (খ) days-কে সাধারণ feature বানালে model তার coefficient নিজে fit করে; সেটি ১ না হলে “দ্বিগুণ দিন খোলা মানে দ্বিগুণ অর্ডার” কথাটিই ভাঙে। (গ) orders/days-এ সাধারণ regression করলে ৫ দিনের জেলার অনিশ্চিত হার আর ৩০ দিনের জেলার স্থির হার সমান ওজন পায়। Offset-সহ Poisson এই তিন সমস্যাই এড়ায়।

Fit করা হয় maximum likelihood দিয়ে। log y! β-মুক্ত বলে বাদ দিলে log-likelihood:

ℓ(β) = Σi [ yiηi − eηi ],   ∇ℓ = Xᵀ(y − μ),   ∇²ℓ = −XᵀWX, W = diag(μi)

Gradient-এর রূপটি logistic regression-এর Xᵀ(y − p)-এর হুবহু ভাই, কারণ দুটিই canonical link. Hessian −XᵀWX সবসময় negative semidefinite, তাই ℓ concave আর পর্ব ১৫-এর Newton-এর পদ্ধতি এখানে খুব ভালো চলে। পরের অংশে সেই Newton-ই দেখবেন, পরিসংখ্যানবিদদের প্রিয় পোশাকে।

৫ · IRLS — Newton-এর ধাপ weighted least squares-এর রূপে

Newton-এর ধাপ βনতুন = β + (XᵀWX)−1Xᵀ(y − μ). এখানে Xᵀ(y − μ)-কে XᵀW·W−1(y − μ) লিখে একটু সাজালে:

βনতুন = (XᵀWX)−1 XᵀW z,   z = η + (y − μ)/μ

এটি ঠিক একটি weighted least squares-এর সমাধান: “লক্ষ্য” z-কে X দিয়ে regression, প্রতিটি সারির ওজন Wii = μi. z-কে বলে working response: বর্তমান η থেকে শুরু করে link-এর ঢাল ধরে y-এর দিকে একটি সরলরৈখিক সংশোধন। প্রতি ধাপে μ বদলায়, তাই ওজনও বদলায়: iteratively reweighted least squares, সংক্ষেপে IRLS. Offset থাকলে z থেকে offset বাদ দিয়ে regression করা হয়, কারণ offset-এর coefficient fit হয় না। যেকোনো link আর বণ্টনে সাধারণ সূত্র Wii = ১/(V(μi)·g′(μi)²) আর z = η + (y − μ)·g′(μ). Canonical link-এ এটি হুবহু Newton; অন্য link-এ এটি Hessian-এর জায়গায় তার প্রত্যাশিত মান (Fisher information) ব্যবহার করে, যাকে বলে Fisher scoring. নিচের stepper চারটি জেলার ছোট ডেটায় একটি ধাপ হাতে চালায়।

y = (2, 4, 6, 12), promo = (0, 0, 1, 1)Poisson, log link

            
  1. শুরু। β = ০ থেকে শুরু করা যায়, কিন্তু ডেটার গড়ের কাছ থেকে শুরু করলে ধাপ কম লাগে: intercept = log ৬, promo-র coefficient ০. (statsmodels-ও শুরুর μ হিসেবে y আর ȳ-এর গড় নেয়।)
  2. η আর μ. বর্তমান β-তে চারটি জেলার অনুমান একই, ৬টি অর্ডার। promo-র প্রভাব এখনো শূন্য ধরা।
  3. ওজন আর working response. Poisson-এর log link-এ ওজন W = μ = ৬. z হলো η-কে (y − μ)/μ দিয়ে ঠেলে দেওয়া: যে জেলায় ১২টি এসেছে, তার z অনেক ওপরে (২.৭৯১৮), যেখানে ২টি, তার z নিচে (১.১২৫১).
  4. Weighted least squares. X-এ শুধু intercept আর একটি ০/১ column, আর ওজন সমান, তাই সমাধান দলভিত্তিক গড়: β0 = ১.২৯১৮, β1 = ১.০০০০. এক ধাপেই promo-র প্রভাব শূন্য থেকে ১-এ উঠেছে।
  5. Convergence. নতুন μ-তে ওজন আর আলাদা থাকে না (৩.৬৪ বনাম ৯.৮৯)। চার ধাপে β = (log ৩, log ৩): promo ছাড়া জেলার হার ৩ = গড়(২, ৪), promo-সহ ৯ = গড়(৬, ১২), অনুপাত ৩. একটি ০/১ feature-এ Poisson MLE সবসময় দলের হারের অনুপাত দেয়।

এবার পুরো ৬৪ জেলায় তিনটি পথ পাশাপাশি: হাতে লেখা IRLS (β = ০ থেকে, ইচ্ছা করে দূর থেকে), statsmodels-এর GLM (default পদ্ধতি IRLS), আর scikit-learn-এর PoissonRegressor. শেষেরটিতে offset দেওয়ার সরাসরি parameter নেই; তার বদলে হার y/days-কে লক্ষ্য আর days-কে sample_weight করলে log-likelihood-এর β-নির্ভর অংশ হুবহু একই হয়, তাই উত্তরও একই। আর alpha=0 দিতেই হবে: scikit-learn-এ default alpha=1.0, অর্থাৎ L2 শাস্তি (পর্ব ১৭-এর ridge), যা coefficient-কে শূন্যের দিকে টানে।

import numpy as np
import pandas as pd
import statsmodels.api as sm
from sklearn.linear_model import PoissonRegressor
from bazar_district_orders import make_orders

df = make_orders()                                   # ৬৪ জেলা, এক মাস
X = np.column_stack([np.ones(64), df.promo, df.dist])
y, off = df.orders.to_numpy(float), np.log(df.days.to_numpy(float))

b = np.zeros(3)                                      # IRLS, শুরু β = 0
for it in range(1, 30):
    eta = X @ b + off                                # linear predictor + offset
    mu = np.exp(eta)                                 # inverse link
    z = (eta - off) + (y - mu) / mu                  # working response
    b_new = np.linalg.solve(X.T @ (mu[:, None] * X), X.T @ (mu * z))
    print(f"iter {it}: beta = {np.round(b_new, 6)}")
    if np.max(np.abs(b_new - b)) < 1e-10: break
    b = b_new

res = sm.GLM(y, X, family=sm.families.Poisson(), offset=off).fit()
skl = PoissonRegressor(alpha=0, solver="newton-cholesky", tol=1e-12)
skl.fit(df[["promo", "dist"]], y / df.days, sample_weight=df.days)

names = ["const", "promo", "dist"]
print(pd.DataFrame({"IRLS": b, "statsmodels": res.params,
      "sklearn": np.r_[skl.intercept_, skl.coef_]}, index=names).round(4))
ci = np.exp(res.conf_int())                          # 95% CI, তারপর exp
print(pd.DataFrame({"SE": res.bse, "rate_ratio": np.exp(res.params),
      "low": ci[:, 0], "high": ci[:, 1]}, index=names).round(4))
print("statsmodels IRLS iterations:", res.fit_history["iteration"])

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

iter 1: beta = [ 1.985814  0.600538 -0.859639]
iter 2: beta = [ 1.486857  0.433004 -0.701539]
iter 3: beta = [ 1.314721  0.331727 -0.622724]
iter 4: beta = [ 1.297698  0.317158 -0.614058]
iter 5: beta = [ 1.297526  0.316965 -0.613968]
iter 6: beta = [ 1.297526  0.316965 -0.613968]
iter 7: beta = [ 1.297526  0.316965 -0.613968]
         IRLS  statsmodels  sklearn
const  1.2975       1.2975   1.2975
promo  0.3170       0.3170   0.3170
dist  -0.6140      -0.6140  -0.6140
           SE  rate_ratio     low    high
const  0.0545      3.6602  3.2896  4.0727
promo  0.0483      1.3730  1.2489  1.5093
dist   0.0273      0.5412  0.5130  0.5709
statsmodels IRLS iterations: 5

IRLS-এর পথ: β = ০ থেকে শুরু বলে প্রথম ধাপ লক্ষ্য পেরিয়ে যায় (intercept ১.৯৯, promo ০.৬০), তারপর quadratic গতিতে স্থির হয়: চতুর্থ ধাপে তিন দশমিক, পঞ্চম ধাপে ছয় দশমিক মিলেছে। statsmodels শুরুর μ হিসেবে (y + ȳ)/2 নেয়, তাই ৫টি ধাপেই থেমেছে। তিনটি উত্তর চার দশমিক পর্যন্ত এক: β = (১.২৯৭৫, ০.৩১৭০, −০.৬১৪০). আসল সূত্রের (১.২, ০.৪, −০.৬)-এর কাছে, তবে হুবহু নয়: ৬৪টি জেলার নমুনায় এটুকু এলোমেলো পার্থক্য স্বাভাবিক, আর promo-র আসল মান ০.৪-এর rate ratio e০.৪ = ১.৪৯ নিচের CI-এর (১.২৪৮৯ থেকে ১.৫০৯৩) ভেতরে আছে। দ্বিতীয় টেবিল statsmodels-এর standard error (SE), আর coefficient ও তার ৯৫% CI-এর exp: এগুলোই rate ratio, পরের অংশের বিষয়। SE আসে শেষ ধাপের (XᵀWX)−1-এর কর্ণ থেকে; এই SE ঠিক থাকে কেবল যদি Poisson-এর “variance = গড়” অনুমান সত্য হয়, অংশ ৮ সেটি যাচাই করবে।

৬ · Coefficient পড়া — rate ratio আর তার অনিশ্চয়তা

Log link-এ η-তে β যোগ হওয়া মানে μ-তে eβ গুণ হওয়া। তাই Poisson regression-এর coefficient সরাসরি পড়া হয় না; পড়া হয় eβ, যাকে বলে rate ratio (চিকিৎসা-গবেষণায় incidence rate ratio, IRR): অন্য সব feature স্থির রেখে এই feature এক একক বাড়লে প্রতি একক exposure-এ ঘটনার হার কত গুণ হয়। Logistic regression-এর odds ratio-র সঙ্গে এর মিল স্পষ্ট, তবে odds নয়, সরাসরি হার।

Fit: β_promo = 0.3170 (SE 0.0483), β_dist = −0.6140 (SE 0.0273), β0 = 1.2975
প্রশ্ন
হিসাব
ফল
বাক্যে
  1. Rate ratio. e০.৩১৭০ = ১.৩৭৩: দূরত্ব আর খোলা দিন একই রেখে, ক্যাম্পেইনের মাসে দৈনিক অর্ডারের হার প্রায় ৩৭% বেশি। “০.৩২টি বেশি অর্ডার” বলা ভুল।
  2. Confidence interval. log মাপে ০.৩১৭০ ± ১.৯৬ × ০.০৪৮৩ = ০.২২২৩ থেকে ০.৪১১৭, তারপর exp করলে ১.২৪৯ থেকে ১.৫০৯. CI exp করার পর অসমান হয়: ১.৩৭৩ থেকে নিচে ০.১২৪, ওপরে ০.১৩৬.
  3. Continuous feature. প্রতি ১০০ km-এ হার ০.৫৪১ গুণ, অর্থাৎ প্রায় ৪৬% কম। ৫০ km-এ গুণক e−০.৩০৭০ = ০.৭৩৬, আধাআধি নয়: গুণকের হিসাবে দূরত্ব অর্ধেক মানে গুণকের বর্গমূল।
  4. সংখ্যায় অনুবাদ। ৩০ দিনে hub-এর কাছের (৫০ km) জেলায় ক্যাম্পেইন ছাড়া ৮০.৮, ক্যাম্পেইনে ১১০.৯: বাড়তি ৩০.১. দূরের (৩০০ km) জেলায় ১৭.৪ থেকে ২৩.৯: বাড়তি মাত্র ৬.৫. অনুপাত সবখানে ১.৩৭৩, কিন্তু বাড়তি অর্ডার জেলাভেদে আলাদা।
  5. সীমা। এটি পর্যবেক্ষণের ডেটা। যদি বিপণন দল ভালো চলা জেলাতেই ক্যাম্পেইন দেয়, তাহলে ১.৩৭৩-এর একটি অংশ জেলার নিজের গুণ। কারণ-ফল জানতে randomized পরীক্ষা লাগে; সেই আলোচনা পর্ব ৬২ (A/B test-এর পরিকল্পনা — power, sample size আর peeking-এর ফাঁদ) আর পর্ব ৬৪ (Causal inference-এর পরিচয় — DAG, matching আর difference-in-differences)-এ।

দুটি বাড়তি সতর্কতা। প্রথমত, এই CI-গুলো Wald interval, অর্থাৎ “coefficient ± ১.৯৬ × SE”; নমুনা ছোট হলে বা অনুমানগুলো সন্দেহজনক হলে bootstrap দিয়ে যাচাই করা ভালো, পর্ব ২১ (Bootstrap আর resampling — model-এর অনিশ্চয়তা মাপা)-এ যা আসবে। দ্বিতীয়ত, intercept-এর eβ0 = ৩.৬৬ মানে “dist = ০ আর ক্যাম্পেইন নেই এমন জেলার দৈনিক হার”; ডেটায় dist-এর সর্বনিম্ন ০.১৭, তাই এটি সামান্য বাইরে টেনে আনা অনুমান। Feature-কে কেন্দ্রে আনলে (গড় বাদ দিলে) intercept পড়া সহজ হয়।

৭ · গোনা সংখ্যায় OLS কেন ভুল পথে যায়

অনেকে প্রথমে সবচেয়ে পরিচিত পথ নেন: orders-কে লক্ষ্য করে সাধারণ least squares (OLS), আর days-কে একটি feature. নিচের ব্লক একই ডেটায় সেটি আর Poisson GLM পাশাপাশি চালায়, তারপর residual-এর ছবি আঁকে। ছবির লেখা ইংরেজিতে, কারণ পাতার matplotlib বাংলা অক্ষর ঠিকমতো আঁকে না।

import numpy as np
import matplotlib.pyplot as plt
import statsmodels.api as sm
from bazar_district_orders import make_orders

df = make_orders()
X = sm.add_constant(df[["promo", "dist", "days"]].astype(float))
ols = sm.OLS(df.orders, X).fit()                  # সাধারণ least squares, গোনা সংখ্যায়
Xp = sm.add_constant(df[["promo", "dist"]].astype(float))
poi = sm.GLM(df.orders, Xp, family=sm.families.Poisson(),
             offset=np.log(df.days)).fit()

f_ols, f_poi = ols.fittedvalues, poi.fittedvalues
print("OLS: negative predictions =", int((f_ols < 0).sum()), "| min =", round(f_ols.min(), 2))
# নতুন জেলা: hub থেকে 380 km, ক্যাম্পেইন নেই, 8 দিন খোলা
new = np.array([1.0, 0, 3.8, 8])
print("new district, OLS forecast =", round(float(new @ ols.params), 2))
print("new district, Poisson forecast =", round(float(np.exp(new[:3] @ poi.params) * 8), 2))
lo, hi = f_ols < f_ols.median(), f_ols >= f_ols.median()
print("OLS residual SD, low half =", round(ols.resid[lo].std(), 1),
      "| high half =", round(ols.resid[hi].std(), 1))
r_p = poi.resid_pearson
print("Pearson residual SD, low half =", round(r_p[f_poi < f_poi.median()].std(), 2),
      "| high half =", round(r_p[f_poi >= f_poi.median()].std(), 2))

fig, ax = plt.subplots(1, 2, figsize=(8, 3.4))
ax[0].scatter(f_ols, ols.resid, s=14); ax[0].axhline(0, color="k", lw=0.8)
ax[0].axvline(0, color="r", ls="--", lw=0.8)
ax[0].set(title="OLS on counts", xlabel="fitted orders", ylabel="raw residual")
ax[1].scatter(f_poi, r_p, s=14, color="tab:green"); ax[1].axhline(0, color="k", lw=0.8)
ax[1].set(title="Poisson GLM", xlabel="fitted orders", ylabel="Pearson residual")
fig.subplots_adjust(wspace=0.35, bottom=0.18); plt.show()

আসল আউটপুট (ব্রাউজারের Python; ছবিটি ব্লকের নিচে আঁকা হয়):

OLS: negative predictions = 7 | min = -12.85
new district, OLS forecast = -18.53
new district, Poisson forecast = 2.84
OLS residual SD, low half = 8.1 | high half = 11.4
Pearson residual SD, low half = 1.14 | high half = 0.95

ঋণাত্মক অনুমান: ৬৪টির মধ্যে ৭টি জেলায় OLS-এর fitted মান শূন্যের নিচে, সবচেয়ে কম −১২.৮৫টি অর্ডার। ৩৮০ km দূরের, ক্যাম্পেইনহীন, ৮ দিন খোলা একটি নতুন জেলায় OLS বলে −১৮.৫৩টি অর্ডার; Poisson বলে ২.৮৪টি, যা বাস্তবসম্মত। ছবির বাঁ দিকে OLS-এর residual একটি U-আকৃতি আঁকে: প্রভাবগুলো আসলে গুণের, কিন্তু OLS যোগ ধরে নিয়েছে, তাই মাঝের জেলাগুলোতে সে বেশি বলে আর দুই প্রান্তে কম। আর fitted মান বাড়লে residual-এর ছড়ানোও বাড়ে: নিচের অর্ধেকে SD ৮.১, ওপরের অর্ধেকে ১১.৪. লাল ভাঙা রেখার বাঁয়ের বিন্দুগুলোই ঋণাত্মক অনুমান। ডান দিকে Poisson GLM-এর Pearson residual, (y − μ)/√μ: কোনো আকৃতি নেই, আর দুই অর্ধেকে SD ১.১৪ আর ০.৯৫, দুটিই ১-এর কাছে। Variance গড়ের সঙ্গে বাড়ে, এই কথাটি model-এ বসানো আছে বলেই residual-কে √μ দিয়ে মাপলে ছড়ানো সমান হয়।

আরেকটি জনপ্রিয় বিকল্প হলো log(y + ১)-এ OLS. এতে ঋণাত্মক অনুমান যায়, কিন্তু তিনটি সমস্যা থাকে: “+১” একটি ইচ্ছামতো সংখ্যা আর ছোট count-এ ফল তার ওপর অনেক নির্ভর করে; E[log y] থেকে ফিরে exp করলে গড় নয়, তার চেয়ে কম কিছু পাওয়া যায় (Jensen-এর অসমতা), তাই মোট অর্ডারের অনুমান কম আসে; আর heteroscedasticity থাকলে coefficient নিজেও বাঁকা হতে পারে। অর্থনীতির trade data নিয়ে Santos Silva আর Tenreyro-র ২০০৬ সালের পরিচিত প্রবন্ধ “The Log of Gravity” ঠিক এই যুক্তিতে log-linear OLS-এর বদলে Poisson-ভিত্তিক অনুমানের পক্ষে মত দেয়, এমনকি লক্ষ্য count না হলেও।

৮ · Deviance, residual আর overdispersion — negative binomial পর্যন্ত

Linear regression-এ fit কতটা ভালো, তা মাপে residual-এর বর্গের যোগফল। GLM-এ তার সাধারণ রূপ deviance:

D = 2 [ ℓ(saturated) − ℓ(model) ],   Poisson-এ D = 2 Σ [ y log(y/μ) − (y − μ) ]

Saturated model মানে প্রতিটি সারির জন্য আলাদা parameter, যা প্রতিটি y হুবহু মেলায়; অর্থাৎ সবচেয়ে ভালো সম্ভব likelihood. Deviance তাই “নিখুঁত মেলানো থেকে কতটা দূরে”। Gaussian-এ D হুবহু residual-এর বর্গের যোগফল (σ² = ১ ধরে), আর Bernoulli-তে D = ২ × মোট log loss; তাই AI-এর ভিত্তি কোর্সের পর্ব ৩৮-এর cross-entropy আসলে Bernoulli deviance-এর অর্ধেক। Nested দুটি model-এর deviance-এর পার্থক্য likelihood ratio test-এর পরিসংখ্যান; model তুলনার পুরো আলোচনা পর্ব ২২ (Model তুলনার পরিসংখ্যান — paired test, McNemar আর একাধিক তুলনা)-এ। ভালো fit-এ D মোটামুটি residual-এর degrees of freedom (n − p)-এর কাছাকাছি থাকার কথা, যদিও ছোট count-এ এই নিয়ম দুর্বল।

দ্বিতীয় যাচাই Pearson χ² = Σ (y − μ)²/V(μ), অর্থাৎ Pearson residual-এর বর্গের যোগফল। Poisson ঠিক হলে প্রতিটি পদের গড় প্রায় ১, তাই Pearson χ²/df ≈ ১. এর চেয়ে অনেক বেশি হলে variance গড়ের চেয়ে বড়: overdispersion. বাস্তবে এটিই নিয়ম, ব্যতিক্রম নয়। দুটি জেলার দূরত্ব আর ক্যাম্পেইন এক হলেও একটিতে বড় একটি অফিসপাড়া থাকতে পারে, যা feature-এ নেই; ঈদের আগে অর্ডার দলবেঁধে আসে; একজন পাইকার একাই ২০টি অর্ডার দেন। এমন অদেখা পার্থক্য μ-কে নিজেই এলোমেলো করে, আর তাতে variance বাড়ে। নিচের ব্লক দুটি ডেটা নেয়: আগের বিশুদ্ধ Poisson ডেটা, আর একই module-এ প্রতি জেলার μ-কে একটি এলোমেলো gamma গুণক দিয়ে নাড়ানো ডেটা, যা nb_alpha=0.5 দিয়ে বানানো হয়। তারপর দ্বিতীয়টিতে negative binomial model, যার variance μ + αμ².

import numpy as np
import statsmodels.api as sm
from scipy.special import xlogy
from bazar_district_orders import make_orders

def poisson_fit(df):
    X = sm.add_constant(df[["promo", "dist"]].astype(float))
    return X, sm.GLM(df.orders, X, family=sm.families.Poisson(),
                     offset=np.log(df.days)).fit()

for name, df in [("plain Poisson data", make_orders()),
                 ("overdispersed data", make_orders(nb_alpha=0.5))]:
    X, res = poisson_fit(df)
    y, mu = df.orders, res.fittedvalues
    dev = 2 * np.sum(xlogy(y, y / mu) - (y - mu))       # Poisson deviance
    pear = np.sum((y - mu) ** 2 / mu)                  # Pearson χ²
    print(f"{name}: deviance {dev:.1f} (statsmodels {res.deviance:.1f}),"
          f" df {res.df_resid:.0f}")
    print(f"   Pearson chi2/df = {pear / res.df_resid:.2f}"
          f" (statsmodels {res.pearson_chi2 / res.df_resid:.2f})")

nb = sm.NegativeBinomial(df.orders, X, offset=np.log(df.days)).fit(disp=0)
print("NB converged:", nb.mle_retvals["converged"], "| alpha =", round(nb.params["alpha"], 3))
print("promo coef  Poisson", round(res.params["promo"], 3), "SE", round(res.bse["promo"], 3))
print("promo coef  NegBin ", round(nb.params["promo"], 3), "SE", round(nb.bse["promo"], 3))
print("Poisson CI for rate ratio:", np.exp(res.conf_int().loc["promo"]).round(2).tolist())
print("NegBin  CI for rate ratio:", np.exp(nb.conf_int().loc["promo"]).round(2).tolist())

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

plain Poisson data: deviance 73.7 (statsmodels 73.7), df 61
   Pearson chi2/df = 1.14 (statsmodels 1.14)
overdispersed data: deviance 727.7 (statsmodels 727.7), df 61
   Pearson chi2/df = 12.19 (statsmodels 12.19)
NB converged: True | alpha = 0.427
promo coef  Poisson 0.187 SE 0.05
promo coef  NegBin  0.415 SE 0.178
Poisson CI for rate ratio: [1.09, 1.33]
NegBin  CI for rate ratio: [1.07, 2.15]

বিশুদ্ধ Poisson ডেটায় deviance ৭৩.৭, degrees of freedom ৬১ (৬৪ সারি − ৩ parameter), আর Pearson χ²/df = ১.১৪: যেমন হওয়ার কথা। হাতে লেখা সূত্র (xlogy দিয়ে, যাতে y = ০-এ 0·log 0 = ০ ধরা হয়) আর statsmodels-এর মান এক। Overdispersed ডেটায় deviance ৭২৭.৭ আর Pearson χ²/df = ১২.১৯: variance গড়ের প্রায় ১২ গুণ। Poisson model তবু একটি coefficient আর একটি ছোট SE দেয়, কোনো সতর্কবার্তা ছাড়াই। Negative binomial (statsmodels-এর NegativeBinomial, α-ও MLE দিয়ে) ঠিকঠাক converge করেছে, α̂ = ০.৪২৭ (আসল ০.৫-এর কাছে)। promo-র coefficient Poisson-এ ০.১৮৭, NB-তে ০.৪১৫: overdispersion-এ কয়েকটি বড় জেলা Poisson-এর fit-কে বেশি টানে, কারণ Poisson-এর ওজন μ, আর NB-র ওজন μ/(1 + αμ) বড় μ-এ থেমে যায়। সবচেয়ে বড় শিক্ষা SE-তে: ০.০৫ থেকে ০.১৭৮, প্রায় সাড়ে তিন গুণ। Rate ratio-র CI Poisson-এ ১.০৯–১.৩৩, NB-তে ১.০৭–২.১৫. Poisson-এর সরু CI ছিল মিথ্যা আত্মবিশ্বাস।

Overdispersion-এর প্রতিকার কয়েকটি, প্রতিটির নিজের অনুমান আছে:

প্রতিকারVariance-এর রূপstatsmodels-একখন
Quasi-Poissonφ·μGLM(...).fit(scale="X2")coefficient Poisson-এরই থাকে, SE √φ গুণ বাড়ে; সবচেয়ে সরল
Negative binomial (NB2)μ + αμ²sm.NegativeBinomial, বা GLM-এ α জানা থাকলেজেলাভেদে অদেখা পার্থক্য; বড় μ-এ বেশি ছড়ানো
Robust (sandwich) SEঅনুমান করে নাfit(cov_type="HC0")গড়ের model ঠিক, variance-এর রূপ অজানা
Zero-inflated modelশূন্যের আলাদা প্রক্রিয়াZeroInflatedPoissonঅনেক জেলায় সেবাই নেই, তাই কাঠামোগত শূন্য

একটি ফাঁদ: sm.families.NegativeBinomial()-এ α না দিলে statsmodels নিজে α = ১ ধরে নেয় আর একটি ValueWarning দেখায়। GLM-এর NB family α-কে অনুমান করে না; α হয় নিজে দিতে হয়, নয়তো উপরের মতো sm.NegativeBinomial (discrete model) দিয়ে α-ও MLE করতে হয়।

ধনাত্মক আর লম্বা লেজের লক্ষ্য: ডেলিভারির সময় বা ঝুড়ির মোট দাম ধনাত্মক আর ডানে লম্বা লেজের। এদের জন্য Gamma GLM (সাধারণত log link, variance ∝ μ²) প্রচলিত: SD গড়ের সমানুপাতিক। কিছু গ্রাহকের দাম শূন্য আর বাকিদের ধনাত্মক হলে (যেমন মাসে মোট কেনাকাটা) Tweedie বণ্টন, power ১ থেকে ২-এর মধ্যে, যা Poisson সংখ্যক Gamma-র যোগফল; বিমার দাবির হিসাবে এর ব্যবহার সুপরিচিত। scikit-learn-এ GammaRegressor আর TweedieRegressor(power=1.5), statsmodels-এ sm.families.Gamma আর Tweedie.

আরও গভীরে: GLM-এর মূল প্রবন্ধ Nelder ও Wedderburn, “Generalized Linear Models”, Journal of the Royal Statistical Society, Series A (১৯৭২); পাঠ্যবই হিসেবে McCullagh ও Nelder-এর Generalized Linear Models (দ্বিতীয় সংস্করণ, ১৯৮৯) এখনো মানদণ্ড। Wedderburn ১৯৭৪ সালে Biometrika-য় quasi-likelihood দেন: শুধু গড় আর variance-এর সম্পর্ক বললেই চলে, পুরো বণ্টন লাগে না; quasi-Poisson এখান থেকে। Count-এর বিস্তারিত আলোচনা Cameron ও Trivedi-র Regression Analysis of Count Data-য়। Exponential family-র গভীর তত্ত্ব (convex duality, conjugate prior, variational inference-এ তার ভূমিকা) Wainwright ও Jordan-এর ২০০৮ সালের দীর্ঘ প্রবন্ধে পাবেন।

পরীক্ষা ও ইন্টারভিউতে: (১) Poisson-কে exponential family-তে লিখুন। p = (১/y!)·exp(y log λ − λ): θ = log λ, T = y, A = eθ; A′ = A″ = λ. (২) GLM-এর তিন অংশ? Random component, linear predictor η = Xβ, link g(μ) = η. (৩) Poisson-এ β = ০.২৫-এর মানে? হার e০.২৫ ≈ ১.২৮ গুণ, প্রায় ২৮% বেশি, অন্য feature স্থির রেখে। (৪) Offset কেন? Exposure আলাদা হলে log(exposure)-কে coefficient ১ দিয়ে যোগ করলে model হার শেখে। (৫) Overdispersion কীভাবে ধরবেন আর কী করবেন? Pearson χ²/df ≫ ১; quasi-Poisson, negative binomial বা robust SE.

৯ · AI-এর যুগে — AI-এর লেখা GLM কোড যাচাই

“অর্ডারের সংখ্যা predict করার model বানাও” বললে AI assistant প্রায়ই এমন কোড দেয় যা চলে, সংখ্যা ছাপে, আর দেখতে বিশ্বাসযোগ্য; কিন্তু GLM-এর ভুলগুলো চুপচাপ থাকে। নিচের ভুলগুলো বারবার দেখা যায়।

AI প্রায়ই যা করেকেন সমস্যাকীভাবে ধরবেন
Count-এ LinearRegression বা log(y + 1)-এ OLSঋণাত্মক অনুমান, ভুল variance, মোট অর্ডার কম আসেঋণাত্মক fitted মান গুনুন; residual বনাম fitted আঁকুন
Exposure ভুলে যায়, বা days-কে সাধারণ feature বানায়খোলা দিনের পার্থক্য অন্য coefficient-এ চাপেoffset আছে কি না দেখুন; scikit-learn-এ y/days আর sample_weight=days
PoissonRegressor() default রাখেdefault alpha=1.0: L2 শাস্তি, coefficient সংকুচিতব্যাখ্যার জন্য alpha=0, অনুমানের জন্য alpha CV দিয়ে বাছুন
coefficient-কে “এত অর্ডার বেশি” বলে পড়েlog link-এ প্রভাব গুণেরeβ আর CI-এর exp দিয়ে rate ratio লিখুন
Poisson-এর p-value দেখিয়ে “অত্যন্ত significant” বলেOverdispersion-এ SE অনেক ছোটPearson χ²/df ছাপুন; NB, quasi-Poisson বা robust SE-র সঙ্গে তুলনা করুন
পুরোনো API: links.log(), α ছাড়া families.NegativeBinomial()FutureWarning, ValueWarning; ভুল α = ১ চুপচাপনিজের version-এর docs পড়ুন; links.Log(), α স্পষ্ট দিন
Training ডেটায় R² বা deviance দেখিয়ে model “ভালো” বলেGeneralisation-এর কোনো প্রমাণ নয়আলাদা test set বা CV-তে Poisson deviance (mean_poisson_deviance), একটি সরল baseline-এর সঙ্গে

AI-কে শুধু কোড নয়, অনুমান আর যাচাইও চাইতে বলুন:

আমার কাছে ৬৪টি জেলার এক মাসের ডেটা: orders (গোনা সংখ্যা), days (মাসে কত দিন ডেলিভারি খোলা ছিল), promo (0/1), dist (hub থেকে দূরত্ব)। ১. কোন GLM (বণ্টন আর link) নেবে আর কেন? days কীভাবে offset হিসেবে ঢুকবে, সূত্রে লেখো। ২. statsmodels GLM আর scikit-learn PoissonRegressor দুটিতেই কোড দাও; scikit-learn-এ offset-এর বদলে কী করতে হয় আর alpha কেন 0, ব্যাখ্যা করো। ৩. coefficient-কে rate ratio আর 95% CI হিসেবে ছাপানোর কোড দাও, আর একটি বাক্যে ব্যাখ্যার নমুনা লেখো (কারণ-ফল দাবি না করে)। ৪. Overdispersion যাচাইয়ের কোড দাও (Pearson χ²/df), আর বেশি হলে negative binomial বা quasi-Poisson কীভাবে চালাব। ৫. তুমি যে API নাম আর default মান বলছ, সেগুলো কোন statsmodels আর scikit-learn version-এর docs অনুযায়ী, তা লেখো।

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

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

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

প্র ০১১৯৭২ সালের আগে probit, logit আর log-linear model আলাদা আলাদা পদ্ধতি ছিল। Nelder আর Wedderburn-এর GLM কাঠামো কী বদলে দিল?

বিংশ শতকের প্রথমার্ধে প্রতিটি ধরনের ডেটার জন্য আলাদা কৌশল তৈরি হয়েছিল। কীটনাশকের মাত্রা আর মারা যাওয়া পোকার অনুপাতের জন্য Chester Bliss ১৯৩৪ সালে probit বিশ্লেষণ জনপ্রিয় করেন; Joseph Berkson ১৯৪৪ সালে “logit” শব্দটি আর logistic বিকল্প প্রস্তাব করেন; contingency table-এর জন্য log-linear model আলাদা ধারায় বিকশিত হয়। প্রতিটির নিজের হিসাবের কৌশল, নিজের সারণি, নিজের পরিভাষা।

Nelder আর Wedderburn-এর ১৯৭২ সালের প্রবন্ধ দেখাল, এগুলো একই কাঠামোর ভিন্ন বাছাই: বণ্টন exponential family-র কোনো সদস্য, গড়ের একটি function linear, আর সবার জন্য একটিই fit-এর algorithm, iteratively reweighted least squares. সেই সময়ের কম্পিউটারে এটি বিশাল সুবিধা ছিল: একটি weighted least squares-এর routine থাকলেই সব model চলে। এর ভিত্তিতে GLIM নামে একটি software তৈরি হয়, আর পরে S ও R-এর glm() function এই ধারণাকেই সবার হাতে পৌঁছে দেয়। আজ statsmodels-এর GLM আর scikit-learn-এর PoissonRegressor, GammaRegressor, TweedieRegressor একই বংশধর।

তত্ত্বের দিকেও লাভ হলো। Deviance, residual, standard error, likelihood ratio test একবার সাধারণভাবে লিখলেই সব সদস্যের জন্য কাজ করে। ছাত্রছাত্রীকে আর পাঁচটি আলাদা পদ্ধতি মুখস্থ করতে হয় না; তিনটি প্রশ্ন করলেই হয়: বণ্টন কী, link কী, feature কী। চিকিৎসা, বাস্তুবিদ্যা, বিমা, অর্থনীতি: প্রায় সব প্রয়োগক্ষেত্রে GLM পরিসংখ্যানের সাধারণ ভাষায় পরিণত হয়েছে বলে ব্যাপকভাবে মনে করা হয়।

Machine learning-এর দৃষ্টিতে GLM হলো “একটি linear score + একটি উপযুক্ত loss”. Logistic regression-এর log loss, Poisson-এর deviance, Gaussian-এর squared error: প্রতিটি একটি বণ্টনের negative log-likelihood. Neural network-এর শেষ layer আর loss বাছাইয়েও একই ভাবনা চলে: শ্রেণির জন্য softmax আর cross-entropy, গোনার জন্য exp আর Poisson loss.

মূল উপলব্ধি: ভালো একটি বিমূর্ততা অনেকগুলো আলাদা কৌশলকে একটি প্রশ্নমালায় নামিয়ে আনে; GLM-এর তিনটি প্রশ্ন আজও নতুন model বানানোর সময় সবচেয়ে কাজের চেকলিস্ট।

প্র ০২Bortkiewicz-এর ঘোড়ার লাথির ডেটা থেকে Greenwood আর Yule-এর দুর্ঘটনা-প্রবণতা পর্যন্ত: Poisson কখন খাটে, আর কখন negative binomial লাগে?

Siméon Denis Poisson ১৮৩৭ সালে বিচারের রায়ের সম্ভাবনা নিয়ে লেখা একটি বইয়ে এই বণ্টন বের করেন, binomial-এর একটি সীমা হিসেবে: অনেকবার চেষ্টা, প্রতিবার সফলতার সম্ভাবনা খুব কম, কিন্তু গড় সংখ্যা স্থির। বণ্টনটির বিখ্যাত প্রয়োগ আসে ১৮৯৮ সালে, Ladislaus von Bortkiewicz-এর “Das Gesetz der kleinen Zahlen” (ছোট সংখ্যার নিয়ম) বইয়ে। প্রুশিয়ান সেনাবাহিনীর কয়েকটি অশ্বারোহী দলে প্রায় বিশ বছরে ঘোড়ার লাথিতে বছরে কতজন মারা গেছেন, সেই গোনার সঙ্গে Poisson-এর অনুমান প্রায় হুবহু মিলেছিল বলে পাঠ্যবইয়ে বারবার উদ্ধৃত হয়।

কেন মিলেছিল? প্রতিটি সৈনিকের ঝুঁকি ছোট, ঘটনাগুলো একে অপরের থেকে প্রায় স্বাধীন, আর দলগুলোর মধ্যে ঝুঁকি মোটামুটি সমান। Poisson-এর এই তিন শর্তের যেকোনোটি ভাঙলে variance গড় ছাড়িয়ে যায়। বাজারঘরের অর্ডারে স্বাধীনতা ভাঙে যখন একটি অফিসের সবাই একসঙ্গে দুপুরের খাবার অর্ডার করেন; সমান ঝুঁকি ভাঙে যখন একই দূরত্বের দুটি জেলার একটিতে বিশ্ববিদ্যালয় আছে।

১৯২০ সালে Major Greenwood আর G. Udny Yule কারখানার শ্রমিকদের দুর্ঘটনার সংখ্যা নিয়ে কাজ করতে গিয়ে দেখেন, Poisson-এর চেয়ে বেশি মানুষের শূন্য আর বেশি মানুষের অনেকগুলো দুর্ঘটনা। তাঁদের ব্যাখ্যা ছিল “accident proneness”: প্রতিটি মানুষের নিজের হার আছে, আর হারগুলো একটি gamma বণ্টন মেনে ছড়ানো। Poisson-এর হার gamma থেকে এলে মোট বণ্টন হয় negative binomial, যার variance μ + αμ². এই পর্বের overdispersed ডেটা ঠিক এভাবেই বানানো।

তবে একটি সূক্ষ্মতা আছে, যা পরে নিজেই বিতর্কের জন্ম দিয়েছিল: একই negative binomial আসতে পারে “contagion” থেকেও, যেখানে একটি দুর্ঘটনা পরেরটির সম্ভাবনা বাড়ায়। শুধু সংখ্যার বণ্টন দেখে দুটি গল্প আলাদা করা যায় না; সময়ের ক্রম বা বারবার মাপা ডেটা লাগে। অর্থাৎ ভালো fit মানেই সঠিক ব্যাখ্যা নয়।

মূল উপলব্ধি: Poisson একটি আদর্শ অবস্থা: স্বাধীন, বিরল, সমান-ঝুঁকির ঘটনা। বাস্তব ডেটায় আগে Pearson χ²/df দেখুন, আর overdispersion পেলে তার সম্ভাব্য কারণ নিয়ে ভাবুন, শুধু model বদলে থামবেন না।

প্র ০৩Canonical link গাণিতিকভাবে সুন্দর। তবু কেন অনেকে binomial-এ log link বা probit, আর Gamma-তে log link নেন? Link বাছাইয়ের নীতি কী?

Canonical link-এর সুবিধাগুলো বাস্তব। Xᵀy একটি sufficient statistic হয়, MLE-এর সমীকরণ Xᵀ(y − μ) = ০ হয়, observed আর expected information এক হয় (তাই Newton আর Fisher scoring একই), আর log-likelihood concave থাকে। Intercept থাকলে মোট মান হুবহু মেলে, যা ব্যবসার রিপোর্টে খুব কাজের।

কিন্তু link আসলে একটি বৈজ্ঞানিক প্রশ্নের উত্তর: feature-গুলোর প্রভাব কোন মাপে যোগ হয়? চিকিৎসা-গবেষণায় অনেক সময় relative risk চাওয়া হয়, odds ratio নয়; ঘটনাটি সাধারণ হলে (ধরুন ৪০% ফেরত) দুটি অনেক আলাদা, আর odds ratio-কে relative risk ভেবে পড়া একটি পরিচিত ভুল। তখন binomial-এ log link (log-binomial model) নেওয়া হয়, যার eβ সরাসরি ঝুঁকির অনুপাত; তবে সেখানে μ ১ ছাড়াতে পারে আর fit প্রায়ই অস্থির হয়, তাই কেউ কেউ Poisson model আর robust SE দিয়ে একই সংখ্যা অনুমান করেন।

Probit আসে একটি অদেখা ধারাবাহিক চলকের গল্প থেকে: কেউ কিনবেন যদি তাঁর অদেখা “আগ্রহ” একটি সীমা পেরোয়, আর আগ্রহ normal বণ্টন মানে। Logit আর probit-এর বক্ররেখা মাঝখানে প্রায় একই, পার্থক্য লেজে; অনুমানের নির্ভুলতায় সাধারণত পার্থক্য সামান্য। Complementary log-log আসে “একটি সময়ের মধ্যে অন্তত একটি ঘটনা” ধরনের প্রশ্ন থেকে, যেখানে ভিত্তি একটি Poisson প্রক্রিয়া।

Gamma-র canonical link inverse (১/μ), যা μ ধনাত্মক থাকার নিশ্চয়তা দেয় না আর ব্যাখ্যা করা কঠিন। log link-এ প্রভাব শতাংশে পড়া যায় (“দূরত্ব ১০০ km বাড়লে ডেলিভারির সময় ১৮% বাড়ে”), তাই বাস্তবে প্রায় সবাই সেটিই নেন। statsmodels আর scikit-learn দুটিতেই Gamma-র জন্য log link বাছা যায়।

বাছাইয়ের নীতি তিনটি: প্রথমে ব্যাখ্যার মাপ (যোগ, গুণ, odds), তারপর গড়ের সম্ভাব্য পরিসর (μ > ০, ০ < μ < ১), শেষে ডেটায় যাচাই: residual-এর ছবি আর held-out deviance.

মূল উপলব্ধি: Canonical link হিসাবের সুবিধা দেয়, কিন্তু link বাছাই একটি model-অনুমান; প্রশ্ন যে মাপে কথা বলে, link সেই মাপে হওয়া উচিত।

প্র ০৪Overdispersion উপেক্ষা করলে coefficient প্রায়ই ঠিক থাকে, কিন্তু সিদ্ধান্ত ভুল হয়। Quasi-likelihood আর sandwich SE এই সমস্যা কীভাবে সামলায়?

Poisson MLE-এর একটি চমৎকার গুণ আছে: গড়ের model log μ = Xβ ঠিক থাকলে, বণ্টন Poisson না হলেও coefficient-এর অনুমান consistent থাকে, কারণ সমীকরণ Xᵀ(y − μ) = ০ শুধু গড়ের ওপর নির্ভর করে। যা ভাঙে তা হলো SE: Poisson ধরে নেয় variance = μ, তাই আসল variance বড় হলে SE ছোট আসে, CI সরু হয়, আর p-value অযথা ছোট। এই পর্বের run-এ Pearson χ²/df ছিল ১২.১৯, আর SE প্রায় √১২ ≈ ৩.৫ গুণ ছোট এসেছিল।

Robert Wedderburn ১৯৭৪ সালে quasi-likelihood দেন: শুধু গড় আর variance-এর সম্পর্ক বলুন, যেমন Var = φμ, বাকি বণ্টন অজানা থাকুক। Estimating equation একই থাকে, তাই coefficient Poisson-এরই, কিন্তু SE √φ গুণ বড় হয়, আর φ অনুমান হয় Pearson χ²/df থেকে। এটি quasi-Poisson; statsmodels-এ fit(scale="X2").

আরেকটি পথ sandwich বা robust SE, যার শিকড় Peter Huber-এর ১৯৬৭ আর Halbert White-এর ১৯৮০ সালের কাজে। এখানে variance-এর কোনো রূপও ধরা হয় না: model-এর Hessian (রুটির দুই টুকরো) আর আসল residual থেকে মাপা gradient-এর ছড়ানো (মাঝের পুর) মিলিয়ে SE. নমুনা বড় হলে এটি নির্ভরযোগ্য, ছোট নমুনায় দুর্বল।

Negative binomial এক ধাপ এগিয়ে variance-এর রূপ বদলায়, μ + αμ², আর তাতে ওজনও বদলায়: বড় জেলাগুলো আর একচেটিয়া প্রভাব রাখে না। এই পর্বের run-এ coefficient-ও তাই বদলেছে, ০.১৮৭ থেকে ০.৪১৫. কোনটি ঠিক, তা নির্ভর করে variance-এর কোন রূপ সত্যের কাছে। Bootstrap (পর্ব ২১) আরেকটি স্বাধীন যাচাই দেয়।

বাস্তবে এর প্রভাব গুরুতর: একটি অকার্যকর ক্যাম্পেইনকে “অত্যন্ত significant” দেখানো, বা একটি নতুন জেলায় ঝুঁকি কম বলে মনে করা। Overdispersion নিয়ে রিপোর্ট না করা GLM বিশ্লেষণকে সতর্ক পাঠক সন্দেহের চোখে দেখেন।

মূল উপলব্ধি: Coefficient গড়ের model থেকে আসে, অনিশ্চয়তা variance-এর model থেকে; দ্বিতীয়টি যাচাই না করলে প্রথমটি যত নির্ভুলই হোক, সিদ্ধান্ত ভুল হতে পারে।

প্র ০৫Gradient boosting আর neural network-এর যুগে GLM কি পুরোনো? Poisson deviance আধুনিক ML-এ কোথায় বেঁচে আছে?

GLM-এর সবচেয়ে টেকসই অংশ model নয়, loss. scikit-learn-এর HistGradientBoostingRegressor-এ loss="poisson" আছে, LightGBM আর XGBoost-এ Poisson, Gamma আর Tweedie objective আছে, আর neural network-এ শেষ layer-এ exp বসিয়ে Poisson negative log-likelihood কমানো একটি পরিচিত কৌশল। প্রতিটি ক্ষেত্রে ভাবনা GLM-এর: log link দিয়ে অনুমান ধনাত্মক রাখা, আর variance = গড় ধরে ভুলের ওজন দেওয়া। পার্থক্য শুধু linear predictor-এর জায়গায় একটি নমনীয় function.

মূল্যায়নেও তাই। Count-এর অনুমান MSE দিয়ে মাপলে বড় জেলার ভুলই প্রায় সব ঠিক করে দেয়; scikit-learn-এর mean_poisson_deviance, mean_tweedie_deviance আর d2_tweedie_score GLM-এর deviance-কে metric বানিয়েছে। scikit-learn-এর নিজস্ব একটি উদাহরণ বিমার দাবির সংখ্যায় Poisson GLM আর Poisson loss-এর gradient boosting তুলনা করে; কোনটি ভালো, তা ডেটা আর feature-এর ওপর নির্ভর করে।

তাহলে GLM নিজে কখন জেতে? (১) ডেটা কম, যেমন এই পর্বের ৬৪টি জেলা: boosting-এর নমনীয়তা তখন overfitting-এর ঝুঁকি। (২) ব্যাখ্যা দরকার: rate ratio আর তার CI একটি বাক্যে বলা যায়, কোনো SHAP-এর আশ্রয় ছাড়া। (৩) নিয়ন্ত্রিত ক্ষেত্র: বিমার দাম ঠিক করার মতো কাজে GLM দীর্ঘদিন ধরে মানদণ্ড বলে পরিচিত, কারণ প্রতিটি গুণক যাচাই করা যায়। (৪) Baseline: যেকোনো জটিল count model-এর আগে একটি Poisson GLM সৎ তুলনার ভিত্তি দেয় (পর্ব ০২-এর baseline-এর ভাবনা)।

অন্যদিকে interaction আর অ-linear প্রভাব অনেক হলে GLM-এ সেগুলো হাতে বানাতে হয় (পর্ব ২৩, Linear model গভীরে — basis function, interaction আর regularization path), আর boosting সেগুলো নিজে খোঁজে। কোন পথ সেরা, তা নিয়ে সাধারণ উত্তর নেই; একই held-out Poisson deviance-এ কয়েকটি fold-এ তুলনাই নিয়ম।

মূল উপলব্ধি: GLM-এর তিনটি প্রশ্ন (বণ্টন, link, predictor) আধুনিক ML-এও বাঁচে; predictor বদলালেও বণ্টন আর link-এর বাছাই loss আর metric হয়ে থেকে যায়।

অনুশীলন

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

  1. পূর্ণ numerical: ক্যাম্পেইন ছাড়া জেলাগুলোতে মোট ১২০টি অর্ডার এসেছে মোট ৬০ খোলা দিনে; ক্যাম্পেইনের জেলাগুলোতে ১৫০টি, মোট ৫০ দিনে। Model: log μ = log(days) + β0 + β1·promo, Poisson. (ক) β0, β1-এর MLE আর rate ratio কত? (খ) ২০ দিন খোলা একটি ক্যাম্পেইনের জেলায় প্রত্যাশিত অর্ডার? (গ) সেই জেলায় আসলে ৪৮টি এলে Pearson residual আর deviance-এ তার অবদান কত?

    (ক) একটি ০/১ feature আর intercept-এ MLE-এর সমীকরণ Xᵀ(y − μ) = ০ বলে, প্রতিটি দলের মোট অর্ডার = মোট প্রত্যাশা। তাই দলের হার = মোট অর্ডার / মোট দিন: ক্যাম্পেইন ছাড়া ১২০/৬০ = ২, ক্যাম্পেইনে ১৫০/৫০ = ৩. β0 = ln ২ = ০.৬৯৩১, β1 = ln(৩/২) = ০.৪০৫৫, rate ratio ১.৫. (খ) μ = ২০ × e০.৬৯৩১ + ০.৪০৫৫ = ২০ × ৩ = ৬০. (গ) Pearson residual = (৪৮ − ৬০)/√৬০ = −১২/৭.৭৪৬ = −১.৫৪৯. Deviance-এ অবদান ২[৪৮ ln(৪৮/৬০) − (৪৮ − ৬০)] = ২[৪৮ × (−০.২২৩১৪) + ১২] = ২[−১০.৭১১ + ১২] = ২.৫৭৮. দুটিই বলে ফারাকটি স্বাভাবিক সীমার ভেতরে (Pearson residual-এর বর্গ ২.৪০, deviance অবদান ২.৫৮: কাছাকাছি)।

  2. সমালোচনা ও নকশা: বাজারঘরের একজন বিশ্লেষক রিপোর্টে লিখেছেন: “৬৪ জেলার ডেটায় LinearRegression চালিয়েছি (orders ~ promo + dist), R² = ০.৬১. ক্যাম্পেইনে প্রতি জেলায় গড়ে ১২টি বেশি অর্ডার, p < ০.০০১, তাই সব জেলায় ক্যাম্পেইন চালালে মাসে ৬৪ × ১২ = ৭৬৮টি বেশি অর্ডার।” কী কী সমস্যা, আর আপনি বিশ্লেষণটি কীভাবে নকশা করবেন?

    সমস্যা: (১) Count-এ OLS: ঋণাত্মক অনুমান আর ভুল variance; প্রভাব যোগ ধরে নেওয়ায় “১২টি বেশি” সব জেলায় এক, অথচ দূরের ছোট জেলায় বাড়তি অনেক কম (অংশ ৬-এ ৩০.১ বনাম ৬.৫)। (২) Exposure নেই: কম দিন খোলা জেলার পার্থক্য coefficient-এ মিশেছে। (৩) p-value OLS-এর সমান variance অনুমানে, যা এখানে ভুল। (৪) Training ডেটার R², কোনো held-out মূল্যায়ন নেই। (৫) পর্যবেক্ষণের ডেটা থেকে কারণ-ফল দাবি: ক্যাম্পেইন হয়তো ভালো জেলাতেই দেওয়া হয়েছিল। নকশা: Poisson GLM, log link, offset log(days); rate ratio আর CI রিপোর্ট; Pearson χ²/df দেখে দরকারে negative binomial বা robust SE; সব জেলায় প্রভাবের অনুমান প্রতিটি জেলার নিজের μ থেকে যোগ করে, তবে সেটিকে “model-এর অনুমান” বলুন; আসল প্রভাব জানতে কয়েকটি জেলায় এলোমেলোভাবে ক্যাম্পেইন দিয়ে A/B পরীক্ষা (পর্ব ৬২)।

  3. আউটপুট অনুমান করুন: শুধু intercept-এর Poisson model, log link, IRLS (এখানে Newton) β = ০ থেকে। চালানোর আগে চারটি লাইন আর শেষের rate লিখুন।
    import numpy as np
    y = np.array([0, 2, 4, 6])
    beta = 0.0                         # শুধু intercept, log link
    for k in range(4):
        mu = np.exp(beta)
        beta = beta + (y.mean() - mu) / mu
        print(k + 1, round(beta, 4))
    print("rate =", round(np.exp(beta), 4))

    ডেস্কটপে সত্যিকারের run (Python 3.14, NumPy 2.5):

    1 2.0
    2 1.406
    3 1.1414
    4 1.0995
    rate = 3.0027

    শুধু intercept-এ Newton-এর ধাপ β ← β + (ȳ − eβ)/eβ, কারণ gradient n(ȳ − μ) আর Hessian −nμ. ȳ = ৩. β = ০-এ μ = ১, তাই β = ০ + (৩ − ১)/১ = ২: লক্ষ্য ln ৩ = ১.০৯৮৬ পেরিয়ে গেছে। তারপর ২ + ৩/e² − ১ = ১.৪০৬০, তারপর ১.১৪১৪, ১.০৯৯৫: ভুল ০.৯০, ০.৩১, ০.০৪৩, ০.০০০৯, প্রায় বর্গ হারে কমছে। চার ধাপে rate ৩.০০২৭; পঞ্চম ধাপে ৩.০০০০. y-তে একটি শূন্য থাকলেও কোনো সমস্যা নেই, কারণ log y কোথাও লাগেনি।

  4. কোডিং: দুটি function লিখুন: poisson_deviance(y, mu) আর pearson_dispersion(y, mu, n_params). overdispersed ডেটায় (make_orders(nb_alpha=0.5)) Poisson GLM fit করে statsmodels-এর deviance, pearson_chi2 আর scikit-learn-এর mean_poisson_deviance-এর সঙ্গে মিলিয়ে দেখান।
    import numpy as np
    import statsmodels.api as sm
    from scipy.special import xlogy
    from sklearn.metrics import mean_poisson_deviance
    from bazar_district_orders import make_orders
    
    def poisson_deviance(y, mu):
        return 2 * np.sum(xlogy(y, y / mu) - (y - mu))
    
    def pearson_dispersion(y, mu, n_params):
        return np.sum((y - mu) ** 2 / mu) / (len(y) - n_params)
    
    df = make_orders(nb_alpha=0.5)
    X = sm.add_constant(df[["promo", "dist"]].astype(float))
    res = sm.GLM(df.orders, X, family=sm.families.Poisson(),
                 offset=np.log(df.days)).fit()
    y, mu = df.orders.to_numpy(float), res.fittedvalues.to_numpy()
    print(round(poisson_deviance(y, mu), 3), round(res.deviance, 3))
    print(round(poisson_deviance(y, mu) / len(y), 4), round(mean_poisson_deviance(y, mu), 4))
    print(round(pearson_dispersion(y, mu, 3), 3), round(res.pearson_chi2 / res.df_resid, 3))

    ডেস্কটপে সত্যিকারের run (Python 3.14, statsmodels 0.15, scikit-learn 1.9) ছাপে:

    727.741 727.741
    11.371 11.371
    12.193 12.193

    প্রথম লাইনে deviance দুই হিসাবে ৭২৭.৭৪১, দ্বিতীয় লাইনে প্রতি সারির গড় ১১.৩৭১: scikit-learn-এর mean_poisson_deviance আসলে deviance/n, তাই test set-এ model তুলনার metric হিসেবে এটিই স্বাভাবিক। তৃতীয় লাইনে dispersion ১২.১৯৩. দুটি সতর্কতা: xlogy ছাড়া y = ০-এ 0·log 0 থেকে NaN আসত; আর dispersion-এর হরে n নয়, n − p.

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

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

ক্রমOffset-সহ Poisson GLM-এ IRLS-এর একটি ধাপ সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. বর্তমান β থেকে η = Xβ + offset হিসাব করা
  2. Inverse link দিয়ে গড় μ = eη বের করা
  3. ওজন W = μ আর working response z = (η − offset) + (y − μ)/μ বানানো
  4. z-কে X দিয়ে W ওজনে weighted least squares সমাধান করে নতুন β পাওয়া
  5. β-র বদল খুব ছোট হলে থামা, নইলে আবার শুরু

Poisson(λ) বণ্টনকে p(y) = h(y)·exp(θ·y − A(θ)) রূপে লিখলে natural parameter θ কী?

  • λ
  • ১/λ
  • log λ
  • √λ

Bernoulli বণ্টনে A(θ) = log(1 + eθ). A′(θ) কী, আর তা কী বোঝায়?

  • ১/(১ + e−θ), অর্থাৎ গড় p
  • p(1 − p), অর্থাৎ গড়
  • eθ, অর্থাৎ odds
  • log(1 + eθ), অর্থাৎ log-likelihood

Log link-এর Poisson regression-এ ছাড়ের ক্যাম্পেইনের coefficient ০.২৫. অন্য সব feature স্থির রেখে এর সঠিক ব্যাখ্যা কোনটি?

  • প্রতি একক সময়ে ০.২৫টি বেশি অর্ডার
  • অর্ডারের হার প্রায় ১.২৮ গুণ, অর্থাৎ প্রায় ২৮% বেশি
  • অর্ডারের হার ঠিক ২৫% বেশি
  • অর্ডারের odds ১.২৮ গুণ

Offset-সহ Poisson model: log μ = log(days) + Xβ. একটি জেলা ১০ দিন খোলা ছিল আর তার Xβ = ১.০. প্রত্যাশিত মোট অর্ডার μ প্রায় কত?

  • ২.৭২
  • ১২.৭২
  • ৩.৩০
  • ২৭.১৮

একটি Poisson GLM-এ Pearson χ² = ১৮৩, residual degrees of freedom ৬১. কোন সিদ্ধান্তটি সবচেয়ে ঠিক?

  • Fit চমৎকার, কারণ Pearson χ² ধনাত্মক
  • Overdispersion আছে (φ ≈ ৩); Poisson-এর SE প্রায় ১.৭৩ গুণ ছোট এসেছে
  • Coefficient সব ৩ গুণ বড় এসেছে, SE ঠিক আছে
  • Underdispersion, তাই binomial model নিতে হবে

Log link-এর Poisson GLM-এ IRLS-এর প্রতিটি ধাপে i-তম সারির ওজন Wii কত?

  • μi
  • ১/μi
  • μi(1 − μi)
  • ১

শব্দকোষ

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

মনে রাখুন

  1. Exponential family: p(y) = h(y)·exp(θ·T(y) − A(θ)); A′(θ) = গড়, A″(θ) = variance. Bernoulli-তে θ = log-odds, Poisson-এ log λ, Gaussian-এ μ.
  2. GLM: random component + linear predictor η = Xβ + link g(μ) = η. Canonical link (identity, logit, log)-এ MLE-এর সমীকরণ Xᵀ(y − μ) = ০ আর log-likelihood concave.
  3. Poisson + offset: log μ = log(exposure) + Xβ; eβ = rate ratio, CI-ও exp করে। scikit-learn-এ y/exposure লক্ষ্য, exposure ওজন, alpha = ০.
  4. IRLS: W = ১/(V(μ)g′(μ)²), z = η + (y − μ)g′(μ); Poisson-এ W = μ, z = η + (y − μ)/μ. Canonical link-এ এটিই Newton.
  5. যাচাই: deviance, Pearson residual, Pearson χ²/df; overdispersion-এ quasi-Poisson, negative binomial বা robust SE; ধনাত্মক লম্বা-লেজের লক্ষ্যে Gamma বা Tweedie.

পরের পর্বে: এখানে অনিশ্চয়তা এসেছে সূত্র থেকে: Hessian-এর inverse আর Wald interval. অনুমান সন্দেহজনক হলে বা সূত্রই না থাকলে? পর্ব ২১ (Bootstrap আর resampling — model-এর অনিশ্চয়তা মাপা) দেখাবে, ডেটা থেকেই বারবার নমুনা নিয়ে কীভাবে যেকোনো সংখ্যার অনিশ্চয়তা মাপা যায়।

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

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