Logistic Regression : Practical Example
Logistic regression is a powerful and widely used algorithm in machine learning, particularly for binary classification problems. Despite its name, it is not a regression model in the traditional sense. Instead, it is used to predict the probability of one of two possible outcomes, such as “yes” vs. “no” or “spam” vs. “not spam.” In this article, we’ll break down what logistic regression is, how it works, and why it is a go-to tool for many data scientists.
What is Logistic Regression?
At its core, it models the relationship between a set of independent variables (also known as features) and a dependent binary outcome. Unlike linear regression, which predicts continuous values, logistic regression outputs probabilities, making it suitable for classification tasks. The key function used in this model is the logistic function (also called the sigmoid function), which maps any real-valued number into a probability between 0 and 1.
How Does it Work?
It starts by calculating a linear combination of input features and their respective weights (coefficients). The formula for this is:
[math]z= w_1x_1 + w_2x_2 + \dots + w_nx_n + b[/math]
Where, [math]w_1, w_2, \dots, w_n[/math] are the model coefficients and [math]x_1, x_2, \dots, x_n[/math] are the features. The result, [math]z[/math] is then passed through the logistic function to generate the predicted probability:
[math]P(y = 1 | X) = \frac{1}{1 + e^{-z}}[/math]
This probability is used to classify the input data into one of two categories based on a threshold, typically 0.5.
Why is Logistic Regression Popular?
It is favored for its simplicity, interpretability, and efficiency. It is easy to implement, and its coefficients can provide valuable insights into how each feature influences the prediction. Additionally, it is computationally efficient, making it suitable for large datasets.
It is an essential tool for binary classification problems. By modeling the probability of a particular outcome, it can be applied to a wide range of tasks, including spam detection, customer churn prediction, and medical diagnosis. Its ease of use and interpretability make it a must-know technique for anyone working with data.
This supervised learning technique is designed to assign observations to one of several discrete classes. In practice, it categorizes data into distinct groups, resulting in discrete output. Also referred to as Logit Regression, it is widely regarded as one of the simplest, most intuitive, and flexible algorithms for tackling classification tasks.
In statistics, this model is commonly used for classification tasks. It helps classify a set of observations into two or more distinct categories. Therefore, the target variable in this model is discrete.

Reference:
- Cooks Distance – Absence of Outliers or Influential Points
- Heteroscedasticity in Logistic Regression
- Logistic Regression Classifier Tutorial
- What is Logistic Regression?
- Logistic Regression: A Comprehensive Tutorial
- Demystifying Logistic Regression: A Gentle Introduction to Maximum Likelihood Estimation
- Demystifying Statistics and Machine Learning in Analysis of Structured Tabular Data
Cooks Distance – Absence of Outliers or Influential Points
Cook’s Distance is a statistical measure used to identify influential data points or outliers in regression analysis. Specifically, it helps us determine if any particular data point significantly influences the estimated regression model.
What does Cook’s Distance measure?
Cook’s Distance quantifies the influence of each data point on the fitted regression model. It combines both the residuals (how far the point is from the predicted value) and the leverage (how far the point’s predictor values are from the average of all predictor values).
Leverage
This refers to the position of a data point in the predictor space. A point that is far from the average of the predictor values has high leverage, meaning it could disproportionately influence the fitted regression model.
Residual
This is the difference between the observed value and the predicted value (how much the point “deviates” from the model’s prediction).
Cook’s Distance is calculated for each data point i as:
[math]D_i = \frac{(e_i^2)}{p \cdot \text{MSE}} \cdot \frac{h_{ii}}{(1 – h_{ii})^2}[/math]
Where:
- [math]e_i[/math] is the residual for the i-th point.
- [math]h_{ii}[/math] is the leverage of the i-th point (a measure of how much the point affects the predicted values).
- p is the number of predictors in the model.
- MSE is the mean squared error of the model.
Heteroscedasticity in Logistic Regression
In simple terms, homoscedasticity means that the spread (or variance) of the residuals (the differences between observed and predicted values) remains constant across all levels of the predicted values. In other words, the errors (or residuals) should not get larger or smaller as the predicted values change.
This concept comes from linear regression, but it can be adapted for logistic regression, even though logistic regression deals with binary outcomes (0 or 1). In logistic regression, instead of predicting a continuous value, we predict probabilities (between 0 and 1), but homoscedasticity is still relevant when assessing the residuals.
Real-World Example
Imagine you are building a model to predict whether someone will buy a product based on their age, income, and previous purchase history. Your model outputs probabilities, such as a 70% chance of buying (0.7) or a 30% chance of not buying (0.3).
Residuals: These are the differences between the predicted probability and the actual outcome (whether they bought the product or not).
Homoscedasticity: You want to make sure that the spread of these residuals doesn’t change depending on how confident the model is in its predictions. For example, if the model predicts 0.9 (high probability of buying), the residual should not be much smaller or larger than when the model predicts 0.1 (low probability).

Image take from Wikipedia
If the variance of these residuals increases (or decreases) as the predicted probability changes, that’s called heteroscedasticity, and it could signal a problem in the model.
To measure and visualize this, we can use plots such as residual plots, which help us see if the residuals spread evenly or if there’s any pattern that suggests heteroscedasticity.

Image take from Wikipedia
End to End Machine Learning Project – Part 2
This Post is a continuation of the part 1 where we discussed about the various steps that should be kept in mind before starting a project in machine learning. To have a high level recap, below are the steps that we need to follow:
- Understand the overall context.
- Gather relevant data.
- Analyze and visualize the data for insights.
- Process the data for machine learning applications.
- Choose a model and train it.
- Optimize your model for performance.
- Share your solution effectively.
- Implement, monitor, and sustain your system.
Now that we have already gone through the theoretical part, let get to the implementation part of it. We will implement different strategies for pre-processing the dataset. Keep in mind that pre-processing is the most critical step of any machine learning project. A data scientist spends most of his/her time cleaning and pre-processing the dataset. You have to have a very solid understanding of each of the features and its importance, Without a deep understanding of the data set, it will be a garbage in garbage out model. So, without any further ado, let’s dive into it.
End to End Machine Learning Project – Part 1
This post is part 1 of approaching a machine learning project. Given a problem statement, we will try to see how this can be solved using machine learning and what type of algorithm is best suited to solve it. Often, we tend to jump into solving the problem without understanding the underlying complexities. This increases the chances of inaccurate predictions. This post tries to change that mindset, and helps you to approach the problem from a investigative mindset. So, let’s dive into it right away!!
This post, including the code logic has been taken from the following book, as the objective is to teach and explain whatever I learn from this book :
To solve a problem, below are the important steps that one should keep in mind
- Understand the overall context.
- Gather relevant data.
- Analyze and visualize the data for insights.
- Process the data for machine learning applications.
- Choose a model and train it.
- Optimize your model for performance.
- Share your solution effectively.
- Implement, monitor, and sustain your system.
Understand the overall context
The problem statement here is to create a model that predicts housing prices in California using census data. This dataset includes various metrics for each block group in the state, such as population, median income, and median housing price. Block groups, which typically contain 600 to 3,000 residents, represent the smallest geographical unit for which the US Census Bureau releases sample data. For simplicity, we’ll refer to these as “districts.” The model should analyze this information and accurately predict the median housing price for any district based on the other available metrics.
Framing the problem right:
- The first step is to clarify the business objective; remember, building a model isn’t necessarily the end goal. How does the company plan to utilize and benefit from this model? Understanding this is crucial, as it will influence how you define the problem, select algorithms, determine performance metrics, and allocate time for fine-tuning. You find out, that the output of your model, which predicts the median housing price for a district, will be integrated into another Machine Learning system alongside various other data points. This subsequent system will assess the investment potential of specific areas. Getting this right is vital, as it has a direct impact on revenue.
- Next, inquire about the current solution, if one exists. This can provide a benchmark for performance and valuable insights into the problem at hand. Your boss shares that district housing prices are currently estimated manually by a team of experts. They compile up-to-date information for each district, and when precise median housing prices aren’t available, they resort to complex estimation rules. This process is costly and time-consuming, and the estimates often miss the mark, sometimes by over 20%. This inefficiency is why the company believes training a model to predict median housing prices based on available data could be beneficial. The census data appears to be a valuable resource, offering median housing prices and additional metrics for thousands of districts.
With this context, you can begin designing your system. First, you need to frame the problem:
- Is it supervised, unsupervised, or reinforcement learning?
- Is it a classification task, a regression task, or something else?
- Should you apply batch learning or online learning techniques?
In this case, it’s clearly a supervised learning problem since you have labeled training examples (each instance includes the expected output—the district’s median housing price). Furthermore, it qualifies as a regression task, as you are tasked with predicting a value. More specifically, this is a multiple regression problem because multiple features (such as population and median income) will inform the prediction. It is also a univariate regression problem, given that you are predicting a single value for each district; if multiple values were being predicted, it would be a multivariate regression problem. Lastly, since there isn’t a continuous stream of incoming data that requires rapid adjustments, and the dataset is manageable enough to fit in memory, a standard batch learning approach should suffice.
Selecting a Performance Measure
Root Mean Squared Error (RMSE) is a commonly used metric to evaluate the performance of regression models. It measures the average magnitude of the errors between predicted and actual values, providing insight into how well the model is performing.
The RMSE is calculated using the following steps:
- Calculate the Residuals: For each predicted value [math]\hat{Y}_i[/math] and actual value [math]Y_i[/math], compute the residual (error): [math]e_i = y_i – \hat{y}_i[/math]
- Square the Residuals: Square each of the residuals: [math]\text{MSE} = \frac{1}{n} \sum_{i=1}^{n} e_i^2[/math], where n is the number of observation.
- Square Root of the Mean: Finally, take the square root of the mean squared error: [math]\text{RMSE} = \sqrt{\text{MSE}} = \sqrt{\frac{1}{n} \sum_{i=1}^{n} (y_i – \hat{y}_i)^2}[/math]
Root Mean Squared Error (RMSE) gives greater weight to larger errors due to the squaring of the residuals in its calculation. Here’s a breakdown of how this works:
Squaring the Errors:
1. Residuals: For each prediction, the error is calculated as the difference between the actual value [math]Y_i[/math] and the predicted value [math]\hat{Y}_i[/math] (i.e., [math]e_i = y_i – \hat{y}_i[/math]).
2. Squaring: Each residual is squared, which means that larger errors become disproportionately larger:
– For example, if an error is 2, squaring it gives 4, while an error of 10 results in 100. This non-linear transformation emphasizes the impact of larger errors.
Mean Calculation:
3. Mean of Squared Errors: The Mean Squared Error (MSE) is calculated by averaging these squared errors:
[math]\text{MSE} = \frac{1}{n} \sum_{i=1}^{n} e_i^2[/math]
Larger squared errors contribute significantly more to the mean compared to smaller ones.
Square Root:
4. Taking the Square Root: The final RMSE value is obtained by taking the square root of the MSE. While this step brings the RMSE back to the original units of the target variable, the influence of large errors remains due to their earlier squaring.
Summary:
Amplified Impact: Because RMSE squares the errors, larger discrepancies between predicted and actual values have a much larger impact on the final RMSE score. This means that a model with a few significant outliers can have a much higher RMSE than one with smaller, more uniformly distributed errors.
Sensitivity to Outliers: This characteristic makes RMSE particularly sensitive to outliers, making it an important consideration when evaluating model performance.
In practice, if you’re concerned about the influence of large errors or outliers, you might also consider alternative metrics like Mean Absolute Error (MAE), which treats all errors equally regardless of their size.
Mean Squared Error (MAE) :
Mean Absolute Error (MAE) is a commonly used metric for evaluating the accuracy of regression models. It measures the average magnitude of errors in a set of predictions, without considering their direction (i.e., whether they are overestimates or underestimates).
Formula:
The MAE is calculated using the following steps:
Calculate the Absolute Residuals: For each actual value [math]Y_i[/math] and the predicted value [math]\hat{Y}_i[/math], compute the absolute error:
[math]|e_i| = |y_i – \hat{y}_i|[/math]
Mean of the Absolute Errors: Take the average of these absolute errors:
[math]\text{MAE} = \frac{1}{n} \sum_{i=1}^{n} |y_i – \hat{y}_i|[/math]
where n is the number of observations.
Key Characteristics:
- Interpretability: MAE is easy to interpret since it is in the same units as the target variable. For example, if predicting prices, an MAE of $10 means the predictions are, on average, $10 off.
- Sensitivity to Outliers: Unlike Root Mean Squared Error (RMSE), MAE treats all errors equally since it does not square the residuals. This makes MAE more robust to outliers, as large errors do not disproportionately influence the overall metric.
- Linear Measurement: Because MAE uses absolute values, it provides a linear score that reflects the average error magnitude without exaggerating the influence of larger errors.
Use Cases:
MAE is particularly useful in situations where:
- You want a straightforward measure of average error.
- You want to minimize the impact of outliers.
- Interpretability is a priority, as MAE provides a clear understanding of model accuracy.
Comparison with Other Metrics:
- MAE vs. RMSE: While RMSE gives more weight to larger errors (sensitive to outliers), MAE treats all errors uniformly, which can be beneficial in specific contexts.
- MAE vs. MSE: Like RMSE, Mean Squared Error (MSE) emphasizes larger errors due to squaring, whereas MAE does not.
Summary:
Mean Absolute Error (MAE) is a valuable metric for assessing the performance of regression models, providing a clear and interpretable measure of average prediction error while being less influenced by outliers compared to metrics like RMSE.
Review the assumptions : It’s essential to identify and verify the assumptions that have been made—either by you or others—throughout the process. This step can help catch significant issues early on. For instance, your model outputs district prices that will feed into a downstream Machine Learning system. We assume these prices will be used directly, but what if the downstream system actually categorizes them into groups like “cheap,” “medium,” or “expensive”? In that case, the precise price accuracy may be irrelevant; the model only needs to assign the correct category. If that’s the situation, the problem should have been framed as a classification task rather than a regression task. You certainly don’t want to discover this after investing months in developing a regression model.
Feature Scaling
What is Feature Scaling
Feature Scaling is a preprocessing step in Machine Learning that involves transforming the features of your dataset onto a similar scale. The primary purpose of Feature Scaling is to ensure that all features contribute equally to the computation of distances and gradients during the training process, preventing certain features from dominating due to their larger scales.
Let’s try to understand this concept with the help of an example:
We have a dataset with 2 features: “Age”, and “Income”. The “Age” feature ranges from 0 to 100, while the “Income” feature ranges from 20000 to 100000. If we simply fit these data into the model, it might lead to biased results and the model wouldn’t perform optimally. Without Feature Scaling, the algorithm might give more weight to “Income” because its values are much larger than those of “Age”.
So, how do we deal with this scaling issue ? Well, there are few methods for Feature Scaling
Different Methods for Feature Scaling
Min-Max Scaling (Normalization):
This technique scales the values to a fixed range, usually between 0 and 1. It’s calculated using the formula:
[math]X_{\text{norm}} = \frac{X_{\text{max}} – X_{\text{min}}}{X_{\text{max}} – X_{\text{min}}}[/math]
where:
- [math]X_{\text{norm}}[/math] is the scaled value
- [math]X[/math] is the original value of the feature
- [math]X_{\text{min}}[/math] is the minimum value of the feature in the dataset
- [math]X_{\text{max}}[/math] is the maximum value of the feature in the dataset
Now let’s dive into the mathematical derivation of Min-Max Scaling by breaking it down into few steps:
- Normalization to the range [0,1]:
The goal of Min-Max Scaling is to map the original values to a range between 0 and 1. We do this by subtracting the minimum value [math](X_{\text{min}})[/math] from each value [math](X)[/math] to shift the range so that the minimum value becomes 0. Then, we divide by the difference between the maximum value [math](X_{\text{max}})[/math] and the minimum value [math](X_{\text{min}})[/math] to scale the values within the range 0 to 1.
- Maintaining the relative distance between values:
Min-Max Scaling maintains the relative distances between values in the original range. By subtracting the minimum value and dividing by the range, we ensure that the relative distances between values are preserved. This is important in many machine learning algorithms, particularly those that rely on distance calculations (such as k-nearest neighbors or support vector machines).
- Ensuring the transformed data has a specific range:
By using the minimum and maximum values of the original data, Min-Max Scaling guarantees that the transformed data will fall within the specified range [0, 1]. This can be useful for algorithms that require input data to be within a certain range, or for interpretability purposes.
Limitations of Min-Max Scaling
Overall, Min-Max Scaling is a simple and effective technique for scaling features to a specific range, making it easier to compare and combine different features in machine learning models. However, Min-Max Scaling id not suitable for all datasets, especially if the data contains outliers or if the distribution of the data is highly skewed. In such cases, other scaling techniques like Standardization (Z-score normalization) may be more appropriate.
Let’s see this with a very basic example, once that is done, we will implement that in code as well
Suppose we have a dataset with following feature:
Feature
——-
10
20
30
40
To apply Min-Max Scaling to this feature, we need to calculate the scaled values using the formula:
[math]X_{\text{norm}} = \frac{X_{\text{max}} – X_{\text{min}}}{X_{\text{max}} – X_{\text{min}}}[/math]
For the given dataset:
- [math]X_{\text{min}}[/math] = 10
- [math]X_{\text{max}}[/math] = 40
Now, we can calculate the scaled values:
- for 10 : [math]\frac{{10 – 10}}{{40 – 10}} = \frac{{0}}{{30}} = 0[/math]
- for 20: [math]\frac{{20 – 10}}{{40 – 10}} = \frac{{10}}{{30}} = 1 \div 3[/math] or approximately 0.33
- for 30 : [math]\frac{{30 – 10}}{{40 – 10}} = \frac{{20}}{{30}} = 2 \div 3[/math] or approximately 0.67
- for 40 : [math]\frac{{40 – 10}}{{40 – 10}}[/math] = [math]\frac{{30}}{{30}} = 1[/math]
Now that we have understood how to mathematically calculate min-max scalar for any given feature, lets write a simple linear regression model and implement Min-Max scaling to the features
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
import numpy as np
# Sample dataset
X = np.array([[1],
[2],
[3],
[4]])
y = np.array([2, 4, 6, 8])
# Split the dataset into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Initialize MinMaxScaler
scaler = MinMaxScaler()
# Fit the scaler to the training data and transform both training and test data
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
print("Scaled Training Data:")
print(X_train_scaled)
print("\nScaled Test Data:")
print(X_test_scaled)
# Initialize and fit the linear regression model
model = LinearRegression()
model.fit(X_train_scaled, y_train)
# Make predictions on the test set
y_pred = model.predict(X_test_scaled)
# Calculate Mean Squared Error
mse = mean_squared_error(y_test, y_pred)
print("\nMean Squared Error:", mse)
Output:
Scaled Training Data:
[[1. ]
[0. ]
[0.66666667]]
Scaled Test Data:
[[0.33333333]]
Mean Squared Error: 1.9721522630525295e-31
Looking at the above code, you might have a question, which I also asked myself, why didn’t we scale the output feature “y”. Let me tell you, this is a very important and valid question. Let’s address this before we move forward!
To understand this, let’s first breakdown the idea behind scaling:
Why do we scale features?
- When we’re training a machine learning model, we want all the features to contribute equally to the model’s predictions.
- If features have different scales (i.e., they’re on different numerical scales), features with larger scales can dominate the learning process. This can lead to biased or inefficient models.
- By scaling features, we ensure that all features are on the same scale, preventing any single feature from having too much influence on the model.
Why don’t we scale the target variable (output feature)?
- The target variable (or output feature) in regression problems is what we’re trying to predict. We’re interested in understanding its relationship with the features.
- Unlike features, which are input to the model, the target variable is the output we’re trying to estimate. Its scale doesn’t affect the model’s ability to learn the relationship between features and the target.
- Scaling the target variable isn’t necessary for most regression tasks because we’re focused on predicting its value based on the features, not comparing its magnitude across different observations.
When might we scale the target variable?
- In some cases, algorithms or techniques used for regression might require the target variable to be on a specific scale.
For example, if you’re using a distance-based algorithm like k-nearest neighbors or an algorithm with regularization terms that involve the scale of the target variable, scaling the target might be necessary to ensure optimal performance.
Now that we have understood Min-Max scaling, let move onto the next type of Feature Scaling technique called Standardization (Z-score Normalization)
Standardization (Z-score Normalization):
This technique scales the values to have a mean of 0 and a standard deviation of 1. It’s calculated using the formula
[math]X_{\text{std}} = \frac{X – \mu}{\sigma}[/math]
Where:
[math]X_{\text{std}}[/math] is the standardized value
[math]X[/math] is the original value of the feature
[math]\mu[/math] is mean of the feature in the dataset
[math]\sigma[/math] is the standard deviation of the feature in the dataset
- Centering around the mean ([math]\mu[/math]):
The first step in Standardization is to shift the distribution of the data so that it is centered around the mean [math]\mu[/math]. This is done by subtracting the mean from each value [math](X)[/math]. By doing this, the mean of the standardized data becomes 0. Let’s understand with the help of an example:
Below is the original values for the “Age” and Income feature respectively:
Age Income
——-|————
30 | 50,000
40 | 70,000
25 | 60,000
35 | 80,000
We calculate the mean and standard deviation for each feature:
Mean of Age: [math]\mu_{\text{Age}} = \frac{{30+40+25+35}}{4}[/math] = [math]32.5[/math]
Mean of Income: [math]\mu_{\text{Income}} = \frac{{50,000 + 70,000 + 60,000 + 80,000}}{4}[/math] = [math]65,000[/math]
- Scaling the Standard deviation([math]\sigma[/math]):
After centering the data around the mean, the next step is to scale the data by dividing each value by the standard deviation [math](\sigma)[/math]. This ensures that the spread of the data is consistent across different features. Scaling by the standard deviation also ensures that the variance of the standardized data is 1.
Standard Deviation of Age: [math]\sigma_{\text{Age}} = \sqrt{\frac{{(30 – 32.5)^2 + (40 – 32.5)^2 + (25 – 32.5)^2 + (35 – 32.5)^2}}{4}}[/math] ≈[math]5.59[/math]
Standard Deviation of Income: [math]\sigma_{\text{Income}}[/math] = [math]\sqrt{\frac{{(50,000 – 65,000)^2 + (70,000 – 65,000)^2 + (60,000 – 65,000)^2 + (80,000 – 65,000)^2}}{4}}[/math]≈[math]10,609.08[/math]
- Preserving the relative distances between values:
Smilar to Min-Max Scaling, Standardization preserves the relative distances between values in the original range. By subtracting the mean and dividing by the standard deviation, the relative distances between values are maintained. This is important for algorithms that rely on distance calculations or assume a standard normal distribution.
- Interpretability: Standardization makes the interpretation of coefficients in linear models easier. Since the standardized values have a mean of 0 and a standard deviation of 1, the coefficients represent the change in the target variable (dependent variable) associated with a one standard deviation change in the predictor variable (independent variable).Similar to Min-Max Scaling, Standardization preserves the relative distances between values in the original range. By subtracting the mean and dividing by the standard deviation, the relative distances between values are maintained. This is important for algorithms that rely on distance calculations or assume a standard normal distribution.
Let’s understand show standardization makes interpretability of coefficients in linear model easier.
Suppose we have a dataset containing information about the weight (in kilograms) and height (in meters) of individuals, and we want to predict their body mass index (BMI), which is calculated as weight (kg) divided by the square of height (m).
Here’s a simplified version of the dataset:
Sure, here’s the table in tabular format:
\[
\begin{array}{|c|c|c|}
\hline
\text{Height (m)} & \text{Weight (kg)} & \text{BMI} \\
\hline
1.60 & 50 & 19.5 \\
1.75 & 65 & 21.2 \\
1.80 & 70 & 21.6 \\
1.68 & 60 & 21.3 \\
1.85 & 75 & 22.0 \\
\hline
\end{array}
\]
Let’s say we want to build a linear regression model to predict BMI based on height and weight.
If we don’t standardize the features (height and weight), the coefficients obtained from the linear regression model will be in terms of the original units (meters for height and kilograms for weight). For example, if the coefficient for height is 2.5, it means that for each additional meter in height, the BMI is expected to increase by 2.5 units.
However, if we standardize the features using standardization (Z-score normalization), the coefficients will be in terms of standard deviations.
Here’s how it works:
- After standardization, the mean of each feature becomes 0 and the standard deviation becomes 1.
- Let’s say the coefficient for height after standardization is 0.7. This means that for each additional standard deviation in height, the BMI is expected to increase by 0.7 units.
- Similarly, if the coefficient for weight after standardization is 0.9, it means that for each additional standard deviation in weight, the BMI is expected to increase by 0.9 units.
This makes the interpretation of coefficients easier because they represent the change in the target variable (BMI) associated with a one standard deviation change in the predictor variable (height or weight).
So, in summary, standardization enhances interpretability by providing coefficients that represent the change in the target variable per standard deviation change in the predictor variable, making comparisons between coefficients more straightforward and meaningful.
Coming back to out original example, let’s calculate the standardized values for Age and Income:
For “Age”:
[math]
\text{For 30:} \quad \frac{30 – 32.5}{5.59} \approx \frac{-2.5}{5.59} \approx -0.45
[/math]
[math]
\text{For 40:} \quad \frac{40 – 32.5}{5.59} \approx \frac{7.5}{5.59} \approx 1.34
[/math]
[math]
\text{For 25:} \quad \frac{25 – 32.5}{5.59} \approx \frac{-7.5}{5.59} \approx -1.34
[/math]
[math]
\text{For 35:} \quad \frac{35 – 32.5}{5.59} \approx \frac{2.5}{5.59} \approx 0.45
[/math]
For “Income”:
[math]
\text{For 50,000:} \quad \frac{50,000 – 65,000}{10,609.08} \approx \frac{-15,000}{10,609.08} \approx -1.41
[/math]
[math]
\text{For 70,000:} \quad \frac{70,000 – 65,000}{10,609.08} \approx \frac{5,000}{10,609.08} \approx 0.47
[/math]
[math]
\text{For 60,000:} \quad \frac{60,000 – 65,000}{10,609.08} \approx \frac{-5,000}{10,609.08} \approx -0.47
[/math]
[math]
\text{For 80,000:} \quad \frac{80,000 – 65,000}{10,609.08} \approx \frac{15,000}{10,609.08} \approx 1.41
[/math]
One thing we need to keep in mind here in the context of Z-score normalization (standardization) is, the standard deviation of the standardized values depends on the spread of the original data. When we standardize a dataset, we transform the data such that the new distribution has a mean of 0 and a standard deviation of 1. However, this doesn’t necessarily mean that the standard deviation of the standardized values will always be exactly 1.
Let’s check if the above hypothesis about standard deviation holds true, by calculating the standard deviation of the standardized values for “Age” and “Income”
For “Income”:
The calculated values for the standardized “Income” feature were:
- For 50,000: approximately -1.41
- For 70,000: approximately 0.47
- For 60,000: approximately -0.47
- For 80,000: approximately 1.41
Now, let’s calculate the standard deviation for these standardized values. We’ll first calculate the mean of the standardized values and then use the formula for standard deviation to compute it.
[math]
\text{Mean of standardized “Income”:} \quad \text{Mean} = \frac{-1.41 + 0.47 – 0.47 + 1.41}{4} = \frac{0}{4} = 0
[/math]
Next, we calculate the standard deviation using the formula:
[math]
\text{Standard deviation} = \sqrt{\frac{(-1.41 – 0)^2 + (0.47 – 0)^2 + (-0.47 – 0)^2 + (1.41 – 0)^2}{4}}
[/math]
[math]
\text{Standard deviation} = \sqrt{\frac{1.9881 + 0.2209 + 0.2209 + 1.9881}{4}}
[/math]
[math]
\text{Standard deviation} = \sqrt{\frac{4.418}{4}}
[/math]
[math]
\text{Standard deviation} = \sqrt{1.1045}
[/math]
[math]
\text{Standard deviation} \approx 1.05
[/math]
The calculated values for the standardized “Age” feature were:
- For 30: approximately -0.45
- For 40: approximately 1.34
- For 25: approximately -1.34
- For 35: approximately 0.45
Now, let’s calculate the standard deviation for these standardized values. We’ll first calculate the mean of the standardized values and then use the formula for standard deviation to compute it as done above.
Here’s the LaTeX code for the provided text:
[math]
\text{Mean of standardized “Age”:} \quad \text{Mean} = \frac{-0.45 + 1.34 – 1.34 + 0.45}{4} = \frac{0}{4} = 0
[/math]
Next, we calculate the standard deviation using the formula:
[math]
\text{Standard deviation} = \sqrt{\frac{(-0.45 – 0)^2 + (1.34 – 0)^2 + (-1.34 – 0)^2 + (0.45 – 0)^2}{4}}
[/math]
[math]
\text{Standard deviation} = \sqrt{\frac{0.2025 + 1.7956 + 1.7956 + 0.2025}{4}}
[/math]
[math]
\text{Standard deviation} = \sqrt{\frac{3.9962}{4}}
[/math]
[math]
\text{Standard deviation} = \sqrt{0.99905}
[/math]
[math]
\text{Standard deviation} \approx 0.999
[/math]
Hence this proves our above hypothesis that the standard deviation of the standardized values will not always be exactly 1 due to the spread of the original data.
Let’s implement the code below using the above dataset for 3 different scenarios:
- Without Scaling
- With Min-Max Scaling
- With Standardization (Z-Score Normalization)
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import MinMaxScaler, StandardScaler
# Sample dataset
X = np.array([[1, 20_000],
[2, 30_000],
[3, 40_000],
[4, 50_000]])
y = np.array([3000, 3500, 4000, 4500]) # Target variable (e.g., salary)
# Splitting the dataset into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Applying feature scaling
# Min-Max Scaling (Normalization)
min_max_scaler = MinMaxScaler()
X_train_min_max_scaled = min_max_scaler.fit_transform(X_train)
X_test_min_max_scaled = min_max_scaler.transform(X_test)
# Standardization (Z-score normalization)
standard_scaler = StandardScaler()
X_train_standard_scaled = standard_scaler.fit_transform(X_train)
X_test_standard_scaled = standard_scaler.transform(X_test)
# Linear Regression model
model = LinearRegression()
# Without feature scaling
model.fit(X_train, y_train)
train_score_no_scaling = model.score(X_train, y_train)
test_score_no_scaling = model.score(X_test, y_test)
# With Min-Max Scaling (Normalization)
model.fit(X_train_min_max_scaled, y_train)
train_score_min_max = model.score(X_train_min_max_scaled, y_train)
test_score_min_max = model.score(X_test_min_max_scaled, y_test)
# With Standardization (Z-score normalization)
model.fit(X_train_standard_scaled, y_train)
train_score_standard = model.score(X_train_standard_scaled, y_train)
test_score_standard = model.score(X_test_standard_scaled, y_test)
# Plotting
plt.figure(figsize=(10, 6))
# Plot without scaling
plt.plot(['Train (No Scaling)', 'Test (No Scaling)'], [train_score_no_scaling, test_score_no_scaling], label='No Scaling', marker='o')
# Plot with Min-Max Scaling
plt.plot(['Train (Min-Max Scaling)', 'Test (Min-Max Scaling)'], [train_score_min_max, test_score_min_max], label='Min-Max Scaling', marker='o')
# Plot with Standardization
plt.plot(['Train (Standardization)', 'Test (Standardization)'], [train_score_standard, test_score_standard], label='Standardization', marker='o')
plt.xlabel('Data Type')
plt.ylabel('R^2 Score')
plt.title('Model Scores with and without Feature Scaling')
plt.legend()
plt.grid(True)
plt.show()
Output:

References:
Multiple Linear Regression
In this post, we will go over all the nitty gritty details around Multiple Linear Regression, but first let’s start with understanding what is Multiple Linear Regression.
Multiple linear regression is a statistical technique used to model the relationship between multiple independent variables (also called predictors or features) and a dependent variable (also called the outcome or response variable). It assumes a linear relationship between the predictors and the outcome variable.
Model Representation
The multiple linear regression model can be represented as:
[math]Y = \beta_0 + \beta_1 X_1 + \beta_2 X_2 + \ldots + \beta_n X_n + \varepsilon[/math]
Where:
- [math]Y[/math] is the dependent variable (outcome).
- [math]X_1, X_2, \ldots, X_n[/math]are the independent variables (predictors).
- [math]\beta_0[/math] is the intercept (constant term).
- [math]\beta_1, \beta_2, \ldots, \beta_n[/math] are the coefficients (parameters) representing the change in [math]Y[/math] for a unit change in each corresponding [math]X[/math].
- [math]\varepsilon[/math] is the error term, representing the difference between the observed and predicted values.
Mathematical Derivation
The objective of Multiple Linear Regression is to estimate the coefficients [math]\beta_1, \beta_2, \ldots, \beta_n[/math] that minimize the sum of squared differences between the observed and predicted values. This is typically done using the method of ordinary least squares (OLS).
The OLS estimates for the coefficients can be obtained by minimizing the sum of squared residuals (errors):
[math]\sum_{i=1}^{n} (Y_i – (\beta_0 + \beta_1 X_{i1} + \beta_2 X_{i2} + \ldots + \beta_n X_{in}))^2[/math]
To minimize this expression, the partial derivatives of the sum of squared residuals with respect to each coefficient are equated to zero:
[math]\frac{\partial}{\partial \beta_0} \sum_{i=1}^{n} (Y_i – (\beta_0 + \beta_1 X_{i1} + \beta_2 X_{i2} + \ldots + \beta_n X_{in}))^2 = 0[/math]
[math]\frac{\partial}{\partial \beta_1} \sum_{i=1}^{n} (Y_i – (\beta_0 + \beta_1 X_{i1} + \beta_2 X_{i2} + \ldots + \beta_n X_{in}))^2 = 0[/math]
[math]\vdots[/math]
[math]\frac{\partial}{\partial \beta_n} \sum_{i=1}^{n} (Y_i – (\beta_0 + \beta_1 X_{i1} + \beta_2 X_{i2} + \ldots + \beta_n X_{in}))^2 = 0[/math]
Before going any further, we should understand why did we do partial derivative on the [math]\sum_{i=1}^{n} (Y_i – (\beta_0 + \beta_1 X_{i1} + \beta_2 X_{i2} + \ldots + \beta_n X_{in}))^2[/math] expression and equate it to zero
As we already saw in the above section [math]\sum_{i=1}^{n} (Y_i – (\beta_0 + \beta_1 X_{i1} + \beta_2 X_{i2} + \ldots + \beta_n X_{in}))^2[/math] expression represents the residual sum of squares (RSS) in the context of linear regression, which quantifies the discrepancy between the observed values [math]Y_i[/math] and the predicted values [math]\hat{Y}_i[/math] generated by the regression model
RSS = [math]\sum_{i=1}^{n} (Y_i – (\beta_0 + \beta_1 X_{i1} + \beta_2 X_{i2} + \ldots + \beta_n X_{in}))^2[/math]
Or it can also be represented as
RSS = [math]\sum_{i=1}^{n} (Y_i – X_i \beta)^2[/math]
The goal in linear regression is to find the values of the coefficients [math]\beta_1, \beta_2, \ldots, \beta_n[/math] that minimize the RSS. This is typically done by finding the values of the coefficients that make the partial derivatives of the RSS with respect to each coefficient equal to zero as show in the equations above. These values are then the critical points where the RSS might be at a minimum.
Now, let’s understand how the partial derivative works:
- Partial Derivatives: Taking the partial derivatives of the RSS with respect to each coefficient [math]\beta_j \quad \text{where} \quad j = 0, 1, \ldots, n[/math] provides information about how the RSS changes with respect to changes in each coefficient individually. Setting these partial derivatives to zero helps identify candidate solutions where the RSS might be minimized. That’s the reason why we are taking the partial derivative of RSS with respect to each coefficient and equating it to 0
[math]\frac{\partial}{\partial \beta_0} \sum_{i=1}^{n} (Y_i – (\beta_0 + \beta_1 X_{i1} + \beta_2 X_{i2} + \ldots + \beta_n X_{in}))^2 = 0[/math]
[math]\frac{\partial}{\partial \beta_1} \sum_{i=1}^{n} (Y_i – (\beta_0 + \beta_1 X_{i1} + \beta_2 X_{i2} + \ldots + \beta_n X_{in}))^2 = 0[/math]
[math]\vdots[/math]
[math]\frac{\partial}{\partial \beta_n} \sum_{i=1}^{n} (Y_i – (\beta_0 + \beta_1 X_{i1} + \beta_2 X_{i2} + \ldots + \beta_n X_{in}))^2 = 0[/math]
Vector Representation
Now that we have understood the mathematical model representation, lets try to interpret it in the form of vector representation, so that it will be easier for us to translate it into code while modeling the data
Consider the Multiple regression model : [math]Y = X\beta + \epsilon[/math]
RSS = [math]\sum_{i=1}^{n} (Y_i – X_i \beta)^2[/math]
To represent RSS in matrix form, let’s denote the following:
- [math]Y[/math] as the vector of observed values of the dependent variable (of length n).
- [math]X[/math] as the matrix of predictor variables (of size n \times (p+1), where p is the number of predictors, and the 1 represents the first column of X whichis a vector of ones for the intercept term).
- [math]\hat{Y}[/math] as the vector of predicted values of the dependent variable.
Then, the predicted value [math]\hat{Y}[/math] can be expressed as [math]\hat{Y}[/math] = [math]X \beta[/math], where [math]X \beta[/math] represents the matrix multiplication of [math]X[/math] and [math]\beta[/math]
Now we could define a residual vector [math]\varepsilon[/math] which denotes the vector difference between observed values of [math]Y[/math] and the predicted values of [math]\hat{Y}[/math], i.e., [math]\varepsilon[/math] = [math]Y[/math] – [math]\hat{Y}[/math]
Using the above equation we can rewrite the equation of RSS as the sum of square of the residual vector [math]\varepsilon[/math]
[math]RSS = \sum_{i=1}^{n} \epsilon_i^2[/math]
In matrix form, this can be written as the dot product of the transpose math]\varepsilon[/math] and [math]\varepsilon[/math]:
[math]RSS = \epsilon^T \epsilon[/math]
Expanding this expression using the definitions of [math]\varepsilon[/math] and [math]\hat{Y}[/math], we get:
[math]RSS = (Y – X\beta)^T (Y – X\beta)[/math]
Expanding and simplifying the above expression:
[math]RSS = Y^T Y – 2\beta^T X^T Y + \beta^T X^T X \beta[/math]
To find the coefficients [math]\beta[/math] that minimize the RSS, we take the derivative of RSS with respect to [math]/beta[/math],set it equal to zero, and solve for [math]/beta[/math]. This is the same method we used above in our partial derivative section. The only difference was we are solving this in a vectorized format and the earlier version was in scaler format.
[math]\frac{\partial RSS}{\partial \beta} = -2X^T Y + 2X^T X\beta = 0[/math]
Now, solving the above equation for [math]\beta[/math] gives us:
[math](X^T X) \beta = X^T Y[/math]
[math]\hat{\beta} = (X^T X)^{-1} X^T y[/math]
where [math]\hat{\beta}[/math] is the vector of estimated coefficients.
Now that we have understood the Model representation and the mathematical derivation of Multiple Linear Regression, lets implement a model in code. I will first write each block of code and provide explaination and later consolidate the whole code
import numpy as np
class MultipleLinearRegression:
def __init__(self):
self.intercept_ = None
self.coef_ = None
This above block of code defines a Python class named MultipleLinearRegression. It initializes two instance variables intercept_ and coef_ to None.
def fit(self, X, y):
ones = np.ones((X.shape[0], 1))
# Adding a column of ones for the intercept term
X = np.concatenate((ones, X), axis=1)
# Compute coefficients using closed-form solution (OLS)
self.coefficients = np.linalg.inv(X.T.dot(X)).dot(X.T).dot(y)
self.intercept_ = self.coefficients[0]
self.coef_ = self.coefficients[1:]
The above method fit is used to train the Multiple Linear Regression model. It takes two arguments X (the input features) and y (the target variable). It first creates a column vector of ones using np.ones((X.shape[0], 1)). This vector is added as the first column to the input features X using np.concatenate. This is done to account for the intercept term in the linear regression model.
Next, it computes the coefficients of the linear regression model using the closed-form solution, also known as Ordinary Least Squares (OLS). It calculates the coefficients by first multiplying the transpose of X with X representing (X.T.dot(X)), then taking the inverse of the resulting matrix (np.linalg.inv) , and finally multiplying the result with the transpose of X and y to get the coefficients. This is something which we already did during the vector representation section.
The intercept term is stored in self.intercept_, and the remaining coefficients are stored in self.coef_.
def predict(self, X):
ones = np.ones((X.shape[0], 1))
X = np.concatenate((ones, X), axis=1) # Adding a column of ones for the intercept term
return X.dot(self.coefficients)
This method predict is used to make predictions using the trained linear regression model.
Similar to the fit method, it adds a column of ones to the input features to account for the intercept term.
It then calculates the predicted values (y_pred) using the dot product of the input features X and the coefficients self.coefficients
# Example usage:
# Generate some sample data
np.random.seed(0)
X = np.random.rand(100, 2) # 100 samples, 2 features
# True relationship: y = 2*x1 + 3*x2 + noise
y = 2*X[:,0] + 3*X[:,1] + np.random.randn(100)
# Split the data into train and test sets
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Fit the multiple linear regression model
model = MultipleLinearRegression()
model.fit(X_train, y_train)
# Make predictions on the test set
y_pred = model.predict(X_test)
# Evaluate the model
from sklearn.metrics import r2_score
accuracy = r2_score(y_test, y_pred)
print('Accuracy (R^2 score):', accuracy)
print('Intercept:', model.intercept_)
print('Coefficients:', model.coef_)
This block of code demonstrates the usage of the MultipleLinearRegression class.It generates some sample data X and y where the relationship between features and the target variable y is known.
Then, it splits the data into training and testing sets using train_test_split from scikit-learn.It fits the MultipleLinearRegression model to the training data using the fit method.After that, it makes predictions on the test set using the predict method.Finally, it evaluates the model’s performance using the R^2 score and prints the result.
Finally running the whole code
import numpy as np
class MultipleLinearRegression:
def __init__(self):
self.intercept_ = None
self.coef_ = None
def fit(self, X, y):
ones = np.ones((X.shape[0], 1))
X = np.concatenate((ones, X), axis=1) # Adding a column of ones for the intercept term
# Compute coefficients using closed-form solution (OLS)
self.coefficients = np.linalg.inv(X.T.dot(X)).dot(X.T).dot(y)
self.intercept_ = self.coefficients[0]
self.coef_ = self.coefficients[1:]
def predict(self, X):
ones = np.ones((X.shape[0], 1))
X = np.concatenate((ones, X), axis=1) # Adding a column of ones for the intercept term
return X.dot(self.coefficients)
# Example usage:
# Generate some sample data
np.random.seed(0)
X = np.random.rand(100, 2) # 100 samples, 2 features
y = 2*X[:,0] + 3*X[:,1] + np.random.randn(100) # True relationship: y = 2*x1 + 3*x2 + noise
# Split the data into train and test sets
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Fit the multiple linear regression model
model = MultipleLinearRegression()
model.fit(X_train, y_train)
# Make predictions on the test set
y_pred = model.predict(X_test)
# Evaluate the model
from sklearn.metrics import r2_score
accuracy = r2_score(y_test, y_pred)
print('Accuracy (R^2 score):', accuracy)
print('Intercept:', model.intercept_)
print('Coefficients:', model.coef_)
# Plotting the model's predictions vs. actual values
plt.scatter(y_test, y_pred, color='blue')
plt.plot(y_test, y_test, color='red', linestyle='--') # Plotting the y = x line for reference
plt.title('Model Predictions vs. Actual Values')
plt.xlabel('Actual Values')
plt.ylabel('Predicted Values')
plt.show()
Accuracy (R^2 score): 0.43228286040209773
Intercept: 0.04493496059558341
Coefficients: [1.56277657 3.09631373]

References:
Mathematical derivation of Cost Function and Gradient Descent
In Linear Regression, Cost Function and Gradient Descent are considered fundamental concepts that play a very crucial role in training a model. Let’s try to understand this in detail and also implement this in code with a simple example.
Cost Function
The cost function is a very important metric when it comes to quantifying the error or mismatch between the model’s prediction and the actual values. The choice of cost function depends on the type of problem you are trying to solve and the nature of the output. What does the nature of output mean in this context,
- Suppose you are trying to solve a regression problem where your goal is to predict continuous values, Mean Squared Error (MSE) is the most common cost function. The MSE measures the average squared difference between the predicted values and the actual values. Lower MSE indicates better performance.
[math]J(\theta) = \frac{1}{2m} \sum_{i=1}^{m} (h_\theta(x^{(i)}) – y^{(i)})^2 [/math]
2. In classification problems, where the goal is to predict discrete class labels, the cross-entropy loss or the hinge loss might be used. These loss functions penalize the model more when it makes incorrect predictions, with the magnitude of the penalty depending on the degree of error.
Let’s come back to the main discussion now!
We will try to break down the above equation of cost function into smaller components first and try to understand each component, and later use that understanding to derive cost function from scratch. This might sound counter-intuitive at the beginning but will start making sense, so stay with me as we go through it one step at a time!
Let’s re-write the equation for cost function again :
[math]J(\theta) = \frac{1}{2m} \sum_{i=1}^{m} (h_\theta(x^{(i)}) – y^{(i)})^2 [/math]
Where:
- is the cost function.
- is the number of training examples.
- [math]h_\theta(x^{(i)})[/math] is the predicted value for the -th example.
- is the actual value for the -th example.
- represents the parameters of the linear regression model.
The goal during training is to find the values of that minimize this cost function.
So far, we understood that the above cost function is a MSE, where were are calculating the squared of difference between the predicted and actual value and taking their mean, which is the number of training examples (m) in our case, but what is that [math]\frac{1}{2}[/math] in that formula, what’s it significance.
Well, the fraction [math]\frac{1}{2}[/math] in the cost function is included for mathematical convenience, and it does not change the optimization problem. It is commonly used to simplify the calculations during the gradient descent optimization process.
When you take the derivative of the cost function with respect to the parameters (), the [math]\frac{1}{2}[/math] term cancels out when you apply the chain rule, leading to simpler expressions. If you want to learn the basics of calculus, you can go through my following blog post as well.
Specifically, when you take the derivative with respect to , you get:
[math]\frac{\partial J}{\partial \theta_j} = \frac{1}{m} \sum_{i=1}^{m} (h_\theta(x^{(i)}) – y^{(i)}) \cdot x_j^{(i)}[/math]
The [math]\frac{1}{2}[/math] factor is introduced to cancel the 2 that arises when taking the derivative of the squared term in the Mean Squared Error (MSE) cost function. It does not affect the optimal values of that minimize the cost function, but it simplifies the subsequent computations. This will make more sense, when we go over the derivation of Gradient Descent in the subsequent section.
So, the presence of [math]\frac{1}{2}[/math] in the cost function is a convention to make the optimization calculations more convenient and does not change the underlying objective of finding parameters that minimize the prediction error.
Now, let’s derive the cost function from the ground up,
- Hypothesis function : we consider a hypothesis function for the linear regression as below
[math]h_\theta(x) = \theta_0 + \theta_1 x_1 + \theta_2 x_2 + \ldots + \theta_n x_n[/math]
Here, [math]x[/math] represents the feature vector [math][x_0, x_1, x_2, \ldots, x_n][/math], where [math]x_0 = 1[/math] is the bias term.
2. Difference between predicted and actual value
[math](h_\theta(x^{(i)}) – y^{(i)})[/math]
3. Square the difference between predicted and actual value : We typically do this to account for both overestimations and underestimations of error
[math](h_\theta(x^{(i)}) – y^{(i)})^2[/math]
Pieceing all the above 3 steps together, we write the cost function, [math]J(\theta) = \frac{1}{2m} \sum_{i=1}^{m} (h_\theta(x^{(i)}) – y^{(i)})^2 [/math] which the average of the squared differences over all training examples. We sum up the squared differences for each example and divide by twice the number of training examples () for mathematical convenience as explained above.
Now that we have understood Cost Function in detail, let’s understand what is gradient descent and its relation with cost function in detail
Gradient Descent
Gradient descent is an optimization algorithm used to minimize the cost function by adjusting the model parameters (). It iteratively updates in the direction of the steepest decrease in the cost function. The update formula for gradient descent is given by:
[math]\frac{\partial J}{\partial \theta_j} = \frac{1}{m} \sum_{i=1}^{m} (h_\theta(x^{(i)}) – y^{(i)}) \cdot x_j^{(i)}[/math]
Where:
- is the -th parameter.
- is the learning rate, a hyperparameter that determines the size of the steps in the parameter space.
- is the number of training examples.
- [math]h_\theta(x^{(i)})[/math] is the predicted value for the -th example.
- is the actual value for the -th example.
- [math]x_j^{(i)}[/math] is the -th feature of the -th example.
This process is repeated until convergence, where the algorithm finds parameter values that result in a minimal cost function. In short, the cost function quantifies the error in the model’s predictions, and gradient descent is the optimization algorithm that adjusts the model parameters to minimize this error.
Let’s understand how the Gradient descent can be derived from cost function:
- Cost function
[math]J(\theta) = \frac{1}{2m} \sum_{i=1}^{m} (h_\theta(x^{(i)}) – y^{(i)})^2 [/math]
2. Compute the partial derivative of the cost function with respect to
[math]\frac{\partial J}{\partial \theta_j} = \frac{1}{m} \sum_{i=1}^{m} (h_\theta(x^{(i)}) – y^{(i)}) \cdot x_j^{(i)}[/math]
3. Update using the gradient descent update rule:
[math]\theta_j := \theta_j – \alpha \frac{\partial \theta_j}{\partial J}[/math]
Substituting the partial derivative, we get:
[math]\theta_j := \theta_j – \alpha \frac{1}{m} \sum_{i=1}^{m} (h_\theta(x^{(i)}) – y^{(i)}) \cdot x_j^{(i)}[/math]
This update rule is applied simultaneously for all until convergence is reached.The intuition behind this process is that we update each parameter in the direction that reduces the cost function, and the size of the update is determined by the learning rate (). The sum term represents the average contribution of each training example to the update. The process is repeated iteratively until the algorithm converges to parameter values that minimize the cost function.
Below is the visual representation of the above iterative process, showing the eventual convergance to minima which minimizes the cost function.

[Source: wikipedia]
[Source: wikipedia]
Now that we have understood both the concepts and their derivation, we will implement the code by generating randon synthetic data.
1. Function to create random synthetic data.To keep things simple, we will only consider one independent variable with 100 sample size.The assumption here is that, we have already have established the relation between the dependent(y) and independent(X) variable using the below formula: y = 3X +4 Using this understanding, we create a function which generates 100 random values for X and y
def generate_synthetic_data():
np.random.seed(42)
X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)
return X, y
2.We implement the below function to define the Cost function
def compute_cost(X, y, theta):
m = len(y)
predictions = X.dot(theta)
cost = (1/(2*m)) * np.sum((predictions - y)**2)
return cost
3.Function to implement the Gradient Descent. Notice that we have transposed the X matrix while calculating the gradient. That's because, X is a (100,1) column vector, When performing matrix multiplication , the transpose of is not necessary because is already a column vector. However, if the synthetic data had more than one feature (i.e., if had more than one column), then the transpose of would be needed. In that case, you would need to use to ensure that the matrix dimensions match for the dot product operation
def gradient_descent(X, y, theta, learning_rate, iterations):
m = len(y)
cost_history = []
for _ in range(iterations):
predictions = X.dot(theta)
errors = predictions - y
gradient = (1/m) * X.T.dot(errors)
theta = theta - learning_rate * gradient
cost = compute_cost(X, y, theta)
cost_history.append(cost)
return theta, cost_history
4.Generate synthetic data
X, y = generate_synthetic_data()
5. Add a bias term to X (for theta_0)
X_b = np.c_[np.ones((100, 1)), X]
6.Initialize theta with zeros, the value of theta will change with every iteration of gradient descent
theta_initial = np.zeros((2, 1)) print(theta_initial)
7.Set hyperparameters
learning_rate = 0.01 iterations = 10000
8.Run gradient descent
theta_optimal, cost_history = gradient_descent(X_b, y, theta_initial, learning_rate, iterations)
9.Print the optimal parameters
print("Final Parameters (theta):")
print(f"Theta 0 (Intercept): {theta_final[0][0]}")
print(f"Theta 1 (Slope): {theta_final[1][0]}")
10.Plot the cost history
plt.plot(cost_history)
plt.xlabel('Iterations')
plt.ylabel('Cost')
plt.title('Cost Function Convergence')
plt.show()
Piecing all the above code blocks together:
import numpy as np
import matplotlib.pyplot as plt
def generate_synthetic_data():
np.random.seed(42)
X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)
return X, y
def compute_cost(X, y, theta):
m = len(y)
predictions = X.dot(theta)
cost = (1/(2*m)) * np.sum((predictions - y)**2)
return cost
def gradient_descent(X, y, theta, learning_rate, iterations):
m = len(y)
cost_history = []
for _ in range(iterations):
predictions = X.dot(theta)
errors = predictions - y
gradient = (1/m) * X.T.dot(errors)
theta = theta - learning_rate * gradient
cost = compute_cost(X, y, theta)
cost_history.append(cost)
return theta, cost_history
X, y = generate_synthetic_data()
X_b = np.c_[np.ones((100, 1)), X]
theta_initial = np.zeros((2, 1))
learning_rate = 0.01
iterations = 10000
theta_optimal, cost_history = gradient_descent(X_b, y, theta_initial, learning_rate, iterations)
print("Final Parameters (theta):")
print(f"Theta 0 (Intercept): {theta_final[0][0]}")
print(f"Theta 1 (Slope): {theta_final[1][0]}")
plt.plot(cost_history)
plt.xlabel('Iterations')
plt.ylabel('Cost')
plt.title('Cost Function Convergence')
plt.show()
Output: Final Parameters (theta): Theta 0 (Intercept): 4.215096157546533 Theta 1 (Slope): 2.770113386438666

Pros and Cons of Implementing Linear Regression
Linear regression is a statistical method that can be used to model the relationship between a dependent and one or more independent variables.
Now you might be wondering, this is the definition and I am not here looking for a definition of Linear Regression. So, let’s try to understand the concept using an example. Let’s say you want to buy your dream house for which you have been working so hard for years, but at the same time, you are skeptical if you end up paying more for that dream house of yours. To avoid such scenarios, you ask your friend who is a statistician to help you out with a strategy for deciding, whether to buy a particular house or not.
Now your friend asks you, what’s the primary factor which you would look in the house, to which your reply is the size of the house.
Based on this one input from you, he plans to implement Linear regression with the following strategy to better guide you in the decision making:
Data Collection: Collect data on various houses, noting their sizes (in square feet) and corresponding prices
Data Visualization: Plot the data on a scatter plot, where the x-axis represents the size of the house, and the y-axis represents the price.
Linear Regression Model: Apply linear regression to create a model that fits the trend in the data. The model will have a slope (coefficient) and an intercept.
Model Interpretation: The linear regression model will give you an equation like , where is the slope (indicating the price per square foot) and is the y-intercept (representing the base price).
Prediction: Now, if you have the size of a new house that’s not in the input dataset, you can use the model to predict its price. Plug in the size into the equation, and the model will give you an estimated price.
Evaluation: Evaluate the model’s performance using metrics like Mean Squared Error (MSE) to see how well it predicts prices based on size. MSE is measure of the average squared difference between the predicted values and the actual values. In the context of linear regression, it helps assess how well the model is performing. Lower MSE values indicate better predictive performance
In simpler terms, the Linear regression model is essentially learning the relationship between the size of a house and its price from the provided data. Once trained, it can be used to make predictions for new houses.This example illustrates how linear regression, with its straightforward equation, can be applied to real-world situations to make predictions based on a single input variable. In this case, it’s predicting house prices based on the size of the house.
Now that you know what Linear regression can do, let’s try to understand it in a mathematical way, before we implement it in code
The formula for linear regression is :
Where:
- is the dependent variable,
- is the independent variable,
- is the slope of the line (coefficient),
- is the y-intercept.
In the case of multiple independent variables, the equation becomes:
Where:
- is the dependent variable,
- is the y-intercept,
- are the coefficients for the independent variables .
The goal of Linear regression is to find the values of , , , , , etc., that minimize the sum of squared differences between the actual and predicted values of the dependent variable.
Let’s implement the Linear regression to model the above example of house proces, where our independent variable is ‘house_sizes’ and dependent variable which we will be predicting is ‘house_prices’
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
# Generate synthetic data for house sizes and prices
np.random.seed(42)
house_sizes = 150 + 20 * np.random.rand(20, 1) # Random house sizes between 150 and 170 sq. ft
house_prices = 50000 + 300 * house_sizes + 10000 * np.random.randn(20, 1) # Linear relation with some random noise
# Visualize the data
plt.scatter(house_sizes, house_prices, color='blue')
plt.title('House Prices vs. Size')
plt.xlabel('Size (sq. ft)')
plt.ylabel('Price ($)')
plt.show()
# Split the data into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(house_sizes, house_prices, test_size=0.2, random_state=42)
# Create a Linear Regression model
model = LinearRegression()
# Train the model on the training data
model.fit(X_train, y_train)
# Make predictions on the test set
y_pred = model.predict(X_test)
# Calculate Mean Squared Error (MSE) on the test set
mse = mean_squared_error(y_test, y_pred)
print(f"Mean Squared Error on the test set: {mse:.2f}")
# Make predictions for new house sizes
new_sizes = np.array([[160], [165], [170]]) # Sizes of new houses to predict prices
predicted_prices = model.predict(new_sizes)
# Display the predictions
for size, price in zip(new_sizes, predicted_prices):
print(f"Predicted price for a house of size {size[0]} sq. ft: ${price[0]:.2f}")

Mean Squared Error on the test set: 151621882.14
Predicted price for a house of size 160 sq. ft: $94282.63
Predicted price for a house of size 165 sq. ft: $94658.20
Predicted price for a house of size 170 sq. ft: $95033.76
If you look at the output closely, the Mean Squared Error(MSE) is very high which indicates that the linear regression model is not fitting the data well, or it may be overfitting. Let’s investigate more on this. In order to do that, lets visualize the actual vs predicted prices of house.
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
# Generate synthetic data for house sizes and prices
np.random.seed(42)
house_sizes = 150 + 20 * np.random.rand(100, 1)
house_prices = 50000 + 300 * house_sizes + 10000 * np.random.randn(100, 1)
# Split the data into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(house_sizes, house_prices, test_size=0.2, random_state=42)
# Visualize the data
plt.scatter(X_test, y_test, color='blue', label='Actual Prices')
plt.title('Actual vs. Predicted Prices (Test Set)')
plt.xlabel('Size (sq. ft)')
plt.ylabel('Price ($)')
# Create a Linear Regression model
model = LinearRegression()
# Train the model on the training data
model.fit(X_train, y_train)
# Make predictions on the test set
y_pred = model.predict(X_test)
# Visualize the predicted values
plt.scatter(X_test, y_pred, color='red', label='Predicted Prices')
plt.legend()
# Show the plot
plt.show()
# Calculate Mean Squared Error (MSE) on the test set
mse = mean_squared_error(y_test, y_pred)
print(f"Mean Squared Error on the test set: {mse:.2f}")

Looking at the above visualization of the actual vs. predicted prices on the test set, a high Mean Squared Error (MSE) could be attributed to following factors:
Non-linearity: The scatter plot suggests that there may be a non-linear relationship between house size and prices. Linear regression assumes a linear relationship, and if the true relationship is more complex, the model may not capture it well. In such cases we would need a more complex model which captures and models the non-linear relationships efficiently.
Outliers: If there are outliers in the data, they can have a significant impact on the performance of linear regression. Outliers can influence the model’s coefficients and predictions, leading to higher MSE.
Underfitting: Linear regression may be too simple to capture the underlying patterns in the data. If the relationship between house size and prices is more complex, a linear model may underfit the data, resulting in higher MSE.
Randomness in Data Generation: The synthetic data generation process includes random noise (np.random.randn(100, 1)). If this noise is large, it can introduce variability in the data, leading to higher MSE. I have considered the random data generation to illustrate an example, but in real world scenarios, we should always model data using a real data-set.
To address the above issues, you might consider the following:
Feature Engineering: Explore non-linear features or transformations of features to capture more complex relationships.
Outlier Detection and Removal: Identify and handle outliers in the data that might be affecting the model’s performance.
Polynomial Regression: If a linear model is insufficient, consider polynomial regression, which can capture non-linear relationships.
Increase Data Size: If the dataset is small, increasing its size might help the model learn better patterns.
Explore More Realistic Data: Consider using a more realistic dataset that better represents the actual problem you’re trying to solve.
Different types of Encoding
Encoding is a technique of converting categorical variables into numerical values so that it could be easily fitted to a machine learning model.
Before getting into the details, let’s understand about the different types of categorical variables.
Nominal categorical variable:
Nominal categorical variables are those for which we do not have to worry about the arrangement of the categories.
Example,
i. suppose we have a gender column with categories as Male and Female.
ii. We can also have a state column in which we have different states like NY, FL, NV, TX
So here we don’t have to worry about the arrangement of the categories.
Ordinal Categorical variable :
Ordinal categories are those in which we have to worry about the rank. These categories can be rearranged based on ranks.
Example,
i. Suppose in a dataset there is an education column which we will use to predict the salary of the person. The education column has categories like ‘bachelors’,’masters’,’PHD’. Based on the above categories we can rearrange this and assign ranks to each category. Based on the education level ‘PHD’ will get the highest rank (PHD-1, masters-2, bachelors-3).
Now that we have discussed about the type of categorical variables, let’s see the different types of encoding:
- Nominal Encoding
- Ordinal Encoding

1. One Hot Encoding
This method is applied to nominal categorical variables.
Example, suppose we have a column containing 3 categorical variables, then in one hot encoding 3 columns will be created each for a categorical variable.

Dummy Variable Trap
We can skip the last column ‘Green’ as 0,0 signifies green. This means, suppose we have ‘n’ columns, then the one hot encoding should create ‘n-1’ columns.

Disadvantage
Suppose we have a column which has 100 categorical variables. Now if we try to convert the categorical variables into dummy variable then we will get 99 columns. This will increase the dimension of the overall dataset which will lead to curse of dimensionality.
So basically, if there is a lot of categorical variables in a column then we should not apply this technique.
2. Label Encoding
This technique will be used only for Ordinal categories. Ranks are provided based on the importance of the category. Below table illustrates that PHD is considered as the highest degree, so the highest label is given to it and so on.

3. One hot Encoding (Multiple Categories) — Nominal Categories
In this method, we will consider only those categories which has the most number of repetitions and we will consider the top 10 repeating categories and apply one hot-encoding to only those categories.

The above technique was for Nominal variables. What shall we do if such kind of scenario arises for Ordinal variable. Let’s see how to handle such scenario.
4. Target guided ordinal categories
In this method, we calculate the mean of each categorical variable based on the output and then rank them. Below table illustrates this.

We can apply this technique but cant do this with nominal as we dont know the order in case of nominal variables unlike in the case of Ordinal where we know the order of variables.
To overcome this limitation for Nominal variables we use another technique called Mean Encoding
5. Mean Encoding
In this method, we will convert the categories into their mean values based on the output.
This type of approach will be applicable where we have a lot of categorical variables for a particular column.
Example, suppose we have a column as pincode which contains all the pincodes of a city. It will contain many pincodes with multiple occurances. To encode we can use this technique which will convert all the pincodes into their mean values based on the output column.
Below table will illustrate the approach:










