The sigmoid and logistic regression

Squash a straight line through the S-shaped sigmoid and you get logistic regression, a model whose output is a probability. Learn how w and b move the curve.

Lesson · 15 min

In this lesson you will

  • Write the sigmoid function and describe its shape and limits
  • Build the logistic regression model f(x) = g(w·x + b) from a linear part and the sigmoid
  • Read the model's output as the probability that y = 1
  • Explain how w and b change the steepness and position of the curve

The last lesson ended with a wish list: an output that stays between 0 and 1, flattens out far from the boundary and still has a simple boundary to learn. One small function delivers all of it.

The sigmoid function

The sigmoid (also called the logistic function) is

g(z)=11+e−zg(z) = \frac{1}{1 + e^{-z}}

where e≈2.718e \approx 2.718 is the base of natural logarithms you met in Math for ML, section 1. It takes any real number zz and squashes it into the range between 0 and 1. Walk through a few values:

  • Large positive zz, say z=10z = 10: e−10e^{-10} is tiny (about 0.000045), so g(z)≈1/1=1g(z) \approx 1/1 = 1.
  • Large negative zz, say z=−10z = -10: e10e^{10} is huge (about 22,000), so g(z)≈1/22,000≈0g(z) \approx 1/22{,}000 \approx 0.
  • z=0z = 0: e0=1e^{0} = 1, so g(0)=1/2g(0) = 1/2. The midpoint.

So the graph is an S: flat near 0 on the far left, rising steeply through 0.5 at z=0z = 0, flattening near 1 on the far right. It never actually reaches 0 or 1. It is also symmetric: g(−z)=1−g(z)g(-z) = 1 - g(z).

NumPy’s np.exp works on whole arrays, so the sigmoid is one line:

import numpy as np

def sigmoid(z):
    return 1 / (1 + np.exp(-z))

z = np.linspace(-8, 8, 161)
print("g(-10), g(0), g(10) =", sigmoid(np.array([-10.0, 0.0, 10.0])).round(5))
bpl.plot(z, sigmoid(z), kind="line", title="The sigmoid g(z)")

Logistic regression: a line, then a squash

Logistic regression feeds a linear model into the sigmoid. First compute the same quantity as linear regression,

z=w⃗⋅x⃗+b,z = \vec{w} \cdot \vec{x} + b,

using the vector notation from Section 2 (with one feature it’s just z=wx+bz = wx + b). Then squash it:

fw⃗,b(x⃗)=g(w⃗⋅x⃗+b)=11+e−(w⃗⋅x⃗+b)f_{\vec{w},b}(\vec{x}) = g(\vec{w} \cdot \vec{x} + b) = \frac{1}{1 + e^{-(\vec{w} \cdot \vec{x} + b)}}

The output is always between 0 and 1, and we read it as a probability:

fw⃗,b(x⃗)=P(y=1∣x⃗;w⃗,b)f_{\vec{w},b}(\vec{x}) = P(y = 1 \mid \vec{x}; \vec{w}, b)

“The probability that yy is 1, given the features x⃗\vec{x}, for these parameters.” If the model says 0.83 for a tumour, it estimates an 83 % chance it’s malignant, and therefore a 17 % chance it’s benign. You met conditional probability in Math for ML, section 4; this is that idea put to work.

To turn the probability into a yes-or-no answer, apply a threshold, usually 0.5: predict y^=1\hat{y} = 1 when f≥0.5f \ge 0.5 and y^=0\hat{y} = 0 otherwise.

Explore the S-curve

Here are the same tumours, now with a sigmoid model. Move ww and bb and watch the curve. Switch on Show g(z) panel to see the two stages separately: the linear part turns each tumour size into a zz, and the sigmoid turns zz into a probability.

Loading the interactive graphic…

This is the sigmoid on its own. Whatever number goes in, a value between zero and one comes out.

Three things to notice:

  • ww sets the steepness. A large ww makes the S sharp, so the model jumps from “surely benign” to “surely malignant” over a few millimetres. A small ww makes it gentle and hesitant. A negative ww flips it, so larger tumours would look less likely to be malignant.
  • bb slides the curve sideways. The curve crosses 0.5 where z=0z = 0, that is where wx+b=0wx + b = 0, at x=−b/wx = -b/w. With w=2w = 2 and b=−8b = -8 that’s x=4x = 4 cm.
  • Far points no longer matter much. Tick Add very large tumours. The sigmoid is already almost exactly 1 out there, so these points are predicted correctly and the boundary stays put.

Your turn: a sigmoid that works on arrays

Machine learning code calls the sigmoid on whole arrays of zz values at once, one per example.

Exercise · Python

Write the sigmoid

Complete sigmoid(z) so it returns g(z)=11+e−zg(z) = \dfrac{1}{1 + e^{-z}}. It must work for a single number and for a NumPy array of any shape (element by element), so use np.exp, not math.exp.

import numpy as np

def sigmoid(z):
    # Return 1 / (1 + e^(-z)), element by element.
    return None  # replace this line

print(sigmoid(0))                         # should be 0.5
print(sigmoid(np.array([-2.0, 0.0, 2.0])))

Check yourself

Self-check · 4 questions

The sigmoid and logistic regression

1. What is g(0)g(0) for the sigmoid g(z)=11+e−zg(z) = \frac{1}{1 + e^{-z}}?
2. A logistic regression model outputs f(x⃗)=0.8f(\vec{x}) = 0.8 for an email. How should you read that?
3. With one feature, w=2w = 2 and b=−8b = -8, where does the curve cross 0.5?
4. What happens to the S-curve when you make ww larger while keeping the crossing point fixed?