Capitulo 5

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 19

8/5/22, 00:15 Capítulo 5 - SVM - Colaboratory

Una máquina de vectores de soporte (SVM)* es un modelo poderoso y versatil, capaz de realizar
clasificación lineal o no lineal, regresión e incluso detección de anomalías. Este es uno de los
mas populares modelos y que todo entusiasta del Machine Learning debe tener en su caja de
herramientas. SVMs son particularmente adecuados para clasificadión de datasets complejos
aunque pequeños o de tamaño medio.

Clasificación Lineal SVM

La idea fundamental detrás de SVM se explica mejor con imágenes. La siguiente fitura muestra
parte del dataset de "iris".

from sklearn.svm import SVC
from sklearn import datasets
import numpy as np

iris = datasets.load_iris()
X = iris["data"][:, (2, 3)]  # petal length, petal width
y = iris["target"]

setosa_or_versicolor = (y == 0) | (y == 1)
X = X[setosa_or_versicolor]
y = y[setosa_or_versicolor]

# SVM Classifier model
svm_clf = SVC(kernel="linear", C=float("inf"))
svm_clf.fit(X, y)

SVC(C=inf, kernel='linear')

import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.rc('axes', labelsize=14)
mpl.rc('xtick', labelsize=12)
mpl.rc('ytick', labelsize=12)

x0 = np.linspace(0, 5.5, 200)
pred_1 = 5*x0 - 20
pred_2 = x0 - 1.8
pred_3 = 0.1 * x0 + 0.5

def plot_svc_decision_boundary(svm_clf, xmin, xmax):
    w = svm_clf.coef_[0]
    b = svm_clf.intercept_[0]

    # At the decision boundary, w0*x0 + w1*x1 + b = 0
    # => x1 = -w0/w1 * x0 - b/w1
    x0 = np.linspace(xmin, xmax, 200)
https://colab.research.google.com/drive/1GvXk2fxCTo2GwDHIkyCzEYayjyTZi8zv?usp=sharing#printMode=true 1/19
8/5/22, 00:15 Capítulo 5 - SVM - Colaboratory

    decision_boundary = -w[0]/w[1] * x0 - b/w[1]

    margin = 1/w[1]
    gutter_up = decision_boundary + margin
    gutter_down = decision_boundary - margin

    svs = svm_clf.support_vectors_
    plt.scatter(svs[:, 0], svs[:, 1], s=180, facecolors='#FFAAAA')
    plt.plot(x0, decision_boundary, "k-", linewidth=2)
    plt.plot(x0, gutter_up, "k--", linewidth=2)
    plt.plot(x0, gutter_down, "k--", linewidth=2)

fig, axes = plt.subplots(ncols=2, figsize=(10,2.7), sharey=True)

plt.sca(axes[0])
plt.plot(x0, pred_1, "g--", linewidth=2)
plt.plot(x0, pred_2, "m-", linewidth=2)
plt.plot(x0, pred_3, "r-", linewidth=2)
plt.plot(X[:, 0][y==1], X[:, 1][y==1], "bs", label="Iris versicolor")
plt.plot(X[:, 0][y==0], X[:, 1][y==0], "yo", label="Iris setosa")
plt.xlabel("Petal length", fontsize=14)
plt.ylabel("Petal width", fontsize=14)
plt.legend(loc="upper left", fontsize=14)
plt.axis([0, 5.5, 0, 2])

plt.sca(axes[1])
plot_svc_decision_boundary(svm_clf, 0, 5.5)
plt.plot(X[:, 0][y==1], X[:, 1][y==1], "bs")
plt.plot(X[:, 0][y==0], X[:, 1][y==0], "yo")
plt.xlabel("Petal length", fontsize=14)
plt.axis([0, 5.5, 0, 2])
plt.show()

Las dos clases están separadas por una linea recta (estos son linealmente separables). La
gráfica de la izquierda muestra los vecindarios de decisión de tres posibles clasificadores
lineales. El modelo cuyo vecindario de decisión está representado por la linea punteada es tan
malo que no separa las clases apropiadamente. Los otros dos modelos trabajan perfectamente
en este conjunto de entrenamiento, pero sus vecindarios de decisión están muy cerca de las
instancia que estos modelos probablemente no se desempeñen tan bien con nuevas instancias.
En contraste la linea sólidaen el gráfico de la derecha representa el vecindario de decisión de
https://colab.research.google.com/drive/1GvXk2fxCTo2GwDHIkyCzEYayjyTZi8zv?usp=sharing#printMode=true 2/19
8/5/22, 00:15 Capítulo 5 - SVM - Colaboratory

una clasificador SVM; esta linea no solo separa las dos clases sino también se mantiene alejada
de las instancias tanto como es posible. Puedes pensar que un clasificador SVM como quien
ajusta la calle mas ancha posible (representada por las lineas punteadas) entre las clases. Esto
se llama clasificación de margen amplio.

Nota que agrega más instancias de entrenamiento "fuera de la calle" no afecta el vecindario de
decisión: este es determinado "soportado" por las instancia localizadas en los lados de la calle.
Estas instancias se llaman vectores de soporte

Xs = np.array([[1, 50], [5, 20], [3, 80], [5, 60]]).astype(np.float64)

ys = np.array([0, 0, 1, 1])

svm_clf = SVC(kernel="linear", C=100)

svm_clf.fit(Xs, ys)

plt.figure(figsize=(9,2.7))

plt.subplot(121)

plt.plot(Xs[:, 0][ys==1], Xs[:, 1][ys==1], "bo")

plt.plot(Xs[:, 0][ys==0], Xs[:, 1][ys==0], "ms")

plot_svc_decision_boundary(svm_clf, 0, 6)

plt.xlabel("$x_0$", fontsize=20)

plt.ylabel("$x_1$    ", fontsize=20, rotation=0)

plt.title("Unscaled", fontsize=16)

plt.axis([0, 6, 0, 90])

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()

X_scaled = scaler.fit_transform(Xs)

svm_clf.fit(X_scaled, ys)

plt.subplot(122)

plt.plot(X_scaled[:, 0][ys==1], X_scaled[:, 1][ys==1], "bo")

plt.plot(X_scaled[:, 0][ys==0], X_scaled[:, 1][ys==0], "ms")

plot_svc_decision_boundary(svm_clf, -2, 2)

plt.xlabel("$x'_0$", fontsize=20)

plt.ylabel("$x'_1$  ", fontsize=20, rotation=0)

plt.title("Scaled", fontsize=16)

plt.axis([-2, 2, -2, 2])

(-2.0, 2.0, -2.0, 2.0)

https://colab.research.google.com/drive/1GvXk2fxCTo2GwDHIkyCzEYayjyTZi8zv?usp=sharing#printMode=true 3/19
8/5/22, 00:15 Capítulo 5 - SVM - Colaboratory

SVMs son sensitivas a las escalas de las caracteríticas, como puedes ver en la figura, la escala
vertical es mucho mas larga que la escala horizontal, de forma que la calle mas acno es
cercana a la horizontal, después de escalarlo el vecindario de decisión se ve mucho mejor.

Clasificación de margen suave


Si es estríctametne imposible que todas las instancias estén fuera de la calle se llama
clasificación de margen duro. Hay dos principales asuntos con la clasificación de margen duro.
Primero, que solo trabaja con la data linealmente separable, y segundo que es altamente
sensible a los datos anómalos. La figura muestra el conjunto "iris" con un dato anómalo
adicional: en la izquierda, es imposible encontrar un margen duro, y a la derecha el vecindario de
decisión termina de forma muy diferente sin el dato anómalo, y probablemente no generalice
muy bien.

X_outliers = np.array([[3.4, 1.3], [3.2, 0.8]])

y_outliers = np.array([0, 0])

Xo1 = np.concatenate([X, X_outliers[:1]], axis=0)

yo1 = np.concatenate([y, y_outliers[:1]], axis=0)

Xo2 = np.concatenate([X, X_outliers[1:]], axis=0)

yo2 = np.concatenate([y, y_outliers[1:]], axis=0)

svm_clf2 = SVC(kernel="linear", C=10**9)

svm_clf2.fit(Xo2, yo2)

fig, axes = plt.subplots(ncols=2, figsize=(10,2.7), sharey=True)

plt.sca(axes[0])

plt.plot(Xo1[:, 0][yo1==1], Xo1[:, 1][yo1==1], "bs")

plt.plot(Xo1[:, 0][yo1==0], Xo1[:, 1][yo1==0], "yo")

plt.text(0.3, 1.0, "Impossible!", fontsize=24, color="red")

plt.xlabel("Petal length", fontsize=14)

plt.ylabel("Petal width", fontsize=14)

plt.annotate("Outlier",
             xy=(X_outliers[0][0], X_outliers[0][1]),

             xytext=(2.5, 1.7),

             ha="center",

             arrowprops=dict(facecolor='black', shrink=0.1),

             fontsize=16,

            )

plt.axis([0, 5.5, 0, 2])

plt.sca(axes[1])

plt.plot(Xo2[:, 0][yo2==1], Xo2[:, 1][yo2==1], "bs")

plt.plot(Xo2[:, 0][yo2==0], Xo2[:, 1][yo2==0], "yo")

plot_svc_decision_boundary(svm_clf2, 0, 5.5)

plt.xlabel("Petal length", fontsize=14)

plt.annotate("Outlier",
             xy=(X_outliers[1][0], X_outliers[1][1]),

https://colab.research.google.com/drive/1GvXk2fxCTo2GwDHIkyCzEYayjyTZi8zv?usp=sharing#printMode=true 4/19
8/5/22, 00:15 Capítulo 5 - SVM - Colaboratory

             xytext=(3.2, 0.08),

             ha="center",

             arrowprops=dict(facecolor='black', shrink=0.1),

             fontsize=16,

            )

plt.axis([0, 5.5, 0, 2])

plt.show()

Para evitar estos asuntos, es preferible usar un modelo más flexible. El objetivo es encontrar un
buen balance entre mantener la calle tan grade como sea posible y limitar las violaciones de
margen (i.e. instancias que terminen en el medio de la calle o aún en el lado equivocado). A esto
se le llama clasificación de margen suave.

En las clases de SVM de Scikit-Learn, puedes controlar este balance usando el parámetro C : un
pequeño valor de C permite tener una calle ancha pero más violaciones de margen. La figura
muestra los vecindarios de decisión y los márgenes de dos clasificadores SVM de margen
suave en un dataset separable no linelamente. A la izquierda, usando un valor de C bajo el
margen es bastante grande, pero muchas instancias terminan sobre la calle. A la derecha,
usando un valor alto de C el clasificador hace pocas violaciones de margen pero termina con un
pequeño margen. Sin embargo, al parecer el primer clasificador puede generalizar mejor: de
hecho aún con este conjunto de entrenamiento este comete pocos errores de predicción, dado
que la mayoría de violaciones de margen están actualmente en el lado correcto del vecindario
de decisión.

import numpy as np

from sklearn import datasets

from sklearn.pipeline import Pipeline

from sklearn.preprocessing import StandardScaler

from sklearn.svm import LinearSVC

iris = datasets.load_iris()

X = iris["data"][:, (2, 3)]  # petal length, petal width

y = (iris["target"] == 2).astype(np.float64)  # Iris virginica

svm_clf = Pipeline([

        ("scaler", StandardScaler()),

        ("linear_svc", LinearSVC(C=1, loss="hinge", random_state=42)),

    ])

https://colab.research.google.com/drive/1GvXk2fxCTo2GwDHIkyCzEYayjyTZi8zv?usp=sharing#printMode=true 5/19
8/5/22, 00:15 Capítulo 5 - SVM - Colaboratory

svm_clf.fit(X, y)

Pipeline(steps=[('scaler', StandardScaler()),

('linear_svc', LinearSVC(C=1, loss='hinge', random_state=42))])

Luego como es usual, puedes usar el modelo para hacer predicciones

svm_clf.predict([[5.5, 1.7]])

array([1.])

scaler = StandardScaler()

svm_clf1 = LinearSVC(C=1, loss="hinge", random_state=42)

svm_clf2 = LinearSVC(C=100, loss="hinge", random_state=42)

scaled_svm_clf1 = Pipeline([

        ("scaler", scaler),

        ("linear_svc", svm_clf1),

    ])

scaled_svm_clf2 = Pipeline([

        ("scaler", scaler),

        ("linear_svc", svm_clf2),

    ])

scaled_svm_clf1.fit(X, y)

scaled_svm_clf2.fit(X, y)

/usr/local/lib/python3.7/dist-packages/sklearn/svm/_base.py:1208: ConvergenceWarning
ConvergenceWarning,

Pipeline(steps=[('scaler', StandardScaler()),

('linear_svc',

LinearSVC(C=100, loss='hinge', random_state=42))])

El siguiente código de Scikit-learn, carga el dataset iris, escala las características y entrena el
modelo SVM (usando el LinearSVC con C=1 y la función hinge loss) para detectar las flores Iris-
Virginica. El resultado del modelo se representa en la figura.

# Convert to unscaled parameters

b1 = svm_clf1.decision_function([-scaler.mean_ / scaler.scale_])

b2 = svm_clf2.decision_function([-scaler.mean_ / scaler.scale_])

w1 = svm_clf1.coef_[0] / scaler.scale_

w2 = svm_clf2.coef_[0] / scaler.scale_

svm_clf1.intercept_ = np.array([b1])

svm_clf2.intercept_ = np.array([b2])

svm_clf1.coef_ = np.array([w1])

svm_clf2.coef_ = np.array([w2])

https://colab.research.google.com/drive/1GvXk2fxCTo2GwDHIkyCzEYayjyTZi8zv?usp=sharing#printMode=true 6/19
8/5/22, 00:15 Capítulo 5 - SVM - Colaboratory

# Find support vectors (LinearSVC does not do this automatically)

t = y * 2 - 1

support_vectors_idx1 = (t * (X.dot(w1) + b1) < 1).ravel()

support_vectors_idx2 = (t * (X.dot(w2) + b2) < 1).ravel()

svm_clf1.support_vectors_ = X[support_vectors_idx1]

svm_clf2.support_vectors_ = X[support_vectors_idx2]

fig, axes = plt.subplots(ncols=2, figsize=(10,2.7), sharey=True)

plt.sca(axes[0])

plt.plot(X[:, 0][y==1], X[:, 1][y==1], "g^", label="Iris virginica")

plt.plot(X[:, 0][y==0], X[:, 1][y==0], "bs", label="Iris versicolor")

plot_svc_decision_boundary(svm_clf1, 4, 5.9)

plt.xlabel("Petal length", fontsize=14)

plt.ylabel("Petal width", fontsize=14)

plt.legend(loc="upper left", fontsize=14)

plt.title("$C = {}$".format(svm_clf1.C), fontsize=16)

plt.axis([4, 5.9, 0.8, 2.8])

plt.sca(axes[1])

plt.plot(X[:, 0][y==1], X[:, 1][y==1], "g^")

plt.plot(X[:, 0][y==0], X[:, 1][y==0], "bs")

plot_svc_decision_boundary(svm_clf2, 4, 5.99)

plt.xlabel("Petal length", fontsize=14)

plt.title("$C = {}$".format(svm_clf2.C), fontsize=16)

plt.axis([4, 5.9, 0.8, 2.8])

(4.0, 5.9, 0.8, 2.8)

Alternativamente, puedes usar la clase SVC, usando SVC(kernel="linear", C=1) , pero es


mucho mas lento, especialmente con grandes conjuntos de entrenamiento, por lo que no es
recomendado. Otra opción es usar la clase SGDClassifier , con SGDClassifier(loss="hinge",
alpha=1/(m*C)) . Esto aplica el Gradiente estocástico descendente para entrenar un clasificador
SVM. Este no converge tan rápido como la clase LinearSVC , pero puede ser util para manejar
grandes conjuntos de datos que no entran en la memoria (entrenamiento out-of-core) o para
manejar tareas de clasificiación en linea.

Clasificación SVM no lineal

https://colab.research.google.com/drive/1GvXk2fxCTo2GwDHIkyCzEYayjyTZi8zv?usp=sharing#printMode=true 7/19
8/5/22, 00:15 Capítulo 5 - SVM - Colaboratory

A pesar de que los clasificadores SVM lineales son eficientes y trabajan asombrosamente bien
en muchos casos, muchos conjuntos de datos no son linealmente separables. Una forma de
manejar conjuntos de datos es agregar más características, tales como características
polinomiales; en algunos casos esto puede resultar en un conjunto linealmente separable.
Considere el gráfico de la izquierda: representa un conjunto de datos con exáctamente una
característica x1 . Este conjunto de datos no es linealmente separable, como puedes ver. Pero si
agregas una segunda característica x2 = (x1 )
2
, el resutlado es un conjunto de datos 2D que
es perfectamente separable de forma lineal

X1D = np.linspace(-4, 4, 9).reshape(-1, 1)

X2D = np.c_[X1D, X1D**2]

y = np.array([0, 0, 1, 1, 1, 1, 1, 0, 0])

plt.figure(figsize=(10, 3))

plt.subplot(121)

plt.grid(True, which='both')

plt.axhline(y=0, color='k')

plt.plot(X1D[:, 0][y==0], np.zeros(4), "bs")

plt.plot(X1D[:, 0][y==1], np.zeros(5), "g^")

plt.gca().get_yaxis().set_ticks([])

plt.xlabel(r"$x_1$", fontsize=20)

plt.axis([-4.5, 4.5, -0.2, 0.2])

plt.subplot(122)

plt.grid(True, which='both')

plt.axhline(y=0, color='k')

plt.axvline(x=0, color='k')

plt.plot(X2D[:, 0][y==0], X2D[:, 1][y==0], "bs")

plt.plot(X2D[:, 0][y==1], X2D[:, 1][y==1], "g^")

plt.xlabel(r"$x_1$", fontsize=20)

plt.ylabel(r"$x_2$  ", fontsize=20, rotation=0)

plt.gca().get_yaxis().set_ticks([0, 4, 8, 12, 16])

plt.plot([-4.5, 4.5], [6.5, 6.5], "r--", linewidth=3)

plt.axis([-4.5, 4.5, -1, 17])

plt.subplots_adjust(right=1)

plt.show()

https://colab.research.google.com/drive/1GvXk2fxCTo2GwDHIkyCzEYayjyTZi8zv?usp=sharing#printMode=true 8/19
8/5/22, 00:15 Capítulo 5 - SVM - Colaboratory

Para implementar esta idea usando Scikit-Learn, puedes crear un Pipeline conteniendo un
transformador PolinomialFeatures , seguido por un StandardScaler y un LinearSVC . Vamos a
probar esto en el dataset "moons": este es un dataset de juego en el cual los puntos de datos
tienen la forma de dos semicírculos intercalados. Puedes generar este datasetet usando la
función make_moons() :

from sklearn.datasets import make_moons

X, y = make_moons(n_samples=100, noise=0.15, random_state=42)

def plot_dataset(X, y, axes):

    plt.plot(X[:, 0][y==0], X[:, 1][y==0], "bs")

    plt.plot(X[:, 0][y==1], X[:, 1][y==1], "g^")

    plt.axis(axes)

    plt.grid(True, which='both')

    plt.xlabel(r"$x_1$", fontsize=20)

    plt.ylabel(r"$x_2$", fontsize=20, rotation=0)

plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])

plt.show()

from sklearn.datasets import make_moons

from sklearn.pipeline import Pipeline

from sklearn.preprocessing import PolynomialFeatures

polynomial_svm_clf = Pipeline([

        ("poly_features", PolynomialFeatures(degree=50)),

        ("scaler", StandardScaler()),

        ("svm_clf", LinearSVC(C=10, loss="hinge", random_state=42))

    ])

polynomial_svm_clf.fit(X, y)

/usr/local/lib/python3.7/dist-packages/sklearn/svm/_base.py:1208: ConvergenceWarning
ConvergenceWarning,

https://colab.research.google.com/drive/1GvXk2fxCTo2GwDHIkyCzEYayjyTZi8zv?usp=sharing#printMode=true 9/19
8/5/22, 00:15 Capítulo 5 - SVM - Colaboratory

Pipeline(steps=[('poly_features', PolynomialFeatures(degree=50)),

('scaler', StandardScaler()),

('svm_clf', LinearSVC(C=10, loss='hinge', random_state=42))])

def plot_predictions(clf, axes):

    x0s = np.linspace(axes[0], axes[1], 100)

    x1s = np.linspace(axes[2], axes[3], 100)

    x0, x1 = np.meshgrid(x0s, x1s)

    X = np.c_[x0.ravel(), x1.ravel()]

    y_pred = clf.predict(X).reshape(x0.shape)

    y_decision = clf.decision_function(X).reshape(x0.shape)

    plt.contourf(x0, x1, y_pred, cmap=plt.cm.brg, alpha=0.2)

    plt.contourf(x0, x1, y_decision, cmap=plt.cm.brg, alpha=0.1)

plot_predictions(polynomial_svm_clf, [-1.5, 2.5, -1, 1.5])

plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])

plt.show()

Kernel polinomial
Agregar características polinomiales es sencillo y puede trabajar con todos los algoritmos de
Machine Learning (no precisamente SVM), pero con grados de polinomios bajos no se puede
manejar datasets complejos, y con un polinomio de alto grado se crean una gran cantidad de
características, haciendo el modelo mas lento.

Afortunadamente, cuando usamos SMVs puedes aplicar algunas técnicas matemáticas


milagrosas llamadas trucos de kernel. Esto hace posible obtener el mismo resultado como si
agregas muchas características polinomiales, aun con polinomios de alto grado, sin tener que
agregarlos. No hay una explosión combinatoria de la cantidad de características dado que no
agregas ninguna. Este truco es implementado po la clase SVC. Probémoslo en la base de datos
"moons":

from sklearn.svm import SVC

https://colab.research.google.com/drive/1GvXk2fxCTo2GwDHIkyCzEYayjyTZi8zv?usp=sharing#printMode=true 10/19
8/5/22, 00:15 Capítulo 5 - SVM - Colaboratory

poly_kernel_svm_clf = Pipeline([

        ("scaler", StandardScaler()),

        ("svm_clf", SVC(kernel="poly", degree=3, coef0=1, C=5))

    ])

poly_kernel_svm_clf.fit(X, y)

Pipeline(steps=[('scaler', StandardScaler()),

('svm_clf', SVC(C=5, coef0=1, kernel='poly'))])

Este contiene un clasificador SVM usando un kernel polinomial de grado 3º. En el lado derecho
hay otro clasificador SVM usando un kernel polinomial de grado 10º. Obviamente si tu modelo
estar sobreentrenado podrías desear reducir el grado del polinomio. El hiperparámetro coef0
controla cuánto el modelo es influencidado por polinomios de alto grado versus polinomios de
grado bajo.

poly100_kernel_svm_clf = Pipeline([

        ("scaler", StandardScaler()),

        ("svm_clf", SVC(kernel="poly", degree=10, coef0=100, C=5))

    ])

poly100_kernel_svm_clf.fit(X, y)

Pipeline(steps=[('scaler', StandardScaler()),

('svm_clf', SVC(C=5, coef0=100, degree=10, kernel='poly'))])

# Este bloque solo genera el gráfico, puede pasarse por alto

fig, axes = plt.subplots(ncols=2, figsize=(10.5, 4), sharey=True)

plt.sca(axes[0])

plot_predictions(poly_kernel_svm_clf, [-1.5, 2.45, -1, 1.5])

plot_dataset(X, y, [-1.5, 2.4, -1, 1.5])

plt.title(r"$d=3, r=1, C=5$", fontsize=18)

plt.sca(axes[1])

plot_predictions(poly100_kernel_svm_clf, [-1.5, 2.45, -1, 1.5])

plot_dataset(X, y, [-1.5, 2.4, -1, 1.5])

plt.title(r"$d=10, r=100, C=5$", fontsize=18)

plt.ylabel("")

plt.show()

https://colab.research.google.com/drive/1GvXk2fxCTo2GwDHIkyCzEYayjyTZi8zv?usp=sharing#printMode=true 11/19
8/5/22, 00:15 Capítulo 5 - SVM - Colaboratory

Agregando características similares


Otra forma de abordar problemas lineales es agregar características calculadas usando una
función de similaridad que mida cúanto se asemeja cada instancia a una referencia particular.
Por ejemplo, vamos a tomar el conjunto unidimensional visto anteriormente y agregar dos
referencias a este en x1 = −2 y x1 = 1 . Luego vamos a definir la función de similitud que será
la función de base radial gausiana (RBF) con γ = 0.3 .
2
ϕ (x, l) = exp(−γ∥x − l∥ )
γ

Esta es una función en forma de campana que varía de 0 (muy lejos de la marca) a 1 (en la
marca). Ahora estamos listos para calcular las nuevas características. Por ejemplo, vamos a ver
la instancia x1 = −1 : esta se localiza a 1 de la primera referencia y 2 de la segunda referencia.
Por tanto sus nuevas características son x2 = exp(−0.3 × 1 ) ≈ 0.74
2
y
x2 = exp(−0.3 × 2 ) ≈ 0.30
2
. La gráfica de la derecha muestra el dataset transformado.
Como puedes ver ahora es linealmente separable.

# Este bloque solo genera el gráfico, puede pasarse por alto

def gaussian_rbf(x, landmark, gamma):

    return np.exp(-gamma * np.linalg.norm(x - landmark, axis=1)**2)

gamma = 0.3

x1s = np.linspace(-4.5, 4.5, 200).reshape(-1, 1)

x2s = gaussian_rbf(x1s, -2, gamma)

x3s = gaussian_rbf(x1s, 1, gamma)

XK = np.c_[gaussian_rbf(X1D, -2, gamma), gaussian_rbf(X1D, 1, gamma)]

yk = np.array([0, 0, 1, 1, 1, 1, 1, 0, 0])

plt.figure(figsize=(10.5, 4))

plt.subplot(121)

plt.grid(True, which='both')

plt.axhline(y=0, color='k')

plt.scatter(x=[-2, 1], y=[0, 0], s=150, alpha=0.5, c="red")

plt.plot(X1D[:, 0][yk==0], np.zeros(4), "bs")

plt.plot(X1D[:, 0][yk==1], np.zeros(5), "g^")

plt.plot(x1s, x2s, "g--")

plt.plot(x1s, x3s, "b:")

plt.gca().get_yaxis().set_ticks([0, 0.25, 0.5, 0.75, 1])

plt.xlabel(r"$x_1$", fontsize=20)

plt.ylabel(r"Similarity", fontsize=14)

plt.annotate(r'$\mathbf{x}$',

https://colab.research.google.com/drive/1GvXk2fxCTo2GwDHIkyCzEYayjyTZi8zv?usp=sharing#printMode=true 12/19
8/5/22, 00:15 Capítulo 5 - SVM - Colaboratory

             xy=(X1D[3, 0], 0),

             xytext=(-0.5, 0.20),

             ha="center",

             arrowprops=dict(facecolor='black', shrink=0.1),

             fontsize=18,

            )

plt.text(-2, 0.9, "$x_2$", ha="center", fontsize=20)

plt.text(1, 0.9, "$x_3$", ha="center", fontsize=20)

plt.axis([-4.5, 4.5, -0.1, 1.1])

plt.subplot(122)

plt.grid(True, which='both')

plt.axhline(y=0, color='k')

plt.axvline(x=0, color='k')

plt.plot(XK[:, 0][yk==0], XK[:, 1][yk==0], "bs")

plt.plot(XK[:, 0][yk==1], XK[:, 1][yk==1], "g^")

plt.xlabel(r"$x_2$", fontsize=20)

plt.ylabel(r"$x_3$  ", fontsize=20, rotation=0)

plt.annotate(r'$\phi\left(\mathbf{x}\right)$',

             xy=(XK[3, 0], XK[3, 1]),

             xytext=(0.65, 0.50),

             ha="center",

             arrowprops=dict(facecolor='black', shrink=0.1),

             fontsize=18,

            )

plt.plot([-0.1, 1.1], [0.57, -0.1], "r--", linewidth=3)

plt.axis([-0.1, 1.1, -0.1, 1.1])

    

plt.subplots_adjust(right=1)

plt.show()

x1_example = X1D[3, 0]

for landmark in (-2, 1):

    k = gaussian_rbf(np.array([[x1_example]]), np.array([[landmark]]), gamma)

    print("Phi({}, {}) = {}".format(x1_example, landmark, k))

Phi(-1.0, -2) = [0.74081822]

Phi(-1.0, 1) = [0.30119421]

https://colab.research.google.com/drive/1GvXk2fxCTo2GwDHIkyCzEYayjyTZi8zv?usp=sharing#printMode=true 13/19
8/5/22, 00:15 Capítulo 5 - SVM - Colaboratory

Kernel RBF gausiano


Tal como el método de características polinomiales, el método de caracteíticas similares puede
ser util con cualquier algoritmo de Machine Learning, pero esto puede ser computacionalmente
caro para calcular todas las características adicionales, especialmente en grandes conjuntos de
datos. Sin embargo, una vez mas el truco del kernel es la magia de SVM: es posible obtener un
resultado similar a haber agregado características similares, sin tener que agregarlas
efectivamente. Vamos a probar el kernel RBF Gausiano usando la clase SVC:

rbf_kernel_svm_clf = Pipeline([

        ("scaler", StandardScaler()),

        ("svm_clf", SVC(kernel="rbf", gamma=5, C=0.001))

    ])

rbf_kernel_svm_clf.fit(X, y)

Pipeline(steps=[('scaler', StandardScaler()),

('svm_clf', SVC(C=0.001, gamma=5))])

Este modelo se representa en la figura abajo a la izquierda. Los otros gráficos muestran
modelos entrenados con diferentes valores de los hiperparámetros gamma (γ ) y C.
Incrementando gamma se estrecha la campana de la curva, y como resultado cada rango de
instancia de influencia es pequeño: el vecindario de decisión termina siendo más irregular,
moviendose al rededora de instancias individuales. Por el contrario, un gamma pequeño hace que
la campana se anche, de forma que las instancias tienen un rango grande de influencia, y el
vecindario de decisión termina siendo más suave. De modo que γ funciona como un
hiperparámetro de regularización: si tu modelo está sobreentrenado deberías reducirlo, y si
estaá subentrenado, deberísa incrementarlo (similar al hiperparámetro C).

from sklearn.svm import SVC

gamma1, gamma2 = 0.1, 5

C1, C2 = 0.001, 1000

hyperparams = (gamma1, C1), (gamma1, C2), (gamma2, C1), (gamma2, C2)

svm_clfs = []

for gamma, C in hyperparams:

    rbf_kernel_svm_clf = Pipeline([

            ("scaler", StandardScaler()),

            ("svm_clf", SVC(kernel="rbf", gamma=gamma, C=C))

        ])

    rbf_kernel_svm_clf.fit(X, y)

    svm_clfs.append(rbf_kernel_svm_clf)

# Esta parte genera los cuatro gráficos de esta sección

fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(10.5, 7), sharex=True, sharey=True)

https://colab.research.google.com/drive/1GvXk2fxCTo2GwDHIkyCzEYayjyTZi8zv?usp=sharing#printMode=true 14/19
8/5/22, 00:15 Capítulo 5 - SVM - Colaboratory

for i, svm_clf in enumerate(svm_clfs):

    plt.sca(axes[i // 2, i % 2])

    plot_predictions(svm_clf, [-1.5, 2.45, -1, 1.5])

    plot_dataset(X, y, [-1.5, 2.45, -1, 1.5])

    gamma, C = hyperparams[i]

    plt.title(r"$\gamma = {}, C = {}$".format(gamma, C), fontsize=16)

    if i in (0, 1):

        plt.xlabel("")

    if i in (1, 3):

        plt.ylabel("")

plt.show()

Existen otros kernels, pero son usados mucho mas ráramente. Por ejemplo, algunos kernels
están especializados para estructuras de datos específicas. Los kernel de cadena son algunas
veces usadas cuando clasificamos documentos de texto o secuencias de ADN (i.e, usando el
kernel de subsecuencias de cadenas o kernels basados en la distancia Levensthein).

Complejidad computacional
La clase LinearSVC se basa en la biblioteca liblinear , la cual implementa un algoritmo
optimizado para SVMs linealies. Esto no soporta el truco del kernel, pero esto escala
linealmente con la cantidad de instancias de entrenamiento y la candidad de características: su
complejidad computacional de entrenamiento es aproximadamente O(m × n .

https://colab.research.google.com/drive/1GvXk2fxCTo2GwDHIkyCzEYayjyTZi8zv?usp=sharing#printMode=true 15/19
8/5/22, 00:15 Capítulo 5 - SVM - Colaboratory

La clase SVC está basada en la biblioteca libsvm , la cual implementa un algoritmo que soporta
el truco del kernel. El tiempo de corrida está usualmente entre O(m2 × n) y O(m3 × n) .
Desafortunadamente, esto significa que será extremadamente lento cuando la cantidad de
instancias de entrenamiento es grande (cientos o miles de instancias). Este algoritmo es
perfecto para conjuntos de entrenamiento complejos pero pequeños. Sin embargo, esto escala
bien con el número de características, especialmente con características poco densas.

Regresión SVM

Como mencionamos anteriormente, el algoritmo SVM es bastante versatil: no solo soporta


clasificación lineal y no lineal, sino que también soporta regresión lineal y no lineal. El truco es
invertir el objetivo: en lugar de tratar de ajustar el ancho de la calle a lo más ancha posible entre
dos clases mientras se limitan las violaciones de margen, la regresión SVM intenta encajar
muchas instancias como sea posible sobre la calle, mientras se limitan las violaciones de
margen (instancias fuera de la calle). El ancho de la calles es controlado por un hiperparámetro ϵ
. La figura muestra dos modelos de regresijón SVM entrenados en alguna data lineal aleatoria,
uno con un margen grande (ϵ = 1.5 ) y las otras con un pequeño margen (ϵ = 0.5 ).

np.random.seed(42)

m = 50

X = 2 * np.random.rand(m, 1)

y = (4 + 3 * X + np.random.randn(m, 1)).ravel()

from sklearn.svm import LinearSVR

svm_reg = LinearSVR(epsilon=1.5, random_state=42)

svm_reg.fit(X, y)

LinearSVR(epsilon=1.5, random_state=42)

Agregar más instancias de entrenamiento dentro del margen no afecta a la predicción del
modelo; por lo tanto, se dice que el modelo es ϵ − insensivio*.

Puees usar las clase LinearSVR Scikit-Learn para realizar regresión SVM. El siguiente código
produce el modelo representado en la figura de la izquierda (la data de entrenamiento debe
estar escalada y centrada):

svm_reg1 = LinearSVR(epsilon=1.5, random_state=42)

svm_reg2 = LinearSVR(epsilon=0.5, random_state=42)

svm_reg1.fit(X, y)

svm_reg2.fit(X, y)

https://colab.research.google.com/drive/1GvXk2fxCTo2GwDHIkyCzEYayjyTZi8zv?usp=sharing#printMode=true 16/19
8/5/22, 00:15 Capítulo 5 - SVM - Colaboratory

def find_support_vectors(svm_reg, X, y):

    y_pred = svm_reg.predict(X)

    off_margin = (np.abs(y - y_pred) >= svm_reg.epsilon)

    return np.argwhere(off_margin)

svm_reg1.support_ = find_support_vectors(svm_reg1, X, y)
svm_reg2.support_ = find_support_vectors(svm_reg2, X, y)

eps_x1 = 1

eps_y_pred = svm_reg1.predict([[eps_x1]])

# Este codigo es solo para generar el gráfico

def plot_svm_regression(svm_reg, X, y, axes):

    x1s = np.linspace(axes[0], axes[1], 100).reshape(100, 1)

    y_pred = svm_reg.predict(x1s)

    plt.plot(x1s, y_pred, "k-", linewidth=2, label=r"$\hat{y}$")

    plt.plot(x1s, y_pred + svm_reg.epsilon, "k--")

    plt.plot(x1s, y_pred - svm_reg.epsilon, "k--")

    plt.scatter(X[svm_reg.support_], y[svm_reg.support_], s=180, facecolors='#FFAAAA')

    plt.plot(X, y, "bo")

    plt.xlabel(r"$x_1$", fontsize=18)

    plt.legend(loc="upper left", fontsize=18)

    plt.axis(axes)

fig, axes = plt.subplots(ncols=2, figsize=(9, 4), sharey=True)

plt.sca(axes[0])

plot_svm_regression(svm_reg1, X, y, [0, 2, 3, 11])

plt.title(r"$\epsilon = {}$".format(svm_reg1.epsilon), fontsize=18)

plt.ylabel(r"$y$", fontsize=18, rotation=0)

#plt.plot([eps_x1, eps_x1], [eps_y_pred, eps_y_pred - svm_reg1.epsilon], "k-", linewidth=2
plt.annotate(

        '', xy=(eps_x1, eps_y_pred), xycoords='data',

        xytext=(eps_x1, eps_y_pred - svm_reg1.epsilon),

        textcoords='data', arrowprops={'arrowstyle': '<->', 'linewidth': 1.5}

    )

plt.text(0.91, 5.6, r"$\epsilon$", fontsize=20)

plt.sca(axes[1])

plot_svm_regression(svm_reg2, X, y, [0, 2, 3, 11])

plt.title(r"$\epsilon = {}$".format(svm_reg2.epsilon), fontsize=18)

plt.show()

https://colab.research.google.com/drive/1GvXk2fxCTo2GwDHIkyCzEYayjyTZi8zv?usp=sharing#printMode=true 17/19
8/5/22, 00:15 Capítulo 5 - SVM - Colaboratory

Para hacer regresión no lineal, puedes usar un modelo SMV kernelizado. Por ejemplo, la figura
siguiente muestra una regresión SVM en un conjunto de entrenamiento aleatorio cuadrático,
usando un kernel polinomial de grado 2. Hay una pequeña regularización en el gráfico de la
izquierda (i.e. un valor C grande), y mucha mayor regularización en el gráfico de la derecha (i.e
con un valor de C pequeño).

El siguiente código produce el modelo representado en la figura de la izquierda usando la clase


SVR de Scikit-Learn (el cual soporta el truco del kernel). La clase SVR es la regresión equivalente
a la clase SVC, y la clase LinearSVR es la regresión equivalente a la clase LinearSVC . La clase
LinearSVR escala linealmente con el tamaño del conjunto de entrenamiento (igual que la clase
LinearSVC ), mientras que la clase SVR es un poco mas lenta cuando el conjunto de
entrenamiento crece (justo como la clase SVC).

np.random.seed(42)

m = 100

X = 2 * np.random.rand(m, 1) - 1

y = (0.2 + 0.1 * X + 0.5 * X**2 + np.random.randn(m, 1)/10).ravel()

from sklearn.svm import SVR

svm_poly_reg = SVR(kernel="poly", degree=2, C=100, epsilon=0.1, gamma="scale")

svm_poly_reg.fit(X, y)

SVR(C=100, degree=2, kernel='poly')

from sklearn.svm import SVR

svm_poly_reg1 = SVR(kernel="poly", degree=2, C=100, epsilon=0.1, gamma="scale")

svm_poly_reg2 = SVR(kernel="poly", degree=2, C=0.01, epsilon=0.1, gamma="scale")

svm_poly_reg1.fit(X, y)

svm_poly_reg2.fit(X, y)

SVR(C=0.01, degree=2, kernel='poly')

fig, axes = plt.subplots(ncols=2, figsize=(9, 4), sharey=True)

plt.sca(axes[0])

plot_svm_regression(svm_poly_reg1, X, y, [-1, 1, 0, 1])

plt.title(r"$degree={}, C={}, \epsilon = {}$".format(svm_poly_reg1.degree, svm_poly_reg1.C
plt.ylabel(r"$y$", fontsize=18, rotation=0)

plt.sca(axes[1])

plot_svm_regression(svm_poly_reg2, X, y, [-1, 1, 0, 1])

plt.title(r"$degree={}, C={}, \epsilon = {}$".format(svm_poly_reg2.degree, svm_poly_reg2.C
plt.show()

https://colab.research.google.com/drive/1GvXk2fxCTo2GwDHIkyCzEYayjyTZi8zv?usp=sharing#printMode=true 18/19
8/5/22, 00:15 Capítulo 5 - SVM - Colaboratory

https://colab.research.google.com/drive/1GvXk2fxCTo2GwDHIkyCzEYayjyTZi8zv?usp=sharing#printMode=true 19/19

También podría gustarte