Skip to main content

Generalized additive models (GAM)

note

GAM models are currently experimental.

Introduction​

A Generalized Additive Model (GAM) is a type of Generalized Linear Model (GLM) where the linear predictor has a linear relationship with predictor variables and smooth functions of predictor variables. H2O's GAM implementation is closely based on the approach described in "Generalized Additive Models: An Introduction with R, Texts in Statistical Science [1]" by Simon N. Wood. Another useful resource on GAMs can be found in "Generalized Additive Models" by T.J. Hastie and R.J. Tibshirani [2].

MOJO support​

GAM supports importing and exporting MOJOs.

Defining a GAM model​

Parameters are optional unless specified as required. GAM shares many GLM parameters.

Algorithm-specific parameters​

  • bs: An array specifying the spline types for each GAM predictor. You must include one value for each GAM predictor. One of:

    • 0 (default) specifies cubic regression spline.
    • 1 specifies thin plate regression with knots.
    • 2 specifies monotone splines (or I-splines).
    • 3 specifies NBSplineTypeI M-splines (which can support any polynomial order).
  • gam_columns: Required Include an array of column names representing the smoothing terms used for prediction. GAM will build a smoother for each specified column.

  • keep_gam_cols: Specify whether to save keys storing GAM columns. This option defaults to False (disabled).

  • knot_ids: A string array storing frame keys/IDs that contain knot locations. Specify one value for each GAM column specified in gam_columns.

  • num_knots: An array that specifies the number of knots for each predictor specified in gam_columns.

  • scale: An array specifying the smoothing parameter for GAM. If specified, must be the same length as gam_columns.

  • scale_tp_penalty_mat: Scale penalty matrix for thin plate smoothers. This option defaults to False.

  • splines_non_negative: (Applicable for I-spline or bs=2 only) Set this option to True if the I-splines are monotonically increasing (or monotonically non-decreasing). Set this option to False if the I-splines are monotonically decreasing (or monotonically non-increasing). If specified, this option must be the same size as gam_columns. Values for other spline types will be ignored. This option defaults to True (enabled).

  • spline_orders: Order of I-splines (also known as monotone splines) and NBSplineTypeI M-splines used for GAM predictors. For I-splines, the spline_orders will be the same as the polynomials used to generate the splines. For M-splines, the polynomials will be spline_orders −1-1. For example, spline_orders=3 for I-splines means a polynomial of order 3 will be used in the splines while for M-splines it means a polynomial of order 2 will be used. If specified, this option must be the same size as gam_columns. Values for bs=0 or bs=1 will be ignored.

  • standardize_tp_gam_cols: Standardize thin plate predictor columns. This option defaults to False.

  • subspaces: List model parameters that can vary freely within the same subspace list, allowing the user to group model parameters with restrictions. If specified, the following parameters must have the same array dimsension:

    • gam_columns
    • num_knots
    • scale
    • bs

    Here is an example specifying these parameters:

    gam = H2OGeneralizedAdditiveEstimator(family='binomial',
    gam_columns=["C11", "C12", "C13", ["C14", "C15"]],
    knot_ids=[frameKnotC11.key, framKnotC12.key, frameKnotC13.key, frameKnotC145.key],
    bs=[0,2,3,1],
    standardize=True,
    lambda_=[0],
    alpha=[0],
    max_iterations=1,
    store_knot_locations=True)

Common parameters​

  • auc_type: Set the default multinomial AUC type. Must be one of:

    • "AUTO" (default)
    • "NONE"
    • "MACRO_OVR"
    • "WEIGHTED_OVR"
    • "MACRO_OVO"
    • "WEIGHTED_OVO"
  • early_stopping: Specify whether to stop early when there is no more relative improvement on the training or validation set. This option defaults to True (enabled).

  • export_checkpoints_dir: Specify a directory to which generated models will automatically be exported.

  • fold_assignment: (Applicable only if a value for nfolds is specified and fold_column is not specified) Specify the cross-validation fold assignment scheme. One of:

    • AUTO (default; uses Random)
    • Random
    • Modulo (read more about Modulo)
    • Stratified (which will stratify the folds based on the response variable for classification problems)
  • fold_column: Specify the column that contains the cross-validation fold index assignment per observation.

  • ignore_const_cols: Enable this option to ignore constant training columns as they provide no useful information. This option defaults to True (enabled).

  • ignored_columns: (Python only) Specify the column or columns to be excluded from the model.

  • keep_cross_validation_fold_assignment: Enable this option to preserve the cross-validation fold assignment. This option defaults to False (disabled).

  • keep_cross_validation_models: Specify whether to keep the cross-validated models. Keeping cross-validation models may consume significantly more memory in the H2O cluster. This option defaults to True (enabled).

  • keep_cross_validation_predictions: Specify whether to keep the cross-validation predictions. This option defaults to False (disabled).

  • max_active_predictors: Specify the maximum number of active predictors during computation. This value is used as a stopping criterium to prevent expensive model building with many predictors. This value defaults to -1 (unlimited). This default indicates that if the IRLSM solver is used, the value of max_active_predictors is set to 5000, otherwise it is set to 100000000.

  • max_iterations: Specify the number of training iterations. This option defaults to -1 (unlimited).

  • max_runtime_secs: Maximum allowed runtime in seconds for model training. Use 0 (default) to disable.

  • missing_values_handling: Choose how to handle missing values (one of: Skip, MeanImputation (default), or PlugValues).

  • model_id: Provide a custom name for the model to use as a reference. By default, H2O automatically generates a destination key.

  • nfolds: Specify the number of folds for cross-validation. The value can be 0 (default) to disable or ≥\geq 2.

  • offset_column: Specify a column to use as the offset; the value cannot be the same as the weights_column.

    Note: Offsets are per-row "bias values" that are used during model training. For Gaussian distributions, they can be seen as simple corrections to the response (y) column. Instead of learning to predict the response (y-row), the model learns to predict the (row) offset of the response column. For other distributions, the offset corrections are applied in the linearized space before applying the inverse link function to get the actual response values.

  • score_each_iteration: Enable this option to score during each iteration of the model training. This option defaults to False (disabled).

  • seed: Specify the random number generator (RNG) seed for algorithm components dependent on randomization. The seed is consistent for each H2O instance so that you can create models with the same starting conditions in alternative configurations. This option defaults to -1 (time-based random number).

  • standardize: Specify whether to standardize the numeric columns to have a mean of zero and unit variance. Standardization is highly recommended; if you do not use standardization, the results can include components that are dominated by variables that appear to have larger variances relative to other attributes as a matter of scale, rather than true contribution. This option defaults to False (disabled).

  • stopping_metric: Specify the metric to use for early stopping. The available options are:

    • AUTO (default): (This defaults to logloss for classification and deviance for regression)
    • deviance
    • logloss
    • MSE
    • RMSE
    • MAE
    • RMSLE
    • AUC (area under the ROC curve)
    • AUCPR (area under the Precision-Recall curve)
    • lift_top_group
    • misclassification
    • mean_per_class_error
  • stopping_rounds: Stops training when the option selected for stopping_metric doesn't improve for the specified number of training rounds, based on a simple moving average. To disable this feature, specify 0 (default).

    Note: If cross-validation is enabled:

    • All cross-validation models stop training when the validation metric doesn't improve.
    • The main model runs for the mean number of epochs.
    • N+1 models may be off by the number specified for stopping_rounds from the best model, but the cross-validation metric estimates the performance of the main model for the resulting number of epochs (which may be fewer than the specified number of epochs).
  • stopping_tolerance: Specify the relative tolerance for the metric-based stopping to stop training if the improvement is less than this value. This option defaults to 0.001.

  • training_frame: Required Specify the dataset used to build the model.

  • validation_frame: Specify the dataset used to evaluate the model's accuracy.

  • weights_column: Specify a column to use for the observation weights, which are used for bias correction. The specified weights_column must be included in the specified training_frame.

    Python only: To use a weights column when passing an H2OFrame to x instead of a list of column names, the specified training_frame must contain the specified weights_column.

    Note: Weights are per-row observation weights and do not increase the size of the data frame. This is typically the number of times a row is repeated, but non-integer values are supported as well. During training, rows with higher weights matter more due to the larger loss function pre-factor.

  • x: Specify a vector containing the names or indices of the predictor variables to use when building the model. If x is missing, then no predictors will be used.

  • y: Required Specify the column to use as the dependent variable.

    • For a regression model, this column must be numeric (Real or Int).
    • For a classification model, this column must be categorical (Enum or String). If the family is Binomial, the dataset must contain two levels.

A simple linear model​

For nn observations, xix_i with response variable yiy_i, where yiy_i is an observation on random variable YiY_i. and ui≡E(Yi)u_i \equiv E(Y_i). Assuming a linear relationship between the predictor variables and the response, the relationship between xixi and YiY_i is:

Yi=ui+ϵi where ui=βixi+β0Y_i = u_i + \epsilon_i \text{ where } u_i = \beta_i x_i + \beta_0

and βi,β0\beta_i, \beta_0 are unknown parameters, ϵi\epsilon_i are i.i.d zero mean variables with variances δ2\delta^2. We can estimate βi,β0\beta_i, \beta_0 using GLM.

A simple linear GAM model​

Using the same observations as in the previous A Simple Linear Model section, a linear GAM model can be:

Yi=f(xi)+ϵi where f(xi)=Σj=1kbj(xi)βj+β0Y_i = f(x_i) + \epsilon_i \text{ where } f(x_i) = {\Sigma_{j=1}^k}b_j(x_i)\beta_j+\beta_0

Again, β=[β0,β1,…,bk]\beta = [\beta_0, \beta_1, \ldots, b_k] is an unknown parameter vector that can also be estimated using GLM. This can be done by using [b1(xi),b2(xi),…,bK(xi)][b_1(x_i), b_2(x_i), \ldots , b_K(x_i)] as the predictor variables instead of xix_i. We are essentially estimating f(xi)f(x_i) using a set of basis functions:

{b1(xi),b2(xi),…,bK(xi)}\{b_1(x_i), b_2(x_i), \ldots, b_K(x_i)\}

where kk is the number of basis functions used. Note that for each predictor variable, we can decide the types and number of basis functions that we want to use to generate best GAM.

Understanding simple piecewise linear basis functions​

To comprehend the role of basis functions, let's take a look at a linear tent function. This will help us understand how piecewise basis functions work.

When working with piecewise basis functions, it's crucial to pay attention to the points where the function's derivative discontinuities occur. These points are where the linear pieces connect, and they are known as knots. We denote these knots by {xi∗:j=1,…,K}\{x_i^*:j=1, \ldots, K\}. And suppose that the knots are sorted, meaning that xi∗>xi−1∗x_i^* > x_{i-1}^*.

For j=2,…,K−1j=2, \ldots, K - 1, the basis function bj(x)b_j(x) defined as:

Piecewise linear basis function b_j(x) definition

Piecewise linear basis function b_j(x) boundary conditions

This function helps us understand how the linear pieces join together and interact with each other.

Using piecewise tent functions to approximate a single predictor variable​

To illustrate how we can use the piecewise tent functions to approximate a predictor variable, let's take an example where the predictor value ranges from 0.0 to 1.0.:

We will use 10 piecewise tent functions, with K = 10. The knots will be located at 0, 1/9, 2/9, 3/9, …, 8/9, 1. The figure below shows the basis function values, which overlap with their neighbors except for the first and last basis functions.

Ten piecewise tent basis functions over [0, 1]

For simplicity, assume we have 21 predictor values uniformly distributed between 0 and 1, with values of 0, 0.05, 0.1, 0.15, …, 1.0. Our goal is to convert each xjx_j into a set of 10 basis function values. For every xjx_j value, 10 values correspond to each of the basis functions.

For the predictor value at 0 (xj=0x_j = 0), the only relevant basis function is the first one. All other basis functions contribute 0 to the predictor value. Thus, for xj=0x_j = 0 the vector representing all basis functions has these values: {1, 0, 0, 0, 0, 0, 0, 0, 0, 0}. This is because the first basis function value is 1 at xj=0x_j = 0:

b1(x)=(19−x)(29−19)b_1(x) = \frac{\big(\frac{1}{9} - x \big)}{\big(\frac{2}{9} - \frac{1}{9} \big)}

For predictor value 0.05, only the first and second basis functions contribute, while the others are 0 at 0.05. The value of the first basis function is 0.55. which can be obtained by substituting x=0.05x=0.05 in the first basis function:

b1(x)=(19−x)(29−19)b_1(x) = \frac{\big(\frac{1}{9} - x \big)}{\big(\frac{2}{9} - \frac{1}{9} \big)}

The value of the second basis function at 0.05 is 0.45. Note Substitute x=0.05x=0.05 to the second basis function

b2(x)=x(19)b_2(x) = \frac{x}{\big(\frac{1}{9}\big)}

Hence, for xj=0.05x_j = 0.05, the vector corresponding to all basis function is {0.55,0.45,0,0,0,0,0,0,0,0}.

We have calculated the expanded basis function vector for all predictor values, and they can be found in following table.

xjx_jb1b_1b2b_2b3b_3b4b_4b5b_5b6b_6b7b_7b8b_8b9b_9b10b_{10}
01000000000
0.050.550.4500000000
0.10.10.900000000
0.1500.650.350000000
0.200.20.80000000
0.25000.750.25000000
0.3000.30.7000000
0.350000.850.1500000
0.40000.40.600000
0.4500000.950.050000
0.500000.50.50000
0.5500000.050.950000
0.6000000.60.4000
0.65000000.150.85000
0.70000000.70.300
0.750000000.250.7500
0.800000000.80.20
0.8500000000.350.650
0.9000000000.90.1
0.95000000000.450.55
10000000001

Spline functions​

Natural cubic splines are proven to be the smoothest interpolators, as shown in [2]. Given a set of points xi,yi:i=1,…,n{x_i, y_i:i = 1, \ldots, n} where xi≤xi+1x_i \leq x_{i+1}, the natural cubic spline, g(x)g(x), interpolates these points using sections of cubic polynomial for each [xi,xi+1][x_i, x_{i+1}]. These sections are joined together so that the entire spline is continuous to the second derivative, with g(xi)=yig(x_i) = y_i and g′′(xi)=g′′(xn)=0g^{''}(x_i) = g^{''}(x_n) = 0. To ensure a smooth function, a penalty function J(f)=∫x1xn(f′′(x))2dxJ(f) = \int_{x_1}^{x_n} {(f^{''}(x))^2}dx can be added to the objective function being optimized. This penalty is based on the idea that a function's second derivative measures gradient change, and a higher second derivative magnitude indicates more wriggling.

Cubic regression splines​

Implemented based on [1], cubic regression splines are used for a single predictor variable. This approach defines the splines in terms of their values at the knots. A cubic spline function, f(x)f(x), with kk knots, x1,x2,…,xkx_1, x_2, \ldots, x_k, is defined using βj=f(xj)\beta_j = f(x_j) and δj=f′′(xj)=d2f(xj)d2x\delta_j = f^{''}(x_j) = \frac{d^2f(x_j)}{d^2x}.

The splines can be expressed as:

f(x)=aj−(x)βj+aj+(x)βj+1+cj−(x)δj+cj+(x)δj+1 for xj≤x≤xj+1f(x) = a_j^-(x)\beta_j + a_j^+(x)\beta_{j+1} + c_j^-(x)\delta_j + c_j^+(x) \delta_{j+1} \text{ for } x_j \leq x \leq x_{j+1}

where:

  • aj−(x)=(xj+1−x)/hj,aj+(x)=(x−xj)/hja_j^-(x) = (x_{j+1} - x)/h_j, a_j^+(x) = (x - x_j) / h_j
  • cj−(x)=[(xj+1−x)3hj−hj(xj+1−x)]/6,cj+(x)=[(x−xj)3hj−hj(x−xj]/6c_j^-(x) = \big[\frac{(x_{j+1}-x)^3}{h_j} - h_j(x_{j+1} - x)\big] /6, c_j^+(x) = \big[\frac{(x-x_j)^3}{h_j} - h_j(x-x_j \big] / 6

To ensure smooth fitting functions at the knots, the spline must be continuous to the second derivative at xjx_j and have zero second derivative at x1x_1 and xkx_k. It can be shown that βδ−=DB\beta\delta^- = DB, where

Cubic regression spline matrix B and D definition

Let BinvD=B−1DBinvD = B^{-1}D and let F=[0BinvD0]F = {\begin{bmatrix}0\\BinvD\\0\end{bmatrix}}

The spline can be rewritten entirely in terms of β\beta as

f(x)=aj−(x)βj+aj+(x)βj+1+cj−(x)Fjβ+cj+(x)Fj+1β for xj≤x≤xj+1f(x) = a_j^-(x)\beta_j + a_j^+(x)\beta_{j+1} + c_j^-(x)F_j\beta + c_j^+(x)F_{j+1}\beta \text{ for } x_j \leq x \leq x_{j+1}

which can be expressed as f(xi)=∑j=1kbj(xi)βj+β0f(x_i) = \sum_{j=1}^{k}b_j(x_i)\beta_j+\beta_0 where bj(xi)b_j(x_i) are the basis functions and β0,β1,…,βk\beta_0, \beta_1, \ldots, \beta_k are the unknown parameters that can be estimated using GLM. Additionally, the penalty term added to the final objective function can be derived as:

∫x1xk(f′′(x))2dx=βTDTB−1Dβ=βTDTBinvDβ=βTSβ\int_{x_1}^{x_k} (f^{''}(x))^2dx = \beta^T D^T B^{-1} D\beta = \beta^T D^T BinvD\beta = \beta^T S\beta

where S=DTB−1DS = D^T B^{-1} D

For linear regression models, the final objective function to minimize is

∑i=1n(yi−(∑j=1kbj(xi)βj+β0))+λβTSβ\sum_{i=1}^n \bigg( y_i - \big( \sum_{j=1}^k b_j(x_i)\beta_j + \beta_0 \big) \bigg) + \lambda \beta^T S \beta

Note that the user will choose λ\lambda using grid search. In future releases, cross-validation may be used to automatically select lambdalambda.

At this point, GLM can be called, but the contribution of the penalty term to the gradient and Hessian calculation still needs to be added.

Thin plate regression splines​

For documentation on thin plate regression splines, refer to Thin plate regression splines.

Monotone splines​

We have implemented I-splines, which are used as monotone splines. Monotone splines do not support multinomial or ordinal families. To specify the monotone spline, you need to set bs = 2 and specify spline_orders, which will be equal to the polynomials used to generate the splines. B-splines: Qi,k(t)Q_{i,k}(t)

B-splines are generated using a recursive formula over a set of knots t0,t1,…,tNt_0,t_1,\dots ,t_N that covers the input range of interest. The number of basis functions for B-splines over the original knots is N+1+k−2N+1+k-2, where N+1N+1 is the number of knots without duplication and kk is the spline order.

Qi,k(t)=(t−ti)(ti+k−ti)Qi,k−1(t)+(ti+k−t)(ti+k−ti)Qi+1,k−1(t)Q_{i,k}(t) = {\frac{(t-t_{i})}{(t_{i+k}-t_{i})}} Q_{i,k-1}(t) + {\frac{(t_{i+k}-t)}{(t_{i+k}-t_{i})}} Q_{i+1,k-1}(t)

Using knotes t0,t1,…,tNt_0,t_1,\dots ,t_N over the range of inputs of interest from t0t_0 to tNt_N, an order 1 B-spline is defined as [4]:

Qi,1(t)={1(ti+1−ti),ti≤t<ti+10,t<ti or t≥ti+1\begin{aligned} Q_{i,1}(t) = \begin{cases}{\frac{1}{(t_{i+1}-t_i)}},t_i \leq t < t_{i+1} \\ 0,t<t_i \text{ or } t \geq t_{i+1} \end{cases} \end{aligned}

Extending the number of knots

To generate higher order splines, you have to extend the original knots t0,t1,…,tNt_0,t_1,\dots ,t_N over the range of inputs of interest. You do this by adding k−1k-1 knots of value t0t_0 to the front of the knots and k−1k-1 knots of value tNt_N to the end of the knots. The new duplication will look like:

t0,t0,…,t0,t1,t2,…,tN−1,tN,tN,…,tNt_0,t_0,\dots ,t_0,t_1,t_2,\dots ,t_{N-1},t_N,t_N,\dots ,t_N

where:

  • t0,t0,…,t0t_0,t_0,\dots ,t_0 and tN,tN,…,tNt_N,t_N,\dots ,t_N are the kk duplicates.

The formula we used to calculate the number of basis functions over the original knots t0,t1,…,tNt_0,t_1,\dots ,t_N is:

N+1+k−2N+1+k-2

where:

  • N+1N+1 is the number of knots over the input range without duplication
  • kk is the order of the spline

M-splines: Mi,k(t)M_{i,k}(t)

M-splines serve two functions: they are part of the construction of I-splines and they are normal (non-monotonic) splines that are implemented separate of the monotone spline as part of the GAM toolbox. You must set bs = 3 for M-splines. spline_orders must also be set where the polynomials used to generate the splines will be equal to spline_orders-1. If you set spline_orders = 1 then you must set num_knots >= 3.

The B-spline function can be normalized and denoted as Mi,k(t)M_{i,k}(t) where it has an integration of 1 over the range of interest and is non-zero. This is the normalized B-spline Type I, and it is defined as:

Mi,k(t)=k×Qi,k(t)M_{i,k}(t) = k \times Q_{i,k}(t)

with the property ∫−∞+∞Mi,k(t)dt=∫titi+kMi,k(t)dt=1\int_{- \infty}^{+ \infty} M_{i,k}(t)dt = \int_{t_i}^{t_{i+k}} M_{i,k}(t)dt = 1.

You can derive Mi,k(t)M_{i,k}(t) using the following recursive formula:

Mi,k(t)=kk−1((t−t1)(ti+k−ti)Mi,k−1(t)+(ti+k−t)(ti+k−ti)Mi+1,k−1(t))M_{i,k}(t) = \frac{k}{k-1} \bigg(\frac{(t-t_1)}{(t_{i+k}-t_i)} M_{i,k-1}(t) + \frac{(t_{i+k}-t)}{(t_{i+k}-t_i)} M_{i+1,k-1}(t) \bigg)

Note that Mi,k(t)M_{i,k}(t) is defined over the same knot sequence as the original B-spline, and the number of Mi,k(t)M_{i,k}(t) splines is the same as the number of B-splines over the same known sequence.

N-splines: Ni,k(t)N_{i,k}(t)

The N-splines are normalized to have a summation of 1 when t0≤t<tNt_0 \leq t < t_N as ∑i=0N+k−1Ni,k(t)=1\sum_{i=0}^{N+k-1}N_{i,k}(t) = 1. Ni,k(t)N_{i,k}(t) is the normalized B-spline Type II in this implementation. The N-splines share the same knot sequence with the original M-spline and B-spline. The N-spline can be derived from the M-spline or the B-spline using:

Ni,k(t)=(ti+k−ti)kMi,k(t)=(ti+k−ti)Qi,k(t)N_{i,k}(t) = {\frac{(t_{i+k}-t_i)}{k}}M_{i,k}(t) = (t_{i+k}-t_i)Q_{i,k}(t)

Or, you can use the recursive formula where higher order N-splines can be derived from two lower order N-splines:

Ni,k(t)=t−titi+k−1−tiNi,k−1(t)+ti+k−tti+k−ti+1Ni+1,k−1(t)N_{i,k}(t) = {\frac{t-t_i}{t_{i+k-1}-t_i}}N_{i,k-1}(t)+{\frac{t_{i+k}-t}{t_{i+k}-t_{i+1}}}N_{i+1,k-1}(t)

I-splines: Ii,k(t)I_{i,k}(t)

I-splines are used to build monotone spline functions by restricting the gamified column coefficients to be ≥\geq 0. They are constructed using the N-splines.

Ii,k(t)=∑l=1i+rNl,k+1(t),t≤ti+r+1I_{i,k}(t) = \sum_{l=1}^{i+r}N_{l,k+1}(t), t \leq t_{i+r+1}

Penalty Matrix

The objective function used to derive the coefficients for regression is:

∑i=0n(yi−(∑j=0numBasis−1Ij,k(ti)βj+β0))2+λβTpenaltyMatβ\sum_{i=0}^n \Bigg(y_i- \bigg(\sum_{j=0}^{numBasis-1}I_{j,k}(t_i)\beta_j + \beta_0 \bigg)\Bigg)^2 + \lambda\beta^T penaltyMat\beta

The second derivative of all basis functions is defined as:

IksecondDeriv=[d2(I0,k(t))d2td2(I1,k(t))d2t⋮d2(InumBasis−2,k(t))d2td2(InumBasis−1,k(t))d2t]\begin{aligned} I_k^{secondDeriv} = \begin{bmatrix} {\frac{d^2(I_{0,k}(t))}{d^2t}} \\ {\frac{d^2(I_{1,k}(t))}{d^2t}} \\ \vdots \\ {\frac{d^2(I_{numBasis-2,k}(t))}{d^2t}} \\ {\frac{d^2(I_{numBasis-1,k}(t))}{d^2t}} \end{bmatrix} \end{aligned}

where the penalty matrix (penaltyMatpenaltyMat) for I-spline Ij,k(t)I_{j,k}(t) is defined as:

penaltyMat=∫t0tNIksecondDeriv× transpose of (IksecondDeriv)dtpenaltyMat = \int_{t_0}^{t_N}I_k^{secondDeriv} \times \text{ transpose of }(I_k^{secondDeriv})dt

Element at row mm and column nn of penaltyMatpenaltyMat is

penaltyMatm,n=∫t0tNd2(Im,k(t))d2td2(In,k(t))d2tdtpenaltyMat_{m,n} = \int_{t_0}^{t_N}{\frac{d^2(I_{m,k}(t))}{d^2t}}{\frac{d^2(I_{n,k}(t))}{d^2t}}dt

Derivative of M-splines

The penalty matrix written in terms of the second derivative of M-spline as:

penaltyMatm,n=∫t0tNd2Mm,k(t)dt2d2Mn,k(t)dt2dtpenaltyMat_{m,n} = \int_{t_0}^{t_N} \frac{d^2M_{m,k} (t)}{dt^2} \frac{d^2M_{n,k} (t)}{dt^2}dt

Instead of using the recursive expression, look at the coefficients associated with Mm,k(t)M_{m,k}(t), take the second derivative, and go from there. This is the procedure to use:

  • generate the coefficients of d2Mi,k(t)dt2\frac{d^2M_{i,k}(t)}{dt^2};
  • implement multiplication of coefficients of d2Mi,k(t)dt2d2Mj,k(t)dt2dt\frac{d^2M_{i,k} (t)}{dt^2} \frac{d^2M_{j,k} (t)}{dt^2}dt. Due to the commutative property, d2Mi,k(t)dt2d2Mj,k(t)dt2dt=d2Mj,k(t)dt2dtd2Mi,k(t)dt2\frac{d^2M_{i,k} (t)}{dt^2} \frac{d^2M_{j,k} (t)}{dt^2}dt = \frac{d^2M_{j,k} (t)}{dt^2}dt \frac{d^2M_{i,k} (t)}{dt^2}, so you only need to perform the multiplication once and the penaltyMatm,npenaltyMat_{m,n} is symmetrical;
  • implement the integration of d2Mi,k(t)dt2d2Mj,k(t)dt2\frac{d^2M_{i,k}(t)}{dt^2} \frac{d^2M_{j,k}(t)}{dt^2} by easy integration of the coefficients.

General GAM​

In a generalized additive model (GAM), using the GLM jargon, the link function can be constructed using a mixture of predictor variables and smooth functions of predictor variables as follows:

g(ui)=β0+β1x1i+⋯+βmxmi+∑j=1k1bji(xli)βm+j+⋯+∑j=1kqbjq(xli)βm+k1+⋯+kq−1+jg(u_i) = \beta_0 + \beta_1 x_{1i} + \cdots + \beta_mx_{mi} + \sum_{j=1}^{k_1}b_j^i(x_{li})\beta_{m+j} + \cdots + \sum_{j=1}^{k_q}b_j^q(x_{li})\beta_{m+k_1+\cdots+k_{q-1} + j}

This is the GAM we implemented in H2O. However, with multiple predictor variables in any form, we need to resolve the identifiability problems by adding identifiability constraints.

Identifiability constraints​

Consider GAM with multiple predictor smooth functions like the following:

yi=a+f1(xi)+f2(v1)+ϵiy_i = a+f_1(x_i) + f_2(v_1) + \epsilon_i

The model now contains more than one function introduces an identifiability problem: f1f_1 an f2f_2 are each only estimable to within an additive constant. This is due to the fact that f1(xi)+f2(vi)=(f1(xi)+C)+(f2(vi)−C)f_1(x_i) + f_2(v_i) = (f_1(x_i) + C) + (f_2(v_i) - C). Hence, identifiability constraints have to be imposed on the model before fitting to avoid the identifiability problem. The following sum-to-zero constraints are implemented in H2O:

∑i=1nfp(xi)=0=1Tfp\sum_{i=1}^n f_p(x_i) = 0 = 1^Tf_p

where 1 is a column vector of 1, and fpf_p is the column vector containing fp(x1),…,fp(xn)f_p(x_1), \ldots ,f_p(x_n). To apply the sum-to-zero constraints, a Householder transform is used. Refer to [1] for details. This transform is applied to each basis function of any predictor column we choose on its own.

Note: this does not apply to monotone splines because the coefficients for these splines must be ≥0\geq 0.

Sum-to-zero constraints implementation​

Let XX be the model matrix that contain the basis functions of one predictor variable, the sum-to-zero constraints required that

1Tfp=0=1TXβ1^Tf_p = 0 = 1^TX\beta

where β\beta contains the coefficients relating to the basis functions of that particular predictor column. The idea is to create a k by (k−1)k \text{ by } (k-1) matrix ZZ such that β=Zβz\beta = Z\beta_z, then 1TXβ=01^TX\beta =0 for any βz\beta_z. To see how this works, let's go through the following derivations:

  • With ZZ, we are looking at 0=1TXβ=1TXZβz0 = 1^TX\beta = 1^TXZ\beta_z
  • Let C=1TXC=1^TX, then the QR decomposition of CT=U[P0]C^T = U {\begin{bmatrix}P\\0\end{bmatrix}} where CTC^T is of size k×1k \times 1, UU is of size k×kk \times k, PP is the size of 1×11\times1
  • Substitute everything back to 1TXZβz=[PT 0][DTZT]Zβz=[PT 0][DTZβzZTZβz]=PTDTZβz+0ZTZβz=01^TXZ\beta_z = [P^T \text{ } 0]{\begin{bmatrix}D^T\\Z^T\end{bmatrix}} Z\beta_z = [P^T \text{ } 0]{\begin{bmatrix}D^TZ\beta_z\\Z^TZ\beta_z\end{bmatrix}} = P^TD^TZ\beta_z + 0Z^TZ\beta_z=0 since DTZ=0D^TZ=0

Generating the Z matrix​

One Householder reflection is used to generate the ZZ matrix. To create the ZZ matrix, we need to calculate the QR decomposition of CT=XT1C^T = X^T1 Since CTC^T is of size k×1k \times 1, the application of one householder reflection will generate HCT=[R0]HC^T = {\begin{bmatrix}R\\0\end{bmatrix}} where RR is of size 1×11 \times 1. This implies that H=QT=QH = Q^T = Q, since the householder reflection matrix is symmetrical. Hence, computing XZXZ is equivalent to computing XHXH and dropping the first column.

Generating the Householder reflection matrix H​

Let xˉ=XT1\bar{x} = X^T1 and xˉ′=[∥xˉ∥0]\bar{x}' = {\begin{bmatrix}{\parallel{\bar{x}}\parallel}\\0\end{bmatrix}}, then H=(I−2uuT(uTu))H = (I - \frac{2uu^T}{(u^Tu)}) and u=xˉ=xˉ′u = \bar{x} = \bar{x}'.

Estimation of GAM coefficients with identifiability constraints​

The following procedure is used to estimate the GAM coefficients:

  • Generating ZZ matrix for each predictor column that uses smoothe functions
  • Generate new model matrix for each predictor column smooth function as Xz=XZX_z = XZ, new penalty function βzTZTSZβz{\beta{^T_z}}Z^TSZ\beta_z.
  • Call GLM using model matrix XzX_z, penalty function βzTZTSZβz{\beta{^T_z}}Z^TSZ\beta_z to get coefficient estimates of βz\beta_z
  • Convert βz\beta_z to β\beta using β=Zβz\beta = Z\beta_z and performing scoring with β\beta and the original model matrix XX.

Examples​

Below are simple examples showing how to use GAM in R and Python.

General GAM​

library(h2o)
h2o.init()

# create frame knots
knots1 <- c(-1.99905699, -0.98143075, 0.02599159, 1.00770987, 1.99942290)
frame_Knots1 <- as.h2o(knots1)
knots2 <- c(-1.999821861, -1.005257990, -0.006716042, 1.002197392, 1.999073589)
frame_Knots2 <- as.h2o(knots2)
knots3 <- c(-1.999675688, -0.979893796, 0.007573327, 1.011437347, 1.999611676)
frame_Knots3 <- as.h2o(knots3)

# import the dataset
h2o_data <- h2o.importFile("https://s3.amazonaws.com/h2o-public-test-data/smalldata/glm_test/multinomial_10_classes_10_cols_10000_Rows_train.csv")

# Convert the C1, C2, and C11 columns to factors
h2o_data["C1"] <- as.factor(h2o_data["C1"])
h2o_data["C2"] <- as.factor(h2o_data["C2"])
h2o_data["C11"] <- as.factor(h2o_data["C11"])

# split into train and test sets
splits <- h2o.splitFrame(data = h2o_data, ratios = 0.8)
train <- splits[[1]]
test <- splits[[2]]

# Set the predictor and response columns
predictors <- colnames(train[1:2])
response <- 'C11'

# specify the knots array
numKnots <- c(5, 5, 5)

# build the GAM model
gam_model <- h2o.gam(x = predictors,
y = response,
training_frame = train,
family = 'multinomial',
gam_columns = c("C6", "C7", "C8"),
scale = c(1, 1, 1),
num_knots = numKnots,
knot_ids = c(h2o.keyof(frame_Knots1), h2o.keyof(frame_Knots2), h2o.keyof(frame_Knots3)))

# get the model coefficients
coefficients <- h2o.coef(gam_model)

# generate predictions using the test data
pred <- h2o.predict(object = gam_model, newdata = test)

GAM using monotone splines​

# Import the GLM test data:
gam_test = h2o.importFile("https://s3.amazonaws.com/h2o-public-test-data/smalldata/glm_test/binomial_20_cols_10KRows.csv")

# Split into train and validation sets:
splits <- h2o.splitFrame(data = gam_test, ratios = 0.8)
train <- splits[[1]]
test <- splits[[2]]

# Set the factors, predictors, and response:
gam_test["C1"] <- as.factor(gam_test["C1"])
gam_test["C2"] <- as.factor(gam_test["C2"])
gam_test["C21"] <- as.factor(gam_test["C21"])
predictors <- c("C1","C2")
response <- "C21"

# Build and train the model using spline order:
monotone_model <- h2o.gam(x = predictors, y = response,
training_frame = train,
family = 'binomial',
gam_columns = c("C11", "C12", "C13"),
scale = c(0.001, 0.001, 0.001),
bs = c(0, 2, 2),
num_knots = c(3, 4, 5),
spline_orders = c(2, 3, 4))

# Generate predictions using the test data:
pred <- h2o.predict(object = monotone_model, newdata = test)

GAM using M-splines​

# Import the GLM test data set:
gam_test = h2o.importFile("https://s3.amazonaws.com/h2o-public-test-data/smalldata/glm_test/binomial_20_cols_10KRows.csv")

# Set the factors, predictors, and response:
gam_test["C1"] <- as.factor(gam_test["C1"])
gam_test["C2"] <- as.factor(gam_test["C2"])
gam_test["C21"] <- as.factor(gam_test["C21"])
predictors <- c("C1","C2")
response <- "C21"

# Split into train and validation sets:
splits <- h2o.splitFrame(data = gam_test, ratios = 0.8)
train <- splits[[1]]
test <- splits[[2]]

# Build and train the model using spline order:
mspline_model <- h2o.gam(x = predictors,
y = response,
training_frame = train,
family = "binomial",
gam_columns = c("C11", "C12", "C13"),
scale = c(0.001, 0.001, 0.001),
bs = c(2, 0, 3),
spline_orders = c(10, -1, 10),
num_knots = c(3, 4, 5))
# Retrieve the coefficients:
h2o.coef(mspline_model)

References​

  1. Simon N. Wood, Generalized Additive Models: An Introduction with R, Texts in Statistical Science, CRC Press, Second Edition.
  1. T.J. Hastie, R.J. Tibshirani, Generalized Additive Models, Chapman and Hall, First Edition, 1990.
  1. Lecture 7 Divided Difference Interpolation Polynomial by Professor R.Usha, Department of Mathematics, IITM, https://www.youtube.com/watch?v=4m5AKnseSyI .
  1. Carl De Boor et. al., ON CALCULATING WITH B-SPLINES II. INTEGRATION, ResearchGate Article, January 1976.
  1. J.O. Ramsay, “Monotone Regression Splines in Action”, Statistical Science, 1988, Vol. 3, No. 4, 425-461.

Feedback