Machine learning desde cero · Lección 5.5 · 30 de 33 lecciones listas · 12 min
Máquinas de vectores de soporte y núcleos
En pocas palabras
Una SVM busca la franja más ancha entre las clases. Si C sube de 0,01 a 100, la franja pasa de 3,9 a 0,15 cm y la prueba apenas se mueve.
Calentamiento Dos preguntas de lecciones anteriores. Un minuto, y no bloquea la lección.
Dos especies de iris, dos medidas del pétalo. En el entrenamiento muchas rectas las separan igual de bien. Una máquina de vectores de soporte (SVM, del inglés support vector machine) elige la que deja a su alrededor la franja vacía más ancha, el margen. Las flores en el borde de la franja, dentro de ella o del lado equivocado son los vectores de soporte. Solo ellas fijan la frontera: podrías borrar todas las demás flores y la frontera seguiría igual. Cuando las clases se solapan, la franja no puede quedar vacía, así que la SVM paga una penalización por cada flor dentro de la franja o del lado equivocado. El número C fija el precio de esa penalización. Una C pequeña permite una franja ancha con muchas violaciones; una C grande obliga a mantener las flores en su lado, aunque la franja quede estrecha.
Elige una respuesta y tu grado de seguridad.
Responde para desbloquear el experimento.
Experimento interactivo
Este experimento interactivo funciona en un navegador con JavaScript activado. El texto que lo rodea describe lo que muestra.
Ver la respuesta
Respuesta correcta: Apenas cambia: con cualquier C el modelo acierta 26 o 27 de las 30
Casi igual: con los 9 valores de C la prueba da 26 o 27 de 30. La franja se estrecha 26 veces y el resultado se mueve una flor. En otras 20 particiones la exactitud de prueba con distintos valores de C varía como mucho en 2 flores de 30.
Prueba
- Empieza con los pétalos y C = 100. ¿Cuántas flores sostienen la frontera y qué ancho tiene la franja?
- Baja C. ¿Con qué C es mejor la validación, y con cuál es más ancha la franja para casi el mismo resultado?
- Cambia a los sépalos. ¿Alguna C separa aquí las especies?
- Pasa a los vinos. Compara «solo x», las variables √2·x y x², y el núcleo (x·z + 1)². Luego pasa al núcleo RBF y mueve γ (lo rápido que cae el parecido de dos vinos cuando se alejan) hasta 1000.
Cuando lo hayas comprobado en el lab, pongamos nombre a lo que ves.
La idea en palabras sencillas
Una SVM no calcula probabilidades como la regresión logística. Dibuja una frontera y dos líneas paralelas a ella, una a cada lado. La franja entre ellas debe ser lo más ancha posible, y las flores deben quedar fuera de ella, en su propio lado. Una flor que no cumple recibe una penalización igual a cuánto se ha metido en la franja. Es la pérdida bisagra. El modelo busca un equilibrio: una franja ancha por un lado, C por la suma de penalizaciones por el otro.
Con C = 0,01 las penalizaciones casi no cuestan nada. La franja mide 3,9 cm, y las 70 flores de entrenamiento son vectores de soporte: cada una mueve un poco la frontera. Con C = 100 cada violación sale cara. La franja mide 0,15 cm, y la sostienen 4 flores pegadas a la frontera. Mientras tanto, la prueba cambia en una flor. En los pétalos las especies están lo bastante lejos como para que casi cualquier recta razonable acierte las mismas flores.
C se elige como cualquier hiperparámetro: con la validación, no con la prueba. El lab hace una validación cruzada de 5 partes dentro de las 70 flores de entrenamiento. La mejor sale con C = 10: 69 de 70. Con C = 0,1 son 68 de 70, y la franja mide 1,4 cm en lugar de 0,34 cm. Una flor de diferencia es ruido en validación, y una franja más ancha apoya la frontera en más flores. La C más pequeña se pasa hacia el otro lado: 64 de 70.
La frontera no tiene por qué ser recta. En los vinos solo preguntamos si un vino es del cultivar 2 o de otro, así que hay dos clases. El cultivar 2 está en medio del eje de flavonoides: el cultivar 1 tiene 2,98 de media, el cultivar 2 2,08 y el cultivar 3 0,78. Un solo umbral no puede recortar el centro, así que la SVM lineal prefiere no señalar el cultivar 2 en ninguna parte. Validación: 60,0%, lo mismo que responder «siempre otro cultivar». Cambiemos x por dos variables: √2·x y x². Los vinos caen en una parábola, y una recta en el plano (√2·x, x²) son dos umbrales en el eje, es decir, un intervalo. La validación sube a 72,0%.
Un núcleo hace lo mismo sin añadir columnas. La SVM solo necesita productos escalares de pares de ejemplos. El producto escalar es la suma de los productos de las coordenadas correspondientes de dos ejemplos; la SVM lo usa como medida de su parecido. El núcleo (x·z + 1)² da exactamente el producto escalar de los vectores (1, √2·x, x²), así que la frontera sale idéntica a la de las variables √2·x y x². Es el truco del núcleo. El núcleo RBF (de radial basis function, función de base radial) exp(−γ(x − z)²) mide el parecido por la distancia: γ dice lo rápido que cae el parecido cuando dos vinos se alejan. Equivale a infinitas variables de ese tipo. Con γ = 3 la validación es 78,4%, la mejor de los ocho valores de γ, y la prueba da después 84,9%. Con γ = 0,3, 1 y 10 la validación es 76,8%, solo 2 vinos de 125 menos. Como con C en los iris, es prácticamente un empate. Con γ = 1000 el entrenamiento sube a 89,6% y la validación baja a 69,6%. La frontera se rompe entonces en 11 intervalos ajustados a vinos sueltos. La prueba más alta, 86,8%, sale con γ = 100, pero elegir por la prueba es elegir con los datos con los que evalúas.
Jak to policzono
- Iris: Iris (Fisher, 1936; UCI, licencia CC BY 4.0), 100 flores versicolor y virginica. Medidas en centímetros sin escalar, porque las dos variables tienen la misma unidad. Partición estratificada: 70 flores para aprender, 30 para la prueba.
- Vinos: Wine (Aeberhard y Forina, 1992; UCI, licencia CC BY 4.0), 178 vinos, solo el contenido de flavonoides, C = 1. Partición: 125 vinos para aprender, 53 para la prueba. Las puntuaciones z salen solo de los vinos de aprendizaje, y en la validación de los vinos de aprendizaje de cada parte.
- Validación: validación cruzada estratificada de 5 partes dentro del entrenamiento. Modelo: una SVM de margen blando; los números coinciden con
SVCde scikit-learn.
Por qué funciona
Una franja ancha es regularización, como la penalización de ridge de la lección 3.4. La franja mide 2/‖w‖, así que una franja ancha significa pesos pequeños y una frontera que depende menos de flores sueltas. El margen con penalización por violaciones lo describieron Cortes y Vapnik (1995). El truco del núcleo llegó antes a las SVM, con Boser, Guyon y Vapnik (1992): como el modelo solo usa productos escalares, cada uno puede sustituirse por un núcleo.
Que C cambie la geometría más que el resultado no es casualidad de una partición. En otras 20 particiones de los iris, la exactitud de prueba con los 9 valores de C varía como mucho en 2 flores de 30, mientras la franja pasa de 3,8–4,1 cm a 0,17–0,36 cm.
El núcleo RBF mide distancias, así que las unidades importan. Los vinos tienen 13 medidas: la prolina llega a 1680 y el tono como mucho a 1,71. Sin escalar, la distancia es casi solo prolina. Una SVM con RBF sobre las 13 medidas acierta entonces, en 20 particiones, el 67,2% de los vinos de validación de media. Con un escalado aprendido solo en la parte de aprendizaje: 97,9%.
Un error frecuente
Verdadero o falso: «usa siempre el núcleo RBF, es el más flexible». Falso. En los pétalos de iris, una SVM lineal y una SVM con RBF, cada una con su configuración elegida por validación, aciertan de media 28,5 de 30 flores de prueba en 20 particiones. RBF es mejor en 1 partición y peor en 1. La frontera aquí es casi recta, así que la flexibilidad no aporta nada y sí cuesta: dos hiperparámetros que ajustar en lugar de uno y un sobreajuste más fácil (γ = 1000 en los vinos). En el código de abajo la lineal logra 0,950, la RBF 0,947, y RBF es mejor en 2 particiones y peor en 4.
La lección práctica: empieza con un núcleo lineal o con una regresión logística. Recurre a RBF cuando la validación muestre que la frontera no es recta, como en los vinos.
Test
Tres preguntas para comprobar lo aprendido. Elige una respuesta o despliega la solución bajo cada pregunta. Con el teclado: las flechas eligen, Enter o Espacio confirma.
Ideas clave
- Una SVM elige la frontera con la franja más ancha; solo la fijan los vectores de soporte.
- C es el precio de las violaciones: de C = 0,01 a 100 la franja pasa de 3,9 a 0,15 cm y la prueba apenas se mueve (26–27 de 30).
- Un núcleo da fronteras que no son rectas sin calcular variables nuevas; (x·z + 1)² equivale exactamente a las variables √2·x y x².
- Elige C, el núcleo y γ por validación. RBF se sobreajusta con una γ grande y falla mucho más sin escalado.
Más a fondo: fórmula, código, práctica
Las matemáticas
Margen blando: minimizamos ½‖w‖² + C · Σᵢ ξᵢ sujeto a yᵢ(w·xᵢ + b) ≥ 1 − ξᵢ y ξᵢ ≥ 0. La franja mide 2/‖w‖, y ξᵢ = max(0, 1 − yᵢ f(xᵢ)) es la pérdida bisagra.
En la forma dual f(x) = Σᵢ αᵢ yᵢ K(xᵢ, x) + b con 0 ≤ αᵢ ≤ C; los vectores de soporte son los ejemplos con αᵢ > 0. El núcleo polinómico K(x, z) = (x·z + 1)² = φ(x)·φ(z) para φ(x) = (1, √2·x, x²), el núcleo RBF K(x, z) = exp(−γ‖x − z‖²).
En código (Python)
El mismo experimento en scikit-learn: la franja y los vectores de soporte con tres valores de C, después una SVM lineal y otra con RBF, cada una con su configuración elegida por validación, en 20 particiones, y por último los vinos sin escalar y escalados.
import numpy as np
from sklearn.datasets import load_iris, load_wine
from sklearn.model_selection import train_test_split, StratifiedKFold, GridSearchCV, cross_val_score
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
iris = load_iris(as_frame=True)
keep = iris.target > 0
X, y = iris.data.loc[keep, ["petal length (cm)", "petal width (cm)"]], iris.target[keep]
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.3, stratify=y, random_state=0)
for C in [0.01, 1, 100]:
m = SVC(kernel="linear", C=C).fit(X_tr, y_tr)
print(f"C = {C:>6}: band {2 / np.linalg.norm(m.coef_):.2f} cm, {m.n_support_.sum()} support vectors of {len(y_tr)}, test {m.score(X_te, y_te):.3f}")
grids = {"linear": {"svc__kernel": ["linear"], "svc__C": [0.01, 0.1, 1, 10, 100]},
"RBF": {"svc__kernel": ["rbf"], "svc__C": [0.1, 1, 10, 100], "svc__gamma": [0.01, 0.1, 1, 10]}}
test = {k: [] for k in grids}
for seed in range(20):
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.3, stratify=y, random_state=seed)
cv = StratifiedKFold(5, shuffle=True, random_state=seed)
for k, grid in grids.items():
search = GridSearchCV(make_pipeline(StandardScaler(), SVC()), grid, cv=cv).fit(X_tr, y_tr)
test[k].append(search.score(X_te, y_te))
lin, rbf = np.array(test["linear"]), np.array(test["RBF"])
print(f"20 splits, settings chosen in validation: test linear {lin.mean():.3f}, RBF {rbf.mean():.3f}; RBF better {(rbf > lin).sum()}, worse {(rbf < lin).sum()}, equal {(rbf == lin).sum()}")
wine = load_wine()
cv = StratifiedKFold(5, shuffle=True, random_state=0)
for name, model in [("raw", SVC()), ("scaled inside each fold", make_pipeline(StandardScaler(), SVC()))]:
print(f"wine, RBF SVM, {name}: validation {cross_val_score(model, wine.data, wine.target, cv=cv).mean():.3f}")
Resultado
C = 0.01: band 4.21 cm, 70 support vectors of 70, test 1.000
C = 1: band 0.71 cm, 22 support vectors of 70, test 1.000
C = 100: band 0.37 cm, 12 support vectors of 70, test 1.000
20 splits, settings chosen in validation: test linear 0.950, RBF 0.947; RBF better 2, worse 4, equal 14
wine, RBF SVM, raw: validation 0.657
wine, RBF SVM, scaled inside each fold: validation 0.983
La partición es distinta de la del lab, así que los números también son algo distintos. La imagen es la misma: la franja pasa de 4,21 a 0,37 cm y la prueba no cambia (1,000). En 20 particiones el núcleo RBF no supera al lineal (0,947 frente a 0,950). Sin escalar, la SVM logra 0,657 en los vinos; con escalado dentro de cada parte, 0,983.
Descargar el notebook (.ipynb). Ábrelo en Jupyter o súbelo a Google Colab; descarga por sí solo los datos que falten.
Para profesionales
- En scikit-learn:
SVC(kernel="linear")ySVC(kernel="rbf")(por defectogamma="scale"), y para conjuntos grandesLinearSVC, que aprende mucho más rápido. Según la documentación, el tiempo de entrenamiento deSVCcrece al menos de forma cuadrática con el número de ejemplos, así que con decenas de miles de filas puede volverse impracticable. - Escala siempre las variables en un pipeline:
make_pipeline(StandardScaler(), SVC()). El escalador del pipeline aprende solo con la parte de aprendizaje de cada validación (lección 4.6). - Ajusta C y γ juntos, en una rejilla en escala logarítmica (
GridSearchCV, lección 3.5). Una C grande y una γ grande son dos caminos hacia el sobreajuste. - Una SVM no da probabilidades directamente.
SVC(probability=True)las ajusta con una calibración aparte en validación cruzada (lección 2.4), así que tarda más en aprender.
¿Te ha sido útil?
¡Gracias! Nos ayuda a mejorar.
Profundiza en el Atlas
Entradas de la enciclopedia sobre las ideas de esta lección: