ML Atlas

Machine learning from zero · Lesson 3.4 · 17 of 33 lessons ready · 12 min

Regularisation: ridge and lasso

In short

A penalty on large weights calms a model. Ridge shrinks every weight, lasso zeroes them one by one: with 3 of 10 features test R² falls from 0.470 to 0.386.

Warm-up Two questions from earlier lessons. A minute, and it never blocks the lesson.
From lesson 3.3: Learning curves: more data or a better model? Training is 100%, validation 82.3%, and the validation curve is still rising. What will most likely help?

Show the answer

Correct answer: More training data, because a big gap means the model memorises

A big gap and rising validation mean high variance. On the full training set the same model reaches 95.8% on validation, and the gap falls to 2.2 points.

From lesson 3.2: Validation and cross-validation Model A has a mean cross-validated accuracy slightly above model B, but the spread across folds is much larger than the difference. What follows?

Show the answer

Correct answer: The data do not show a reliable difference between these models

When the difference in means is smaller than the variation between folds, it could easily be due to chance.

In lesson 3.1 a model with many parameters learned the noise: the training error fell while the test error rose. Instead of taking features away from a model, you can charge it for them. Regularisation is a penalty added to the training error: the larger the weights, the larger the penalty. A number α sets its strength. Today you will try two penalties on 10 features of patients with diabetes and see that one of them picks the features by itself.

Predict, then check A linear regression predicts how far diabetes has progressed a year later from 10 features: age, sex, BMI, blood pressure and six blood test results (s1–s6). Lasso is a penalty that, as α grows, sets the weights of the features to zero one by one. Which feature is left last?
How sure are you?

Interactive lab

This interactive lab runs in a browser with JavaScript enabled. The text around it describes what the lab shows.

Show the answer

Correct answer: BMI, the body mass index, because on its own it carries the most

BMI is left last: at α = 39.8 it is the only feature with a weight. s2 is the first to go for good (from α = 0.398). With 3 features (BMI, blood pressure and s5) the test R² falls from 0.470 to 0.386.

Try it

  • Move α in lasso up from the smallest value. In what order do the weight lines reach zero? How many features are left at α = 15.8, and what does that cost on the test?
  • Watch s3. When does it first touch zero, and what happens to it after that?
  • Switch to ridge and move α all the way up. Does any weight become exactly zero?

Once you have checked it in the lab, let's name what you see.

The idea in plain words

Ordinary linear regression picks the weights that make the error on the training data as small as possible. Regularisation adds a penalty for the size of the weights to that error. The model now has to choose: fit the data better or keep the weights smaller. A feature keeps a large weight only if it really improves the predictions.

Ridge penalises the sum of the squared weights. It pulls every weight towards zero but sets none to zero: in the lab at α = 398 the test R² falls to 0.405 and all 10 features still have a weight. Lasso penalises the sum of the absolute values of the weights and sets them to zero one by one. At α = 15.8 three features are left: BMI, blood pressure and s5. Without a penalty the test R² is 0.470, with three features 0.386. The model is simpler and cheaper to use, but it predicts less well.

The features are expressed in standard deviations. That matters: the penalty counts the size of the weights, so a feature measured in large units would get a small weight and escape the penalty.

Jak to policzono
  • Data: Diabetes, Efron et al. (2004), Annals of Statistics. 442 patients, 10 features measured at the start of the study, target: progression of the disease a year later. The authors gave no licence; scikit-learn redistributes the data set (BSD-3-Clause licence).
  • Random split: 309 patients to learn from, 133 for the test. Means and standard deviations of the features computed on the training part only.
  • Lasso: 11 values of α from 0.01 to 100. Ridge: 11 values of α from 0.1 to 100000 (ridge in scikit-learn counts the penalty on a different scale from lasso, hence the different numbers).
  • Test R²: the share of the variation in the test patients' outcome that the model explains (1 = all of it, 0 = no better than the mean).

Why it works

The lasso penalty grows linearly: shrinking a weight by one unit always saves the same amount of penalty, whether the weight is large or small. If a feature improves the fit by less than the penalty costs, it pays to give it a weight of exactly zero. The ridge penalty grows with the square of the weight, so small weights cost almost nothing. Ridge keeps shrinking a weight but never has to set it to zero.

The penalty also calms the weights of features that say almost the same thing. s1 and s2 are two blood test results strongly linked with each other (in the code below their correlation is 0.90). At the weakest lasso penalty s1 has a weight of −39.0 and s2 of +26.2: two large numbers of opposite sign that almost cancel out. Weights like that change a lot from sample to sample. The penalty pulls them in, and soon lasso removes s2 altogether, because s1 carries almost the same information.

This is not a quirk of one split. In the code below the same experiment runs on 50 random splits: BMI is left last in 37 of them, s5 in the other 13. The feature that goes first changes even more from split to split: age in 21 splits, s2 in 16.

Common mistake

True or false: "lasso gave a feature a weight of zero, so that feature does not matter". False. s2 goes first even though it is strongly linked with s1: lasso picks one feature of the pair, and the other gets zero because it adds nothing new next to s1. Without s1, s2 could be important. The lab shows it with s3 too: at α = 0.158 its weight is zero, at α = 0.398 it is back, because it is linked with s4 (r = −0.73) and lasso moves the weight between them.

The choice between correlated features can be a matter of chance. In the code below the first to go is sometimes s2, sometimes age, and even the last one left is sometimes s5 instead of BMI, depending on which patients end up in the training part. A zero in lasso means "this model did not need this feature, with these data and this α". It does not mean "this feature has nothing to do with the disease".

Quiz

Three questions to check yourself. Pick an answer, or open the answer under a question. Keyboard: arrow keys choose, Enter or Space confirms.

1. How does the ridge penalty differ from the lasso penalty?
Show the answer

Correct answer: Ridge pulls every weight towards zero, lasso sets some of them exactly to zero

Ridge penalises squared weights and zeroes none; lasso penalises absolute values and zeroes the weights one by one. In the lab ridge at α = 398 has 10 non-zero weights, lasso at α = 15.8 only 3.

2. In the lab lasso keeps 3 features and the test R² falls from 0.470 to 0.386. What does that mean?
Show the answer

Correct answer: That a simpler model costs some accuracy: both goals have to be weighed

Fewer features make a model easier to use and explain, but here it explains a smaller share of the variation in the outcome. The strength of the penalty is chosen on validation, weighing simplicity against accuracy.

3. True or false: "lasso gave a feature a weight of zero, so it has nothing to do with the disease".
Show the answer

Correct answer: False, because of a pair of correlated features lasso often keeps just one

s2 goes first even though it is strongly linked with s1. It adds nothing new next to s1, so lasso gives it zero. Over 50 splits the first to go is sometimes s2, sometimes age.

Key takeaways

  • Regularisation adds a penalty for large weights to the error; α sets its strength.
  • Ridge pulls every weight in and zeroes none; lasso zeroes them one by one and picks features.
  • A simpler model usually costs some accuracy: you choose α on validation, not on the test.
  • A zero weight in lasso does not mean the feature is irrelevant: of a pair of correlated features lasso often keeps one.

Going further: formula, code, practice

The math

Ridge: min_w ‖y − Xw‖² + α · Σⱼ wⱼ². Lasso: min_w (1/(2n)) · ‖y − Xw‖² + α · Σⱼ |wⱼ|. That is how scikit-learn counts them, which is why the same numbers for α mean different things in the two penalties.

Why lasso zeroes: with features of mean 0 and variance 1, one weight after an update is wⱼ = sign(ρⱼ) · max(|ρⱼ| − α, 0), where ρⱼ = (1/n) · xⱼᵀ rⱼ is the link between the feature and the remaining error without it. When |ρⱼ| ≤ α, the weight is exactly zero. Ridge gives wⱼ = ρⱼ / (1 + α/n) under the same assumptions: the weight shrinks, but it is zero only when ρⱼ = 0.

R²: R² = 1 − Σ(yᵢ − ŷᵢ)² / Σ(yᵢ − ȳ)², computed on the test patients.

In code (Python)

The same experiment in scikit-learn on 50 random splits (70% to learn from, 30% for the test): which feature lasso keeps last, which it drops first, and what keeping at most 3 features costs.

import numpy as np
from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression, Lasso, lars_path
from sklearn.metrics import r2_score

X, y = load_diabetes(return_X_y=True)
names = np.array(["age", "sex", "bmi", "bp", "s1", "s2", "s3", "s4", "s5", "s6"])
alphas = 10 ** np.linspace(-2, 2, 41)  # the lab's range of penalties, finer
last, first, r2 = [], [], []
for s in range(50):
    tr, te = train_test_split(np.arange(len(y)), test_size=0.3, random_state=s)
    sc = StandardScaler().fit(X[tr])
    Xtr, Xte = sc.transform(X[tr]), sc.transform(X[te])
    path_alphas, _, coefs = lars_path(Xtr, y[tr] - y[tr].mean(), method="lasso")  # the exact lasso path, from the strongest penalty down
    enters = [np.flatnonzero(coefs[j])[0] if coefs[j].any() else len(path_alphas) for j in range(10)]  # where each feature first gets a weight
    last.append(names[np.argmin(enters)]); first.append(names[np.argmax(enters)])  # first to get a weight = last to lose it as the penalty grows
    W = np.array([Lasso(alpha=a, max_iter=100000).fit(Xtr, y[tr]).coef_ for a in alphas])
    k3 = min(k for k in range(len(alphas)) if (W[k] != 0).sum() <= 3)  # the weakest penalty that leaves at most 3 features
    m3 = Lasso(alpha=alphas[k3], max_iter=100000).fit(Xtr, y[tr])
    r2.append((r2_score(y[te], LinearRegression().fit(Xtr, y[tr]).predict(Xte)), r2_score(y[te], m3.predict(Xte))))
r2 = np.array(r2)
print(f"last feature lasso keeps, 50 splits: {', '.join(f'{n} {last.count(n)}' for n in sorted(set(last), key=lambda n: (-last.count(n), n)))}")
print(f"first feature it drops for good: {', '.join(f'{n} {first.count(n)}' for n in sorted(set(first), key=lambda n: (-first.count(n), n)))}")
print(f"test R2: no penalty {r2[:, 0].mean():.3f}, at most 3 features {r2[:, 1].mean():.3f}; lower with 3 in {(r2[:, 1] < r2[:, 0]).sum()} of 50")
print(f"correlation of s1 and s2: {np.corrcoef(X[:, 4], X[:, 5])[0, 1]:.2f}")

Output

last feature lasso keeps, 50 splits: bmi 37, s5 13
first feature it drops for good: age 21, s2 16, s4 9, s1 2, s6 2
test R2: no penalty 0.471, at most 3 features 0.379; lower with 3 in 50 of 50
correlation of s1 and s2: 0.90

The splits differ from the lab's, so the numbers differ too. The picture is the same: BMI is left last in 37 splits of 50 (s5 in the other 13), and keeping at most 3 features lowers the test R² in every split, on average from 0.471 to 0.379. The first to go is sometimes s2, sometimes age: lasso chooses between linked features, and the choice depends on which patients ended up in the training part.

For practitioners
  • In scikit-learn: Ridge, Lasso and ElasticNet (a mix of both penalties, helpful with groups of correlated features). Put the standardisation in a pipeline (make_pipeline(StandardScaler(), Lasso())), so that means and standard deviations are computed on the training data only.
  • Choose the strength of the penalty by cross-validation: RidgeCV and LassoCV do it in one go. Do not choose α by the test score: that is tuning on the test set (lesson 3.5).
  • Logistic regression in scikit-learn has a ridge penalty by default (penalty="l2"); its strength is set by C, the inverse of α: a smaller C means a stronger penalty.
  • If you want to talk about the importance of features, do not read the zeros of lasso alone. Check how stable the choice is over many splits, or use the methods of module 6.

Go deeper in the Atlas

Encyclopedia entries on the ideas in this lesson: