Support Vector Machines & The Kernel Trick
Derive geometric margins, the primal and dual quadratic programs, KKT complementary slackness, the Mercer kernel condition, and zero-dependency implementations in Python and Rust.
Support Vector Machines (SVMs) represent one of the most mathematically rigorous algorithms in statistical learning theory. Grounded in Vapnik-Chervonenkis (VC) Dimension bounds, SVMs seek the unique decision boundary that maximizes the geometric margin separating two classes, minimizing structural risk rather than empirical risk alone.
The optimal separating hyperplane maximizes the margin width while maintaining strict constraint boundaries.
1. Geometric Margin and Primal Formulation#
Consider a linearly separable binary dataset with feature vectors and binary polar labels .
A separating affine hyperplane is defined by:
Derivation of the Margin Width#
Let be any point in space. The signed Euclidean distance from to the hyperplane is computed by orthogonal projection along unit normal vector :
For classified samples with labels , the geometric margin is:
Step 1: Maximize the Minimum Margin
We seek parameters that maximize the distance to the closest training sample:
Step 2: Fix the Functional Margin Scale
Because scaling both and by any arbitrary constant does not change the hyperplane , we can fix the functional margin of the closest point to :
This introduces the constraint for all .
Step 3: Formalize the Objective
With the numerator set to , maximizing the geometric margin is equivalent to minimizing its reciprocal .
For mathematical convenience in differentiation, we minimize :
2. Soft-Margin SVM & Slack Variables#
Real-world datasets contain label noise and overlapping distributions where no linear hyperplane can achieve 100% clean separation.
To tolerate controlled margin violations, Cortes and Vapnik introduced non-negative slack variables :
- If : The point is correctly classified outside or on the margin.
- If : The point violates the margin but remains on the correct side of the decision boundary.
- If : The point is misclassified.
- Hyperparameter : Controls the penalty for margin violations. Large enforces hard margins (high variance), while small tolerates errors to widen the margin (high bias).
3. The Dual Problem and KKT Optimality Conditions#
To solve this constrained optimization problem, we construct the Lagrangian function with dual multipliers and :
First-Order Karush-Kuhn-Tucker (KKT) Stationarity#
Setting partial derivatives with respect to primal variables to zero:
- With respect to :
- With respect to :
- With respect to :
Substituting into the Lagrangian (The Wolfe Dual)#
Substituting these stationarity relations back into eliminates primal variables , , and :
4. The Kernel Trick: Infinite Dimensional Feature Spaces#
Observe that the dual objective and inference decision rule depend solely on the inner product between feature vectors:
If data is non-linear in input space , we map it via non-linear feature expansion into a higher-dimensional Hilbert space where linear separation is achievable.
By Mercer's Theorem, any symmetric positive semi-definite function computes this inner product directly without ever explicitly materializing high-dimensional vector :
Mathematical Proof: RBF Kernel as Infinite Dimensions#
The Radial Basis Function (Gaussian) kernel is:
Applying the Taylor series expansion of :
Because is the polynomial kernel generating all polynomial terms up to degree , the infinite series summation proves that the RBF kernel maps inputs into an infinite-dimensional reproducing kernel Hilbert space (RKHS).
5. From-Scratch Implementation: Zero Dependencies#
The following implementations solve Linear SVM using subgradient descent on the regularized Hinge Loss:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253from typing import List class LinearSVMFromScratch: def __init__(self, learning_rate: float = 0.001, lambda_param: float = 0.01, num_epochs: int = 1000): self.learning_rate = learning_rate self.lambda_param = lambda_param self.num_epochs = num_epochs self.weights: List[float] = [] self.bias: float = 0.0 def fit(self, X: List[List[float]], y: List[int]) -> None: # Expect labels in {-1, +1} num_samples = len(X) num_features = len(X[0]) self.weights = [0.0] * num_features self.bias = 0.0 for _ in range(self.num_epochs): for i in range(num_samples): # Functional margin: y_i * (w^T x_i + b) linear_output = sum(self.weights[j] * X[i][j] for j in range(num_features)) + self.bias condition = y[i] * linear_output >= 1.0 if condition: # Only regularization gradient for j in range(num_features): self.weights[j] -= self.learning_rate * (self.lambda_param * self.weights[j]) else: # Regularization + Hinge loss subgradient for j in range(num_features): self.weights[j] -= self.learning_rate * ( self.lambda_param * self.weights[j] - y[i] * X[i][j] ) self.bias += self.learning_rate * y[i] def predict(self, X: List[List[float]]) -> List[int]: num_features = len(self.weights) predictions = [] for row in X: score = sum(self.weights[j] * row[j] for j in range(num_features)) + self.bias predictions.append(1 if score >= 0.0 else -1) return predictions if __name__ == "__main__": X_train = [[1.0, 2.0], [2.0, 3.0], [2.0, 1.0], [6.0, 7.0], [7.0, 8.0], [8.0, 6.0]] y_train = [-1, -1, -1, 1, 1, 1] svm = LinearSVMFromScratch(learning_rate=0.01, lambda_param=0.01, num_epochs=500) svm.fit(X_train, y_train) preds = svm.predict(X_train) print(f"Learned weights: {svm.weights}") print(f"Learned bias: {svm.bias:.4f}") print(f"Predictions: {preds}")
6. Active Knowledge Checks#
In the dual formulation of Support Vector Machines, what happens to training samples that lie strictly on the correct side of the margin boundary with margin > 1?
Why can the Radial Basis Function (RBF) Gaussian kernel separate datasets that are non-linearly separable in any finite-dimensional polynomial feature space?