Blog (mostly math)

MLS-2 Multiple Linear Regression

Ref:

  • CodeSignal’s “Implementing AI algorithms from scratch” course. Link to the course: Link.

  • MIT Machine Learning course. Link to the course: Link.

  • Stanford Machine Learning course. Link to the course: Link.

Machine Learning from Scratch - 2.

Multiple Linear Regression.

ROUGH NOTES (!)
Updated: 5/11/2026

Eg: Suppose we want to predict house prices. Note that they depend on multiple factors, like location, size, and number of rooms.

Can we approximately model house price as a linear function (with some offset) of features like size, number of rooms, age of the house, etc.?

This is the idea of multiple linear regression.

Suppose we have ${ n }$ data points, each with ${ m }$ features and one corresponding ${ y }$ value.

We can arrange the feature values in a matrix

\[{ \mathbf{X} = \begin{pmatrix} 1 &x _{1,1} &\cdots &x _{1,m} \\ 1 &x _{2, 1} &\cdots &x _{2, m} \\ \vdots &\vdots &\ddots &\vdots \\ 1 &x _{n,1} &\cdots &x _{n, m} \end{pmatrix} . }\]

Here each row represents the ${ m }$ features for a single data point.

For each row, there is a corresponding ${ y }$ value. We can arrange these ${ y }$ values like

\[{ \mathbf{y} = \begin{pmatrix} y _1 \\ \vdots \\ y _n \end{pmatrix} . }\]

Now we are looking for

\[{ \text{Looking for: } \quad \beta _0, \ldots, \beta _m }\]

such that

\[{ \begin{pmatrix} y _1 \\ y _2 \\ \vdots \\ y _n \end{pmatrix} \approx {\begin{pmatrix} 1 &x _{1,1} &\cdots &x _{1,m} \\ 1 &x _{2, 1} &\cdots &x _{2, m} \\ \vdots &\vdots &\ddots &\vdots \\ 1 &x _{n,1} &\cdots &x _{n, m} \end{pmatrix}} \begin{pmatrix} \beta _0 \\ \beta _1 \\ \vdots \\ \beta _m \end{pmatrix} . }\]

Equivalently we are looking for a vector ${ \beta }$ such that

\[{ \mathbf{y} \approx \mathbf{X} \beta . }\]

Hence we can consider the loss function

\[{ L(\beta) = \lVert \mathbf{X} \beta - \mathbf{y} \rVert ^{2} }\]

and look for the vectors ${ \beta }$ which minimize the loss.

Note that the partial derivative

\[{ {\begin{aligned} &\, \frac{\partial L}{\partial \beta _i} \\ = &\, \frac{\partial}{\partial \beta _i} (\mathbf{X} \beta - \mathbf{y}) ^T (\mathbf{X} \beta - \mathbf{y}) \\ = &\, \frac{\partial}{\partial \beta _i} \left( \beta ^T (\mathbf{X} ^T \mathbf{X} ) \beta - 2 \beta ^T \mathbf{X} ^T \mathbf{y} + \mathbf{y} ^T \mathbf{y} \right) \\ = &\, 2 ((\mathbf{X} ^T \mathbf{X}) \beta ) _{i} - 2 (\mathbf{X} ^T \mathbf{y}) _{i} . \end{aligned}} }\]

For more details, consider this post. Link to post: Link.

Hence the gradient of the loss is

\[{ \nabla _{\beta} L(\beta) = 2 (\mathbf{X} ^T \mathbf{X} \beta - \mathbf{X} ^{T} \mathbf{y}) . }\]

Hence the optimal ${ \beta }$ is typically given by

\[{ \hat{\beta} = (\mathbf{X} ^T \mathbf{X}) ^{-1} \mathbf{X} ^T \mathbf{y} . }\]

This is called the normal equation.

[Python Implementation]

First, we set up our dataset:

X = np.array([[73, 67, 43], 
              [91, 88, 64], 
              [87, 134, 58], 
              [102, 43, 37], 
              [69, 96, 70]], dtype='float32')

y = np.array([56, 81, 119, 22, 103], dtype='float32')

Now we append a column of ${ 1 }$s to ${ \mathbf{X} . }$

ones = np.ones(shape=(len(X), 1))
X = np.append(ones, X, axis=1)

Now we compute the coefficients ${ \beta }$ using the normal equation.

beta = np.linalg.inv(X.T @ X) @ X.T @ y

# Alternately, 
# even beta = np.linalg.inv(X.T.dot(X)).dot(X.T).dot(y)
# would work. 

[Model’s Performance Evaluation]

After completing our model, we need to evaluate its performance. We will use the coefficient of determination (${ R ^2 }$ score) for that. It indicates how well our model fits the data.

Consider this post about ${ R ^2 . }$ Link to the post: Link.

Note that in the context of the discussion and the red triangle in picture

\[{ R ^2 = \frac{\lVert \hat{\mathbf{y}} - \bar{y} \mathbf{1} _n \rVert ^2 }{\lVert \mathbf{y} - \bar{y} \mathbf{1} _{n} \rVert ^2} = \cos ^{2} (\theta) . }\]

Hence intuitively the larger the ${ R ^2 }$ the better the fit.

Here is the Python Implementation:

predictions = X.dot(beta)
ss_residuals = np.sum(np.square(y - predictions))
ss_total = np.sum(np.square(y - np.mean(y)))
r2_score = 1 - (ss_residuals/ss_total)

print("R^2 Score:", r2_score)  # Output: R^2 Score: 0.9992
comments powered by Disqus