chapter 02 / classical-ml · estimated study time 120-150 min

Classical Machine Learning Algorithms
Four Worldviews of Classification

AUDIO // Chapter Audio Guide
Chapter Contents
  1. From Regression to Classification: A Seemingly Tiny Change
  2. Logistic Regression: Sigmoid and the Probability View
  3. Cross-Entropy Loss: Full Derivation
  4. Softmax: Generalizing to Multiclass
  5. Decision Trees: Carving Up the World with Questions
  6. Interactive Lab: Decision-Boundary Showdown
  7. Ensemble Learning: Random Forests and Gradient Boosting
  8. SVM: Maximum Margin and the Kernel Trick
  9. Code in Action: Four Algorithms Compete Head-to-Head
  10. Chapter Quiz

From Regression to Classification: A Seemingly Tiny Change

Chapter 1 predicted a continuous value (house price). Now we switch to predicting a category: Is this email spam or not? Is this tumor benign or malignant? The label $y \in \{0, 1\}$.

Can we just use linear regression and call anything $>0.5$ class 1? In theory yes, in practice terribly: ① the output range is $(-\infty, +\infty)$, with no probability interpretation—what does "predicted value 3.7" even mean? ② MSE also penalizes points that are "too correct," far from the boundary; a single extreme but correctly classified sample can drag the decision boundary out of shape.

We need two new parts: a function that squashes the output into a probability, and a loss function that matches probabilities. These two parts—sigmoid/softmax and cross-entropy—carry all the way to GPT: the pretraining loss of an LLM is exactly softmax + cross-entropy, only the number of classes goes from 2 to about 100,000 (each token in the vocabulary is one class). The derivations in this chapter are the direct foundation for Chapter 7.

Logistic Regression: Sigmoid and the Probability View

Keep the linear skeleton $z = \mathbf{w}^\top\mathbf{x} + b$ (this $z$ has a name you'll use for a lifetime: the logit), then wrap a squashing function around it:

$$\sigma(z) = \frac{1}{1 + e^{-z}}, \qquad \hat{p} = \sigma(\mathbf{w}^\top\mathbf{x} + b) = P(y{=}1 \mid \mathbf{x})$$

The sigmoid monotonically squeezes any real number into $(0,1)$: at $z=0$ it outputs 0.5 (maximally uncertain), and the larger $z$ is, the closer to 1. Its derivative has an elegant form (derivation: apply the chain rule to $\sigma(z)=(1+e^{-z})^{-1}$ and simplify):

$$\sigma'(z) = \sigma(z)\,(1 - \sigma(z))$$
Why the sigmoid and not just any S-shaped function? Because it is derived backward from the "log-odds": assume that the log of the odds is linear, $\ln\frac{p}{1-p} = \mathbf{w}^\top\mathbf{x} + b$, and solving for $p$ gives exactly the sigmoid. So the true name of logistic regression is "log-odds regression"—it assumes a linear logit, and the decision boundary (where $p=0.5$, i.e. $z=0$) is therefore a straight line / hyperplane. Remember this; later in the lab you'll watch it struggle on the two-moons data.

Cross-Entropy Loss: Full Derivation

The loss function isn't picked out of thin air—it follows the maximum-likelihood route you learned in Chapter 1. A single sample $y \in \{0,1\}$ follows a Bernoulli distribution, and the two cases can be written together as a single expression:

$$P(y \mid \mathbf{x}) = \hat{p}^{\,y}\,(1-\hat{p})^{\,1-y}$$

The likelihood of the whole dataset is the product over all samples. Take the log (turning the product into a sum, without changing the optimum), then negate it to make it a "smaller is better" loss, and divide by $n$ to average:

$$L = -\frac{1}{n}\sum_{i=1}^n \Big[ y^{(i)} \ln \hat{p}^{(i)} + (1 - y^{(i)}) \ln (1 - \hat{p}^{(i)}) \Big]$$

This is Binary Cross-Entropy. A sanity check: if the truth is $y=1$, the loss is $-\ln\hat{p}$—as the prediction $\hat{p}\to 1$ the loss goes to 0, and as $\hat{p}\to 0$ the loss explodes to infinity. Unbounded penalty for confident mistakes—exactly the temperament a classification task wants.

Now for the gradient. Apply the chain rule with respect to $z$: $\frac{\partial L_i}{\partial z} = \frac{\partial L_i}{\partial \hat{p}}\cdot\frac{\partial \hat{p}}{\partial z}$. Substitute $\sigma' = \sigma(1-\sigma)$, and a string of seemingly complicated terms magically cancels (strongly recommend deriving this by hand once), leaving:

$$\frac{\partial L_i}{\partial z} = \hat{p}^{(i)} - y^{(i)} \qquad\Longrightarrow\qquad \nabla_\mathbf{w} L = \frac{1}{n}\sum_i \big(\hat{p}^{(i)} - y^{(i)}\big)\,\mathbf{x}^{(i)}$$
It's "error × input" again! Exactly the same shape as the gradient of linear regression in Chapter 1—and that's no coincidence. MSE + identity output, cross-entropy + sigmoid, and multiclass cross-entropy + softmax: the gradient of all three pairings is $(\hat{y}-y)\cdot x$. The unifying theory behind this is called "generalized linear models + canonical link function": when the loss and output function are paired correctly, the gradient always comes out as clean as if no nonlinearity were ever involved. This is also why deep learning frameworks always fuse sigmoid/softmax with cross-entropy into a single operator (numerical stability + gradient simplicity).
If you paired MSE with sigmoid for classification, what would go wrong with the gradient?
$\frac{\partial}{\partial z}\frac{1}{2}(\hat{p}-y)^2 = (\hat{p}-y)\cdot \sigma'(z) = (\hat{p}-y)\cdot\hat{p}(1-\hat{p})$. The extra factor $\hat{p}(1-\hat{p})$ goes to zero when $\hat{p}$ approaches 0 or 1—meaning the more confidently the model is wrong (e.g. $y=1$ but $\hat p \approx 0$), the closer the gradient is to zero, and it can barely learn. Cross-entropy cancels exactly this factor, so the more outrageous the error, the larger the gradient. This is the most vivid counterexample to "the loss function must match the output layer," and one of the reasons early neural networks trained so slowly.
VIDEO 01
Logistic Regression (clearly explained)
StatQuest with Josh Starmer 8:47
Viewing Guide
  • 01:20 Why fitting a straight line directly to 0/1 labels goes wrong—corresponds to §1 of this chapter.
  • 03:30 The S-shaped curve and its probability interpretation; note where he stresses "what's being fit is the log-odds."
  • 06:00 A summary of the similarities and differences with linear regression—test your grasp of "swap the loss, keep the skeleton."

Softmax: Generalizing to Multiclass

For $K$ classes, let the model output $K$ logits $z_1,\dots,z_K$, and normalize them into a probability distribution with softmax:

$$\hat{p}_k = \frac{e^{z_k}}{\sum_{j=1}^{K} e^{z_j}}, \qquad L = -\ln \hat{p}_{y}\ \text{(only penalize the probability of the correct class)}$$

A few details you must know in engineering practice:

Decision Trees: Carving Up the World with Questions

Switch to a completely different worldview: don't learn weights, learn a sequence of questions. "Area > 90㎡?" → Yes → "Age < 10 years?" → … Each internal node is one feature split, and each leaf is one prediction. It naturally handles nonlinearity, doesn't care about feature scale, and is human-readable.

How do we pick the "best question"?

Greedily pick the split that makes the child nodes purest. Two purity measures:

$$\text{Entropy: } H(S) = -\sum_k p_k \log_2 p_k \qquad\qquad \text{Gini: } G(S) = 1 - \sum_k p_k^2$$

Both are maximal when "all classes are evenly mixed" and 0 when "only one class remains." Information gain = the entropy of the parent node − the weighted average entropy of the child nodes; the CART algorithm uses Gini (no logarithm, so it computes faster), with results almost indistinguishable from entropy. The search over the split point $t$ is just this: for every candidate threshold of every feature, compute the weighted purity once and take the best—you'll see with your own eyes in the lab the rectangular patchwork boundary that this "axis-aligned splitting" draws.

The fatal weakness of a single tree is high variance (in the bias-variance language of Chapter 1): grown without limit, it can split the training set down to one sample per leaf—perfectly memorizing all the noise. Go to the lab and set the tree depth to 12 to see what the boundary looks like: tiny, fragmented rectangles with support of 1—that's the shape of overfitting. Ways to control it: limit depth / minimum samples per leaf (pre-pruning), cost-complexity pruning (post-pruning)—or, simply change your approach: plant a whole forest.
VIDEO 02
Decision and Classification Trees
StatQuest with Josh Starmer 18:08
Viewing Guide
  • 04:00 Computing Gini impurity step by step with real numbers—working through it once beats watching the formula ten times.
  • 10:30 How to choose split thresholds for numeric features (sweep the midpoints of adjacent values)—exactly the logic of the JS implementation in the lab.
  • 15:00 The problem of too few samples per leaf—the precursor to overfitting.

Interactive Lab: Decision-Boundary Showdown

On the same data, three algorithms see completely different worlds. Logistic regression can only draw straight lines; kNN's boundary follows the data but is jagged and mottled; the decision tree only draws horizontal-and-vertical rectangles. Required experiments: ① compare all three on the two-moons data; ② let logistic regression fail spectacularly on XOR data (the limit of linear models, and the reason neural networks make their entrance in Chapter 3); ③ set tree depth to 12 to see overfitting, dial it back to 2 to see underfitting; ④ compare kNN at k=1 vs k=15 (variance vs bias).

decision-boundary.compare

Click the canvas to place points · background color blocks = the model's predicted class for that region · training accuracy shown in real time

Training accuracy = Note: 100% training accuracy ≠ a good model (think back to the overfitting in Chapter 1)

Ensemble Learning: Random Forests and Gradient Boosting

Two scientific versions of "two heads are better than one," pointing in opposite directions:

Bagging → Random Forest: parallel, to reduce variance

Take $B$ bootstrap samples (sampling with replacement) from the training set, train one unpruned deep tree on each, and vote at prediction time. Averaging $B$ estimators each with variance $\sigma^2$ and pairwise correlation coefficient $\rho$, the variance becomes:

$$\rho\sigma^2 + \frac{1-\rho}{B}\sigma^2$$

The second term vanishes as $B$ grows, but the first term is stuck at $\rho\sigma^2$—the more correlated the trees are, the more limited the benefit of averaging. This is where the masterstroke of the random forest comes from: at each split, choose only from a randomly drawn subset of $\sqrt{d}$ features, forcing the trees to "think differently" from one another and pushing $\rho$ down. This is the mathematical expression of "diversity matters more than individual strength."

Boosting → Gradient Boosting: sequential, to reduce bias

Conversely, use a sequence of shallow trees (weak learners), each dedicated to correcting the residual errors of all the trees before it. The clever perspective at step $m$: treat the current model's prediction $F_{m-1}(x)$ as a "parameter," compute the negative gradient of the loss with respect to it $r_i = -\frac{\partial L(y_i, F)}{\partial F}\big|_{F_{m-1}}$, train a small tree to fit this negative gradient, then set $F_m = F_{m-1} + \eta \cdot \text{tree}_m$. Under MSE the negative gradient is exactly the residual $y - F(x)$, so the intuitive version of the story is "each tree learns the residual of the previous round"—but the negative-gradient view is why it's called "gradient boosting": it's doing gradient descent in function space.

XGBoost (2014) pushes this to its industrial extreme: a second-order Taylor expansion of the objective (Newton's method rather than plain gradient), L2 regularization on the leaf weights, automatically learning a default direction for missing values, histogram acceleration and parallelism. On small- to medium-scale tabular data, "XGBoost/LightGBM beats neural networks" remains a basic fact even in 2026—don't finish this course thinking everything is deep learning.

VIDEO 03
Gradient Boost Part 1: Regression Main Ideas
StatQuest with Josh Starmer 15:52
Viewing Guide
  • 03:00 Starting from a constant prediction (initial model $F_0$ = the mean).
  • 06:30 Fitting the residuals with a tree, multiplying by the learning rate and adding it on—corresponds to $F_m = F_{m-1} + \eta\cdot\text{tree}_m$.
  • 12:00 Why multiply by the learning rate (trust each step only a little)—the same philosophy as the η of gradient descent in Chapter 1.

SVM: Maximum Margin and the Kernel Trick

Separable data has infinitely many separating lines—which is best? SVM's answer: the one that is farthest from the nearest samples on both sides—maximize the margin. The geometric derivation: the distance from a point to the hyperplane $\mathbf{w}^\top\mathbf{x}+b=0$ is $\frac{|\mathbf{w}^\top\mathbf{x}+b|}{\|\mathbf{w}\|}$; fixing $|\mathbf{w}^\top\mathbf{x}+b|=1$ at the support vectors, the margin is $\frac{2}{\|\mathbf{w}\|}$. Maximizing the margin is equivalent to:

$$\min_{\mathbf{w},b}\ \frac{1}{2}\|\mathbf{w}\|^2 \quad \text{s.t.}\quad y^{(i)}(\mathbf{w}^\top\mathbf{x}^{(i)} + b) \ge 1,\ \forall i \qquad (y \in \{-1,+1\})$$

A convex quadratic program, with a global optimum. Real data is noisy, so introduce slack variables $\xi_i \ge 0$ to let a few points cross the boundary, with the cost controlled by the hyperparameter $C$ (soft margin, 1995). After converting it into the dual problem via Lagrange multipliers, two big things happen:

$$\max_{\alpha}\ \sum_i \alpha_i - \frac{1}{2}\sum_{i,j} \alpha_i \alpha_j y^{(i)} y^{(j)} \langle \mathbf{x}^{(i)}, \mathbf{x}^{(j)} \rangle$$
The kernel trick: replace the inner product with a kernel function $K(\mathbf{x}, \mathbf{x}')$, which is equivalent to first mapping the data into a high-dimensional space $\phi(\mathbf{x})$ and then running a linear SVM, yet never explicitly computing $\phi$. The RBF kernel $K(\mathbf{x},\mathbf{x}') = e^{-\gamma\|\mathbf{x}-\mathbf{x}'\|^2}$ corresponds to an infinite-dimensional feature space—drawing arbitrarily curved boundaries in the original space, at the cost of just computing an exponential. Logistic regression has no solution on the "concentric circles" data, while RBF-SVM handles it with ease (verify it for yourself in Code in Action). This idea of "raising the dimension to make the inseparable separable" reaches the same goal as deep learning's "learn a good representation space" by a different road—only that SVM's mapping is fixed, while a neural network's mapping is learned. This is the watershed between two eras.
VIDEO 04
Support Vector Machines Part 1 (main ideas)
StatQuest with Josh Starmer 20:32
Viewing Guide
  • 04:30 The maximum-margin classifier and its sensitivity to outliers—why we need the soft margin.
  • 12:00 One-dimensional inseparable data becoming linearly separable after being lifted to two dimensions—the geometric intuition of the kernel trick, the most important frame in the whole video.
  • 17:30 An intuitive comparison of the polynomial kernel and the RBF kernel.

Code in Action: Four Algorithms Compete Head-to-Head

python · classifiers_showdown.py
# Four worldviews face off on two datasets
import numpy as np
from sklearn.datasets import make_moons, make_circles
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.svm import SVC

datasets = {
    "moons":   make_moons(n_samples=500, noise=0.25, random_state=0),
    "circles": make_circles(n_samples=500, noise=0.1, factor=0.4, random_state=0),
}
models = {
    "LogReg(linear)": LogisticRegression(),
    "Tree(d=4)":      DecisionTreeClassifier(max_depth=4),
    "RandomForest":   RandomForestClassifier(n_estimators=200),
    "GradBoost":      GradientBoostingClassifier(),          # shallow trees in sequence
    "SVM-RBF":        SVC(kernel="rbf", C=1.0, gamma="scale"),
}
for dname, (X, y) in datasets.items():
    Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.3, random_state=0)
    print(f"\n== {dname} ==")
    for mname, m in models.items():
        acc = m.fit(Xtr, ytr).score(Xte, yte)     # Note: evaluate on the test set!
        print(f"  {mname:14s} {acc:.3f}")
# Expected: on moons/circles LogReg clearly lags (the limit of a linear boundary),
# while RBF-SVM and the ensemble methods are near perfect. Crank up noise and rerun—how does the ranking change?
When should you choose a tree model (XGBoost), and when a neural network?
The practical criterion in 2026 is still: tabular data (features are already engineered, semantic columns) → try gradient-boosted trees first; with sample sizes from a few thousand to a few million and feature dimensions from dozens to thousands, tree models are often more accurate, faster, and require less tuning. Perceptual data (images / audio / text—raw signals whose features must be learned) → deep learning is irreplaceable, because its essential advantage is representation learning (Chapters 3 and 4). For mixed scenarios (tabular + text columns), a common practice is to extract features with an LLM/embedding and feed them to XGBoost. This judgment comes up often in both interviews and practice.

Chapter Quiz