Skip to content

Logistic Regression

Binary classification - sigmoid, cross-entropy, decision boundaries


One-Sentence Summary

Logistic regression models the probability of a binary outcome using a linear combination of features passed through a sigmoid function.


Core Formulation

Model

P(y=1|x)=σ(wx+b)=11+e(wx+b)

The sigmoid function maps any real number to (0,1):

σ(z)=11+ez

Sigmoid Function

Why Sigmoid?

PropertyBenefit
Maps R(0,1)Interpretable as probability
S-shaped curveCaptures threshold behavior
Nice derivative: σ(z)=σ(z)(1σ(z))Efficient gradient computation

Decision Boundary

  • Predict y=1 if P(y=1|x)>0.5, i.e., wx+b>0
  • The decision boundary is the hyperplane wx+b=0

Decision Boundary


Loss Function

Why Not MSE?

MSE loss with sigmoid creates a non-convex optimization problem with many local minima.

Cross-Entropy Loss (Log Loss)

L(w)=1ni=1n[yilog(y^i)+(1yi)log(1y^i)]

where y^i=σ(wxi+b)

Cross-Entropy Loss

Intuition:

ScenarioLoss
y=1, y^10 (correct, confident)
y=1, y^0 (wrong, confident)
y=0, y^00 (correct, confident)
y=0, y^1 (wrong, confident)

Maximum Likelihood Interpretation

Cross-entropy equals negative log-likelihood under Bernoulli distribution:

P(y|x)=y^y(1y^)1ylogL=ylog(y^)+(1y)log(1y^)

Minimizing cross-entropy = Maximizing likelihood


Optimization

Gradient Descent

Lw=1ni=1n(y^iyi)xi

Remarkably similar to linear regression gradient!

python
def logistic_regression_gd(X, y, lr=0.01, n_iters=1000):
    n, d = X.shape
    w = np.zeros(d)
    b = 0

    for _ in range(n_iters):
        z = X @ w + b
        y_pred = 1 / (1 + np.exp(-z))

        dw = (1/n) * X.T @ (y_pred - y)
        db = (1/n) * np.sum(y_pred - y)

        w -= lr * dw
        b -= lr * db

    return w, b

Newton's Method

For faster convergence, use second-order optimization:

wnew=wH1L
  • Pros: Converges in fewer iterations
  • Cons: Each iteration is O(d3) due to Hessian inversion

Regularization

TypeFormulaEffect
L2 (Ridge)L+λ|w|22Prevents large weights
L1 (Lasso)L+λ|w|1Produces sparse weights (feature selection)

sklearn's C Parameter

sklearn uses C=1/λ:

  • Large C: Less regularization, more complex model
  • Small C: More regularization, simpler model

Multiclass Extension

One-vs-Rest (OvR)

Train k binary classifiers:

  • Classifier 1: Class 1 vs rest
  • Classifier 2: Class 2 vs rest
  • ...

Prediction: Class with highest probability

Softmax Regression (Multinomial)

Generalizes sigmoid to k classes:

P(y=j|x)=ewjxk=1Kewkx

Loss: Categorical cross-entropy

L=i=1nj=1kyijlogP(y=j|xi)

Key Properties

Assumptions

AssumptionDescription
Linearity in log-oddslogp1p=wx is linear
IndependenceObservations are independent
No multicollinearityFeatures not perfectly correlated
Large sample sizeMLE needs sufficient data

Strengths vs Weaknesses

StrengthsWeaknesses
Probabilistic output (confidence scores)Linear decision boundary only
Interpretable coefficients (odds ratios)Requires feature engineering for non-linearity
Efficient: O(nd) per iterationSensitive to outliers
Built-in regularizationClass imbalance issues
No distributional assumptions on X

Coefficient Interpretation

Log-Odds

logp1p=w0+w1x1+w2x2+

wj = change in log-odds for one-unit increase in xj

Odds Ratio

Odds Ratio for xj=ewj
Odds RatioInterpretation
>1Feature increases odds of positive class
<1Feature decreases odds
=1No effect

Example: If w1=0.5 for "years_experience":

  • Odds ratio =e0.51.65
  • "Each additional year increases odds of success by 65%"

Interview Questions

Q1: Why logistic over linear regression for classification?

Key points:

  1. Bounded output - Linear regression can predict outside [0,1]
  2. Probabilistic interpretation - Calibrated probabilities for ranking/thresholding
  3. Proper loss function - Cross-entropy correctly penalizes confident mistakes

Q2: How to handle class imbalance?

ApproachDescription
Class weightsUpweight minority class (class_weight='balanced')
Threshold adjustmentDon't use 0.5; tune based on precision-recall
ResamplingSMOTE, undersampling (after train/test split!)
Different metricsUse precision, recall, F1, PR-AUC instead of accuracy

Q3: Non-linear relationships?

Options (in order of complexity):

  1. Feature engineering (polynomial, interaction terms)
  2. Generalized additive models (GAMs)
  3. Non-linear models (trees, neural networks)

Q4: Sigmoid vs Softmax?

SigmoidSoftmax
Single value (0,1)Vector of k values k probabilities summing to 1
Binary classificationMulticlass classification
σ(z)softmax([z1,,zk])j=ezjkezk

For binary: softmax([z,0]) = [σ(z),1σ(z)]


Code Reference

python
import numpy as np

class LogisticRegression:
    def __init__(self, lr=0.01, n_iters=1000, regularization='l2', lambda_=0.1):
        self.lr = lr
        self.n_iters = n_iters
        self.regularization = regularization
        self.lambda_ = lambda_
        self.weights = None
        self.bias = None

    def sigmoid(self, z):
        return 1 / (1 + np.exp(-np.clip(z, -500, 500)))

    def fit(self, X, y):
        n, d = X.shape
        self.weights = np.zeros(d)
        self.bias = 0

        for _ in range(self.n_iters):
            z = X @ self.weights + self.bias
            y_pred = self.sigmoid(z)

            # Gradients
            dw = (1/n) * X.T @ (y_pred - y)
            db = (1/n) * np.sum(y_pred - y)

            # Regularization
            if self.regularization == 'l2':
                dw += (self.lambda_ / n) * self.weights
            elif self.regularization == 'l1':
                dw += (self.lambda_ / n) * np.sign(self.weights)

            # Update
            self.weights -= self.lr * dw
            self.bias -= self.lr * db

    def predict_proba(self, X):
        return self.sigmoid(X @ self.weights + self.bias)

    def predict(self, X, threshold=0.5):
        return (self.predict_proba(X) >= threshold).astype(int)


class SoftmaxRegression:
    """Multiclass logistic regression."""
    def __init__(self, lr=0.01, n_iters=1000):
        self.lr = lr
        self.n_iters = n_iters
        self.weights = None

    def softmax(self, z):
        exp_z = np.exp(z - np.max(z, axis=1, keepdims=True))
        return exp_z / np.sum(exp_z, axis=1, keepdims=True)

    def fit(self, X, y):
        n, d = X.shape
        k = len(np.unique(y))
        y_onehot = np.zeros((n, k))
        y_onehot[np.arange(n), y] = 1
        self.weights = np.zeros((d, k))

        for _ in range(self.n_iters):
            y_pred = self.softmax(X @ self.weights)
            self.weights -= self.lr * (1/n) * X.T @ (y_pred - y_onehot)

    def predict(self, X):
        return np.argmax(self.softmax(X @ self.weights), axis=1)

Quick Reference Card

LOGISTIC REGRESSION
-----------------------------------------------------
Model:    P(y=1|x) = sigma(w.x + b)
Sigmoid:  sigma(z) = 1/(1 + e^(-z))
Loss:     Cross-entropy = -[y log(y_hat) + (1-y) log(1-y_hat)]
Gradient: dL/dw = (1/n) sum (y_hat - y)x

DECISION BOUNDARY
-----------------------------------------------------
Linear: w.x + b = 0
Predict y=1 if w.x + b > 0

MULTICLASS
-----------------------------------------------------
OvR:     Train k binary classifiers
Softmax: P(y=j|x) = e^(w_j.x) / sum_k e^(w_k.x)

INTERPRETATION
-----------------------------------------------------
w_j = change in log-odds per unit increase in x_j
e^(w_j) = odds ratio for x_j

USE WHEN
-----------------------------------------------------
- Binary/multiclass classification
- Need probability estimates
- Want interpretable coefficients
- Linear decision boundary is sufficient