Machine Learning · Insper

Gradient Descent & Regularization

Walk downhill, and put a brake on complexity

The optimization engine behind nearly all of modern ML, and the standard cure for overfitting

Why the closed form is not enough

it does not scale

\((X^\top X)^{-1}\) costs \(O(d^3)\): hopeless with huge feature counts

it does not exist

for most other models, starting with neural networks

it does not brake

it says nothing about controlling overfitting

Two tools for this lesson: gradient descent (the engine) and regularization (the brake).

The path of this lesson

  1. Walk downhillthe gradient, derived and computed by hand
  2. Tune the descentloss landscape, scaling, learning rate, batches
  3. Lines → curvespolynomial features; underfitting and overfitting
  4. Regularizeridge (L2), lasso (L1) and the knob α
Each stage answers one question: how do we find the minimum without solving for it? → how big a step, and on how much data? → what happens when the model gets more flexible? → how do we keep that flexibility in check?
Stage 1 · how do we find the minimum without solving for it?

Gradient descent: walk downhill

Instead of solving \(\nabla J = 0\) on paper: measure the slope where you stand, step against it, repeat.

\[ w^{(t+1)} = w^{(t)} - \eta \, \nabla_w J\big(w^{(t)}\big) \]
  • \(\eta\): the learning rate, the size of the step.
  • \(\nabla_w J\) points where the loss grows fastest; the minus sign makes the method descend.
Predict. To turn this into code we need \(\nabla_w J\). For a line \(w_0 + w_1 x\) with squared error, what do you expect the derivative to be made of?

Where the gradient comes from

\[ J(w_0, w_1) = \frac{1}{n}\sum_{i=1}^{n}\big(\underbrace{y_i - w_0 - w_1 x_i}_{e_i}\big)^2 \]

Chain rule: the derivative of \(e_i^2\) is \(2e_i\) times how the residual reacts to the weight.

\(\partial e_i / \partial w_0 = -1\)

raising the intercept by one lowers every residual by one

\(\partial e_i / \partial w_1 = -x_i\)

raising the slope lowers the residual by \(x_i\): points far from the origin feel it more

\[ \frac{\partial J}{\partial w_0} = -\frac{2}{n}\sum_i e_i, \qquad \frac{\partial J}{\partial w_1} = -\frac{2}{n}\sum_i e_i x_i \]

One formula for every parameter

With the column of ones \(x_{i0} = 1\), the intercept is just one more weight:

\[ \frac{\partial J}{\partial w_j} = -\frac{2}{n}\sum_{i=1}^{n} e_i\,x_{ij} \qquad\Longrightarrow\qquad \nabla_w J = -\frac{2}{n}X^\top e = -\frac{2}{n}X^\top(y - Xw) \]

first reading

set it to zero: \(X^\top(y - Xw) = 0\), the normal equations. Same statement, two routes: solve it at once, or walk there.

second reading

a residual-weighted sum: well-predicted points barely vote, badly missed points dominate the step.

Move each weight in the direction the errors point, by an amount proportional to how large they are. Word for word true in a 100-layer network.

One step, by hand

Points \((1,2), (2,4), (3,7)\); start at \(w_0 = w_1 = 0\); \(\eta = 0.1\).

The line predicts 0 everywhere, so \(e = y = (2, 4, 7)\): \(\sum e_i = 13\), \(\sum e_i x_i = 2 + 8 + 21 = 31\).

\[ \frac{\partial J}{\partial w_0} = -\tfrac{2}{3}(13) = -8.667, \qquad \frac{\partial J}{\partial w_1} = -\tfrac{2}{3}(31) = -20.667 \] \[ w_0 \leftarrow 0 - 0.1(-8.667) = 0.867, \qquad w_1 \leftarrow 0 - 0.1(-20.667) = 2.067 \]
Both derivatives negative → both weights too low → the step raises both. The loss falls from 23 to 0.625.

The next steps

iteration\(w_0\)\(w_1\)\(J\)\(\partial J/\partial w_0\)\(\partial J/\partial w_1\)
00.0000.00023.000−8.667−20.667
10.8672.0670.6251.3332.089
20.7331.8580.3440.231−0.394
30.7101.8970.3270.343−0.119

first step enormous

23 → 0.63, then it crawls: a fast early fall says little about convergence

signs flip

a step overshoots the minimum, the next walks it back

uneven pace

\(\partial J/\partial w_1\) is 2.4× \(\partial J/\partial w_0\): residuals enter multiplied by \(x_i\)

After 2000 iterations: \(w_0 = -0.667\), \(w_1 = 2.5\), exactly the normal equations.

Stage 2 · how big a step, and on how much data?

The landscape you are walking down

  • Every point of the plane \((w_0, w_1)\) is a candidate line; the height is its error.
  • Minimizing = finding the bottom of the valley; \(-\nabla J\) is the direction of steepest descent.
Predict. The table showed \(w_1\) feeling the residuals 2.4 times more than \(w_0\). What shape do you expect the valley to have?

Simulator: the loss surface

Click on the left to drop a start and press Step. Then switch center x on and start again from a similar point.

Why scaling comes before fitting

uncentered

a long narrow valley: tilting a line whose \(x\) values sit far from zero also raises or lowers it, so \(w_0\) and \(w_1\) are coupled. The gradient points across the valley; the path zigzags and drags.

centered

a round bowl: the same steps go nearly straight to the answer.

The closed form returns the same line either way. The iterative route cares enormously: standardizing reshapes the surface the optimizer crosses.

The learning rate

  • \(\eta\) too small → tiny steps, slow convergence
  • \(\eta\) too large → the steps overshoot; the loss oscillates or diverges
  • recipe: try \(\eta \in \{10^{-3}, 10^{-2}, 10^{-1}\}\) and watch the training loss fall smoothly

on the loss of the next slide

\(\eta < 0.5\)creeps in
\(\eta = 0.5\)the minimum in one move
\(0.5 < \eta < 1\)bounces, but closes in
\(\eta = 1\)bounces between two points for ever
\(\eta > 1\)explodes

Simulator: the learning rate

Predict. Set \(\eta = 1.05\) and step through it. Does it converge, bounce for ever or explode?

Batch, stochastic and mini-batch

variantgradient computed oncost per stepbehavior
batch GDthe full dataset\(O(nd)\)exact, smooth descent
stochastic GD (SGD)one random sample\(O(d)\)noisy but cheap; escapes shallow traps
mini-batch GDa batch of ~32–512in betweenthe modern default

from sklearn.linear_model import SGDRegressor
model = SGDRegressor(loss='squared_error', penalty='l2', alpha=1e-4,
                     learning_rate='invscaling', max_iter=1000)
  
The same loop, with other losses, trains logistic regression, SVMs and neural networks. Learn it once.
Stage 3 · what happens when the model gets more flexible?

From lines to curves: polynomial features

Linear regression is linear in the parameters, not in the inputs: \(x \mapsto (x, x^2, \dots, x^p)\).


from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import PolynomialFeatures

model = make_pipeline(PolynomialFeatures(degree=3), LinearRegression())
  
Predict. 25 noisy points of a sine. Degree 1, degree 15: what goes wrong with each?

Flexibility cuts both ways

Polynomial underfit, overfit, and Ridge regularization

Degree 1 underfits: too rigid. Degree 15 overfits: 16 parameters chase 25 noisy points. Right: the same 16 parameters plus a Ridge penalty, and the curve relaxes back to the signal.

Underfitting and overfitting, measured

100 points of \(y = 0.5x^2 + x + 2\), noise with standard deviation 3; five-fold cross-validation.

degreetrain RMSEvalidation RMSE (mean)spread across folds
13.223.260.19
22.642.750.33
302.4610.4313.46

Degree 30, fold by fold: \(3.14 \quad 3.02 \quad 5.51 \quad \mathbf{37.29} \quad 3.20\)

The lowest training error belongs to the worst model. Overfitting is being wrong unpredictably: the spread across folds gives it away before the mean does.

A pocket diagnostic

too simple (underfitting)too complex (overfitting)
training errorhighlow
validation errorhigh, consistentlyhigh on average, occasionally low
spread across foldssmalllarge
what to domore capacity, better featuresregularize, more data, less capacity
There is a floor: the noise (std 3) does not depend on \(x\), and no model removes it. Degree 2 lands near that floor.

"Too complex" depends on how much data you have

A degree-10 polynomial has 11 free parameters.

training points5810111530
train RMSE3·10⁻¹⁵1·10⁻¹¹1·10⁻¹¹0.400.771.53
  • Up to 11 points the curve passes through every one: zero training error means nothing was left over.
  • From the eleventh point on it has to choose, and the error lifts off the floor.
The same degree-10 model is too complex for 10 points and reasonable for 100.
Stage 4 · how do we keep that flexibility in check?

Regularization: penalize the magnitude

Ridge (L2) · Tikhonov 1943; Hoerl & Kennard 1970

\[ \lVert y - Xw \rVert^2 + \alpha \sum_j w_j^2 \]
  • shrinks smoothly, never to exactly zero
  • spreads weight across correlated features
  • closed form: \((X^\top X + \alpha I)^{-1} X^\top y\)

Lasso (L1) · Tibshirani 1996

\[ \lVert y - Xw \rVert^2 + \alpha \sum_j \lvert w_j \rvert \]
  • lands exactly at zero for weak features
  • automatic feature selection
  • among correlated features, keeps one arbitrarily

Elastic Net blends both (l1_ratio): a robust default with many correlated features.

Why L1 zeroes and L2 does not

Write the penalty as a budget: minimize the error subject to \(\sum |w_j| \le t\) (lasso) or \(\sum w_j^2 \le t^2\) (ridge).

  • The error contours are ellipses centered on the OLS solution.
  • The solution is where the growing ellipse first touches the region.
Predict. The ridge region is a circle; the lasso region is a diamond with corners on the axes. Which one will usually be touched exactly on an axis?

Simulator: circle versus diamond

Drag the OLS optimum around and count how often each region is touched on an axis.

The one-dimensional case, in closed form

\[ \hat{w}^{\text{ridge}} = \frac{\hat{w}}{1+\alpha} \] \[ \hat{w}^{\text{lasso}} = \operatorname{sign}(\hat{w})\,\max\!\left(|\hat{w}| - \tfrac{\alpha}{2},\ 0\right) \]

one feature, \(\sum_i x_i^2 = 1\), \(\alpha = 1\)

\(\hat{w}\) (OLS)ridgelasso
2.001.0001.500
1.000.5000.500
0.600.3000.100
0.500.2500
0.200.1000
Ridge divides, and division never reaches zero. Lasso subtracts and clips: below \(\alpha/2\) a coefficient disappears. That subtraction is the corner of the diamond.

Watching the path

Eight features: three with real signal, three pure noise, two near-copies (\(c_1, c_2\), both truly 2). Predict. As \(\alpha\) grows, which coefficients does lasso drop first?

What the path shows

αlasso: non-zeroridge: non-zero
0.058 / 88 / 8
0.27 / 88 / 8
0.66 / 88 / 8
2.05 / 88 / 8

Lasso removes the noise features first; ridge keeps all eight, small.

The correlated pair at \(\alpha = 2\):

lasso (1.89, 0.98): starts to pick one, almost arbitrarily.

ridge (1.02, 0.97): splits the weight evenly.

That is why ridge is the standard answer to multicollinearity, and lasso is not.

Ridge also fixes the numerics

two nearly identical columns

condition number of \(X^\top X\): \(7.2\times10^{7}\)

OLS: \((1.000, -0.000)\), an arbitrary split

\(\alpha = 0.01\) on the diagonal

condition number: \(6\times10^{3}\)

answer: \((0.500, 0.500)\), the sum preserved

Stable coefficients, an invertible matrix, faster gradient convergence: one mechanism, \(\alpha I\) raising every eigenvalue.

The knob α

  • \(\alpha \to 0\): plain OLS, no brake.
  • \(\alpha \to \infty\): every coefficient crushed to ~0, the model predicts the mean.
  • The right \(\alpha\) is not known in advance: choose it by cross-validation (RidgeCV, LassoCV, a grid search).
Scale before regularizing (in a Pipeline): the penalty compares coefficients across features. And the intercept is not penalized; scikit-learn does that for you.

Summary

  1. \(w \leftarrow w - \eta\nabla J\); for squared error, \(\nabla J = -\frac{2}{n}X^\top e\), a residual-weighted sum.
  2. Scale the features: a round bowl instead of a narrow valley.
  3. \(\eta\) too large diverges; mini-batch is the default.
  4. Overfitting = low training error, unpredictable validation error.
  5. Ridge divides, lasso subtracts and clips; choose \(\alpha\) by cross-validation.
In class

Gradient descent, in practice

  • Open the handout notebook on Colab (in Portuguese).
  • A one-variable slope; the learning rate pushed until it breaks.
  • The three points of one step, by hand, checked number by number.
  • The loss landscape with and without centering; 20k California houses with and without standardization; mini-batch.