AI Knowledge Tree

Generalized linear models

Wrap linear regression, logistic regression, and Poisson regression into one exponential-family framework with link functions.

Linear, logistic, and softmax regression look like distinct models, but they are three instances of the same template: a linear predictor inside an exponential-family distribution, connected by a link function. The generalized linear model (GLM) framework formalizes this and turns model choice into picking a distribution and a link.

The three ingredients

A GLM is specified by:

  1. Random component: y∣xy \mid x follows an exponential-family distribution with mean μ(x)\mu(x).
  2. Linear predictor: η(x)=x⊤β\eta(x) = x^\top \beta.
  3. Link function: g(μ)=ηg(\mu) = \eta, an invertible function connecting the mean to the linear predictor.

Equivalently, μ=g−1(η)=g−1(x⊤β)\mu = g^{-1}(\eta) = g^{-1}(x^\top\beta). The link's job is to match the support of μ\mu to the real line R\mathbb{R} that η\eta lives on[1].

The natural (canonical) link

The exponential-family lesson defined the natural parameter θ\theta of a distribution. The canonical link sets θ=η\theta = \eta: the linear predictor is the natural parameter[2]. With the canonical link the math simplifies dramatically:

  • Gradient: ∇ℓ=X⊤(y−μ)\nabla \ell = X^\top(\mathbf{y} - \boldsymbol\mu), a clean data-residual expression.
  • Hessian: −X⊤VX-X^\top V X with V=diag⁡(variance(yi))V = \operatorname{diag}(\text{variance}(y_i)), always PSD, so the log-likelihood is concave.
  • IRLS has the same shape across all canonical-link GLMs.

The standard catalog

ResponseDistributionCanonical link g(μ)g(\mu)Inverse link g−1(η)g^{-1}(\eta)Result
ContinuousGaussianidentity μ\muη\etaLinear regression
BinaryBernoullilogit log⁡μ1−μ\log\frac{\mu}{1 - \mu}sigmoid 11+e−η\frac{1}{1 + e^{-\eta}}Logistic regression
CountsPoissonlog log⁡μ\log \muexp eηe^{\eta}Poisson regression
Positive continuousGammareciprocal 1/μ1/\mu (or log)1/η1/\etaGamma regression
Multi-classCategoricallog log⁡pk\log p_ksoftmaxSoftmax regression

Each row is a model you have already met or are about to meet. The framework makes their relationship explicit, and the implementation generalizes: iteratively reweighted least squares (IRLS) with the right WW and zz fits any of them.

Why pick a different link

Two reasons to use a noncanonical link:

  • Match the data scale. For positive continuous responses, the log link keeps the predicted mean positive even when the linear predictor is large negative. The reciprocal canonical link can produce negative means.
  • Interpretation. A log link on counts (Poisson regression) means coefficients are multiplicative effects on the rate. A logit link on binary outcomes means coefficients are log-odds-ratios. These are the units of the conclusions you want to report.

Canonical links give the cleanest math; the right link gives the cleanest inference.

The deviance

The GLM analogue of squared error is the deviance, twice the gap between the log-likelihood of the fitted model and the log-likelihood of a saturated model that perfectly fits every observation:

D=2(ℓsat−ℓ(β^)).D = 2\big(\ell_{\text{sat}} - \ell(\hat\beta)\big). [3]

For Gaussian + identity link, the deviance is exactly the residual sum of squares. For other GLMs it generalizes appropriately, and is the goodness-of-fit quantity that hypothesis tests and information criteria are built on.

Check it on arrays

python · numpy
import numpy as np

rng = np.random.default_rng(0)

def fit_glm(X, y, link, inverse_link, var_fn, steps=20):
    """Generic IRLS for a canonical-link GLM.
    link: g(mu),  inverse_link: g^{-1}(eta),  var_fn: variance V(mu)."""
    n, d = X.shape
    mu = (y + y.mean()) / 2 + 1e-3                  # init
    eta = link(mu)
    for _ in range(steps):
        W = 1.0 / var_fn(mu)                         # canonical-link weights
        z = eta + (y - mu) / mu * 0 + (y - mu) * (1 / mu) * 0  # placeholder
        # working response for canonical link: z = eta + (y - mu) / (dmu/deta)
        # canonical link has dmu/deta = var(mu), so z = eta + (y - mu) / var(mu)
        z = eta + (y - mu) / var_fn(mu)
        beta = np.linalg.solve(X.T @ (W[:, None] * X), X.T @ (W * z))
        eta = X @ beta
        mu = inverse_link(eta)
    return beta, mu

n, d = 800, 3
X = np.column_stack([np.ones(n), rng.normal(size=(n, d - 1))])

# Gaussian + identity = linear regression
beta_lin_true = np.array([1.0, 2.0, -1.0])
y_lin = X @ beta_lin_true + 0.3 * rng.normal(size=n)
b_lin, _ = fit_glm(X, y_lin, link=lambda m: m, inverse_link=lambda e: e, var_fn=lambda m: np.ones_like(m))
print("Gaussian/identity beta:", np.round(b_lin, 3), " (vs OLS)")

# Bernoulli + logit = logistic regression
beta_log_true = np.array([-0.3, 1.5, -1.0])
p_true = 1 / (1 + np.exp(-X @ beta_log_true))
y_bin = (rng.random(n) < p_true).astype(float)
b_log, _ = fit_glm(X, y_bin,
                   link=lambda m: np.log(m / (1 - m)),
                   inverse_link=lambda e: 1 / (1 + np.exp(-e)),
                   var_fn=lambda m: m * (1 - m) + 1e-8)
print("Bernoulli/logit beta:  ", np.round(b_log, 3), " (vs true ", beta_log_true, ")")

# Poisson + log = Poisson regression
beta_pois_true = np.array([0.5, 0.4, -0.3])
mu_true = np.exp(X @ beta_pois_true)
y_count = rng.poisson(mu_true)
b_pois, _ = fit_glm(X, y_count,
                    link=lambda m: np.log(m),
                    inverse_link=lambda e: np.exp(e),
                    var_fn=lambda m: m)
print("Poisson/log beta:      ", np.round(b_pois, 3), " (vs true", beta_pois_true, ")")

A single IRLS routine, parameterized by link and variance function, recovers linear, logistic, and Poisson regression.

Exercises

Exercise 1. Derive the canonical link for the Bernoulli distribution from the exponential-family form. Write Bernoulli(μ)\text{Bernoulli}(\mu) as exp⁡(θy−b(θ))\exp(\theta y - b(\theta)) with natural parameter θ\theta, read off θ\theta as a function of μ\mu, and confirm the canonical link g(μ)=θg(\mu) = \theta is the logit.

Solution

Step 1. The Bernoulli pmf is p(y)=μy(1−μ)1−yp(y) = \mu^y (1 - \mu)^{1-y} for y∈{0,1}y \in \{0, 1\}. Take logs and exponentiate:

p(y)=exp⁡ ⁣(ylog⁡μ+(1−y)log⁡(1−μ))=exp⁡ ⁣(ylog⁡μ1−μ+log⁡(1−μ)).p(y) = \exp\!\big(y\log\mu + (1 - y)\log(1 - \mu)\big) = \exp\!\Big(y\log\tfrac{\mu}{1 - \mu} + \log(1 - \mu)\Big).

Step 2. Matching to exp⁡(θy−b(θ))\exp(\theta y - b(\theta)), the natural parameter is the coefficient of yy: θ=log⁡μ1−μ\theta = \log\frac{\mu}{1 - \mu}, and b(θ)=−log⁡(1−μ)=log⁡(1+eθ)b(\theta) = -\log(1 - \mu) = \log(1 + e^\theta).

Step 3. The canonical link sets g(μ)=θ=log⁡μ1−μg(\mu) = \theta = \log\frac{\mu}{1 - \mu}, the logit. Inverting, μ=11+e−θ\mu = \frac{1}{1 + e^{-\theta}} is the sigmoid, matching the catalog row for logistic regression.

Exercise 2. For a canonical-link GLM the score is ∇ℓ=X⊤(y−μ)\nabla\ell = X^\top(\mathbf{y} - \boldsymbol\mu). Show that at the MLE the residual vector y−μ^\mathbf{y} - \hat{\boldsymbol\mu} is orthogonal to every column of XX, and explain why this generalizes the OLS normal equations.

Solution

Step 1. The MLE β^\hat\beta is a stationary point of the log-likelihood, so ∇ℓ(β^)=0\nabla\ell(\hat\beta) = 0. With the canonical-link gradient this reads X⊤(y−μ^)=0X^\top(\mathbf{y} - \hat{\boldsymbol\mu}) = 0, where μ^=g−1(Xβ^)\hat{\boldsymbol\mu} = g^{-1}(X\hat\beta).

Step 2. Each component Xj⊤(y−μ^)=0X_j^\top(\mathbf{y} - \hat{\boldsymbol\mu}) = 0 says the residual is orthogonal to column jj. The residual lies in the orthogonal complement of the column space of XX.

Step 3. For the Gaussian/identity GLM, μ^=Xβ^\hat{\boldsymbol\mu} = X\hat\beta, so the condition becomes X⊤(y−Xβ^)=0X^\top(\mathbf{y} - X\hat\beta) = 0, the OLS normal equations. The canonical-link GLM keeps the same residual-orthogonality structure, with μ^\hat{\boldsymbol\mu} a nonlinear function of Xβ^X\hat\beta instead of a linear one.

Exercise 3 (code). Fit a Poisson/log GLM by Newton's method (which equals IRLS for the canonical link) and verify the orthogonality condition from Exercise 2: X⊤(y−μ^)≈0X^\top(\mathbf{y} - \hat{\boldsymbol\mu}) \approx 0 at the fitted optimum.

Solution
python · numpy
import numpy as np

rng = np.random.default_rng(0)
n, d = 600, 3
X = np.column_stack([np.ones(n), rng.normal(size=(n, d - 1))])
beta_true = np.array([0.3, 0.5, -0.4])
y = rng.poisson(np.exp(X @ beta_true)).astype(float)

beta = np.zeros(d)
for _ in range(50):
    mu = np.exp(X @ beta)                    # inverse log link
    g = X.T @ (y - mu)                       # canonical-link score
    H = X.T @ (mu[:, None] * X)              # -Hessian = X^T diag(mu) X
    beta = beta + np.linalg.solve(H, g)      # Newton step

mu = np.exp(X @ beta)
score = X.T @ (y - mu)
assert np.max(np.abs(score)) < 1e-6          # residuals orthogonal to every column
print("max |X^T(y - mu)| at MLE =", float(np.max(np.abs(score))))
print("beta:", np.round(beta, 3), " true:", beta_true)

The fitted residuals are orthogonal to every column to machine precision, the GLM generalization of the OLS normal equations, and the recovered coefficients sit near the truth.

The next lesson zeroes in on the last two GLMs from the table: Poisson for counts and Gamma for durations.


References

  1. [1]Nelder, J. A., & Wedderburn, R. W. M. (1972). "Generalized linear models". JRSS-A, 135(3), 370-384. doi:10.2307/2344614. The paper that introduced the GLM framework.↩
  2. [2]McCullagh, P., & Nelder, J. A. (1989). Generalized Linear Models (2nd ed.), §2. Chapman & Hall. Exponential family, canonical links, and the catalog of GLMs.↩
  3. [3]Hastie, T., Tibshirani, R., & Friedman, J. (2009). The Elements of Statistical Learning (2nd ed.), §4.4. Springer. doi:10.1007/978-0-387-84858-7. Deviance as the GLM goodness-of-fit.↩