InterviewPrepKit

Home / Coding / Machine Learning Coding / Linear Models / Ridge Regression

Ridge Regression

medium 00:00
Solving tips
  • Prepend a column of ones to X for the bias, then build the penalty matrix so its first diagonal entry (the bias) is 0. Regularizing the intercept just shrinks predictions toward 0 for no reason.
  • Never form the inverse explicitly. Build A = X^T X + lambda * I_reg and b = X^T y, then call np.linalg.solve(A, b) — it is faster and more numerically stable than np.linalg.inv.
  • Sanity check the shapes: X is (n, d), the augmented matrix is (n, d+1), A is (d+1, d+1), and the returned weight vector is (d+1,) with w[0] the intercept.

Implement ridge regression from scratch with NumPy using the closed-form normal equations. Ridge is ordinary least squares plus an L2 penalty on the coefficients, which shrinks them toward zero, keeps the fit stable when features are correlated, and makes X^T X invertible even when it otherwise would not be. Interviewers use it to check that you know the normal equations, that you handle the bias term correctly, and that you solve a linear system instead of inverting a matrix.

Definition

Augment X with a leading column of ones so the first weight is the intercept: X_aug has shape (n, d + 1). Ridge minimizes the penalized squared error

J(w) = ||X_aug @ w - y||^2 + lam * ||w[1:]||^2

where the sum in the penalty runs over the feature coefficients only, not the bias. Setting the gradient to zero gives the normal equations

(X_aug^T X_aug + lam * R) w = X_aug^T y

with R = diag(0, 1, 1, ..., 1) a (d + 1, d + 1) matrix whose first diagonal entry is 0 so the bias is left unpenalized. The lam * R term also lifts the diagonal, which is why the system stays solvable even when X_aug^T X_aug is singular.

Task

Complete ridge_regression(X, y, lam) so it returns the weight vector w of shape (d + 1,), where w[0] is the intercept and w[1:] are the feature coefficients. Build the augmented matrix, form A = X_aug^T X_aug + lam * R and b = X_aug^T y, and solve A w = b with np.linalg.solve. Do not call np.linalg.inv, and do not use sklearn, scipy, or any built-in ridge/regression helper.

Example

X = np.array([[1.0], [2.0], [3.0], [4.0]])
y = np.array([2.0, 4.0, 6.0, 8.0])   # exactly y = 2*x, intercept 0

w = ridge_regression(X, y, lam=0.0)
w   # -> array([0., 2.])   # lam=0 recovers ordinary least squares: bias 0, slope 2

w = ridge_regression(X, y, lam=1.0)
w   # -> array([0.83333333, 1.66666667])   # penalty shrinks the slope; bias absorbs the offset

With lam = 0 ridge is exactly OLS and recovers the true slope of 2 with a zero intercept. Turning up lam shrinks the slope while the unpenalized intercept shifts to compensate, so predictions stay reasonable rather than collapsing to zero.

Constraints

  • 1 <= n <= 10^5, 1 <= d <= 500; use vectorized NumPy, not Python loops over rows or features.
  • lam >= 0. At lam = 0 the result must equal the ordinary least squares solution (when it exists).
  • The bias term (weight index 0) must never be regularized.
  • Solve the linear system with np.linalg.solve; do not form an explicit inverse. Values fit in float64.

Write your solution, then hit Run tests to check it — or get a mock grade from the AI coach.

The coach remembers this session — revise your code and ask again, and it grades your progress. It gives hints, not the answer.
Report a bug