Contents
, where is a score (logit), usually ; is read as a probability; , , and the derivative peaks at .
Deep Breakdown
What it tells me (Situation Awareness): The logistic sigmoid squashes any real number into the open interval along a smooth S-curve. Large negative scores map close to , large positive scores close to , and maps to exactly . Its inverse is the logit (log-odds), , so a linear model on the log-odds scale becomes a probability model on the scale: this is logistic regression. One unit of is one unit of log-odds; near the middle the curve is almost linear (slope , so in moves from to ), while beyond it is flat (). The derivative has the closed form , which makes gradients cheap: the forward-pass output is all that is needed. Paired with the log-loss, the gradient with respect to collapses to , which is why logistic regression and sigmoid output layers train cleanly. The same flatness that makes the output saturate also makes hidden layers built from sigmoids hard to train: each layer multiplies the backpropagated gradient by at most , so ten layers shrink it by at least a factor of . In DA/DS: the sigmoid is the output of every binary classifier that reports a probability (churn, fraud, conversion, default), the link function of the logistic GLM, the gate inside LSTM and GRU cells, and the reason modern hidden layers use ReLU instead.
Flowchart
Real-world use
Where it is used
Logistic regression (credit scoring, churn, conversion, click-through), the output unit of binary neural-network classifiers, multi-label outputs (one sigmoid per label), gates in LSTM/GRU recurrent cells, attention gating, Platt scaling for calibrating SVM or boosted-tree scores, and the logistic growth curve in demand and adoption modelling.
Problem it solves
A linear score can take any real value, but a probability must lie in . Clipping the score loses the gradient and gives no probabilistic meaning; the sigmoid maps the whole real line onto smoothly, monotonically and differentiably, and gives the score a direct interpretation as log-odds.
How it solves the problem
The model predicts log-odds linearly, , and the sigmoid inverts that relation to return . Because , the chain rule through the sigmoid needs no extra exponentials. With the binary cross-entropy loss , the factor cancels and : the gradient is the prediction error, bounded in . Each weight then has an odds-ratio reading: increasing feature by multiplies the odds by .
Example in a business
A subscription business scores churn risk from months since the last purchase: . A customer inactive for 3 months has and ; one more month of inactivity multiplies the odds by , giving . Retention offers go to customers above , so the threshold on months is where , i.e. months.
The formula, piece by piece
- : the input score, also called the logit or log-odds; any real number
- : positive for every ; very large for , close to for
- : always greater than , so lies strictly between and
- : zero log-odds means even odds
- : point symmetry around ; the probability of class is the sigmoid of the negated score
- : the logit, the inverse function
- : maximum at ; at ; at
- Relation to : , the same curve rescaled to
- Relation to softmax: for two classes with logits , returns
The formula in words
"Take the score, turn it into odds with , and convert the odds into a probability with ; the slope of that curve at any point is the probability times its complement." Dividing numerator and denominator of by gives , the same function. Tiny example: means odds , so and the slope is .
Critical Concepts
- Derivative from the output alone: , since . Backpropagation reuses the stored activation; no second exponential is evaluated.
- Sigmoid + log-loss is the matched pair: . With squared error instead, the gradient is , which goes to when the model is confidently wrong, so learning stalls. See Cross-Entropy Loss (Binary) and Negative Log-Likelihood.
- Saturation and vanishing gradients: , so a gradient passing through sigmoid layers is scaled by at most : for 5 layers, for 10. Hidden layers in deep networks use ReLU or GELU; the sigmoid stays at the output and in gates. See General Neural Network Layer Mapping and Reverse Mode Backpropagation.
- Not zero-centred: outputs are always positive, so all weight gradients of the next layer share a sign within a sample; (range ) is preferred when a sigmoid-shaped hidden activation is required.
- Numerical stability: the naive overflows for in float64. Stable implementations branch on the sign: for . Compute losses from logits ( via
log1p/log-sum-exp), never as of a probability that has already rounded to or . See Log-Sum-Exp Trick (prevents underflowoverflow) and Floating-Point Precision. - Probability, not certainty: is a calibrated probability only when the model is well specified and trained with log-loss; a threshold of is a default, not a rule; it should follow the cost of false positives versus false negatives.
Step-by-step calculation (tiny example)
Evaluate and at , then score one customer.
Step 1: Exponential .
Step 2: Sigmoid .
Step 3: Derivative from the output .
Step 4: Symmetry check , and as well: the derivative is an even function.
Step 5: Logistic regression unit With , , : , . The logit recovers the score: .
Code examples
Beginner example (Python)
import numpy as np
def sigmoid(z):
return 1 / (1 + np.exp(-z))
def sigmoid_grad(z):
s = sigmoid(z)
return s * (1 - s)
for z in [-4, -2, 0, 2, 4]:
print(f"z = {z:+d}: sigma = {sigmoid(z):.6f}, sigma' = {sigmoid_grad(z):.6f}")
# Symmetry: sigma(-z) = 1 - sigma(z)
print("\nsigma(-2) + sigma(2) =", sigmoid(-2) + sigmoid(2))
# Check the derivative against a finite difference
z, h = 2.0, 1e-6
numeric = (sigmoid(z + h) - sigmoid(z - h)) / (2 * h)
print(f"analytic sigma'(2) = {sigmoid_grad(z):.8f}, finite difference = {numeric:.8f}")
# Logistic regression unit: churn score from months since last purchase
w, b, x = 0.8, -2.0, 3.0
z = w * x + b
print(f"\nz = {w}*{x} + ({b}) = {z:.1f} -> P(churn) = {sigmoid(z):.4f}")
# Inverse: the logit recovers z from a probability
p = sigmoid(z)
print(f"logit(p) = ln(p / (1 - p)) = {np.log(p / (1 - p)):.4f}")Beginner example (R)
sigmoid <- function(z) 1 / (1 + exp(-z))
sigmoid_grad <- function(z) { s <- sigmoid(z); s * (1 - s) }
z <- c(-4, -2, 0, 2, 4)
print(data.frame(z = z, sigma = round(sigmoid(z), 6), grad = round(sigmoid_grad(z), 6)))
# Built-in equivalents: plogis() is the sigmoid, qlogis() is the logit
print(all.equal(sigmoid(z), plogis(z))) # TRUE
print(qlogis(sigmoid(0.4))) # 0.4
# Logistic regression: glm() with the logit link fits the sigmoid model
set.seed(1)
x <- rnorm(500)
y <- rbinom(500, 1, plogis(-0.5 + 1.5 * x))
fit <- glm(y ~ x, family = binomial(link = "logit"))
print(round(coef(fit), 3)) # close to -0.5 and 1.5Professional example (Python)
import numpy as np
from scipy.special import expit, log_expit
from sklearn.linear_model import LogisticRegression
rng = np.random.default_rng(42)
# --- Synthetic churn data: 2 standardised features, known true weights -------
n = 5_000
X = rng.normal(size=(n, 2))
true_w, true_b = np.array([1.5, -1.0]), -0.5
y = (rng.random(n) < expit(X @ true_w + true_b)).astype(float)
# --- Logistic regression by gradient descent on the mean log-loss -----------
# dL/dz = sigma(z) - y: the sigma(1 - sigma) factor cancels against the log-loss
w, b, lr = np.zeros(2), 0.0, 0.5
for step in range(2_000):
p = expit(X @ w + b)
w -= lr * X.T @ (p - y) / n
b -= lr * np.mean(p - y)
z = X @ w + b
loss = -np.mean(y * log_expit(z) + (1 - y) * log_expit(-z)) # stable: no log(0)
print(f"gradient descent : w = {np.round(w, 3)}, b = {b:.3f}, log-loss = {loss:.4f}")
sk = LogisticRegression(C=np.inf, max_iter=1_000).fit(X, y)
print(f"scikit-learn : w = {np.round(sk.coef_[0], 3)}, b = {sk.intercept_[0]:.3f}")
print(f"true parameters : w = {true_w}, b = {true_b}")
# --- Numerical stability: naive formula overflows for large negative z ------
z_big = np.array([-800.0, -30.0, 0.0, 30.0, 800.0])
print(f"\n{'z':>6} {'naive sigma':>12} {'expit':>12} {'log(naive)':>14} {'log_expit':>14}")
for zi in z_big:
with np.errstate(over="ignore", divide="ignore"):
nv = 1 / (1 + np.exp(-zi))
lnv = np.log(nv)
print(f"{zi:>6.0f} {nv:>12.4e} {expit(zi):>12.4e} {lnv:>14.6e} {log_expit(zi):>14.6e}")
print()
# --- Vanishing gradient: sigma'(z) <= 0.25, multiplied once per layer --------
for depth in (1, 5, 10, 20):
print(f"max gradient factor through {depth:>2} sigmoid layers: {0.25 ** depth:.2e}")Gradient descent on the log-loss, using only as the error signal, lands on , , matching scikit-learn's unpenalised fit to three decimals and the true , within sampling noise for 5,000 rows. At the naive formula evaluates , which overflows to infinity (the RuntimeWarning is suppressed here) and yields , whose logarithm is ; log_expit returns the exact . At , of the rounded probability gives against the exact : a 0.1 % error from cancellation near . The last block shows the vanishing-gradient bound: .
Source: domain knowledge