Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 67

Logistic Regression from Scratch

Recommended resources

Hello! Welcome to the first lesson in our module on classification.

In the last module, we mastered supervised learning for regression, where the goal was to predict a continuous numerical value. You learned to build, interpret, and evaluate models like linear regression. We concluded by assessing model performance using metrics like R-squared and through residual analysis.

Today, we pivot to a new and powerful area of machine learning: classification. Our goal is no longer to predict how much, but which one—to assign an observation to a discrete category. We'll start with the cornerstone algorithm for binary classification: Logistic Regression. Despite its name, it is a classification algorithm, not a regression one.

Our main goal is to implement logistic regression from scratch. This will give you a deep, foundational understanding of how classification models work, connecting the math you've refreshed (calculus and linear algebra) with your Python skills. By the end of this lesson, you will have built and trained your very own classifier.

From Linear Regression to Probabilities

Let's start with a familiar concept: the linear regression equation.

As you know, this produces a continuous output. But for binary classification, we need a prediction of either 0 or 1. How can we map the continuous output z to a binary outcome?

A naive approach might be to set a threshold: if z > 0, predict 1; otherwise, predict 0. However, this is very sensitive to outliers and doesn't give us a measure of confidence. A much better approach is to convert the output z into a probability.

This is where the Sigmoid function (also called the logistic function) comes in. It takes any real number and "squashes" it into a value between 0 and 1.

Linear Regression vs. Logistic Regression for Classification
This image illustrates the key difference. Linear regression (left) can predict values outside the 0-1 range. Logistic regression (right) uses the S-shaped sigmoid curve to ensure the output is always a probability between 0 and 1.

The output, , can be interpreted as the probability that the label y is 1, given the input x and parameters . For example, if , the model is 80% confident that the class is 1.

To make a final decision, we use a decision boundary. A common choice is a threshold of 0.5:

  • If , we predict class 1.
  • If , we predict class 0.

Let's watch a video that walks through this core concept.

Logistic Regression From Scratch in Python (Mathematical)

The video 'Logistic Regression From Scratch in Python (Mathematical)' by NeuralNine provides an excellent mathematical foundation. This first segment explains how logistic regression uses the sigmoid function to output probabilities for binary classification.

Watch from 02:54 to 14:11. Focus on understanding the role of the linear combination (logits), the properties of the sigmoid function, and how it transforms the linear output into a probability.


The Cost Function for Classification

In linear regression, we used Mean Squared Error (MSE) to measure our model's error. Why can't we use it here? If we plug our sigmoid-based hypothesis into the MSE formula, the resulting cost function is non-convex—it has many local minima. This makes it very difficult for an optimization algorithm like gradient descent to find the optimal parameters.

Instead, we need a cost function that is convex and suited for measuring the error between a predicted probability and a true binary label (0 or 1). This function is called Cross-Entropy Loss, or Log Loss.

The formula for a single training example is:

  • If the true label y is 1: The cost is -\log(prediction). If our model correctly predicts a probability close to 1 (e.g., 0.99), the cost is very low. If it incorrectly predicts a probability close to 0 (e.g., 0.01), the cost is very high.
  • If the true label y is 0: The cost is -\log(1 - prediction). The logic is the same but reversed.

This piecewise function can be elegantly combined into a single equation for the average cost over all m training examples:

This is the function we want to minimize. The following video segment derives this formula starting from the principle of maximum likelihood, which provides a strong theoretical justification for its use.

Logistic Regression From Scratch in Python (Mathematical)

Let's continue with the NeuralNine video. This part explains why MSE isn't suitable and masterfully derives the cross-entropy loss function from the concept of likelihood. This connects directly to the statistical foundations of the algorithm.

Watch from 14:11 to 24:10. Pay close attention to how the likelihood function is constructed and then transformed into the cross-entropy loss by taking the negative average log-likelihood.


Optimization with Gradient Descent

Now that we have a convex cost function , we can use gradient descent to find the parameters that minimize it. As you'll recall from our calculus module, this involves iteratively updating the parameters in the opposite direction of the gradient.

The update rule remains the same:

Our task is to find the partial derivative of the cross-entropy cost function with respect to each parameter . Given your background in calculus, you'll appreciate seeing the full derivation, which uses the chain rule.

The final result for the partial derivative is remarkably simple and elegant:

This formula calculates the average error for each data point, weighted by the corresponding feature value, to determine how to adjust each weight.

The next video segment will walk you through this derivation step-by-step.

Logistic Regression From Scratch in Python (Mathematical)

This final theory segment from the NeuralNine video uses the chain rule to derive the gradient of the cross-entropy cost function. This is the mathematical core of the training process and shows exactly how the model learns.

Watch from 24:10 to 35:15. Follow the application of the chain rule to find the partial derivative. The final, simplified vectorized form of the gradient is what we will implement in our code.

Test your understanding!

The final gradient update for a single weight is proportional to (prediction - actual_label) * feature_value.

Suppose the model predicts 0.9 for an instance where the true label is 0. The error (0.9 - 0) is large and positive. How will gradient descent adjust the weights to correct this mistake?

Show answer

Since the error (prediction - actual_label) is positive, the gradient will also be positive (assuming the feature value is positive). Gradient descent updates the weights by subtracting the gradient (). Therefore, the weights will be decreased.

Decreasing the weights will lower the value of , which in turn lowers the sigmoid output . This moves the prediction away from 0.9 and closer to the correct label of 0.


Implementing Logistic Regression from Scratch

Now for the most exciting part: translating this theory into working Python code. We will build a LogisticRegression class from scratch using NumPy. This will solidify your understanding of every component.

Our implementation will consist of a few key parts:

  1. A _sigmoid function: To compute the sigmoid activation.
  2. A fit method: To train the model using gradient descent. This method will initialize the weights and iteratively update them by calculating the gradient.
  3. A predict method: To make predictions on new data using the learned weights and a decision threshold.

The final segments of the video we've been watching provide a complete, line-by-line walkthrough of this implementation. It uses vectorized NumPy operations, which is highly efficient and builds upon what you learned in the NumPy module.

Logistic Regression From Scratch in Python (Mathematical)

It's time to code! These final parts of the NeuralNine video translate all the math we've discussed into a clean, vectorized Python implementation. You'll see how to structure the code, train it on a real dataset, and evaluate its performance.

Watch from 36:05 to the end (48:09). I recommend pausing the video and coding along in your own environment. Pay special attention to: The implementation of the sigmoid and gradient_descent functions. How the bias term is handled by prepending a column of ones to the feature matrix X. The final training and evaluation loop using a scikit-learn dataset.

Here's a summary of the key functions you'll be building, based on the video's approach. This structure is a great template for your own implementation.

import numpy as np

class LogisticRegression:
    def __init__(self, learning_rate=0.01, n_iterations=1000):
        self.learning_rate = learning_rate
        self.n_iterations = n_iterations
        self.weights = None
        self.bias = None

    def _sigmoid(self, z):
        return 1 / (1 + np.exp(-z))

    def fit(self, X, y):
        n_samples, n_features = X.shape
        
        # Initialize parameters
        self.weights = np.zeros(n_features)
        self.bias = 0

        # Gradient Descent
        for _ in range(self.n_iterations):
            # Calculate linear model and apply sigmoid
            linear_model = np.dot(X, self.weights) + self.bias
            y_predicted = self._sigmoid(linear_model)

            # Calculate gradients
            dw = (1 / n_samples) * np.dot(X.T, (y_predicted - y))
            db = (1 / n_samples) * np.sum(y_predicted - y)

            # Update parameters
            self.weights -= self.learning_rate * dw
            self.bias -= self.learning_rate * db
    
    def predict(self, X):
        linear_model = np.dot(X, self.weights) + self.bias
        y_predicted_probs = self._sigmoid(linear_model)
        
        # Apply decision boundary
        y_predicted_class = [1 if i > 0.5 else 0 for i in y_predicted_probs]
        return np.array(y_predicted_class)

Note: The video implements the bias term slightly differently by adding a column of ones to X. The separate bias term shown here is an alternative common practice. Both are mathematically equivalent and good to know.


Conclusion

Fantastic work! You have successfully built a fundamental machine learning algorithm from the ground up. You've gone from the theoretical underpinnings to a practical, working implementation.

Key Takeaways:

  • Logistic Regression is a classification algorithm that models the probability of a binary outcome.
  • It combines a linear equation with the sigmoid function to produce a probability between 0 and 1.
  • The appropriate cost function is Cross-Entropy Loss, which is convex and penalizes confident, incorrect predictions.
  • Gradient descent is used to minimize this cost function by iteratively updating the model's weights and bias based on the calculated gradients.

By implementing this yourself, you've gained an intuition that is impossible to get just from using a library.

Preview of the Next Lesson:

Now that you understand the mechanics of logistic regression, we'll see how to use the highly-optimized implementation available in scikit-learn. We will also explore how to extend the model from binary to multiclass classification, allowing you to tackle problems with more than two categories.