Separating Hyperplanes
Chapter 9 predicted a real number. Chapter 11 estimated a density. Chapter 12 predicts one of two labels — the last of the book’s four pillars — and it does so without any probability at all.
The set-up is three lines long. A predictor is a function
and the model is the affine function whose sign we take:
with and . The separating hyperplane is the set where that function vanishes:
The running example
Section titled “The running example”Eight points in , four of each class, chosen so that the answer the next five pages derive is exact and can be checked by hand:
import numpy as np
X = np.array([[3.0, 1.0], [3.0, -1.0], [6.0, 1.0], [6.0, -1.0],
[1.0, 0.0], [0.0, 1.0], [0.0, -1.0], [-1.0, 0.0]])
Y = np.array([ 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0])
N, D = X.shape # 8, 2
W_STAR = np.array([1.0, 0.0]) # the answer, derived on 1203
B_STAR = -2.0The two classes are separated by the vertical line . The nearest positive points are and the nearest negative point is , so the gap between the classes is exactly and the maximum margin is exactly . Under the scaling Section 12.2.2 adopts this makes and , with .
from scipy.optimize import minimize
margins = Y * (X @ W_STAR + B_STAR)
print(f"y_n(<w*,x_n> + b*) : {margins}")
print(f"min over n : {margins.min():.10f}")
print(f"||w*|| : {np.linalg.norm(W_STAR):.10f}")
print(f"support vectors : {list(np.where(np.isclose(margins, 1))[0])}")
# and solve (12.18)-(12.19) numerically, to be sure
res = minimize(lambda v: 0.5 * v[:2] @ v[:2], np.array([0.3, 0.3, 0.0]),
constraints=[{"type": "ineq",
"fun": lambda v: Y * (X @ v[:2] + v[2]) - 1}],
method="SLSQP", options={"maxiter": 2000, "ftol": 1e-14})
print(f"SLSQP w = {np.round(res.x[:2], 10)}, b = {res.x[2]:.10f}")
print(f"largest deviation : "
f"{max(np.abs(res.x[:2] - W_STAR).max(), abs(res.x[2] - B_STAR)):.3e}")y_n(<w*,x_n> + b*) : [1. 1. 4. 4. 1. 2. 2. 3.]
min over n : 1.0000000000
||w*|| : 1.0000000000
support vectors : [0, 1, 4]
SLSQP w = [ 1. -0.], b = -2.0000000000
largest deviation : 3.997e-15Three of the eight points sit exactly on the margin and five do not. That split is the whole subject of page 1206.
Why w is normal to the hyperplane
Section titled “Why w is normal to the hyperplane”The book’s argument is four lines and worth following, because it is the only place in the chapter where the geometry is proved rather than asserted. Take any two points , on the hyperplane. Then
and since both points are on the hyperplane, both values are zero, so . Every vector lying in the hyperplane is orthogonal to . The intercept never appears in the conclusion — it cancels in the first step — which is why alone fixes the orientation and alone fixes the position.
rng = np.random.default_rng(0)
worst = 0.0
for _ in range(200000):
t1, t2 = rng.normal(0, 50, 2) # two points on x = 2
p1, p2 = np.array([2.0, t1]), np.array([2.0, t2])
worst = max(worst, abs(W_STAR @ (p1 - p2)))
print(f"worst |<w, x_a - x_b>| over 200,000 random pairs : {worst:.3e}")worst |<w, x_a - x_b>| over 200,000 random pairs : 0.000e+00Exactly zero, not nearly zero — because the difference vector is and is , so the product of the only surviving term is .
A hyperplane is a set; (w, b) is not
Section titled “A hyperplane is a set; (w, b) is not”Equation 12.3 defines the classifier as a set, and a set does not determine the parameters that name it. Every positive multiple of gives the same set and the same predictions:
base = np.sign(X @ W_STAR + B_STAR)
print(f"{'c':>8} {'w':>16} {'b':>9} {'same labels?':>14}")
for c in (0.5, 1.0, 2.0, 17.0, 1000.0):
w2, b2 = c * W_STAR, c * B_STAR
same = bool((np.sign(X @ w2 + b2) == base).all())
print(f"{c:>8.1f} {str(np.round(w2, 1)):>16} {b2:>9.1f} {str(same):>14}") c w b same labels?
0.5 [0.5 0. ] -1.0 True
1.0 [1. 0.] -2.0 True
2.0 [2. 0.] -4.0 True
17.0 [17. 0.] -34.0 True
1000.0 [1000. 0.] -2000.0 TrueSo “the distance from a point to the hyperplane” has no meaning until a scale is fixed. The value can be made as large or as small as you like without moving anything. This one-parameter freedom is the “technical wrinkle” §12.2.1 opens with, and Sections 12.2.1 and 12.2.2 are two different ways of spending it: fix , or fix the value at the closest point to .
Equations 12.5, 12.6 and 12.7 are not quite equivalent
Section titled “Equations 12.5, 12.6 and 12.7 are not quite equivalent”The book writes the two conditions for correct classification
and then combines them:
saying “Equation (12.7) is equivalent to (12.5) and (12.6) when we multiply both sides of (12.5) and (12.6) with and , respectively.”
Multiplying an inequality by reverses it, so 12.6’s strict becomes a strict , and 12.7 should read for the negative class. As printed, the two are not the same condition:
z = np.array([2.0, 0.0]) # exactly ON the hyperplane
fz = W_STAR @ z + B_STAR
print(f"f(z) = {fz:.1f}, and suppose its label is y = -1")
print(f" 12.6 requires f(z) < 0 : {fz < 0}")
print(f" 12.7 requires y*f(z) >= 0 : {(-1.0) * fz >= 0}")f(z) = 0.0, and suppose its label is y = -1
12.6 requires f(z) < 0 : False
12.7 requires y*f(z) >= 0 : TrueEquation 12.7 admits a point that Equation 12.6 forbids. The disagreement is confined to the hyperplane itself, a set of measure zero, and it never matters again — from Equation 12.9 onwards every condition asks for a strictly positive margin, which excludes the boundary anyway. But the asymmetry in 12.5 and 12.6 is a real choice the book makes silently: a point sitting exactly on the decision boundary is classified , because is the test the text gives. Ties go to the positive class.
The measurement that motivates the whole chapter
Section titled “The measurement that motivates the whole chapter”Figure 12.3 shows several green lines, all separating the data, with the caption “there are many linear classifiers that separate orange crosses from blue discs.” That is the problem Section 12.2 exists to solve. Here is how bad it actually is.
Method. Sample pairs with ; keep the ones that separate all eight training points; score each survivor on held-out points drawn from two isotropic Gaussians centred at and . Equal isotropic covariance means the Bayes-optimal rule is the perpendicular bisector of the two means, — a known target to measure against.
MU_POS, MU_NEG, SD, M = np.array([4.5, 0.0]), np.array([0.0, 0.0]), 1.0, 200000
rt = np.random.default_rng(7)
XT = np.vstack([rt.normal(MU_POS, SD, (M // 2, 2)),
rt.normal(MU_NEG, SD, (M // 2, 2))])
YT = np.r_[np.ones(M // 2), -np.ones(M // 2)]
rg = np.random.default_rng(11)
accs, margins = [], []
for _ in range(400000):
th = rg.uniform(0, 2 * np.pi)
w, b = np.array([np.cos(th), np.sin(th)]), rg.uniform(-8, 8)
if not (Y * (X @ w + b) > 0).all(): # must separate the training set
continue
accs.append(float((np.sign(XT @ w + b) == YT).mean()))
margins.append(float((Y * (X @ w + b)).min()))
accs, margins = np.array(accs), np.array(margins)
print(f"separators found : {len(accs)} of 400,000")
print(f"held-out accuracy: worst {accs.min():.4f} median "
f"{np.median(accs):.4f} best {accs.max():.4f}")
print(f"correlation(margin, accuracy) : {np.corrcoef(margins, accs)[0,1]:.6f}")
print(f"{'margin bin':>14} {'count':>7} {'mean acc':>10} {'worst acc':>10}")
edges = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0]
for lo, hi in zip(edges[:-1], edges[1:]):
m = (margins >= lo) & (margins < hi)
print(f" [{lo:.1f}, {hi:.1f})".rjust(14) + f" {int(m.sum()):>7} "
f"{accs[m].mean():>10.4f} {accs[m].min():>10.4f}")separators found : 9628 of 400,000
held-out accuracy: worst 0.8757 median 0.9662 best 0.9877
correlation(margin, accuracy) : 0.590999
margin bin count mean acc worst acc
[0.0, 0.2) 2998 0.9381 0.8757
[0.2, 0.4) 2630 0.9544 0.9037
[0.4, 0.6) 2113 0.9685 0.9297
[0.6, 0.8) 1349 0.9779 0.9606
[0.8, 1.0) 538 0.9832 0.9777Nine thousand six hundred and twenty-eight classifiers, all with zero training error, and their held-out accuracy spans . Empirical risk on the training set cannot tell them apart — every one of them scores perfectly. Something else has to choose, and the table says what.
The margin bounds the worst case, not the best one. That distinction is worth being precise about, because it is easy to overclaim:
| claim | true? | measured |
|---|---|---|
| larger margin correlates with better held-out accuracy | yes | |
| the maximum-margin separator beats most others | yes | it beats of the |
| the maximum-margin separator is the best of them | no | best sampled vs its |
| the maximum-margin separator is Bayes-optimal | no | Bayes at , SVM at |
The best separator found scores — essentially the Bayes rate — at a margin of only . It got there by luck, and nothing on the training set could have identified it. The maximum-margin rule gives up against that lucky draw in exchange for never landing in the tail.
-
pch.quizShowAnswer
C — Every positive multiple of (w, b) names the same set and the same classifier, so the pair is determined only up to a positive scale
-
pch.quizShowAnswer
B — Because it cancels when subtracting f(x_b) from f(x_a) — which is why w alone fixes the hyperplane's orientation and b alone its position
-
pch.quizShowAnswer
C — On the hyperplane itself: 12.6 is strict, so multiplying by -1 should give a strict inequality, but 12.7 is written with >=. A point with f(z) = 0 and label -1 satisfies 12.7 and violates 12.6
-
pch.quizShowAnswer
C — A raised floor: the worst accuracy per margin bin climbs from 0.8757 to 0.9777 while the best barely moves. The max-margin solution beats 91.76 percent of them but is not the best
Exercises
Section titled “Exercises”Exercise 1 – Show that w is normal to the hyperplane
Section titled “Exercise 1 – Show that w is normal to the hyperplane”Exercise 2 – Rescale the parameters and find nothing changes
Section titled “Exercise 2 – Rescale the parameters and find nothing changes”Exercise 3 – Count the separators, and score them
Section titled “Exercise 3 – Count the separators, and score them”Recall card
Section titled “Recall card”- Chapter 12 is the last of the four pillars, classification, and it reasons geometrically rather than probabilistically — closer to Chapter 10’s derivation of PCA than to Chapter 9’s likelihood.
- The chapter prints no numbers at all. It has no worked examples, so every value on these pages is this module’s own, computed on an eight-point running example whose answer is exact.
- A hyperplane is the zero set of an affine function, and the classifier is the sign of that function.
- w is normal to the hyperplane, proved by subtracting the function’s value at two points on it. The intercept cancels in that subtraction, which is why w fixes the orientation and b fixes the position.
- The set does not determine the parameters. Every positive multiple of w and b names the same classifier, so the raw value of f carries no notion of distance until a scale is chosen.
- That freedom is spent twice in Section 12.2, once by demanding a unit-length w and once by demanding the value one at the closest point — and page 1203 shows the two agree.
- Equations 12.5 and 12.6 are not symmetric. One is inclusive and one is strict, so ties on the boundary go to the positive class, and the combined Equation 12.7 is very slightly weaker than the pair it replaces.
- Zero training error does not pick a classifier. 9,628 sampled hyperplanes separate the eight points perfectly and their held-out accuracy spans 0.1119.
- Margin and held-out accuracy correlate at 0.590999, and the maximum-margin solution beats 91.76 percent of those separators.
- But it is not the best of them, scoring 0.9853 against a best sampled 0.9877 and a Bayes rate of 0.9876. The margin raises the floor — the worst case per bin climbs from 0.8757 to 0.9777 — while the ceiling barely moves.
- So maximising the margin is insurance rather than optimisation, which is exactly what the generalisation bounds the book cites actually prove.
Next: The Concept of the Margin — §12.2.1, and the wrinkle that a distance needs a scale.
pch.coffeeTagline
pch.coffeeCtapch.feedbackHeading
pch.feedbackSubheading