Key ideas
The model1. Despite its name, logistic regression is a classifier4: it predicts a category (ripe or not, spam or not) from a linear score . The sigmoid turns the score into a probability strictly between 0 and 1. Solving for gives : the linear part is the log-odds (logit)1. Useful values: and . Pick it over linear regression because the target is a category, not because the features correlate weakly with it.
Odds and coefficients5. The odds are . One more unit of adds to , so it multiplies the odds by — the odds ratio; the change in itself depends on where you start. With an interaction term , one more unit of adds to the log-odds. Statistically it is a generalized linear model: Bernoulli response, linear predictor, logit link5.
Decisions. Predict class 1 when . Since is increasing, that is the same as , so the decision boundary is a hyperplane (a line for 2 features) — a linear classifier, even though is a nonlinear function of . Changing shifts the boundary in parallel and changes decisions, not probabilities. Points near the boundary get ; points far away get confident probabilities that can still be wrong. When a missed positive costs and a false alarm , the expected cost is lowest if you predict positive whenever — far below 0.5 when misses are expensive.
Training. Maximum likelihood means minimizing the log loss (binary cross-entropy)2. There is no closed-form solution5, but the log loss is the convex logistic loss of the margin6, and is linear in , so the objective is convex in the weights: gradient descent reaches the global minimum from any start — no bad local minima. The gradient per example is : prediction error times input. With the squared error the gradient is ; the extra factor nearly vanishes exactly when the model is confidently wrong (at , the gradient is about 75 times smaller than with log loss).
Regularization4. scikit-learn's LogisticRegression is regularized by default, and C is the inverse strength: smaller C → stronger penalty, smaller weights, simpler model; a very large C means effectively none. An penalty shrinks weights; an penalty sets some exactly to zero4. On linearly separable data the unpenalized loss keeps falling as the weights grow, so some penalty (or early stopping) keeps them finite.
More than two classes2. Softmax regression (multinomial logistic regression4) computes one score per class and normalizes with the softmax. The softmax keeps the order of the logits, so the predicted class is . The loss is the cross-entropy , and its gradient with respect to the logits is simply 2. With two classes, softmax over equals : binary logistic regression on the score difference. The ratio of two class probabilities, , does not depend on the other classes. Dividing logits by a temperature flattens the distribution (higher entropy) for and sharpens it toward the arg max as . Alternatives are one-vs-rest binary models, and ordinal logistic regression for ordered categories5.
Numerics3. overflows for large logits (above about 88.7 in float32, about 11.09 in float16), and inf / inf is nan. Subtracting the largest logit leaves the softmax unchanged and prevents overflow; combining softmax and log into one log-sum-exp step avoids as well. That is why framework cross-entropy losses (e.g. PyTorch's F.cross_entropy) take raw logits and apply log-softmax themselves3.
Formulas
Threshold on the logit scale: gives . Log-sum-exp: — it shifts by ; only the softmax is shift-invariant.
Worked example
, , :
- , so and the odds are .
- One more unit of : , , odds . The odds were multiplied by 2.23; the probability rose by only 0.17.
- With threshold 0.8 () neither input is flagged.
- If the label is 1, the log loss is and the gradient is .
Softmax of the logits is about . If the true class is the second one, the loss is and the gradient is : the true logit is pushed up, the others down.
In code
import numpy as np
def softmax(o):
e = np.exp(o - o.max()) # shift by the largest logit: same result
return e / e.sum()
o = np.array([3.0, 1.0, 0.0]) # logits
print(softmax(o).round(4)) # [0.8438 0.1142 0.042 ]
print(softmax(o + 1000).round(4)) # [0.8438 0.1142 0.042 ] shift-invariant
with np.errstate(over="ignore", invalid="ignore"):
print(np.exp(o + 1000) / np.exp(o + 1000).sum()) # [nan nan nan] naive overflow
lse = o.max() + np.log(np.exp(o - o.max()).sum()) # stable log-sum-exp
print(round(lse - o[1], 4)) # 2.1698 = cross-entropy, true class = 2nd
print((softmax(o) - [0, 1, 0]).round(4)) # [ 0.8438 -0.8858 0.042 ] = gradient wrt o
Common pitfalls
- is a log-odds, not a probability; apply the sigmoid. The sigmoid never outputs exactly 0 or 1.
- A weight multiplies the odds by ; it does not add to or multiply the probability.
- Moving the threshold changes decisions, not predicted probabilities, and the boundary stays a straight line (shifted in parallel). 0.5 is a poor threshold when the two errors cost very differently.
- Smaller
Cmeans stronger regularization — the opposite direction from ridge'salpha. - Passing softmax probabilities to a loss that expects logits (PyTorch's
F.cross_entropy) applies the softmax twice and cripples training. - A naive
expsoftmax overflows tonanfor large logits; subtract the maximum first. - The log loss of logistic regression is convex: no spurious local optima, but also no closed-form solution.
- A two-class softmax is not a different model: it is the sigmoid of the score difference.
References
- Machine Learning Crash Course — Logistic regression: Calculating a probability with the sigmoid function
- Dive into Deep Learning — Sec. 4.1 Softmax Regression (4.1.1.2 The Softmax, 4.1.2 Loss Function, 4.1.3 Information Theory Basics)
- Dive into Deep Learning — Sec. 4.5.2 Softmax Revisited
- scikit-learn User Guide — Sec. 1.1.11 Logistic regression (intro notes on regularization and GLM; 1.1.11.1 Binary Case; 1.1.11.2 Multinomial Case)
- Wikipedia: Logistic regression — Background (Definition of the odds, The odds ratio); Interpretations (As a generalized linear model); Model fitting; Extensions
- Wikipedia: Loss functions for classification — Logistic loss