Predict a household’s online spending from the share of its browsing time at each of 1,000 websites. Fit on 8,000 households, test on 2,000 others.
model
sites_used
in_sample_r2
out_of_sample_r2
OLS, all 1,000 sites
999
0.282
-0.097
Lasso, lambda.min
228
0.177
0.107
Lasso, lambda.1se
86
0.125
0.100
OLS uses every site and fits the training sample best, yet it predicts new households worse than their average: its out-of-sample R^2 is negative.
The lasso keeps a few hundred sites, or under a hundred, and predicts better.
Last lecture’s lesson was that complexity must be chosen out of sample. Today’s tool, regularization, generates the candidates to choose from and makes 1,000 regressors manageable.
Today’s route
Why regularize: too many candidate models, and a penalty that tames them.
Lasso and ridge: two cost functions, why one of them selects variables, and the regularization path.
library(tidyverse)library(Matrix) # sparse matriceslibrary(glmnet) # lasso, ridge and elastic net paths, with cross-validation
File
Role
data/browser-totalspend.csv
Online spending of 10,000 households
data/browser-domains.csv
Visits by household and website, 2.3 million rows
data/browser-sites.txt
The names of the 1,000 websites
data/oj.csv
Orange juice sales, for the factor-coding example
Why regularize
Cross-validation needs candidates, and with many regressors there are too many
Cross-validation needs a set of candidates
Last lecture: given candidate models, K-fold CV picks the one with the best estimated out-of-sample performance.
For the binned sample mean the candidates were manageable: one per number of bins, a few thousand at most.
Even with a single regressor there are many other candidate families: bins cut in other ways (regression trees, next lecture), polynomials, Fourier series, wavelets.
With d regressors the natural candidate set is every subset of them, and that set explodes.
Subsets explode
Regressors d
Subsets 2^d
10
1,024
20
1,048,576
50
about 10^{15}
100
about 10^{30}
For d = 100 there are more candidate models than 16-digit passwords, so checking them all takes longer than a brute-force password attack.
Typical high-dimensional data have d in the thousands. The browsing data have 1,000 sites, and interactions would multiply that.
We need a structured family of candidates that is cheap to generate and indexed by a single tuning parameter.
High-dimensional problems
When the number of parameters p is large relative to the sample size n:
If p \ge n, the design matrix X has a nonzero null space, so the least squares solution is not unique: if \hat\beta minimizes \lVert y - X\beta \rVert^2, so does \hat\beta + v for every v with Xv = 0.
Even with p < n, OLS with many regressors has high variance and overfits, which is the pattern on the opening slide.
For logistic regression the MLE may not exist at all: with enough regressors the classes can be separated perfectly.
A thousand coefficients are hard to interpret. Simpler, structured solutions are easier to read.
Add a penalty to the deviance
Regression models are estimated by minimizing an in-sample deviance: the sum of squared errors for OLS, -2 \log \text{likelihood} for maximum likelihood.
Regularization adds a penalty for model complexity:
c(\beta_j) \ge 0 is a cost attached to the size of \beta_j, and \lambda \ge 0 is the penalty parameter that says how much complexity costs.
We choose c(\cdot) and \lambda, then minimize the regularized objective instead of the deviance alone. The minimizer is a new, regularized estimator, one for every value of \lambda.
\lambda is a complexity dial
\lambda = 0 gives the original estimator. As \lambda \to \infty only the penalty matters, and with c increasing in |\beta_j| the minimizer is \hat\beta = 0.
A \hat\beta closer to zero is a less complex model: the prediction \hat{\mathbf{E}}[Y \mid X] = G(X'\hat\beta) varies less across values of X.
The penalty asks the computer to fit the data but to prefer the simpler model when two fit about equally well. This is the parsimony principle, and \lambda sets the exchange rate.
The constrained view
Minimizing the penalized objective is equivalent to a constrained problem:
\min_\beta\ \text{dev}_{IS}(\beta) \quad \text{subject to} \quad \sum_{j=1}^d c(\beta_j) \le s.
Minimize the deviance, but do not let the total cost exceed the budget s.
s plays the opposite role of \lambda: a large s makes the constraint irrelevant (OLS), while s = 0 forces the least complex model.
Every \lambda > 0 corresponds to some budget s, and this view is what makes the geometry on the next slides possible.
Checkpoint: regularization
With 30 candidate regressors, how many subset models are there? Could you cross-validate them all?
Why does a large \lambda reduce variance, and what does it cost?
If c(\beta_j) = \beta_j^2 and you double \lambda, does the penalized estimator become more or less complex?
Lasso and ridge
Two cost functions, and why one of them selects variables
Two cost functions
Both costs are zero at zero and increase in |\beta_j|, so the cheapest model is the least complex one.
Lasso: c(\beta_j) = |\beta_j|. Moving \beta_j by x units costs x, near zero or far from it.
Ridge: c(\beta_j) = \beta_j^2. The cost grows faster the larger \beta_j is, and is nearly flat near zero.
The lasso penalty is the \ell_1 norm \sum_j |\beta_j| (the “Manhattan” norm). Ridge uses the squared \ell_2 or Euclidean norm \sum_j \beta_j^2.
Many other costs are possible. These two dominate because they have useful theory and are easy to compute for the workhorse deviances.
Contours of |\beta_1|^q + |\beta_2|^q
q = 2 is ridge, q = 1 the lasso. The set \{\beta: |\beta_1|^q + |\beta_2|^q \le s\} is the constraint region of the previous slide.
The lasso region has corners on the axes, where one coefficient is exactly zero. The ridge disc has none.
q < 1 is even spikier but not convex, which makes the optimization hard (see computation below).
Why the lasso sets coefficients exactly to zero
Both constrained solutions sit where the smallest reachable deviance contour touches the constraint region. The lasso’s touches at a corner.
Reading the picture
Blue areas are the constraint regions, the \beta’s with c(\beta_1) + c(\beta_2) \le s.
Red ellipses are contours of the deviance: pairs (\beta_1, \beta_2) with the same sum of squared errors. Their center is the unconstrained OLS estimate, and ellipses further out have higher deviance.
The constrained solution is the point of the region on the lowest contour it can reach. It has higher deviance than OLS, but it satisfies the budget.
With a spiky region the lowest contour usually lands on a corner, where one coefficient is exactly zero: the lasso selects a smaller model. The ridge disc has no corners, so ridge coefficients are shrunk but almost never exactly zero.
Relative to OLS both solutions are closer to the origin: both penalties shrink.
Shrinkage versus selection, in formulas
When the columns of X are orthonormal (X'X = I, so the OLS coefficients are \hat\beta_j = X_j'y), the two estimators have closed forms, coefficient by coefficient:
Ridge scales every coefficient by the same factor below one. Nothing is zero.
The lasso subtracts \lambda from every |\hat\beta_j| and sets to zero whatever falls below \lambda: soft-thresholding.
The derivation is a one-line calculus exercise for each j (appendix).
Sparsity
A coefficient vector \hat\beta = (\hat\beta_1, \dots, \hat\beta_d)' with mostly zeros is sparse.
The lasso’s corners make its solutions sparse: it does variable selection and estimation in one step, and the resulting model is simpler and easier to interpret. That accounts for much of its popularity over ridge.
A large body of theory (not covered here) shows that the lasso predicts well when the true coefficient vector is sparse: a few regressors matter, and the rest are noise.
Ridge shrinks toward zero without selecting. It is the better choice when many regressors each contribute a little, which the elastic net section revisits.
Both penalties shrink. Only the lasso selects.
Computation
The lasso and ridge penalties are convex, and convexity is what makes these problems easy: there is one minimum and fast algorithms find it. glmnet and gamlr have built-in solvers, so no optimization details are needed for this class.
We could impose sparsity directly with the cost c(\beta_j) = \mathbf{1}\{\beta_j \ne 0\}, the \ell_0 penalty. But that is not convex, and solving it means checking the 2^d subsets again.
Not every ML estimator is computationally nice. Deep neural networks (later in the course) are non-convex and in principle impossible to solve exactly, yet they work well with ad hoc optimizers thanks to massive parallelization and empirical evidence that the global minimum is not needed.
The regularization path
Each \lambda gives a different lasso estimator. Which one? Generate a sequence of candidates and let cross-validation choose.
Start at \lambda_1, the smallest penalty for which \hat\beta^1 = 0.
For t = 2, \dots, T, set \lambda_t = \delta\,\lambda_{t-1} for some \delta \in (0, 1) and compute \hat\beta^t under penalty \lambda_t.
The sequence \hat\beta^1, \dots, \hat\beta^T is the lasso regularization path: it starts at the least complex model and adds complexity in small steps.
glmnet() does this automatically, with T = 100 values by default, spaced so that \lambda_T is a small fraction of \lambda_1.
Starting from the top is also computationally smart: each solution is a warm start for the next, slightly smaller \lambda.
Checkpoint: lasso and ridge
Two regressors are both useful and the OLS estimate is (2, 1.5). With a tight budget s, which estimator is more likely to drop one of them, lasso or ridge?
In the orthonormal case with \lambda = 1, what are the lasso and ridge estimates of an OLS coefficient equal to 0.6? Of one equal to 3?
Why does the path start at the largest \lambda rather than at \lambda = 0?
Online spending with glmnet
A sparse design matrix, the path, cross-validation and prediction
Three files
browser_spend <-read.csv("data/browser-totalspend.csv") # one row per householdyspend <- browser_spend$spendweb <-read.csv("data/browser-domains.csv") # one row per household-site pairsitenames <-scan("data/browser-sites.txt", what ="character", quiet =TRUE)c(households =length(yspend), rows_in_web =nrow(web), sites =length(sitenames))
## id site visits
## 1 991 032439.com 1
## 2 7940 032439.com 2
## 3 2453 032439.com 12
Spending is heavily skewed: median 510, mean 1,946, maximum above 400,000. We model \log(\text{spend}).
The outcome is a 10,000-vector and the regressors will be one column per site.
From visit counts to visit shares
We use the percentage of a household’s visits at each site rather than raw counts, so that heavy and light browsers are comparable.
machinetotals <-as.vector(tapply(web$visits, web$id, sum)) # total visits per householdvisitpercent <-100* web$visits / machinetotals[web$id] # share of that household's visitshead(visitpercent)
tapply(a, b, f) applies f to a within every level of the factor b, so machinetotals has one entry per household.
Indexing machinetotals[web$id] uses the factor’s integer codes to line each row up with its household’s total.
The task: predict spending from browsing history. For that, each row of the design matrix must describe one household across all 1,000 sites.
Sparse matrices
A matrix with mostly zeros is sparse. A data frame or an ordinary matrix stores every zero, which wastes memory. A sparse format stores only the nonzero entries as triplets:
Row i, column j holds visitpercent for household i at site j: household 1 spent 12% of its visits at yahoo.com.
Sparse storage takes a third of the dense memory here, since 23% of the entries are nonzero; for the hotel reviews in the logit lecture the factor was 170.
glmnet() computed 86 lasso fits, starting at \lambda = 0.23 where every coefficient is zero and shrinking \lambda by 9% each step. It stops early once the deviance explained stops improving.
Along the path the number of nonzero coefficients rises from 0 to 993 and the share of deviance explained from 0 to 25%.
family = "gaussian" is least squares; "binomial" gives a penalized logit with the same syntax.
Each line is one site’s coefficient as \lambda falls from right to left. The axis on top counts the nonzero coefficients. glmnet does not choose a point on this path; cross-validation does.
Choosing \lambda by cross-validation
The path is a set of candidate estimators, one per \lambda_t. Apply last lecture’s recipe:
Compute the regularization path \lambda_1, \dots, \lambda_T on the full data.
Split the data at random into K folds.
For each fold k, estimate \hat\beta^t for every \lambda_t on the other folds, and compute \text{dev}_{OOS}(\hat\beta^t) on fold k.
Average the K deviances for each \lambda_t and choose the \hat\lambda with the lowest average.
Refit the lasso on all the data with penalty \hat\lambda to get the final \hat\beta.
cv.glmnet() does all five steps, with K = 10 by default.
cv.glmnet() does it all
set.seed(0)cv_lasso <-cv.glmnet(xweb, log(yspend), family ="gaussian")plot(cv_lasso)
The fold assignment is random, so set.seed() makes the result reproducible. The loop over 10 folds and 86 penalties takes about 15 seconds.
Each point is the average out-of-sample mean squared error over the 10 folds, with plus or minus one standard error across folds.
lambda.min (left dotted line) minimizes the CV error: 236 sites, error 2.47 against 2.78 for the intercept-only model at the top of the path, a CV R^2 of about 0.11.
lambda.1se (right dotted line) is the largest \lambda within one standard error of the minimum: 100 sites, error 2.51.
Coefficients at the chosen \lambda
b_min <-coef(cv_lasso, s ="lambda.min") # a sparse 1001 x 1 matrix: intercept + 1,000 sitescoefs <-tibble(site =rownames(b_min)[-1], coefficient =as.vector(b_min)[-1]) %>%filter(coefficient !=0)bind_cols(coefs %>%slice_max(coefficient, n =4), coefs %>%slice_min(coefficient, n =4)) %>% knitr::kable(digits =2, col.names =c("site", "coefficient", "site", "coefficient"))
site
coefficient
site
coefficient
shopyourbargain.com
1.25
cursormania.com
-0.96
victoriassecret.com
0.98
coolsavings.com
-0.73
bizrate.com-o01
0.96
tickle.com
-0.69
atomz.com
0.92
limewire.com
-0.65
236 of the 1,000 coefficients are nonzero: bargain and retail sites positive, cursor downloads and coupon sites negative.
Coefficients are per percentage point of browsing share, and most shares are far below one point. Predictive associations, not causal effects.
Predict for new households
predict(cv_lasso, newx = xweb[1:3, ], s ="lambda.min")
predict() on a cv.glmnet object takes a design matrix with the same 1,000 columns and the penalty to use. s = "lambda.1se" would use the sparser fit.
The predictions are on the log scale, like the outcome. Household 2 is predicted to spend far more than it did; a CV R^2 of 0.11 means most of the variation in spending is not explained by where people browse.
Does it beat OLS out of sample?
set.seed(0)n <-length(yspend); ly <-log(yspend)test <-sample.int(n, size =round(0.2* n)); train <-setdiff(1:n, test) # 2,000 held outr2 <-function(y, pred, ybar) 1-sum((y - pred)^2) /sum((y - ybar)^2)X_dense <-cbind(1, as.matrix(xweb))b_ols <-lm.fit(X_dense[train, ], ly[train])$coefficientsb_ols[is.na(b_ols)] <-0# one site is never visited in the training setset.seed(0)cv_train <-cv.glmnet(xweb[train, ], ly[train])c(ols_in =r2(ly[train], X_dense[train, ] %*% b_ols, mean(ly[train])),ols_out =r2(ly[test], X_dense[test, ] %*% b_ols, mean(ly[train])),lasso_out =r2(ly[test], predict(cv_train, xweb[test, ], s ="lambda.min"), mean(ly[train])),lasso_1se_out =r2(ly[test], predict(cv_train, xweb[test, ], s ="lambda.1se"), mean(ly[train])))
OLS with 1,000 regressors on 8,000 observations fits the training sample best and predicts the held-out households worse than their training-sample mean.
The lasso’s out-of-sample R^2 matches its CV estimate, which is what CV is for.
Checkpoint: the spending lasso
Why fit the model to \log(\text{spend}) rather than to spending in dollars?
cv.glmnet() reports lambda.min with 236 sites and lambda.1se with 100. A marketing team can only track 100 sites. Which fit should it use, and what does it give up?
Suppose you add the 1,000 squared visit shares as extra columns. What happens to the path’s starting point, and how would you choose among the 2,000 regressors?
Practical details
Folds, the one-standard-error rule, factors and standardization
How many folds?
cv.glmnet(..., nfolds = K) sets the number of folds. The default is K = 10.
The standard error reported for each \lambda_t is SD_{OOS}(\hat\beta^t)/\sqrt{K}: the sample standard deviation of the K fold deviances divided by \sqrt{K}, like the standard error of a sample mean. These are the bars on the CV plot.
Wide bars that overlap across many \lambda’s mean the location of the minimum is uncertain.
More folds mean more fits, training sets closer to the full sample, and more fold estimates to average. But each fold estimate is noisier when the folds are small, so the standard error does not shrink mechanically with K.
K = 5 or K = 10 is the usual compromise. Leave-one-out means n fits and is rarely worth it here.
Ten and twenty folds agree on \lambda and on 236 sites; five folds choose a slightly larger \lambda and 206 sites. The 20-fold run takes twice as long as the 10-fold one for the same answer.
The one-standard-error rule
The standard errors are also used to choose\lambda. Any \lambda whose CV error is within one standard error of the minimum is statistically as good as the best.
Among those, prefer the largest\lambda: it has the fewest nonzero coefficients, so a prediction needs less data to collect and the model is easier to read.
cv.glmnet() returns this choice as lambda.1se, and coef() and predict() accept s = "lambda.1se".
tibble(choice =c("lambda.min", "lambda.1se"),lambda =c(cv_lasso$lambda.min, cv_lasso$lambda.1se),nonzero =c(sum(coef(cv_lasso, s ="lambda.min")[-1] !=0),sum(coef(cv_lasso, s ="lambda.1se")[-1] !=0)))
Without regularization, many estimators are invariant to common reparameterizations:
Rescale a regressor (miles to yards) and the OLS coefficient rescales accordingly; fitted values and the economic meaning are unchanged.
Drop a different dummy from a factor and the OLS fit is the same; only the interpretation of the coefficients shifts.
With a penalty on the size of the coefficients, neither holds any more. The penalty is attached to the numbers \beta_j, and the numbers depend on how the regressors are written down.
Two practical fixes follow: keep every level of a factor, and standardize the covariates.
Categorical variables: keep every level
OLS with a factor drops one dummy, the reference level, to avoid perfect collinearity with the intercept.
Under regularization, shrinking a coefficient to zero means pushing that level toward the reference level. Change the reference level and the results change.
The fix: keep all the dummies plus an intercept. Every level is then shrunk toward a shared intercept, which treats the levels symmetrically.
OLS could not do this (perfect collinearity), but the penalty makes the solution unique even with collinear columns.
Omitting the intercept instead would shrink every level toward zero, which rarely makes sense: why should the orange juice intercepts be near zero?
In R, model.matrix(~ brand + price - 1) keeps all the brand dummies; glmnet() then adds its own intercept. (gamlr has a helper, naref(), for the same purpose.)
Orange juice: two codings, two answers
oj <-read.csv("data/oj.csv"); oj$brand <-factor(oj$brand)x_all <-model.matrix(~ brand + price -1, data = oj) # three brand dummies and pricex_ref <-model.matrix(~ brand + price, data = oj)[, -1] # dominicks dropped, as lm() wouldfit_all <-glmnet(x_all, log(oj$sales)); fit_ref <-glmnet(x_ref, log(oj$sales))b_all <-coef(fit_all, s =0.1); b_ref <-coef(fit_ref, s =0.1)tab <-cbind(all_levels =as.vector(b_all), reference_level =c(b_ref[1], 0, b_ref[-1]))rownames(tab) <-rownames(b_all)round(tab, 3)
Same data, same \lambda = 0.1, different answers. With every level kept, Minute Maid’s coefficient is zero and the other two brands sit symmetrically around the shared intercept.
With Dominicks as the reference, “zero” means equal to Dominicks: Minute Maid is pulled onto Dominicks and Tropicana’s gap is larger. The fitted values differ too.
OLS gives identical fits either way: intercept 11.49 with brand effects 0.72 and 1.45.
Standardize the covariates
With OLS the scale of a covariate does not matter: convert miles to yards and the coefficient adjusts so that X_1\beta_1 is unchanged.
The lasso penalizes every coefficient by the same \lambda, so covariates on different scales are penalized differently: X_1\beta_1 = (2X_1)(\beta_1/2), and the penalty on \beta_1/2 is half the penalty on \beta_1. Doubling X_1 makes it cheaper to include.
To penalize every covariate the same, standardize the penalty: \lambda \sum_{k=1}^d \text{sd}(X_k)\,|\beta_k|. Doubling X_1 now doubles \text{sd}(X_1) and the penalty is unchanged.
glmnet() standardizes by default (standardize = TRUE) and reports the coefficients on the original scale. Turn it off only when the regressors are already on a common scale and you mean to penalize them unequally, for example indicators.
Standardized: the two codings give the same model; the price coefficient is simply 100 times smaller per cent than per dollar.
Unstandardized: measuring price in cents makes its coefficient tiny and cheap to keep, so at the same \lambda the lasso drops all three brand dummies and keeps only price. The selected model depends on the units.
Checkpoint: practical details
A colleague fits a lasso with brand coded with Tropicana as the reference level and finds that Minute Maid “does not matter”. What is the problem?
Income is measured in dollars and age in years. Without standardization, which is penalized more heavily per unit of economic effect?
Why is lambda.1se always at least as large as lambda.min?
Beyond the lasso
The elastic net for correlated regressors, the group lasso for grouped ones
Correlated regressors trouble the lasso
Suppose two columns are identical, X_j = X_k, and the lasso solution has \hat\beta_j > 0. Then
\hat\beta_k \ge 0 too (the signs agree), and
moving the pair to (0, \hat\beta_j + \hat\beta_k) leaves the fit and the penalty unchanged: the solution is not unique, and the lasso picks one of the twins arbitrarily.
With highly (not perfectly) correlated regressors the choice between them is unstable from sample to sample.
Ridge behaves differently: it shrinks correlated regressors’ coefficients toward each other, and twins get identical coefficients.
Ridge, however, does not select. We want both: stability on correlated groups and exact zeros.
Bridge penalties and the elastic net
Lasso and ridge are the q = 1 and q = 2 members of the bridge family \lambda \sum_j |\beta_j|^q:
q between 1 and 2 compromises between the two, but the computation is awkward and the region has no corners for q > 1.
The elastic net mixes the two penalties instead of the two exponents: \min_{\beta} \left\{ \tfrac{1}{2}\lVert y - X\beta \rVert^2 + \lambda\big[(1 - \alpha)\lVert\beta\rVert_1 + \alpha\lVert\beta\rVert_2^2\big] \right\}, \qquad \alpha \in [0, 1], with \alpha = 0 the lasso and \alpha = 1 ridge, in the notation of the notes.
What the elastic net buys
Elastic net with mixing 0.2 (left) and the bridge ball with q = 1.2 (right). The elastic net keeps its corners.
Uniqueness: any ridge weight makes the objective strongly convex, so the solution is unique.
Selection: the corners survive, so coefficients are still set exactly to zero. More than n regressors can be kept, which the lasso cannot do.
Grouping: for two regressors with coefficients of the same sign, |\hat\beta_j - \hat\beta_k| \le \frac{\lVert X_j - X_k \rVert_2}{2\lambda\alpha}\,\lVert y \rVert_2, so similar columns get similar coefficients and twins get equal ones (proof in the appendix).
The elastic net in glmnet
glmnet‘s alpha is the weight on the lasso part: alpha = 1 (the default) is the lasso and alpha = 0 is ridge. The notes’ \alpha is one minus glmnet’s.
alpha_choice <-function(a) {set.seed(0); cv_a <-cv.glmnet(xweb, log(yspend), alpha = a) i <-which(cv_a$lambda == cv_a$lambda.min)tibble(alpha = a, lambda.min = cv_a$lambda.min, cv_error = cv_a$cvm[i], se = cv_a$cvsd[i], nonzero = cv_a$nzero[i])}map_dfr(c(1, 0.5, 0), alpha_choice) %>% knitr::kable(digits =4)
alpha
lambda.min
cv_error
se
nonzero
1.0
0.0227
2.4672
0.0484
236
0.5
0.0454
2.4664
0.0478
235
0.0
2.2191
2.4780
0.0311
1000
Same folds, three mixes: the CV errors differ by a fraction of a standard error, ridge keeps all 1,000 sites, the other two about 236. cv.glmnet() does not search over alpha; try a few values and compare.
The group lasso: zero out groups together
Sometimes regressors come in predefined groups and we want a whole group in or out:
A categorical variable with K levels is K dummy columns. Dropping the variable means dropping all K at once.
In an additive modelf(x) = \sum_j f_j(x_j) with each f_j expanded in basis functions, f_j(x_j) = \sum_{\ell=1}^{q_j} \beta_{j\ell}\,\psi_{j\ell}(x_j), removing regressor j means zeroing (\beta_{j1}, \dots, \beta_{jq_j}) together.
In multitask regression of m outcomes on the same X, \min_B \tfrac12 \lVert Y - XB \rVert_F^2 splits into m separate regressions. If X_j is irrelevant to every outcome we want its whole row B_{j\cdot} to be zero.
The lasso penalizes each coefficient separately and knows nothing about the groups.
The group lasso penalty
Partition \{1, \dots, p\} into groups g_1, \dots, g_L. The group lasso solves
If the group’s OLS coefficients have norm above \lambda w_\ell, the vector is shrunk toward zero by \lambda w_\ell in length, with its direction kept.
If the norm is at most \lambda w_\ell, the entire group is set to zero.
Larger groups have larger norms by construction, which is why the weight w_\ell = \sqrt{|g_\ell|} is the usual choice.
Back to multitask learning: penalizing \sum_j \lVert B_{j\cdot} \rVert_2 ties the m regressions together and selects regressors that matter for all outcomes at once. Groups that overlap, as genes in biological pathways do, need more care (appendix).
Checkpoint: beyond the lasso
Two sites are visited by exactly the same households in the same proportions. What does the lasso do with them, and what does the elastic net do?
A regression has a 12-level factor. Why might you prefer the group lasso to the lasso for deciding whether the factor matters?
With group weights w_\ell = 1 for every group, which groups would the group lasso tend to keep?
Regularization in one page
A penalty turns a combinatorial search into a path. Instead of 2^d subsets, minimize deviance plus \lambda \sum_j c(\beta_j) and let \lambda index the candidates from the empty model to OLS.
Lasso selects, ridge only shrinks. The \ell_1 corners put coefficients exactly at zero; the \ell_2 disc does not. Both trade bias for variance.
glmnet() fits the path, cv.glmnet() picks the point.lambda.min for the best estimated error, lambda.1se for the sparsest model that is statistically as good.
Regularization is not invariant to how the regressors are written. Keep every level of a factor and standardize the covariates, which glmnet does by default.
Elastic net and group lasso extend the idea. Mix in ridge for correlated regressors; penalize group norms to select whole groups.
Taddy, Business Data Science (2019), chapter 3: the browsing data and the spending application.
Additional derivations and code
Optional material for reference and practice
Ridge regression in closed form
Ridge minimizes \lVert y - X\beta \rVert^2 + \lambda \lVert \beta \rVert^2 over \beta \in \mathbb{R}^p. The objective is differentiable, so the first-order condition is
Every eigenvalue of X'X + \lambda I_p is at least \lambda > 0, so the inverse exists whatever X is: ridge always has a unique minimizer, even with p > n or identical columns.
As \lambda \downarrow 0 the solution tends to the minimum-norm least squares solution X^+ y, which is OLS when X'X is invertible.
With y \sim N(X\beta, \sigma^2 I): \mathbf{E}\hat\beta_\lambda = (X'X + \lambda I)^{-1}X'X\beta \ne \beta for \lambda > 0 (biased), and \text{Cov}(\hat\beta_\lambda) = \sigma^2 (X'X + \lambda I)^{-1} X'X (X'X + \lambda I)^{-1} is smaller than the OLS covariance \sigma^2 (X'X)^{-1}.
Ridge: the bias-variance trade-off in the orthonormal case
With X'X = I_p, \hat\beta_\lambda = X'y/(1 + \lambda), so
Setting the derivative to zero gives the MSE-optimal penalty \lambda^\ast = \frac{\sigma^2 p}{\lVert\beta\rVert^2} = \frac{1}{\text{signal-to-noise ratio}}.
High signal-to-noise: \lambda^\ast \approx 0, almost OLS. Low signal-to-noise: heavy shrinkage. Cross-validation estimates this trade-off without knowing \sigma^2 or \beta.
The lasso’s optimality conditions
The lasso objective f(\beta) = \tfrac12 \lVert y - X\beta \rVert^2 + \lambda \lVert\beta\rVert_1 is convex but not differentiable at zero. Replace the gradient by the subdifferential, the set of slopes of lines that support f from below. For the absolute value,
\lambda = 0 recovers the OLS normal equations X'(y - X\hat\beta) = 0.
|X_j'(y - X\hat\beta_\lambda)| < \lambda implies \hat\beta_{\lambda,j} = 0: a regressor enters only when its correlation with the residual reaches \lambda. Hence \lambda_{\max} = \lVert X'y \rVert_\infty zeros everything.
Between the events where a coefficient enters or leaves, the conditions are linear, so the path is piecewise linear in \lambda.
When is the lasso solution unique?
The solution \hat\beta_\lambda need not be unique (twin columns), but several things always are:
The fitX\hat\beta_\lambda: write the problem as minimizing \tfrac12 \lVert y - u \rVert^2 + \lambda\, h(u) with h(u) = \min\{\lVert\beta\rVert_1 : X\beta = u\} convex in u. The objective is strictly convex in u, so \hat u = X\hat\beta_\lambda is unique.
The penalty value\lVert \hat\beta_\lambda \rVert_1 and the subgradient \hat s = X'(y - \hat u)/\lambda, since both are functions of \hat u.
The sign pattern is consistent across solutions: \hat\beta_{\lambda,j} > 0 forces \hat s_j = 1 and hence \tilde\beta_{\lambda,j} \ge 0 in any other solution.
The active set \mathcal{A}_\lambda = \{j : \hat\beta_{\lambda,j} \ne 0\} can differ across solutions, but every active set lies in the equicorrelation set\mathcal{E}_\lambda = \{j : |X_j'(y - X\hat\beta_\lambda)| = \lambda\}, the largest possible active set, which is the one LARS produces. Some solution has at most \min\{n, p\} nonzero coefficients.
Least angle regression
With standardized regressors, X_j'(y - X\beta) is the correlation between X_j and the residual. LARS (Efron, Hastie, Johnstone and Tibshirani 2004) follows how these correlations change as \lambda falls from \lambda_{\max}:
Start with r = y - \bar y and \beta = 0. Find the regressor X_j most correlated with r.
Move \beta_j toward its least squares value until another regressor X_k is as correlated with the residual as X_j.
Move \beta_j and \beta_k jointly in the direction of their joint least squares fit until a third regressor catches up, and so on until all \min\{n - 1, p\} have entered.
Two regressors: X_1 enters first, then X_2 joins and the path turns toward OLS.
From LARS to the lasso path
Like forward selection, LARS is greedy and adds one variable at a time, but it enters only “as much” of a variable as its correlation with the residual deserves.
By construction the coefficients are piecewise linear in the step length, and the step length has a closed form, so the whole path costs about as much as one least squares fit.
Lasso modification. The KKT sign condition says an active coefficient must share the sign of its correlation with the residual. If a coefficient hits zero along the LARS path, drop its variable from the active set and recompute the joint least squares direction. The modified algorithm computes the exact lasso path.
The lars package implements both; its diabetes example in the notes shows the two paths side by side, identical until a coefficient crosses zero.
The elastic net’s grouping bound
Take \hat\beta_j \hat\beta_k > 0 and write \hat r = y - X\hat\beta. The KKT conditions for the two coefficients are
by Cauchy-Schwarz and because the minimized objective is at most its value at \beta = 0, so \tfrac12 \lVert \hat r \rVert^2 \le \tfrac12 \lVert y \rVert^2.
Identical columns give identical coefficients, and the bound tightens as the columns get closer or the ridge weight \lambda\alpha grows.
The ridge part becomes \tfrac12 \lVert \sqrt{2\lambda\alpha}\,\beta \rVert^2 = \lambda\alpha\lVert\beta\rVert_2^2, so any lasso solver handles the elastic net.
The augmented design has n + p rows and full column rank, which is where the uniqueness comes from.
As \alpha \downarrow 0 the solution tends to the minimum-\ell_2-norm lasso solution, which is the one LARS computes.
Overlapping groups
Groups can share coefficients: genes belong to several biological pathways.
With overlapping groups, zeroing a group zeros its whole block, so the support of \hat\beta is the complement of a union of zeroed groups, not a union of kept groups. Zeroing g_2 here also removes \beta_3 and \beta_5 from g_1 and g_3.
The proximal operator then has no closed form. Its dual is a projection problem, \min \tfrac12 \lVert y - \sum_\ell \eta^{(\ell)} \rVert^2 subject to \lVert \eta^{(\ell)} \rVert_2 \le \lambda w_\ell and \eta^{(\ell)} zero outside g_\ell, solved by block coordinate descent; without overlap each block is a projection onto an \ell_2 ball.
The latent overlapping group lasso
Obozinski, Jacob and Vert (2011) decompose \beta into one component per group,
Now the support of \hat\betais a union of groups: each kept v^{(\ell)} switches on its whole group, and overlapping coefficients are shared between components.
P_{\text{LOG}} is convex because it is the partial minimum, over the v’s, of a function that is jointly convex in (v^{(1)}, \dots, v^{(L)}, \beta): the sum of norms plus the indicator functions of the two linear constraints.
glmnet minimizes \tfrac{1}{2n}\lVert y - X\beta \rVert^2 + \lambda\lVert\beta\rVert_1, so with X'X = nI the lasso coefficient is exactly \text{sgn}(\hat\beta_j)(|\hat\beta_j| - \lambda)_+: the middle coefficient, 0.4 under OLS, is set to zero.
Ridge in the same scaling solves (X'X/n + \lambda I)^{-1}X'y/n = \hat\beta/(1 + \lambda); glmnet’s ridge path uses a different internal normalization, so compare it with the closed form of the ridge appendix slide rather than with this formula.
browser-totalspend.csv, browser-domains.csv and browser-sites.txt, the household browsing panel;
oj.csv, the orange juice data from the linear regression lecture.
The notes additionally use gamlr, lars (the diabetes example), gganimate and rgl for illustrations; none is needed to run this deck.
Return to the question
A thousand regressors and ten thousand households: OLS fit the sample and failed the test. The lasso path, with \lambda chosen by cross-validation, kept a few hundred sites and predicted better. Regularization is how we make rich models honest.