Skip to main content

Welcome to a Grasp lesson

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

Lesson 20

Implementing and Visualizing the Sigmoid Function

Recommended resources

Hello! Welcome to the fourth module of our course.

In the previous module, you built a complete linear regression model from scratch, using both gradient descent and the normal equation for optimization. You concluded by analyzing the critical trade-offs between these two methods, understanding that while the normal equation is a clever shortcut for linear regression, gradient descent is the versatile, scalable engine we'll use for most other machine learning models.

Today, we transition from regression (predicting continuous values) to our first classification algorithm: Logistic Regression. Our goal for this lesson is to implement and visualize the core component that makes logistic regression work: the sigmoid function. This function is fundamental not just to logistic regression but also to neural networks, so understanding it well is a crucial step.

1. From Linear Output to Probability

Think back to your linear regression model. Its output was a linear combination of features, , which could result in any number from negative to positive infinity. This is perfect for predicting prices or temperatures, but what if we want to answer a binary question?

  • Is this email spam or not? (0 or 1)
  • Will this customer churn or not? (0 or 1)

We need a way to take the continuous output of a linear model and map it to a probability, a value between 0 and 1. This is precisely the job of the sigmoid function.

The sigmoid function, often denoted by , is defined by the following formula:

Where would be the output of our linear model ().

The function's 'S'-shaped curve (which you'll soon plot yourself) takes any real number and squashes it into the (0, 1) range.

  • If is a large positive number, becomes very small, so .
  • If is a large negative number, becomes very large, so .
  • If , , so .

This behavior allows us to interpret the output of the sigmoid function as the probability of the positive class. For example, if our model outputs 0.8, we can say there's an 80% probability that the sample belongs to class 1.

Activation Functions | Deep Learning Tutorial 8 (Tensorflow Tutorial, Keras & Python)

To see a quick conceptual overview of why this is so useful, watch the first couple of minutes of the following video from the 'codebasics' channel.

Watch from 00:36 to 01:27 and from 05:32 to 06:23. The first part explains why squashing the output to a 0-1 range is useful for making classification decisions. The second part contrasts it with a simple step function, highlighting the benefit of getting a smooth probability.


2. Implementing the Sigmoid Function From Scratch

Now, let's turn theory into practice. Your goal is to write a Python function that implements the sigmoid formula. Given your background, you'll appreciate how NumPy's vectorization capabilities make this incredibly efficient.

The following resource provides a complete, self-contained guide to implementing and visualizing the sigmoid function. It aligns perfectly with your preference for "from scratch" coding.

Building a Sigmoid Activation Function from Scratch in Python

The guide 'Building a Sigmoid Activation Function from Scratch in Python' provides several code implementations. We will focus on the most practical one using NumPy.

Read the first section, 'Slide 1: The Sigmoid Function: An Introduction'. It contains a concise Python function that both implements the sigmoid formula and visualizes it. Pay close attention to the use of np.exp() and np.linspace().

Let's break down the implementation and visualization code from the resource.

Step 1: The Implementation

Create a new Python script or Jupyter Notebook. First, import the necessary libraries:

import numpy as np
import matplotlib.pyplot as plt

Now, define the sigmoid function. Using np.exp() allows this function to work on both single numbers and entire NumPy arrays (a concept known as vectorization). This is far more efficient than looping through an array in Python.

def sigmoid(z):
  """
  Computes the sigmoid of z.
  
  Args:
    z (float or np.ndarray): The input value(s).
    
  Returns:
    (float or np.ndarray): The sigmoid of z.
  """
  return 1 / (1 + np.exp(-z))

You can test it with a few values:

print(f"Sigmoid of 100: {sigmoid(100)}")   # Should be very close to 1.0
print(f"Sigmoid of 0: {sigmoid(0)}")     # Should be exactly 0.5
print(f"Sigmoid of -100: {sigmoid(-100)}") # Should be very close to 0.0

# Test with a NumPy array
test_array = np.array([-10, -2, 0, 2, 10])
print(f"Sigmoid on an array: {sigmoid(test_array)}")

Step 2: The Visualization

Seeing the function's curve makes its behavior intuitive. We'll generate a range of input values and plot their corresponding sigmoid outputs.

# 1. Generate a range of input values
z_values = np.linspace(-10, 10, 200) # 200 points from -10 to 10

# 2. Compute the sigmoid for these values
sigmoid_values = sigmoid(z_values)

# 3. Plot the results
plt.figure(figsize=(10, 6))
plt.plot(z_values, sigmoid_values, label='Sigmoid Function')
plt.title('The Sigmoid Function')
plt.xlabel('z (Input)')
plt.ylabel('sigmoid(z) (Output Probability)')
plt.grid(True)
plt.axhline(y=0.5, color='r', linestyle='--', label='y = 0.5')
plt.axvline(x=0, color='r', linestyle='--')
plt.legend()
plt.show()

When you run this code, you will see the classic 'S'-shaped curve. Notice how it's flat for large positive and negative inputs (this is called saturation) and steepest around . This steep region is where the model is most "uncertain" and small changes in the input lead to large changes in the output probability.

Test your understanding!

In your custom_sigmoid implementation from the GitHub resource (LINK), the author uses a trick:

def custom_sigmoid(x):
    if x >= 0:
        z = math.exp(-x)
        return 1 / (1 + z)
    else:
        z = math.exp(x)
        return z / (1 + z)

Mathematically, is equivalent to . Why would the author implement it differently for negative values of x? (Hint: Think about what happens to exp(-x) if x is a very large negative number, like -1000).

Show answer

This is a trick to improve numerical stability. If x is -1000, then -x is 1000. math.exp(1000) is an astronomically large number that would cause a OverflowError in Python. By switching to the equivalent formula for negative x, we compute math.exp(-1000), which is a very small number close to zero, avoiding the overflow entirely. This is a great example of how practical implementation can differ slightly from the pure mathematical formula to handle the limitations of floating-point arithmetic.


Conclusion

Congratulations on implementing your first component of a classification algorithm! The sigmoid function is a simple but powerful tool that forms the bridge between linear models and probabilistic classification.

Key Takeaways:

  • The sigmoid function maps any real-valued input to an output between 0 and 1.
  • Its mathematical formula is .
  • In classification, it's used to convert a model's raw output into a probability, which is essential for making decisions.
  • Using NumPy allows for an efficient, vectorized implementation that can process entire arrays of data at once.

Preview of the Next Lesson:
You now have a function that can take a linear score, , and produce a predicted probability, . The next logical question is: how do we find the best parameters ()? To do that, we need a way to measure how "wrong" our predictions are. In the next lesson, you will derive and calculate the gradient of the binary cross-entropy loss function, which is the cost function used to train logistic regression.