Máquinas de soporte vectorial (SVM)

Las máquinas de soporte vectorial (SVM) encuentran el hiperplano que separa dos clases con el mayor margen posible. Solo los puntos más cercanos al hiperplano, los vectores de soporte, determinan su posición: el resto de los puntos de entrenamiento son irrelevantes. El truco del núcleo extiende las SVM a fronteras no lineales proyectando implícitamente los datos a un espacio de alta dimensión.

SVM de margen duro

Para datos linealmente separables con etiquetas \(y_i \in \{-1, +1\}\), la frontera de decisión es el hiperplano \(\mathbf{w}^T\mathbf{x} + b = 0\). Los puntos con \(y_i = +1\) deben satisfacer \(\mathbf{w}^T\mathbf{x}_i + b \geq +1\) y los con \(y_i = -1\) deben satisfacer \(\mathbf{w}^T\mathbf{x}_i + b \leq -1\). Combinando: \(y_i(\mathbf{w}^T\mathbf{x}_i + b) \geq 1\) para todo \(i\).

El ancho del margen es \(2/\|\mathbf{w}\|\). Maximizar el margen equivale a minimizar \(\|\mathbf{w}\|^2\):

\[\min_{\mathbf{w},b} \frac{1}{2}\|\mathbf{w}\|^2 \quad \text{s.a.} \quad y_i(\mathbf{w}^T\mathbf{x}_i + b) \geq 1 \quad \forall i\]

Es un programa cuadrático convexo. Las condiciones KKT dan el problema dual, y solo los puntos de entrenamiento donde \(y_i(\mathbf{w}^T\mathbf{x}_i + b) = 1\) (sobre el límite del margen) tienen multiplicadores duales \(\alpha_i > 0\): son los vectores de soporte. Todos los demás puntos son irrelevantes para la solución. Esta dispersión es una propiedad clave: las predicciones usan solo los vectores de soporte.

SVM de margen duro mostrando la frontera de decisión, los límites del margen y los vectores de soporte resaltados

SVM de margen blando

Los datos reales raramente son linealmente separables. La SVM de margen blando introduce variables de holgura \(\xi_i \geq 0\) que permiten a los puntos violar la restricción del margen:

\[\min_{\mathbf{w},b,\boldsymbol{\xi}} \frac{1}{2}\|\mathbf{w}\|^2 + C\sum_{i=1}^n \xi_i \quad \text{s.a.} \quad y_i(\mathbf{w}^T\mathbf{x}_i + b) \geq 1 - \xi_i,\quad \xi_i \geq 0\]

El parámetro \(C > 0\) controla el intercambio:

  • \(C\) grande: penaliza fuertemente las violaciones del margen. Margen más estrecho pero menos clasificaciones erróneas en los datos de entrenamiento. Propenso al sobreajuste.
  • \(C\) pequeño: tolera más violaciones. Margen más amplio, más regularizado. Propenso al infraajuste.

\(C\) es el hiperparámetro más importante y debe seleccionarse mediante validación cruzada. Los vectores de soporte son ahora los puntos que están en el límite del margen (\(\xi_i = 0\), \(\alpha_i > 0\)) o lo violan (\(\xi_i > 0\)): la fracción de vectores de soporte típicamente aumenta al disminuir \(C\).

El problema dual y los núcleos

El dual lagrangiano del problema de margen blando es:

\[\max_{\boldsymbol{\alpha}} \sum_i \alpha_i - \frac{1}{2}\sum_{i,j}\alpha_i\alpha_j y_i y_j \mathbf{x}_i^T\mathbf{x}_j \quad \text{s.a.} \quad 0 \leq \alpha_i \leq C,\quad \sum_i \alpha_i y_i = 0\]

La observación clave: el objetivo depende de los datos solo a través de los productos internos \(\mathbf{x}_i^T\mathbf{x}_j\). La predicción para un nuevo punto también usa solo productos internos:

\[\hat{y}(\mathbf{x}) = \text{signo}\!\left(\sum_{i \in VS} \alpha_i y_i \mathbf{x}_i^T\mathbf{x} + b\right)\]

El truco del núcleo: reemplaza cada producto interno \(\mathbf{x}_i^T\mathbf{x}_j\) con una función de núcleo \(K(\mathbf{x}_i, \mathbf{x}_j) = \phi(\mathbf{x}_i)^T\phi(\mathbf{x}_j)\) donde \(\phi\) es una proyección implícita. Esto permite que la SVM opere en un espacio de características muy alto (o infinito) sin calcular jamás \(\phi(\mathbf{x})\) explícitamente.

Núcleos habituales

Núcleo Fórmula Cuándo usarlo
Lineal \(K(\mathbf{x},\mathbf{x}') = \mathbf{x}^T\mathbf{x}'\) Separable linealmente, \(p\) alto
Polinomial \(K(\mathbf{x},\mathbf{x}') = (\mathbf{x}^T\mathbf{x}' + c)^d\) Interacciones polinomiales
RBF/Gaussiano \(K(\mathbf{x},\mathbf{x}') = e^{-\gamma\|\mathbf{x}-\mathbf{x}'\|^2}\) General no lineal, el más común
Sigmoidal \(K(\mathbf{x},\mathbf{x}') = \tanh(\kappa\mathbf{x}^T\mathbf{x}' + c)\) Similar a red neuronal

El núcleo RBF con ancho de banda \(\gamma\) es el más usado. \(\gamma\) menor: frontera más suave (los puntos lejanos se influyen entre sí). \(\gamma\) mayor: cada punto de entrenamiento influye solo en su vecindario inmediato, llevando al sobreajuste.

Tres paneles comparando fronteras de decisión para núcleo lineal, polinomial y RBF sobre el mismo conjunto de datos no linealmente separable

En datos circulares (no separables linealmente), el núcleo lineal falla completamente. El núcleo polinomial captura algo de no linealidad. El núcleo RBF aprende correctamente la frontera circular.

Ajuste de hiperparámetros

Dos hiperparámetros clave para SVM-RBF: \(C\) y \(\gamma\). Interactúan: \(\gamma\) grande con \(C\) grande sobreajusta; \(\gamma\) pequeño con \(C\) pequeño infraajusta. El enfoque estándar es una búsqueda en rejilla 2D con validación cruzada:

  • Rejilla de \(C\): \(\{0,01;\, 0,1;\, 1;\, 10;\, 100;\, 1000\}\)
  • Rejilla de \(\gamma\): \(\{0,001;\, 0,01;\, 0,1;\, 1;\, 10\}\)

Un punto de partida heurístico para \(\gamma\): \(1/(p \cdot \text{Var}(\mathbf{X}))\) donde \(p\) es el número de características.

⚠️ Las SVM escalan mal con n grande

Entrenar una SVM requiere resolver un programa cuadrático con \(n\) variables. Los solucionadores cuadráticos ingenuos tienen complejidad \(O(n^3)\). La Optimización Mínima Secuencial (SMO), el algoritmo estándar en libsvm, la reduce a aproximadamente \(O(n^2)\) pero puede seguir siendo lenta para \(n > 100{,}000\).

Para conjuntos de datos grandes: usa una SVM lineal (mucho más rápida, \(O(n)\) con métodos de gradiente estocástico) o cambia a gradient boosting o redes neuronales, que escalan mejor. El truco del núcleo es potente pero también costoso computacionalmente en tiempo de predicción: las predicciones requieren evaluar \(K(\mathbf{x}, \mathbf{x}_i)\) para todos los vectores de soporte.

💡 SVM en R

library(e1071)

# SVM lineal
fit_lin <- svm(y ~ ., data=df_train, kernel="linear", cost=1)

# SVM RBF
fit_rbf <- svm(y ~ ., data=df_train, kernel="radial",
               cost=10, gamma=0.1)

# Búsqueda en rejilla de C y gamma
tune_result <- tune(svm, y ~ ., data=df_train,
                    kernel="radial",
                    ranges=list(cost=10^(-1:3), gamma=10^(-3:1)))
best_model <- tune_result$best.model
summary(tune_result)

# Predicciones y probabilidades
predict(fit_rbf, newdata=df_test)
predict(fit_rbf, newdata=df_test, decision.values=TRUE)

# Regresión con vectores de soporte (SVR)
fit_svr <- svm(y ~ ., data=df_train, type="eps-regression",
               kernel="radial", epsilon=0.1)