SVM (Support Vector Machine): Support Vector Machine

SVM (Support Vector Machine) : Guide Complet — Machine à Vecteurs de Support

Support Vector Machine — Support Vector Machine

Summary

The Support Vector Machine (SVM), or machine à vecteurs de support, is one of the most powerful and elegant supervised classification algorithms in machine learning. Invented by Vladimir Vapnik and Alexey Chervonenkis in the 1960s, then popularized in the 1990s, SVM is based on a simple but mathematically profound principle: finding the hyperplane that separates two classes by maximizing the margin — the distance between the hyperplane and the closest points in each class. Unlike many other algorithms, SVM depends only on these critical points, called support vectors, making it robust and efficient even in high-dimensional spaces. Thanks to the kernel trick, it can also solve non-linearly separable problems by implicitly projecting the data into a higher-dimensional space.

Mathematical Principle

The Separating Hyperplane

At the heart of SVM lies the search for a separating hyperplane. In an n-dimensional space, this hyperplane is defined by the equation:

$$\mathbf{w} \cdot \mathbf{x} + b = 0$$

where w is the normal vector to the hyperplane (it determines the orientation), b is the bias (it determines the position), and x is a data point. The hyperplane divides the space into two half-spaces: on one side, all points are classified as belonging to class +1; on the other, they are classified as belonging to class −1. The decision function is written:

$$f(\mathbf{x}) = \text{sign}(\mathbf{w} \cdot \mathbf{x} + b)$$

The Margin

The key to SVM lies in the concept of the margin. The margin is the distance between the separating hyperplane and the closest data points from each class. Maximizing this margin is equivalent to maximizing classification confidence: the larger the margin, the more robust the classifier is when faced with new data.

Mathematically, the margin equals:

$$\text{margin} = \frac{2}{|\mathbf{w}|}$$

The correct classification constraints are written:

$$y_i(\mathbf{w} \cdot \mathbf{x}_i + b) \geq 1 \quad \forall i$$

where yᵢ ∈ {−1, +1} is the label of the i-th point.

Primal Optimization Problem

The SVM optimization problem consists of finding the values of w and b that minimize the norm of w while satisfying the classification constraints:

$$\min_{\mathbf{w}, b} \frac{1}{2} |\mathbf{w}|^2$$

$$\text{subject to: } y_i(\mathbf{w} \cdot \mathbf{x}_i + b) \geq 1 \quad \forall i$$

This formulation is a quadratic convex optimization problem with linear constraints. Solving it guarantees the existence of a unique global minimum.

Dual Formulation with Lagrange Multipliers

By introducing Lagrange multipliers αᵢ ≥ 0 for each constraint, we obtain the dual problem:

$$\max_{\alpha} \sum_{i=1}^{n} \alpha_i – \frac{1}{2} \sum_{i=1}^{n} \sum_{j=1}^{n} \alpha_i \alpha_j y_i y_j (\mathbf{x}_i \cdot \mathbf{x}_j)$$

$$\text{subject to: } \alpha_i \geq 0 \quad \text{and} \quad \sum_{i=1}^{n} \alpha_i y_i = 0$$

The points for which αᵢ > 0 are precisely the support vectors — the only points that influence the position of the hyperplane. All other points have αᵢ = 0 and have no impact on the final model. This sparsity property gives SVM its remarkable memory efficiency.

The Kernel Trick

When the data is not linearly separable in the original space, SVM uses the kernel trick. The idea is to project the data into a higher-dimensional (potentially infinite) space via a transformation function φ, then search for a separating hyperplane there.

The crucial point is that we never need to explicitly compute φ(x). We only need to compute the dot product in the transformed space via a kernel function:

$$K(\mathbf{x}_i, \mathbf{x}_j) = \phi(\mathbf{x}_i) \cdot \phi(\mathbf{x}_j)$$

The most common kernels are:

  • Linear kernel: K(xᵢ, xⱼ) = xᵢ · xⱼ — equivalent to standard SVM without transformation
  • Polynomial kernel: K(xᵢ, xⱼ) = (γ · xᵢ · xⱼ + coef0)^d — captures polynomial interactions of degree d
  • RBF kernel (Radial Basis Function): K(xᵢ, xⱼ) = exp(−γ ||xᵢ − xⱼ||²) — the most widely used, creates complex non-linear decision boundaries
  • Sigmoid kernel: K(xᵢ, xⱼ) = tanh(γ · xᵢ · xⱼ + coef0) — analogous to a single-layer neural network

Geometric Intuition

Imagine you need to draw a road between two enemy villages — the blue dots and the red dots. You want this road to be as wide as possible, so that the inhabitants of each village stay as far as possible from the border and from conflict. The houses that directly touch the edge of the road, on either side, are the support vectors. They are the only houses that matter in determining where the central road runs. All the other houses, those far from the border, have no influence on the layout at all.

This is exactly the principle of SVM: it seeks the widest possible boundary between two classes, and only the points that are “stuck” to this margin — the support vectors — determine the final decision. This intuition explains why SVM is so robust: even if you add or remove points far from the boundary, the hyperplane does not move.

If the two villages are mixed together and no straight road can separate them, the kernel trick is like lifting the terrain into a third dimension: by projecting the data upward along a curve, we can often find a separating plane in this elevated space. It’s as if, by adding a dimension, we make possible what was not possible in the original space.

Python Implementation

Linear SVM with scikit-learn

from sklearn import datasets
from sklearn.svm import SVC, LinearSVC
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import classification_report, confusion_matrix
import numpy as np

# Load the Iris dataset (binary for demonstration)
iris = datasets.load_iris()
X = iris.data[:, :2]  # Keep 2 features for visualization
y = (iris.target != 0).astype(int)  # Binary classification: setosa vs non-setosa

# Train/test split
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42, stratify=y
)

# Standardization — crucial for SVM!
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# Linear SVM
svm_linear = SVC(kernel="linear", random_state=42)
svm_linear.fit(X_train_scaled, y_train)

# Predictions and evaluation
y_pred = svm_linear.predict(X_test_scaled)
print("Confusion matrix (Linear SVM):")
print(confusion_matrix(y_test, y_pred))
print(f"\nSupport vectors: {svm_linear.n_support_}")

RBF vs Polynomial Comparison

import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap

# Create a grid for visualization
def plot_decision_boundary(model, X, y, title):
    x_min, x_max = X[:, 0].min() - 0.5, X[:, 0].max() + 0.5
    y_min, y_max = X[:, 1].min() - 0.5, X[:, 1].max() + 0.5
    xx, yy = np.meshgrid(np.linspace(x_min, x_max, 200),
                         np.linspace(y_min, y_max, 200))

    Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)

    cmap = ListedColormap(["#FFAAAA", "#AAAAFF"])
    plt.contourf(xx, yy, Z, alpha=0.3, cmap=cmap)

    plt.scatter(X[:, 0], X[:, 1], c=y, cmap=ListedColormap(["#FF0000", "#0000FF"]),
                edgecolors="k", s=50)

    # Highlight support vectors
    sv = model.support_vectors_
    plt.scatter(sv[:, 0], sv[:, 1], s=150, facecolors="none",
                edgecolors="gold", linewidths=2, label="Support Vectors")

    plt.title(title, fontsize=14)
    plt.xlabel("Feature 1")
    plt.ylabel("Feature 2")
    plt.legend()
    plt.tight_layout()
    plt.show()

# SVM with RBF kernel (default)
svm_rbf = SVC(kernel="rbf", C=1.0, gamma="scale", random_state=42)
svm_rbf.fit(X_train_scaled, y_train)

# SVM with polynomial kernel
svm_poly = SVC(kernel="poly", degree=3, C=1.0, gamma="scale", coef0=1, random_state=42)
svm_poly.fit(X_train_scaled, y_train)

# Visualization
fig, axes = plt.subplots(1, 3, figsize=(18, 5))

# Linear plot
plot_decision_boundary(svm_linear, X_train_scaled, y_train, "Linear SVM")
# RBF plot
plot_decision_boundary(svm_rbf, X_train_scaled, y_train, "SVM RBF (gamma=scale)")
# Polynomial plot
plot_decision_boundary(svm_poly, X_train_scaled, y_train, "SVM Polynomial (degree=3)")

Impact of the C Parameter

The C parameter controls the tradeoff between a wide margin and a low number of classification errors on the training data:

# Comparing different C values
fig, axes = plt.subplots(1, 3, figsize=(18, 5))
C_values = [0.1, 1.0, 100.0]

for i, C_val in enumerate(C_values):
    svm = SVC(kernel="rbf", C=C_val, gamma="scale", random_state=42)
    svm.fit(X_train_scaled, y_train)

    x_min, x_max = X_train_scaled[:, 0].min() - 0.5, X_train_scaled[:, 0].max() + 0.5
    y_min, y_max = X_train_scaled[:, 1].min() - 0.5, X_train_scaled[:, 1].max() + 0.5
    xx, yy = np.meshgrid(np.linspace(x_min, x_max, 200),
                         np.linspace(y_min, y_max, 200))
    Z = svm.predict(np.c_[xx.ravel(), yy.ravel()]).reshape(xx.shape)

    axes[i].contourf(xx, yy, Z, alpha=0.3, cmap=ListedColormap(["#FFAAAA", "#AAAAFF"]))
    axes[i].scatter(X_train_scaled[:, 0], X_train_scaled[:, 1], c=y,
                    cmap=ListedColormap(["#FF0000", "#0000FF"]), edgecolors="k", s=50)
    axes[i].set_title(f"C = {C_val} | Support vectors: {svm.n_support_}")

plt.tight_layout()
plt.show()
  • Small C (0.1): wide margin, tolerance for errors — more general model, risk of underfitting
  • Medium C (1.0): good balance — often a good starting point
  • Large C (100.0): narrow margin, strict classification — risk of overfitting

Impact of the gamma Parameter (RBF)

The gamma parameter of the RBF kernel controls the influence of each training point:

fig, axes = plt.subplots(1, 3, figsize=(18, 5))
gamma_values = [0.01, 1.0, 10.0]

for i, g in enumerate(gamma_values):
    svm = SVC(kernel="rbf", C=1.0, gamma=g, random_state=42)
    svm.fit(X_train_scaled, y_train)

    x_min, x_max = X_train_scaled[:, 0].min() - 0.5, X_train_scaled[:, 0].max() + 0.5
    y_min, y_max = X_train_scaled[:, 1].min() - 0.5, X_train_scaled[:, 1].max() + 0.5
    xx, yy = np.meshgrid(np.linspace(x_min, x_max, 200),
                         np.linspace(y_min, y_max, 200))
    Z = svm.predict(np.c_[xx.ravel(), yy.ravel()]).reshape(xx.shape)

    axes[i].contourf(xx, yy, Z, alpha=0.3, cmap=ListedColormap(["#FFAAAA", "#AAAAFF"]))
    axes[i].scatter(X_train_scaled[:, 0], X_train_scaled[:, 1], c=y,
                    cmap=ListedColormap(["#FF0000", "#0000FF"]), edgecolors="k", s=50)
    axes[i].set_title(f"gamma = {g} | Support vectors: {svm.n_support_}")

plt.tight_layout()
plt.show()
  • Small gamma (0.01): large influence per point — smooth boundary, risk of underfitting
  • Medium gamma (1.0): localized influence — good compromise
  • Large gamma (10.0): very local influence — highly irregular boundary, risk of overfitting

LinearSVC for Large Datasets

For large datasets, LinearSVC is more efficient than SVC(kernel="linear") because it uses the liblinear algorithm instead of libsvm:

from sklearn.svm import LinearSVC

# LinearSVC — faster on large datasets
linear_svc = LinearSVC(C=1.0, max_iter=10000, random_state=42)
linear_svc.fit(X_train_scaled, y_train)

print(f"Coefficients: {linear_svc.coef_}")
print(f"Bias (intercept): {linear_svc.intercept_}")
print(f"Test accuracy: {linear_svc.score(X_test_scaled, y_test):.4f}")

Hyperparameters

SVM has several key hyperparameters that are essential to understand and tune:

Hyperparameter Description Typical Values
C Regularization parameter. Controls the margin/errors tradeoff. The larger C, the more the model penalizes classification errors. 0.001, 0.01, 0.1, 1.0, 10, 100, 1000
kernel Kernel function used for transformation. Determines the shape of the decision boundary. “linear”, “rbf”, “poly”, “sigmoid”, “precomputed”
gamma Coefficient of the RBF/poly/sigmoid kernel. Defines the influence of a single training point. “scale”, “auto”, or numeric value (0.001 to 100)
degree Degree of the polynomial (only for the “poly” kernel). 2, 3, 4, 5
coef0 Independent term in the “poly” and “sigmoid” kernels. 0.0, 1.0
class_weight Class weighting. Useful for imbalanced data. None, “balanced”, or dictionary

Tuning via Grid Search

from sklearn.model_selection import GridSearchCV

param_grid = {
    "C": [0.1, 1, 10, 100],
    "gamma": ["scale", "auto", 0.1, 0.01],
    "kernel": ["rbf", "poly", "linear"]
}

grid_search = GridSearchCV(SVC(random_state=42), param_grid,
                           cv=5, scoring="accuracy", n_jobs=-1)
grid_search.fit(X_train_scaled, y_train)

print(f"Best parameters: {grid_search.best_params_}")
print(f"Best accuracy (cross-validation): {grid_search.best_score_:.4f}")

Advantages and Limitations

Advantages

  • Effective in high dimensions: SVM remains performant even when the number of features exceeds the number of samples
  • Robust against overfitting: thanks to margin maximization, it generalizes well to new data
  • Versatile through kernels: the kernel trick allows handling both linear and non-linear problems
  • Sparse: the decision depends only on support vectors, not the entire training dataset
  • Guaranteed convexity: unlike neural networks, the optimization is convex — there is no risk of getting stuck in a local minimum
  • Works well on small datasets: excellent performance even with a few hundred samples

Limitations

  • Slow on large datasets: training complexity is O(n²) to O(n³), making SVM impractical beyond ~100,000 samples
  • Sensitive to outliers: a misplaced outlier can strongly influence the position of the hyperplane
  • No direct probabilities: SVM does not naturally produce probabilities (requires recalibration via cross-validation with probability=True)
  • Difficult to interpret: with an RBF kernel, the decision boundary is a complex function impossible to visualize in high dimensions
  • Mandatory standardization: features must be scaled, otherwise SVM gives poor results
  • Not natively multi-class: SVM is fundamentally binary; multi-class requires one-vs-one or one-vs-rest strategies

4 Concrete Use Cases

1. Text Classification and Spam Filtering

SVM excels at text document classification tasks. By combining a linear SVM with a TF-IDF representation, you get an extremely performant spam classifier. The features are very numerous (one word = one dimension), but this is precisely the domain where SVM shines. Foundational work by Thorsten Joachims demonstrated that linear SVMs often outperform probabilistic methods like Naive Bayes on this type of task.

2. Image Recognition and Computer Vision

Before the advent of deep neural networks, SVMs were the dominant approach for image classification. They are still used today in combination with feature extractors such as HOG (Histogram of Oriented Gradients) or SIFT. For example, pedestrian detection in ADAS systems of autonomous vehicles often uses SVMs as the final classifier after feature extraction.

3. Bioinformatics and Gene Classification

In genomics, we often seek to classify samples (healthy vs. diseased) based on the expression of thousands of genes. With typically a few dozen samples and thousands of features — a scenario where n ≤ p — the linear SVM is the algorithm of choice. Its ability to generalize effectively in this regime makes it indispensable for identifying tumor markers and personalized medicine.

4. Anomaly Detection (One-Class SVM)

The One-Class SVM is a variant that learns the distribution of a “normal” class and detects deviations. It is used for bank fraud detection, industrial monitoring (fault detection), and cybersecurity (intrusion detection). Instead of separating two classes, it learns a compact boundary around the normal data and flags everything outside it.

See Also

About Salah YAHIAOUI

Salah YAHIAOUI publie sur The Pingouin des tutoriels Python et des décryptages sur l’intelligence artificielle. Les cas pratiques récents précisent leurs données, leurs tests et leurs limites. À propos, méthode éditoriale et contact.

View all posts by Salah YAHIAOUI →