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.
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.
Choose an answer and how sure you are.
Answer to unlock the lab.
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.
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.
Download the notebook (.ipynb). Open it in Jupyter or upload it to Google Colab; it fetches any missing data itself.
For practitioners
- In scikit-learn:
Ridge,LassoandElasticNet(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:
RidgeCVandLassoCVdo 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 byC, the inverse of α: a smallerCmeans 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.
Was this helpful?
Thanks! This helps us improve the Atlas.
Go deeper in the Atlas
Encyclopedia entries on the ideas in this lesson: