Logistic regression
Build logistic regression from log-odds, write its log-likelihood, and fit it with IRLS or gradient descent.
Linear regression returns a real number; classification needs a probability. Logistic regression keeps the linear predictor but maps it to via the logistic (sigmoid) function. The motivation is the exponential-family machinery from A2: the logistic is the natural inverse link of the Bernoulli.
The model
For binary labels ,
Equivalently, the log-odds are linear in the features:
This is the logit link. From the exponential-family lesson, the Bernoulli's natural parameter is the log-odds: the logistic does not just look natural, it falls out of the Bernoulli's algebra[1].
The link runs the whole real line into , and a few points fix the scale. A score of is the decision boundary, . A score of is already fairly confident (, ), and is nearly committed (). The coefficients act on the odds, not the probability: with the code block's , raising feature 1 by one unit adds to the score, which multiplies the odds by ; raising feature 2 by one unit subtracts , multiplying the odds by . The same coefficient moves the probability by very different amounts depending on where the score starts: near the boundary the sigmoid is steep, out in the tails it is flat.
The log-likelihood
Treat each example as a Bernoulli trial with success probability . The log-likelihood for one example is ; summing,
Equivalently, since simplifies via ,
The negative of is the cross-entropy loss.
Gradient and Hessian
The sigmoid has a clean derivative: . Direct computation gives
with . The Hessian is negative semidefinite, so the log-likelihood is concave: any stationary point is a global maximum. There is no closed form (the gradient equation is nonlinear in ), so fitting is iterative.
Newton's method as IRLS
Newton's method applied to maximizing :
Rewriting, define the working response . Each Newton step is then a weighted least-squares fit , hence the name iteratively reweighted least squares (IRLS)[2]. Quadratic local convergence, the same as Newton on any smooth convex objective.
For large or sparse features, plain gradient descent (or its variants from A3) is simpler and often faster per epoch.
Regularization
Adding L2 gives ridge logistic regression: . Adding L1 gives L1 logistic regression for sparse classifiers. Both are MAP under a Gaussian or Laplace prior on , and both are fit with the same gradient/proximal machinery as their regression cousins.
Check it on arrays
import numpy as np
rng = np.random.default_rng(0)
n, d = 1000, 4
X = np.column_stack([np.ones(n), rng.normal(size=(n, d - 1))])
beta_true = np.array([-0.5, 1.5, -1.0, 0.7])
p_true = 1 / (1 + np.exp(-X @ beta_true))
y = (rng.random(n) < p_true).astype(float)
def sigmoid(z): return 1 / (1 + np.exp(-z))
def fit_irls(X, y, n_steps=10):
beta = np.zeros(X.shape[1])
for _ in range(n_steps):
p = sigmoid(X @ beta)
W = p * (1 - p) + 1e-8
z = X @ beta + (y - p) / W
beta = np.linalg.solve(X.T @ (W[:, None] * X), X.T @ (W * z))
return beta
def fit_gd(X, y, lr=0.1, n_steps=2000):
beta = np.zeros(X.shape[1])
for _ in range(n_steps):
p = sigmoid(X @ beta)
beta += lr * X.T @ (y - p) / len(y)
return beta
beta_irls = fit_irls(X, y)
beta_gd = fit_gd(X, y)
print("IRLS beta:", np.round(beta_irls, 3))
print("GD beta:", np.round(beta_gd, 3))
print("true :", beta_true)
# Predictions
p_hat = sigmoid(X @ beta_irls)
acc = np.mean((p_hat > 0.5) == y)
print(f"accuracy: {acc:.3f}")
# Log-likelihood is concave: Hessian -X^T W X is NSD
W = p_hat * (1 - p_hat)
H = -X.T @ (W[:, None] * X)
print("Hessian eigvals all <= 0:", np.all(np.linalg.eigvalsh(H) <= 1e-10))IRLS converges in a handful of steps; gradient descent reaches the same solution more slowly; the Hessian's eigenvalues are nonpositive, confirming concavity.
Exercises
Exercise 1. A fitted model has (the first entry is the intercept). For a feature vector (the leading 1 is the intercept term), compute the linear score, the predicted probability , and the odds, by hand.
Solution
Step 1. The score is the dot product .
Step 2. Apply the sigmoid:
Step 3. The odds are , which equals , the exponentiated score. This is the log-odds reading: the score is the log of the odds, so the odds are .
Exercise 2. Derive the gradient of the log-likelihood, starting from and using .
Solution
Step 1. Differentiate one example's contribution with respect to . The first term has gradient . For the second term, with ,
using and the chain rule .
Step 2. Combine the two terms for example :
Step 3. Sum over examples. Writing the residuals as the vector and stacking the as rows of ,
The gradient is the design matrix applied to the prediction residuals, the same shape as the OLS gradient with replacing .
Exercise 3 (code). The log-likelihood is concave, so it has a unique maximum. Implement the negative log-likelihood and its gradient, then verify the gradient is correct against a finite-difference approximation at a random . Confirm the maximum absolute difference is below .
Solution
import numpy as np
rng = np.random.default_rng(0)
n, d = 200, 4
X = np.column_stack([np.ones(n), rng.normal(size=(n, d - 1))])
beta_true = np.array([-0.5, 1.5, -1.0, 0.7])
p_true = 1 / (1 + np.exp(-X @ beta_true))
y = (rng.random(n) < p_true).astype(float)
def sigmoid(z): return 1 / (1 + np.exp(-z))
def neg_log_lik(beta):
z = X @ beta
# log(1 + e^z) computed stably as max(z,0) + log(1 + e^{-|z|})
log1pexp = np.maximum(z, 0) + np.log1p(np.exp(-np.abs(z)))
return -np.sum(y * z - log1pexp)
def grad(beta):
return -X.T @ (y - sigmoid(X @ beta)) # negative of X^T(y - p)
beta0 = rng.normal(size=d)
g_analytic = grad(beta0)
# Central finite differences
eps = 1e-6
g_numeric = np.zeros(d)
for j in range(d):
step = np.zeros(d); step[j] = eps
g_numeric[j] = (neg_log_lik(beta0 + step) - neg_log_lik(beta0 - step)) / (2 * eps)
max_diff = np.abs(g_analytic - g_numeric).max()
assert max_diff < 1e-5
print("max |analytic - numeric| gradient:", round(float(max_diff), 9))The analytic gradient matches the finite-difference estimate to well under , confirming the derivation in Exercise 2 and the formula used inside IRLS.
The next lesson generalizes to multi-class with the softmax.
References
- [1]Hastie, T., Tibshirani, R., & Friedman, J. (2009). The Elements of Statistical Learning (2nd ed.), §4.4. Springer. doi:10.1007/978-0-387-84858-7. Logistic regression and its log-odds link.↩
- [2]McCullagh, P., & Nelder, J. A. (1989). Generalized Linear Models (2nd ed.), §4.4. Chapman & Hall. IRLS as the standard fitting algorithm.↩