Why is a lasso fit computed down a grid of lambdas from lambda_max rather than at one lambda?
answer
- you rarely know the right penalty in advance
- one end has a free solution
- start where every coefficient is zero
- reuse the previous fit as the starting point
- spacing is by orders of magnitude
basics
~20 sYou rarely know the right penalty in advance, so the whole path is needed anyway. The descending grid makes it cheap: at lambda_max the solution is known to be all zeros, and each fit resumes from the previous one.
solid answer
~50 sTwo reasons, one statistical and one computational. Statistically you do not know the right penalty in advance, so you need the model at many lambdas anyway. Computationally, the descending grid gives you them almost for free. The grid starts at `lambda_max` — the smallest penalty that zeroes every coefficient — where the solution is known exactly without solving anything, and runs down log-spaced to a small fraction of it, typically a hundredth or a thousandth. Each fit is **warm-started** from the previous lambda's coefficients: neighbouring penalties have nearly the same active set, so the optimiser converges in a handful of passes instead of from scratch. Log spacing matters because coefficient behaviour changes over orders of magnitude of lambda, not linearly. Warm starting changes only how fast you arrive, never where: each grid point still converges to its own optimum.
code
python · 19 linesimport math, random
random.seed(0)
n = 200
x1 = [random.gauss(0, 1) for _ in range(n)]
x2 = [random.gauss(0, 1) for _ in range(n)]
y = [3.0 * a + 0.5 * b + random.gauss(0, 1) for a, b in zip(x1, x2)]
cols = [x1, x2]
soft = lambda z, t: math.copysign(max(abs(z) - t, 0.0), z)
avg = lambda u, v: sum(p * q for p, q in zip(u, v)) / n
lam_max = max(abs(avg(c, y)) for c in cols)
beta = [0.0, 0.0] # exact solution at lam_max
for k in range(6):
lam = lam_max * 0.1 ** (k / 5.0) # log-spaced grid downward
for _ in range(50): # resumed from previous beta
for j, c in enumerate(cols):
other = 1 - j
resid = [yi - beta[other] * cols[other][i] for i, yi in enumerate(y)]
beta[j] = soft(avg(c, resid), lam) / avg(c, c)
print(round(lam, 3), [round(b, 3) for b in beta])go deeper
Know that a penalised fit is normally computed at many penalty values, not one, and that the sequence starts from the penalty where all coefficients are zero. The phrase to recognise is a log-spaced grid of lambdas.
Explain lambda_max as a closed-form quantity from the data, why the grid descends, and what warm starting does — cheaper iterations, identical optimum, because the objective is convex.
Discuss where the grid should stop and why, especially with more predictors than rows, and be able to say what you would look at if fits at the bottom of the grid failed to converge or the path looked ragged.
Frame the tradeoff between grid resolution and compute across many models and refresh cycles, and decide when a coarse grid is enough versus when penalty resolution genuinely changes which predictors ship.
## The problem being solved You almost never know the right penalty strength before looking at the data, so a lasso fit in practice is not one optimisation but a hundred of them, one per lambda on a grid. Doing that naively — pick a hundred lambdas, solve each from a cold start — is wasteful. The standard recipe is: **start at the top of the penalty range, walk down a log-spaced grid, and warm-start every fit from the solution above it.** ## Where the grid starts: lambda_max The top of the grid is not arbitrary. There is a finite penalty `lambda_max` — the smallest penalty for which every coefficient is zero — and it is computable in closed form from the data: with the objective ``` (1 / 2n) * sum_i (y_i - b0 - sum_j x_ij * b_j)^2 + lambda * sum_j |b_j| ``` it is the largest absolute inner product between a (standardised) predictor column and the centred response, divided by n. Above that penalty, no predictor removes enough squared error to pay its own `lambda * |b_j|` cost, so the optimum is the all-zero vector. That gives you the first point of the path for free, exactly, with no iteration — and it also bounds the useful range, since anything above lambda_max is the same null model. ## Where it stops, and why log spacing The grid runs down to a small fraction of lambda_max — commonly a hundredth, or a thousandth when the signal is strong and there are many more rows than predictors. Going all the way to zero is usually pointless and sometimes harmful: as the penalty vanishes the fit approaches ordinary least squares, which is unstable when predictors are correlated and not even unique when predictors outnumber rows. Stopping early keeps the path in the region where the solution is well behaved. Spacing is logarithmic because that is the scale on which lambda acts. Halving lambda has roughly the same qualitative effect whether you are at 1.0 or at 0.001; an evenly spaced grid over the same range would spend nearly all its points near the top, where every coefficient is still zero, and skip the region where the active set is actually changing. ## Warm starts A warm start means initialising the optimiser for grid point k+1 at the converged coefficients of grid point k rather than at zero. This is effective because neighbouring lambdas produce nearly identical solutions — usually the same active set with slightly larger coefficients, occasionally one predictor entering. Coordinate descent, which cycles through predictors updating one at a time by soft-thresholding, then needs a few passes instead of many, and the zero coefficients it inherits let it skip most of the inactive predictors entirely. The key correctness point, and a favourite interview probe: **warm starting affects speed, not the answer.** The lasso objective is convex, so each grid point has a single optimal value of the objective and the optimiser converges to it regardless of where it began. Starting from a good guess only shortens the trip. (This is not true of every fitting problem — for a non-convex objective the starting point can decide which optimum you land in — but the lasso is convex.) ## Why the direction matters Running the grid *upward*, from small lambda to large, throws away the advantage. The small-lambda end has no known solution, so the first fit is a cold start on the hardest, densest problem in the whole range; and the dense solution you carry upward is a poor initialisation for sparse ones. Descending, you always begin at a point whose solution you know exactly and whose active set is empty. ## What you get out of it The by-product is the coefficient path itself. Having solved the whole grid, you can plot every coefficient against lambda, see the order in which predictors enter, and hand the same grid to whatever out-of-sample procedure picks the winning penalty. In other words, the sequence of fits you needed for speed is exactly the sequence of fits you needed for tuning — which is why path-wise fitting is the default rather than an optimisation trick.
- Does warm starting change the coefficients you end up with at a given lambda?No. The lasso objective is convex, so each lambda has a single optimum and any starting point converges to it; warm starting only reduces the number of iterations. The starting point would matter for a non-convex objective, where different initialisations can land in different local optima, but that is not the case here.
- Why log-space the grid instead of using evenly spaced lambdas?Lambda acts multiplicatively: the interesting changes in the active set happen across orders of magnitude. An evenly spaced grid between lambda_max and near zero puts most of its points in the top decade, where nearly every coefficient is still zero, and resolves the bottom of the range — where predictors are entering — with almost no points at all.
- Why stop the grid short of lambda = 0?The near-unpenalised end approaches ordinary least squares, which is unstable under correlated predictors and has no unique solution when predictors outnumber rows. Solving there is slow and the answers are not worth having, so the grid usually stops at a hundredth or a thousandth of lambda_max, and stops higher when the data are wide.
saying these in an interview costs you the question
- Thinks lambda_max has to be found by trial and error
- Believes warm starting changes which solution you converge to
- Runs the grid upward from tiny lambda, losing the free start
- Uses evenly spaced lambdas across several orders of magnitude
- Assumes fitting a hundred lambdas costs a hundred full fits