Elastic Net Regularization Paths for All Generalized Linear Models
J. Kenneth Tay, Balasubramanian Narasimhan, Trevor Hastie
\PlaintitleElastic Net Paths for All GLMs
\ShorttitleElastic Net Paths for All GLMs
\Abstract
The lasso and elastic net are popular regularized regression models for supervised learning. Friedman2010 introduced a computationally efficient algorithm for computing the elastic net regularization path for ordinary least squares regression, logistic regression and multinomial logistic regression, while Simon2011 extended this work to Cox models for right-censored data. We further extend the reach of the elastic net-regularized regression to all generalized linear model families, Cox models with (start, stop] data and strata, and a simplified version of the relaxed lasso. We also discuss convenient utility functions for measuring the performance of these fitted models.
\Keywordslasso, elastic net, penalty, regularization path, coordinate descent, generalized linear models, survival, Cox model
\Plainkeywordslasso, elastic net, l1 penalty, regularization path, coordinate descent, generalized linear models, survival, Cox model, relaxed lasso
\Address
J. Kenneth Tay
Department of Statistics
Stanford University
390 Jane Stanford Way
Stanford, California 94305
United States of America
E-mail:
URL: https://kjytay.github.io/
Balasubramanian Narasimhan
Department of Biomedical Data Sciences, and
Department of Statistics
Stanford University
390 Jane Stanford Way
Stanford, CA 94305
E-mail:
URL: https://web.stanford.edu/~naras/
Trevor Hastie
Department of Biomedical Data Sciences, and
Department of Statistics
Stanford University
390 Jane Stanford Way
Stanford, CA 94305
E-mail:
URL: https://web.stanford.edu/~hastie/
1 Introduction
Consider the standard supervised learning framework. We have data of the form , where is the target and is a vector of potential predictors. The ordinary least squares (OLS) model assumes that the response can be modeled as a linear combination of the covariates, i.e. for some coefficient vector and intercept . The parameters are estimated by minimizing the residual sum of squares (RSS):
(1) |
There has been a lot of research on regularization methods in the last two decades. We focus on the elastic net (Zou2005) which minimizes the sum of the RSS and a regularization term which is a mixture of and penalties:
(2) |
In the above, is a tuning parameter and is a higher level hyperparameter111If the square were removed from the -norm penalty, it would be more natural to have instead of as its mixing parameter. The factor of compensates for the fact that a squared -norm penalty is used, in the sense that the gradient of the penalty with respect to can be seen as a convex combination of the and penalty terms. We note also that there is a one-to-one correspondence between these two parameterizations for the penalty.. We always fit a path of models in , but set a value of depending on the type of prediction model we want. For example, if we want ridge regression (Hoerl1970) we set and if we want the lasso (Tibshirani1996) we set . If we want a sparse model but are worried about correlations between features, we might set close to but not equal to 1. The final value of is usually chosen via cross-validation: we select the coefficients corresponding to the value giving smallest cross-validated error as the final model.
The elastic net can be extended easily to generalized linear models (GLMs) (Nelder1972) and Cox proportional hazards models (Cox1972). Instead of solving the minimization problem (2), the RSS term in the objective function is replaced with a negative log-likelihood term or a negative log partial likelihood term respectively.
The \pkgglmnet \proglangR package (Friedman2010) contains efficient functions for computing the elastic net solution for an entire path of values . The minimization problems are solved via cyclic coordinate descent (vanderKooij2007), with the core routines programmed in \proglangFORTRAN for computational efficiency. Earlier versions of the package contained specialized \proglangFORTRAN subroutines for a handful of popular GLMs and the Cox model for right-censored survival data. The package includes functions for performing -fold cross-validation (CV), plotting coefficient paths and CV errors, and predicting on future data. The package can also accept the predictor matrix in sparse matrix format: this is especially useful in certain applications where the predictor matrix is both large and sparse. In particular, this means that we can fit unpenalized GLMs with sparse predictor matrices, something the \codeglm function in the \pkgstats package cannot do.
From version 4.1 and later, \pkgglmnet is able to compute the elastic net regularization path for all GLMs, Cox models with (start, stop] data and strata, and a simplified version of the relaxed lasso. (Hastie2020).
Friedman2010 gives details on how the \pkgglmnet package computes the elastic net solution for ordinary least squares regression, logistic regression and multinomial logistic regression, while Simon2011 explains how the package fits regularized Cox models for right-censored data. This paper builds on these two earlier works. In Section 2, we explain how the elastic net penalty can be applied to all GLMs and how we implement it in software. In Section LABEL:sec:cox, we detail extensions to Cox models with (start, stop] data and strata. In Section LABEL:sec:relax, we describe an implementation of the relaxed lasso implemented in the package, and in Section LABEL:sec:assess we describe the package’s functionality for assessing fitted models. We conclude with a summary and discussion.
2 Regularized generalized linear models
2.1 Overview of generalized linear models
Generalized linear models (GLMs) (Nelder1972) are a simple but powerful extension of OLS. A GLM consists of 3 parts:
-
•
A linear predictor: ,
-
•
A link function: , and
-
•
A variance function as a function of the mean: .
The user gets to specify the link function and the variance function . For one-dimensional exponential families, the family determines the variance function, which, along with the link, are sufficient to specify a GLM. More generally, modeling can proceed once the link and variance functions are specified via a quasi-likelihood approach (see McCullagh1983 for details); this is the approach taken by the quasi-binomial and quasi-Poisson models. The OLS model is a special case, with link and constant variance function for some constant . More examples of GLMs are listed in Table 1.
GLM family / Regression type | Response type | Representation in R |
---|---|---|
Gaussian | \codegaussian() | |
Logistic | \codebinomial() | |
Probit | \codebinomial(link = "probit") | |
Quasi-Binomial | \codequasibinomial() | |
Poisson | \codepoisson() | |
Quasi-Poisson | \codequasipoisson() | |
Negative binomial | \codeMASS::negative.binomial(theta = 3) | |
Gamma | \codeGamma() | |
Inverse Gaussian | \codeinverse.gaussian() | |
Tweedie | Depends on variance power parameter | \codestatmod::tweedie() |
The GLM parameter is determined by maximum likelihood estimation. Unlike OLS, there is no closed form solution for . Rather, it is typically computed via an iteratively reweighted least squares (IRLS) algorithm known as Fisher scoring. In each iteration of the algorithm we make a quadratic approximation to the negative log-likelihood (NLL), reducing the minimization problem to a weighted least squares (WLS) problem. For GLMs with canonical link functions, the negative log-likelihood is convex in , Fisher scoring is equivalent to the Newton-Raphson method and is guaranteed to converge to a global minimum. For GLMs with non-canonical links, the negative log-likelihood is not guaranteed to be convex222It is not true that the negative log-likelihood is always non-convex for non-canonical links. For example, it can be shown via direct computation that the negative log-likelihood for probit regression is convex in .. Also, Fisher scoring is no longer equivalent to the Newton-Raphson method and is only guaranteed to converge to a local minimum.
It is easy to fit GLMs in \proglangR using the \codeglm function from the \pkgstats package; the user can specify the GLM to be fit using \codefamily objects. These objects capture details of the GLM such as the link function and the variance function. For example, the code below shows the \codefamily object associated with probit regression model: {CodeChunk} {CodeInput} R> class(binomial(link = "probit")) {CodeOutput} [1] "family" {CodeInput} R> str(binomial(link = "probit")) {CodeOutput} List of 12 link : chr "probit" linkinv : function (eta) dev.resids: function (y, mu, wt) mu.eta : function (eta) validmu : function (mu) simulate : function (object, nsim) - attr(*, "class")= chr "family" The \codelinkfun, \codelinkinv, \codevariance and \codemu.eta functions are used in fitting the GLM, and the \codedev.resids function is used in computing the deviance of the resulting model. By passing a class \code"family" object to the \codefamily argument of a \codeglm call, \codeglm has all the information it needs to fit the model. Here is an example of how one can fit a probit regression model in R: {CodeChunk} {CodeInput} R> library(glmnet) R> data(BinomialExample) R> glm(y x, family = binomial(link = "probit"))
2.2 Extending the elastic net to all GLM families
To extend the elastic net to GLMs, we replace the RSS term in (2) with a negative log-likelihood term:
(3) |
where is the log-likelihood term associated with observation . We can apply the same strategy as for GLMs to minimize this objective function. The key difference is that instead of solving a WLS problem in each iteration, we solve a penalized WLS problem.
The algorithm for solving (3) for a path of values is described in Algorithm 1. Note that in Step 2(a), we initialize the solution for at the solution obtained for . This is known as a warm start: since we expect the solution at these two values to be similar, the algorithm will likely require fewer iterations than if we initialized the solution at zero.
-
1.
Select a value of and a sequence of values .
-
2.
For :
-
(a)
Initialize . For , initialize . (Here, denotes the elastic net solution at .)
-
(b)
For until convergence:
-
i.
For , compute and .
-
ii.
For , compute working responses and weights
(4) -
iii.
Solve the penalized WLS problem
(5)
-
i.
-
(a)
2.3 Implementation details
There are two main approaches we can take in implementing Algorithm 1. In the original implementation of \pkgglmnet, the entire algorithm was implemented in \proglangFORTRAN for specific GLM families. In version 4.0 and later, we added a second implementation which implemented just the computational bottleneck, the penalized WLS problem in Step 2(b)iii, in \proglangFORTRAN, with the rest of the algorithm implemented in \proglangR. Here are the relative merits and disadvantages of the second approach compared to the first:
-
✓
Because the formulas for the working weights and responses in (4) are specific to each GLM, the first approach requires a new \proglangFORTRAN subroutine for each GLM family. This is tedious to manage, and also means that users cannot fit regularized models for their bespoke GLM families. The second approach allows the user to pass a class \code"family" object to \codeglmnet: the working weights and responses can then be computed in \proglangR before the \proglangFORTRAN subroutine solves the resulting penalized WLS problem.
-
✓
As written, Algorithm 1 is a proximal Newton algorithm with a constant step size of 1, and hence it may not converge in certain cases. To ensure convergence, we can implement step-size halving after Step 2(b)iii: as long as the objective function (3) is not decreasing, set (with a similar formula for the intercept). Since the objective function involves a log-likelihood term, the formula for the objective function differs across GLMs, and the first approach has to maintain different subroutines for step-size halving. For the second approach, we can write a single function that takes in the class \code"family" object (along with other necessary parameters) and returns the objective function value.
-
It is computationally less efficient than the first approach because (i) \proglangR is generally slower than \proglangFORTRAN, and (ii) there is overhead associated with constant switching between \proglangR and \proglangFORTRAN. Some timing comparisons for Gaussian and logistic regression with the default parameters are presented in Figure 1. The second approach is 10 to 15 times as slow than the first approach.
-
Since each GLM family has its own set of \proglangFORTRAN subroutines in the first approach, it allows for special computational tricks to be employed in each situation. For example, with \codefamily = "gaussian", the predictors can be centered once upfront to have zero mean and Algorithm 1 can be run ignoring the intercept term.


We stress that both approaches have been implemented in \pkgglmnet. Users should use the first implementation for the most popular GLM families including OLS (Gaussian regression), logistic regression and Poisson regression (see \codeglmnet’s documentation for the full list of such families), and use the second implementation for all other GLM families. For example, the code below shows two equivalent ways to fit a regularized Poisson regression model: {CodeChunk} {CodeInput} R> data(PoissonExample) R> glmnet(x, y, family = "poisson") R> glmnet(x, y, family = poisson()) The first call specifies the GLM family as a character string to the \codefamily argument, invoking the first implementation. The second call passes a class \code"family" object to the \codefamily argument instead of a character string, invoking the second implementation. One would never run the second call in practice though, as it returns the same result as the first call but takes longer to fit. The example below fits a regularized quasi-Poisson model that allows for overdispersion, a family that is only available via the second approach: {CodeChunk} {CodeInput} R> glmnet(x, y, family = quasipoisson())
2.4 Details on the penalized WLS subroutine
Since the penalized WLS problem in Step 2(b)iii of Algorithm 1 is the computational bottleneck, we elected to implement it in \proglangFORTRAN. Concretely, the subroutine solves the problem
(6) | ||||
subject to | (7) |
This is the same problem as (5) except for two things. First, the penalty placed on each coefficient has its own multiplicative factor . ((7) reduces to (5) if for all , which is the default value for the \codeglmnet function.) This allows the user to place different penalty weights on the coefficients. An instance where this is especially useful is when the user always wants to include feature in the model: in that case the user could set so that is unpenalized. Second, the coefficient is constrained to lie in the interval . (\codeglmnet’s default is and for all , i.e. no constraints on the coefficients.) One example where these constraints are useful is when we want a certain to always be non-negative or always non-positive.
The \proglangFORTRAN subroutine solves (7) by cyclic coordinate descent: see Friedman2010 for details. Here we describe one major computational trick that was not covered in that paper: the application of strong rules (Tibshirani2012).
In each iteration of cyclic coordinate descent, the solver has to loop through all features to update the corresponding model coefficients. This can be time-consuming if is large, and is potentially wasteful if the solution is sparse: most of the would remain at zero. If we know a priori which predictors will be “active” at the solution (i.e. have ), we could perform cyclic coordinate descent on just those coefficients and leave the others untouched. The set of “active" predictors is known as the active set. Strong rules are a simple yet powerful heuristic for guessing what the active set is, and can be combined with the Karush-Kuhn-Tucker (KKT) conditions to ensure that we get the exact solution. (The set of predictors determined by the strong rules is known as the strong set.) We describe the use of strong rules in solving (7) fully in Algorithm 2.
Assume that we are trying to solve for for some , and that we have already computed . (If , set .)
-
1.
Initialize the strong set .
-
2.
Check the strong rules: for , include in if
-
3.
Perform cyclic coordinate descent only for features in .
-
4.
Check that the KKT conditions hold for each . If the conditions hold for all , we have the exact solution. If the conditions do not hold for some features, include them in the strong set and go back to Step 3.
Finally, we note that in some applications, the design matrix is sparse. In these settings, computational savings can be reaped by representing in a sparse matrix format and performing matrix manipulations with this form. To leverage this property of the data, we have a separate \proglangFORTRAN subroutine that solves (7) when is in sparse matrix format.
2.5 Other useful functionality
In this section, we mention other use functionality that the \pkgglmnet package provides for fitting elastic net models.
For fixed , \codeglmnet solves (3) for a path of values. While the user has the option of specifying this path of values using the \codelambda option, it is recommended that the user let \codeglmnet compute the sequence on its own. \codeglmnet uses the arguments passed to it to determine the value of , defined to be the smallest value of such that the estimated coefficients would be all equal to zero333We note that when , is infinite, i.e. all coefficients will always be non-zero for finite . To avoid such extreme values of , if we return the value for .. The program then computes such that the ratio is equal to \codelambda.min.ratio (default if the number of variables exceeds the number of observations, otherwise). Model (3) is then fit for \codenlambda values (default 100) starting at and ending at which are equally spaced on the log scale.
In practice, it common to choose the value of via cross-validation (CV). The \codecv.glmnet function is a convenience function that runs CV for the tuning parameter. The returned object has class \code"cv.glmnet", which comes equipped with \codeplot, \codecoef and \codepredict methods. The \codeplot method produces a plot of CV error against (see Figure 2 for an example.) As mentioned earlier, we prefer to think of as a higher level hyperparameter whose value depends on the type of prediction model we want. Nevertheless, the code below shows how the user can perform CV for manually using a for loop. Care must be taken to ensure that the same CV folds are used across runs for the CV errors to be comparable. {CodeChunk} {CodeInput} R> alphas <- c(1, 0.8, 0.5, 0.2, 0) R> fits <- list() R> fits[[1]] <- cv.glmnet(x, y, keep = TRUE) R> foldid <- fits[[1]]