Hello! Welcome to the first lesson of our module on building Linear Regression from scratch.
In the previous module, we constructed a complete gradient descent optimizer, even adding a learning rate scheduler to make it more robust. We now have a powerful tool for finding the minimum of a function. The question is, which function?
Today, we connect that optimizer to our first machine learning model. Our goal is to derive and calculate the gradient of the Mean Squared Error (MSE) loss function for linear regression. This is the mathematical bridge that allows our optimizer to "learn" the best possible line for a set of data. This is the crucial first step towards building your from-scratch linear regression model.
1. The Linear Regression Model and its Error
At its heart, simple linear regression is about finding the best-fitting straight line through a set of data points. You might remember the equation of a line from school:
In machine learning, we use slightly different notation, but the concept is identical. We have:
- Features (): The input variable we use to make a prediction (e.g., square footage of a house).
- Target (): The value we want to predict (e.g., price of a house).
- Parameters (): The slope () and y-intercept () of the line. These are the values the model needs to learn from the data. We often call them weights and bias.
The model's prediction, which we'll call ("y-hat"), is given by:
The "best" line is the one that minimizes the error between its predictions () and the actual target values ().
2. Quantifying Error with Mean Squared Error (MSE)
To find the best line, we need a single number that tells us how "bad" the current line is. This number is calculated by a loss function (or cost function). For linear regression, the most common loss function is the Mean Squared Error (MSE).
The MSE is calculated as follows:
- For each data point, calculate the error: .
- Square this error: . Squaring ensures all errors are positive and heavily penalizes large errors.
- Average these squared errors across all data points.
This gives us the formula for MSE, which we'll call :
Our goal is to find the values of and that result in the lowest possible .

To get a clear walkthrough of these concepts, the following video provides an excellent introduction.
Linear Regression From Scratch in Python (Mathematical)
Watch this segment from 'Linear Regression From Scratch in Python' by NeuralNine to see how the linear model and MSE loss function are defined.
Watch from 00:19 to 06:48. This section clearly explains the linear regression problem using a practical example and mathematically defines the Mean Squared Error (MSE) loss function.
3. Deriving the Gradient of MSE
Now for the main task. To use gradient descent, we need the gradient of the loss function . The gradient is a vector containing the partial derivatives of the loss function with respect to each of its parameters ( and ).
These derivatives tell us how a small change in or will affect the total error . This is exactly the information gradient descent needs to know which "direction" to step in to reduce the error.
We'll find each partial derivative using the chain rule from calculus.
Partial Derivative with respect to the slope,
Let's find . We'll treat as a constant.
-
Start with the MSE formula:
-
Apply the chain rule:
- The derivative of the outer function is .
- The derivative of the inner function with respect to is .
-
Combine and simplify:
Since , we can write this more cleanly as:
Partial Derivative with respect to the intercept,
The process for is very similar. This time, we treat as a constant.
-
Start with the MSE formula again:
-
Apply the chain rule:
- The derivative of the outer function is the same: .
- The derivative of the inner function with respect to is .
-
Combine and simplify:
And in terms of :
The following video segment walks through this exact derivation step-by-step.
Linear Regression From Scratch in Python (Mathematical)
This part of the NeuralNine video provides a clear, step-by-step derivation of the partial derivatives we just worked through.
Watch from 07:32 to 10:30. Follow along as the presenter derives the partial derivatives of the MSE with respect to both m and b.
For a more formal, text-based explanation that also introduces the common vector notation used in machine learning literature, the following resource is excellent.
Lecture 3: Linear Regression - Mean Squared Error: Derivatives and Gradients
These lecture notes from the Kuleshov Group at Cornell provide a rigorous derivation of the MSE gradient, introducing vector notation which will be useful as we move to more complex models.
Read the section titled 'Mean Squared Error: Derivatives and Gradients'. Notice how it starts with the derivative for a single example, then generalizes to the entire dataset. The use of θ (theta) is a common convention for the parameter vector.
4. Calculating the Gradient in NumPy
With the math done, let's translate our final formulas into Python code. Given your background in NumPy, this should feel very natural. The summation and averaging can be handled efficiently with np.mean().
Let's set up a small example and calculate the gradient at a specific point.
import numpy as np
# Sample data
X = np.array([1, 2, 3, 4, 5])
y = np.array([2.1, 3.9, 6.2, 7.8, 10.1])
# Let's assume our model has some initial parameters
m = 1.5
b = 1.0
# 1. Calculate the predictions (y_hat)
y_pred = m * X + b
print(f"Predictions (y_hat): {y_pred}")
# 2. Calculate the errors (residuals)
errors = y - y_pred
print(f"Errors (y - y_hat): {errors}")
# 3. Calculate the partial derivatives using our derived formulas
# dJ/dm = -2/n * sum((y - y_hat) * x)
# which is equivalent to -2 * mean((y - y_hat) * x)
gradient_m = -2 * np.mean(errors * X)
# dJ/db = -2/n * sum(y - y_hat)
# which is equivalent to -2 * mean(y - y_hat)
gradient_b = -2 * np.mean(errors)
print("\n--- Gradient ---")
print(f"Partial derivative w.r.t. m (dJ/dm): {gradient_m:.4f}")
print(f"Partial derivative w.r.t. b (dJ/db): {gradient_b:.4f}")
The output values dJ/dm and dJ/db form our gradient vector .
gradient_m = -4.8400tells us that if we increasem, the MSE will decrease (because the gradient is negative).gradient_b = -1.1600tells us that if we increaseb, the MSE will also decrease.
Gradient descent will use this information to update the parameters in the correct direction:
m_new = m_old - learning_rate * gradient_mb_new = b_old - learning_rate * gradient_b
We will implement this update step in the next lesson.
Test your understanding!
In the code above, the initial parameters were m = 1.5 and b = 1.0. Suppose we chose better initial parameters, say m = 2.0 and b = 0.0. Would you expect the magnitude (absolute value) of the calculated gradients gradient_m and gradient_b to be larger or smaller? Why?
Show answer
You would expect the magnitude of the gradients to be smaller. The new parameters (m=2.0, b=0.0) are closer to the true values that generated the data (which looks to be around m=2, b=0). When the parameters are closer to the optimal values, the errors (y - y_pred) will be smaller on average. Since both gradient calculations are directly proportional to these errors, smaller errors lead to smaller gradients. This is a key concept: as the model gets better, the gradient gets smaller, causing the learning steps to shrink naturally.
Conclusion
Fantastic work! You've successfully connected the abstract concept of an optimizer to a concrete machine learning model. You now understand not just what the MSE loss function is, but also how to find its derivative—the engine of gradient-based learning.
Key Takeaways:
- Linear regression models learn the parameters (slope
mand interceptb) of a line that best fits the data. - The Mean Squared Error (MSE) is the standard loss function for regression, measuring the average squared distance between predictions and actual values.
- The gradient of the MSE is a vector of its partial derivatives with respect to each parameter ( and ).
- We derived these partial derivatives using the chain rule, resulting in two clean formulas that can be implemented efficiently in NumPy.
- This gradient vector tells the optimizer the direction of steepest ascent, so our gradient descent algorithm will move in the opposite direction to minimize the error.
Preview of the Next Lesson:
We have all the components we need. In the next lesson, we will put everything together to implement a vectorized version of linear regression using gradient descent and NumPy. You'll combine the gradient calculation from today with the optimizer you built in the last module to train your very first model from scratch.