Generalized Additive Models › Formula
Machine Learning from ScratchAll chapters Generalized Additive Models › FormulaSection view · Sections in this chapter4 / 10 ↓
Reading tools Definitions, links & notes
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
A GAM is best read as the answer to a design question: how do we let every predictor bend, yet keep the model as tractable and interpretable as a linear one? The formula answers that question in three interlocking layers, and each layer exists to solve a specific problem.
- An additive predictor built from smooths replaces the single straight line of linear regression with a sum of curves. This buys flexibility while preserving the "one term per predictor" structure that makes the model readable.
- A link function connects that additive predictor to the mean of the response, so that the same structure can serve a Gaussian, binary, or count outcome.
- A smoothness penalty discourages rough fitted curves. Identifiability constraints are also needed, and the amount of smoothing still requires selection.
It helps to see each problem before its formula. Picture two failure modes we must avoid. If we let the smooths be arbitrarily wiggly, they will thread through noise; if we over-restrict them, we are back to a straight line and lose the whole point. The penalty is the dial between these extremes. And if we ignore the scale of the response, the same smooths cannot respect a probability or a count. The link is what fixes the scale. With both ideas in place, the additive predictor is the piece that ties the predictors together. We build the layers in that order of need.
Once the basis is fixed, the additive predictor is linear in its unknown coefficients. For a Gaussian response with an identity link, fitting reduces to a quadratic penalized least-squares problem. Other response families generally require iterative likelihood fitting. The following steps distinguish the predictor, link, and fitting objective.
Additive predictors and link functions
The additive predictor sums an intercept and one component per predictor for observation :
where:
- : index over observations,
- : the value of the additive predictor for observation ,
- : an intercept shared by all observations,
- : the number of predictors,
- : the value of predictor for observation ,
- : a smooth function of predictor , not necessarily linear.
If for every predictor, this becomes the predictor from Multiple Linear Regression. A GAM relaxes that restriction one term at a time: each may curve, but the terms still add, so we can still read the model one predictor at a time.
Why insist on additivity instead of fitting one flexible surface ? Additivity restricts the model to a smaller family of surfaces. The additive decomposition restricts the unknown surface to a sum of one-dimensional curves, whose coefficients are fitted jointly. That trade between flexibility within each predictor and additivity across predictors is the structural bet of a GAM, and it is exactly what we will exploit when we expand each in a basis.
There is a second gap to close. The smooths naturally produce a real number, but the response need not live on the real line: a binary outcome is zero or one, while a finite logit predictor gives a fitted mean in . A count mean need not be an integer; a log link keeps it positive. The link function bridges that gap:
Here is a monotone, differentiable link function. Its role is to connect the permissible mean to the additive predictor. The log and logit links map their mean domains onto the real line; an inverse link has a restricted predictor domain and requires corresponding constraints. Common choices are:
- identity link for Gaussian regression (this is the case we will spend most time on),
- logit link for binary responses,
- log link for counts,
- inverse link for gamma or inverse-Gaussian responses.
For a Gaussian GAM with identity link, the model predicts the conditional mean directly. For a binary GAM with logit link, the model predicts the log-odds of success, and each describes how the log-odds change as a smooth function of . For a count GAM with log link, each describes how the log expected count changes.
Notice the clean division of labor this creates: the smooths describe shape on the scale of , and the link describes scale. That separation is what allows one spline-and-penalty mechanism to serve radically different response types without being redesigned for each.
We are foreshadowing here because the later chapter Generalized Linear Models develops the link-function framework in full. For this chapter the important point is that the same additive machinery, namely smooths, penalties, and penalized least-squares solves, extends to suitable GLM families and links through penalized likelihood, with additional iterative fitting (iterative reweighting, which we will sketch but not build from scratch).
Spline bases and smoothness penalties
To compute the fit using finitely many coefficients, we represent each smooth with a spline basis:
where:
- is the number of basis functions we allocate to predictor ,
- is the -th basis function, a known deterministic function of ,
- are unknown coefficients we will estimate.
We use B-spline basis functions: low-degree piecewise polynomials with local support. Natural cubic spline bases are another option, but their individual basis functions need not have local support. Taken together, they form a flexible family of curves. The dimension controls the maximum possible wiggliness: with more basis functions we can represent more complex shapes.
A mental picture makes the next step natural. Each is a localized bump, and the fitted smooth is a weighted sum of those bumps, with the weights given by the coefficients . Increasing adds more bumps and therefore more potential detail, but it also adds more coefficients that can conspire to fit noise.
Substituting the basis expansion into the additive predictor gives
This is now linear in the coefficients . If we stack all coefficients into a single vector and expand each predictor into its basis, the model becomes for an augmented design matrix whose columns are the basis functions evaluated at each observation.
We now reach the second problem hinted at earlier. With enough independent basis columns, a spline can interpolate a finite sample at distinct inputs. Fitting noise that closely can hurt predictions on new observations. Too small a basis can instead exclude real structure. Rather than solve the flexibility problem by choosing the basis size, we keep the basis generous and control curvature directly. We penalize it with a quadratic penalty on the (second) differences of neighboring coefficients:
This is the coefficient-difference penalty used in P-splines. Classical P-splines pair it with regularly spaced B-splines; our later Patsy example uses quantile knots with the same coefficient-difference regularizer, so its smoothing is tied to that parameterization. Each difference measures curvature in coefficient-index space. For smooth coefficient sequences on a fine, uniform knot grid with compatible boundary treatment, appropriate knot-spacing scaling makes this a discrete approximation to a derivative penalty. It is not generally identical to integrating the squared second derivative of the curve. Squaring and summing accumulates a measure of total curvature, so the penalty grows when the fitted coefficients oscillate from one basis function to the next. The parameter controls how strongly that penalty acts:
- gives an unpenalized spline, which can be as wiggly as the basis allows;
- forces the coefficient sequence to be affine in its index; the resulting curve depends on the knot arrangement;
- moderate interpolates between the two, giving the data room to bend but discouraging noise.
The penalty is deliberately a local statement about neighboring coefficients. It does not insist that the whole curve be flat; it only makes sharp reversals from one coefficient to the next expensive. That is why a moderate still permits broad bends (like a sinusoid) while a huge one suppresses the penalized directions. An affine coefficient sequence need not produce a globally straight line with quantile knots and repeated boundary knots.
PythonIn [3]Expand
k_coef = 16
index = np.arange(k_coef)
D_pen = np.diff(np.eye(k_coef), n=2, axis=0)
S_pen = D_pen.T @ D_pen
coef_sequences = {
"constant": np.ones(k_coef),
"affine": 0.6 * index - 2.0,
"sinusoid": 1.5 * np.sin(2.0 * np.pi * index / (k_coef - 1)),
"alternating": np.where(index % 2 == 0, 1.0, -1.0),
}
penalty_values = {
name: float(np.sum((D_pen @ coef) ** 2))
for name, coef in coef_sequences.items()
}VisualizationOut [4]Expand

In matrix form the penalty for smooth can be written as
where:
- collects the smooth coefficients,
- is the second-difference matrix, each row of the form ,
- is a symmetric positive semidefinite matrix that encodes the penalty.
Putting the pieces together, the Gaussian GAM is fit by minimizing
Read the two terms as a negotiation. The first term asks the curve to explain the data; the second asks it to stay smooth. The smoothing parameters set the exchange rate between those demands.
In matrix notation, augmenting with a leading column of ones for the intercept and stacking the coefficient blocks,
where is the block-diagonal matrix with zero in the intercept block and in the block for smooth . After imposing centering, the following matrix formulas use the free coefficients and the correspondingly transformed design and penalty; the symbol now denotes those identifiable coordinates. Setting the gradient of with respect to equal to zero and rearranging (exactly as we did for OLS in Ordinary Least Squares (OLS)) gives the closed-form solution
This is identical in form to ridge regression, with playing the role of a feature-specific ridge penalty. The inverse exists only when the design identifies every direction left unpenalized: . Indeed, for any coefficient direction , . A positive smoothing parameter alone cannot remove an unpenalized constant ambiguity. The inverse formula therefore applies after identifiability constraints; in code we solve the system rather than form an inverse. The fitted curve for predictor is , and the full fitted predictor is .
For many family and link choices, such as Bernoulli responses with a logit link or Poisson counts with a log link, the penalized likelihood is non-quadratic. These models are commonly fitted by iterative reweighted least squares (IRLS), which replaces one least-squares solve with a sequence of penalized weighted least-squares solves. The likelihood and link jointly determine the objective; a nonlinear inverse link alone does not determine whether it is quadratic. A common Fisher-scoring update is
where:
- : the IRLS iteration index
- : the diagonal matrix of GLM working weights at iteration , with entries depending on the variance function and link derivative
- : the working response at iteration , formed from the current linear predictor and the link
- : the same block-diagonal penalty matrix as in the Gaussian case
For independent GLM observations, a common working response is , where is the derivative of the link at the current fitted mean. The corresponding working weight is . The response linearizes the mean relationship locally, while the weights account for conditional variance and the link's slope. These expressions require valid interior means, finite positive variances, and nonzero link derivatives. A common dispersion factor can instead be absorbed into the penalty convention. The resulting Fisher-scoring iteration may need damping and convergence checks; the displayed update is not a guarantee of convergence.
We can retain the same chosen coefficient penalty when changing the response family and link, while changing the working weights and response. Each IRLS step couples the additive coefficients through , and a well-defined solution requires the smooth coefficients to satisfy the identifiability conditions described next.
Mathematical properties
The difference-penalized Gaussian model has several properties worth stating:
- The second-difference matrix has a two-dimensional null space: constant and affine coefficient sequences have zero second differences. With arbitrary knot spacing this statement concerns coefficients, not a guarantee of exact linearity in the predictor.
- A constant can be transferred between a smooth and the intercept without changing predictions. We impose on the training observations for each component. This is a constraint during fitting, rather than merely subtracting a mean from a plotted curve afterward.
- With basis and smoothing parameters fixed, the Gaussian fit is linear in the responses. Its total effective degrees of freedom are , where for the identifiable design. This counts the intercept too; a centered component does not generally have degrees of freedom ranging from to .
The matrix maps observed responses to fitted values: . With the basis and smoothing parameters fixed, its diagonal entry measures how sensitive fitted value is to observed response . The trace, , sums these diagonal entries to give an effective parameter count. For an unpenalized, full-column-rank least-squares fit, it equals the number of fitted coefficients. Smoothing shrinks the penalized directions, so the effective count can be fractional.
The next figure isolates one unconstrained, full-rank, 12-column spline basis with no separate intercept. Its total effective degrees of freedom approach the two unpenalized coefficient directions as smoothing grows. This is a separate demonstration from the centered two-feature model, where the intercept and each constraint affect the count.
PythonIn [5]Expand
import numpy as np
import patsy
edf_x = np.linspace(-3.0, 3.0, 120)
edf_basis = patsy.dmatrix(
"0 + bs(edf_x, df=12, degree=3, include_intercept=True, "
"lower_bound=-3, upper_bound=3)",
{"edf_x": edf_x},
return_type="matrix",
)
edf_B = np.asarray(edf_basis)
edf_D = np.diff(np.eye(edf_B.shape[1]), n=2, axis=0)
edf_S = edf_D.T @ edf_D
edf_lambdas = np.logspace(-4, 4, 80)
edf_values = np.empty_like(edf_lambdas)
for i, lam in enumerate(edf_lambdas):
edf_A = edf_B.T @ edf_B + lam * edf_S
edf_values[i] = np.trace(np.linalg.solve(edf_A, edf_B.T @ edf_B))VisualizationOut [6]Expand

For independent errors of variance and fixed smoothing parameters, write . Then . Here collects the sampling covariances of the estimated free coefficients. Its diagonal entries are their variances; their square roots give standard errors conditional on the fixed design and smoothing choices. The factor maps response noise to coefficient noise. These conditional sampling variances do not include smoothing-selection uncertainty or remove smoothing bias.
Comments
Add to the conversation
A question, a correction, or an example from your own work.
No comments yet. Be the first to share your thoughts!