ML Atlas

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.
De la lección 5.4: Naive Bayes: un supuesto falso, buenas decisiones En versicolor la longitud y la anchura del pétalo se correlacionan con r = 0,80, y el modelo acierta el 93,3% de las flores de prueba. ¿Qué se deduce?

Ver la respuesta

Respuesta correcta: Para decidir basta con que la especie correcta obtenga el producto más grande

El supuesto falso cambia los valores de los productos, pero rara vez su orden cuando las especies están lejos. El modelo con correlaciones acierta de media solo un poco más.

De la lección 5.3: Gradient boosting: árboles que corrigen errores Verdadero o falso: «más rondas de boosting siempre ayudan».

Ver la respuesta

Respuesta correcta: Falso, porque pasado un tiempo las rondas extra solo corrigen el ruido restante

El entrenamiento baja con cada ronda, la prueba no. Con paso 1, en validación lo mejor es 1 árbol, y 200 árboles dan 5944 en la prueba.

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.

Predice y comprueba Una SVM lineal distingue iris versicolor de virginica por dos medidas del pétalo. Cuando C sube de 0,01 a 100, la franja pasa de 3,9 cm a 0,15 cm, y solo 4 flores sostienen la frontera en lugar de 70. ¿Qué pasa con la exactitud en las 30 flores de prueba?
¿Qué tan seguro estás?

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 SVC de 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.

1. ¿Qué flores fijan la frontera de una SVM lineal?
Ver la respuesta

Respuesta correcta: Los vectores de soporte: flores en el borde, dentro o más allá

Las demás flores podrían quitarse y la frontera no cambiaría. Con C = 100 la sostienen 4 flores de 70.

2. C sube de 0,01 a 100, la franja pasa de 3,9 a 0,15 cm y la prueba se queda en 26–27 de 30. ¿Qué se deduce?
Ver la respuesta

Respuesta correcta: C cambia la geometría más que el resultado: las especies están lejos

En los pétalos casi cualquier recta razonable acierta las mismas flores. Aun así, la C más pequeña logra en validación 64 de 70 frente a las mejores 69, así que elige C por validación de todos modos.

3. Verdadero o falso: «usa siempre el núcleo RBF, es el más flexible».
Ver la respuesta

Respuesta correcta: Falso: en los pétalos la lineal acierta igual, y RBF exige más ajuste

En 20 particiones ambas aciertan de media 28,5 de 30. Una γ grande lleva al sobreajuste: en los vinos con γ = 1000, entrenamiento 89,6% y validación 69,6%.

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.

Para profesionales
  • En scikit-learn: SVC(kernel="linear") y SVC(kernel="rbf") (por defecto gamma="scale"), y para conjuntos grandes LinearSVC, que aprende mucho más rápido. Según la documentación, el tiempo de entrenamiento de SVC crece 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.

Profundiza en el Atlas

Entradas de la enciclopedia sobre las ideas de esta lección: