!
pip install make-spirals
Support Vector Machines (SVM)
LinearSVC
Las Máquinas de Vectores Soporte (Support Vector Machines) realizan la clasificación
encontrando el hiperplano que maximiza el margen entre las clases presentes en un conjunto de
datos. Este concepto, que de entrada puede parecer confuso, es en realidad una idea bastante
intuitiva y viene descrita por la siguiente figura extraida de Wikipedia:
En ella podemos observar claramente un conjunto de datos compuesto por 19 muestras
(número de puntos) descritas por dos características ( x 1 y x 2) que se clasifican en dos clases (azul
y verde). El objetivo de cualquier clasificador es encontrar un hiperplano que separe todo el
espacio en 2 de tal modo que las muestras de la clase azul queden a un lado de dicho hiperplano
y las muestras de la clase verde queden al lado contrario.
Existen, por tanto, infinitos hiperplanos que son capaces de realizar esta división sin dejar
ninguna muestra mal clasificada. Cuando utilizamos SVM lo que haremos será fijar este
hiperplano como aquel que tenga mayor margen, es decir, aquel que mantenga la mayor
distancia con aquellas muestras que está más cerca de dicho hiperplano. A este hiperplano le
denominaremos límite de decisión (decision boundary).
Esta idea tan básica nos proporciona la definición de vectores soporte (support vectors) que dan
nombre al método. Un vector soporte será aquella muestra que está incorrectamente clasificada
o que se encuentra cerca del límite de decisión. En la figura anterior observamos 3 vectores
soporte: 2 azules y 1 verde.
Entendida la idea conceptual del clasificador, veamos como podemos formalizarla
matemáticamente. No olvidemos que estamos trabajando con un problema de clasificación (por
ahora lineal) por lo que la ecuación que define la salida de nuestro clasificador será de la forma:
m
w ⋅ ⃗x i=b + ∑ w j ⋅ x i , j
^y i=b +⃗
j=1
w =( w1 , w2 , … , wm ) los parámetros que el modelo debe aprender.
Siendo b y ⃗
Las Maquinas de Vector Soporte realizan una clasificación binaria atendiendo al siguiente
criterio:
• w ⋅ ⃗x i ≥1 la muestra pertenece a la clase 0.
Si ^y i=b +⃗
• w ⋅ ⃗x i ≤− 1 la muestra pertenece a la clase 1.
Si ^y i=b +⃗
Nótese que los identificadores clase 0 y clase 1 son meras etiquetas, nada tienen que ver con los
valores límite de 1 y −1 definidos por SVM.
Enlazando esto con la explicación conceptual del método y volviendo a la figura previamente
presentada, entendemos que los vectores soporte de la clase 0 son los que verifican que
w ⋅ ⃗x i=1 mientras que los vectores soporte de la clase 1 son los que verifican que
b+ ⃗
w ⋅ ⃗x i=−1 . Identificaremos por tanto dos nuevos hiperplanos: el hiperplano positivo como
b+ ⃗
aquel en el que se sitúan los vectores soporte de la clase 0 y el hiperplano negativo como aquel
en el que se sitúan los vectores soporte de la clase 1.
Con dichos hiperplanos podemos encontrar el límite de decisión como aquel hiperplano que
equidiste del hiperplano positivo y del hiperplano negativo. Para ello nos apoyaremos en el
concepto de margen (margin) que se encarga de medir la distancia entre los hiperplanos
positivo y negativo. El valor del margen vendrá definido por la siguiente ecuación:
2
m a r g i n=
|) ⃗
w|)
Valor que obtenemos si situamos dos muestras ⃗x 1 y ⃗x 2 una en frente de otra sobre los
hiperplanos positivo y negativo. Analíticamente:
w ⋅ ⃗x 1=1
b+ ⃗
w ⋅ ⃗x 2=−1
b+ ⃗
( b+ ⃗
w ⋅ ⃗x 1 ) − ( b+ ⃗
w ⋅ ⃗x 2 )=1 − ( −1 )
w ⋅ ( ⃗x1 − ⃗x 2 )=2
⃗
2
m a r g i n= ⃗x 1 − ⃗x 2=
|)⃗
w|)
La clave del funcionamiento de las Máquinas de Vector Soporte, y su principal diferencia
respecto a otros clasificadores lineales como la Regresión Logística, radica en el hecho de que
únicamente los vectores soporte tienen impacto en el entrenamiento del modelo. Todas las
muestras del conjunto de datos que no sean vectores soporte no condicionan la definición del
límite de decisión.
Para lograr esto las Máquinas de Vector Soporte se construyen apoyándose en la función de
pérdida Hinge Loss que podemos observar a continuación:
import [Link] as plt
import numpy as np
[Link](figsize=(6, 5))
hinge = [Link](lambda v: abs(min(v-1,0.0)))
x = [Link](-4, 4)
y = hinge(x)
[Link](x, y)
[Link]("b+w·x")
[Link]("hinge(b+w·x)")
[Link]()
A menudo, a esta función se le conoce como c o s t y tiene por objetivo sancionar únicamente a
las muestras que se encuentren mal clasificadas o muy cercanas al límite de decisión.
Retomemos un concepto explicado anteriormente, si se verifica que b+ ⃗ w ⋅ ⃗x i ≥ 1 entonces ⃗x i
pertenecerá a la clase 0. Asumiendo que la gráfica anterior hace referencia a la clase 0, todas las
muestras con b+ ⃗ w ⋅ ⃗x i ≥ 1 tendrán c o s t=0. Dicho de otro modo, todas las muestras bien
clasificadas tendrán c o s t=0. Por el contrario, a medida que nos alejamos del hiperplano
w ⋅ ⃗x i=1) y nos acercamos al límite de decisión (b+ ⃗
positivo (b+ ⃗ w ⋅ ⃗x i=0) el valor de c o s t
aumenta.
Entendida esta idea es evidente que necesitaremos contra función c o s t para la clase 1,
quedando definidas por tanto c o s t 0 y c o s t 1 del siguiente modo:
import [Link] as plt
fig, axs = [Link](1,2, figsize=(12, 5))
cost_0 = [Link](lambda v: max(1-v,0.0))
cost_1 = [Link](lambda v: max(1+v,0.0))
x = [Link](-4, 4)
axs[0].plot(x, cost_0(x))
axs[0].set_xlabel("b+w·x")
axs[0].set_ylabel("cost_0")
axs[1].plot(x, cost_1(x))
axs[1].set_xlabel("b+w·x")
axs[1].set_ylabel("cost_1")
Matemáticamente podemos definir las funciones anteriores con las siguientes expresiones:
w ⋅ ⃗x i
^y i=b +⃗
c o s t 0 ( ^y i ) =ma x ( 0 , 1− ^y i )
c o s t 1 ( ^y i )=ma x ( 0 ,1+ ^y i)
Y unificarlas del siguiente modo:
c o s t ( ^y i ) = y i ⋅ c o s t 1 ( ^y i ) + ( 1 − yi ) ⋅c o s t 0 ( ^y i )
c o s t ( ^y i ) = y i ⋅m a x ( 0 ,1+ ^y i ) + ( 1− y i ) ⋅ ma x ( 0 ,1 − ^yi )
Si tenemos en cuenta los datos de entrada obtenemos:
c o s t ( ⃗x i )= y i ⋅m a x ( 0 , 1+b+ ⃗
w ⋅ ⃗x i ) + ( 1 − y i ) ⋅m a x ( 0 , 1− b − ⃗
w ⋅⃗xi )
Y finalmente podemos definir la función de coste de las máquinas de vector soporte añadiendo
un término de regularización como:
[∑ )
n m
1
l o s s=C y i ⋅ ma x ( 0 ,1+ b+⃗ w ⋅ x⃗ i ) + ∑ w2j
w ⋅ ⃗x i ) + ( 1 − y i ) ⋅m a x ( 0 ,1 −b − ⃗
i=1 2 j=1
Donde n representa el número de muestras y m el número de características o features.
Observamos, además, que se ha añadido un híper-parámetros C al modelo. C , al que podemos
llamar no-regularización, permite controlar el proceso de aprendizaje: valores grandes de C
harán el modelo muy sensible a los outlayers mientras que valores bajos de C harán el método
muy generalista.
Las Máquinas de Vector Soporte (lineales) se encuentra definidas en sklearn en la clase
[Link]. Como se observa en su documenta, la clase dispone de un
parámetro C que se corresponde con el híper-parámetro C que controla la no-regularización.
Una vez ajustado el modelo podemos recuperar sus parámetros accediendo a sus atributos
coef_(que contiene ⃗ w ) e intercep_(que contiene b ).
Veamos un ejemplo.
Supongamos el siguiente conjunto de datos:
import numpy as np
import [Link] as plt
from [Link] import make_blobs
X, y = make_blobs(n_samples=100, n_features=2, centers=2,
random_state=3)
min = [Link](X, axis=0)
max = [Link](X, axis=0)
diff = max - min
min = min - 0.1 * diff
max = max + 0.1 * diff
[Link]()
[Link](min[0], max[0])
[Link](min[1], max[1])
[Link]('X1')
[Link]('X2')
[Link](X[:,0], X[:,1], c=y, cmap=[Link])
Ajustemos un modelo mediante LinearSVC:
from [Link] import LinearSVC
linear_svm = LinearSVC().fit(X, y)
print("coef_ = " + str(linear_svm.coef_))
print("intercept_ = " + str(linear_svm.intercept_))
Y lo pintamos:
import numpy as np
[Link]()
[Link](min[0], max[0])
[Link](min[1], max[1])
[Link]('X1')
[Link]('X2')
[Link](X[:,0], X[:,1], c=y, cmap=[Link])
line = [Link](min[0], max[0])
[Link](line, -(line * linear_svm.coef_[0][0] +
linear_svm.intercept_) / linear_svm.coef_[0][1], color='green',
label='Límite de decisión')
[Link](line, (1-(line * linear_svm.coef_[0][0] +
linear_svm.intercept_)) / linear_svm.coef_[0][1], color='red',
linestyle='--', label='Hiperplano positivo')
[Link](line, (-1-(line * linear_svm.coef_[0][0] +
linear_svm.intercept_)) / linear_svm.coef_[0][1], color='blue',
linestyle='--', label='Hiperplano negativo')
[Link](framealpha=0.9).set_zorder(20)
[Link]()
La clase LinearSVC que hemos utilizado es, aproximadamente, equivalente a la clase
[Link] definiendo el parámetro kernel='linear'.Estudiaremos esto más
adelante, sin embargo, ahora vamos a hacer uso de la clase SVC en lugar de LinearSVC para
poder representar los vectores soporte.
La clase SVC tiene un atributo support_vectors_ que contienen los vectores soporte
utilizados para definir el límite de decisión. Veamos dichos puntos con un ejemplo gráfico:
from sklearn import svm
cluster_std = 1.7 #@param {type: "slider", min: 0.1, max: 2, step:
0.1}
X, y = make_blobs(n_samples=100, n_features=2, centers=2,
cluster_std=cluster_std, random_state=3)
min = [Link](X, axis=0)
max = [Link](X, axis=0)
diff = max - min
min = min - 0.1 * diff
max = max + 0.1 * diff
[Link]()
[Link](min[0], max[0])
[Link](min[1], max[1])
[Link]('X1')
[Link]('X2')
[Link](X[:,0], X[:,1], c=y, cmap=[Link], zorder=15)
svc = [Link](kernel='linear').fit(X, y)
line = [Link](min[0], max[0])
[Link](line, -(line * svc.coef_[0][0] + svc.intercept_) /
svc.coef_[0][1], color='green', label='Límite de decisión')
[Link](line, (1-(line * svc.coef_[0][0] + svc.intercept_)) /
svc.coef_[0][1], color='red', linestyle='--', label='Hiperplano
positivo')
[Link](line, (-1-(line * svc.coef_[0][0] + svc.intercept_)) /
svc.coef_[0][1], color='blue', linestyle='--', label='Hiperplano
negativo')
[Link](svc.support_vectors_[:,0], svc.support_vectors_[:,1],
marker='o', color='yellow', s=140)
[Link](framealpha=0.9).set_zorder(20)
[Link]()
También es posible obtener una salida probabilística del modelo SVC estableciendo el
párametro probability=True en su constructor. Esta variación, en esencia, mide la distancia
de una muestra al límite de decisión y le asocia una probabilidad: las muestras alejadas tendrán
una probabilidad cercana a 1 de pertenecer a su clase, mientras que las más cercanas tendrán
una probabilidad en torno a 0.5. Mediante el método predict_proba() podemos recuperar y
representar dichas probabilidades.
cluster_std = 1.7 #@param {type: "slider", min: 0.1, max: 2, step:
0.1}
X, y = make_blobs(n_samples=100, n_features=2, centers=2,
cluster_std=cluster_std, random_state=3)
min = [Link](X, axis=0)
max = [Link](X, axis=0)
diff = max - min
min = min - 0.1 * diff
max = max + 0.1 * diff
[Link]()
[Link](min[0], max[0])
[Link](min[1], max[1])
[Link]('X1')
[Link]('X2')
svc = [Link](kernel='linear', probability=True).fit(X, y)
line = [Link](min[0], max[0])
[Link](line, -(line * svc.coef_[0][0] + svc.intercept_) /
svc.coef_[0][1], color='black', label='Límite de decisión')
[Link](line, (1-(line * svc.coef_[0][0] + svc.intercept_)) /
svc.coef_[0][1], color='pink', linestyle='--', label='Hiperplano
positivo')
[Link](line, (-1-(line * svc.coef_[0][0] + svc.intercept_)) /
svc.coef_[0][1], color='cyan', linestyle='--', label='Hiperplano
negativo')
x1, x2 = [Link]([Link](min[0], max[0]), [Link](min[1],
max[1]))
proba = svc.predict_proba(np.c_[[Link](), [Link]()])[:,0]
proba = [Link]([Link])
[Link](x1, x2, proba, cmap=[Link].bwr_r, vmin=0, vmax=1)
[Link](X[:,0], X[:,1], c=y, edgecolor='black', cmap='bwr')
[Link](framealpha=0.9).set_zorder(20)
[Link]()
La explicación anterior hacer se ha centrado en detallar cómo funcionan las máquinas de
vectores soporte para clasificación binaria. No obstante, también se pueden emplear para
clasificación multiclase. La única diferencia es que en lugar de realizar una única clasificación
binaria haremos tantas clasificaciones binarias como clases haya utilizando la estrategia one-vs-
rest (también llamada one-vs-all). Esta estrategia asume que todas las muestras que no
pertenecen a la clase evaluada son de la clase contraria.
Ilustremos el resultado con un ejemplo.
Generamos un conjunto de datos con tres clases:
X, y = make_blobs(centers=3, random_state=42)
min = [Link](X, axis=0)
max = [Link](X, axis=0)
diff = max - min
min = min - 0.1 * diff
max = max + 0.1 * diff
[Link]()
[Link](min[0], max[0])
[Link](min[1], max[1])
[Link]('X1')
[Link]('X2')
[Link](X[:, 0], X[:, 1], c=y, cmap='rainbow')
Ajustamos el modelo:
linear_svm = LinearSVC().fit(X, y)
print("coef_ = " + str(linear_svm.coef_))
print("intercept_ = " + str(linear_svm.intercept_))
Analizamos el resultado:
[Link]()
[Link](min[0], max[0])
[Link](min[1], max[1])
[Link]('X1')
[Link]('X2')
[Link](X[:,0], X[:,1], c=y, cmap='rainbow')
line = [Link](min[0], max[0])
[Link](line, -(line * linear_svm.coef_[0][0] +
linear_svm.intercept_[0]) / linear_svm.coef_[0][1],
color=[Link](0.0))
[Link](line, -(line * linear_svm.coef_[1][0] +
linear_svm.intercept_[1]) / linear_svm.coef_[1][1],
color=[Link](0.5))
[Link](line, -(line * linear_svm.coef_[2][0] +
linear_svm.intercept_[2]) / linear_svm.coef_[2][1],
color=[Link](1.0))
Non-linear SVM
En el apartado anterior hemos visto cómo podemos construir una Máquina de Vectores Soporte
que separe clases linealmente. Sin embargo, existen infinidad de problemas en los que la
separación lineal no es posible. Las Máquinas de Vectores Soporte pueden funcionar con
separaciones no lineales haciendo algunas modificaciones.
Principalmente debemos modificar la función de coste por una nueva:
[∑ )
n m
1
l o s s=C y i ⋅ ma x ( 0 , 1+θ ⋅ f ( ⃗x i ) ) + ( 1 − y i ) ⋅m a x ( 0 ,1 −θ ⋅ f ( ⃗x i ) ) + ∑ θ2j
T T
i=1 2 j=1
w por vectores θ y a los valores de los vectores ⃗x i
Como vemos hemos modificado los vectores ⃗
se les ha aplicado una función f .
Está función f hace uso de una función de Kernel y permite determinar la similaridad entre una
muestra y todas las demás. Si dos muestras son idénticas entonces su similaridad deberá ser 1,
mientras que si son opuestas deberá ser 0.
La idea subyacente de las funciones de Kernel consiste en aumentar la dimensión del conjunto
de datos para que, al realizar una separación lineal en un espacio de dimensión superior, se
muestre una separación no lineal en el espacio original. Ilustremos esto con un sencillo ejemplo.
Asumamos la siguiente función de Kernel para comparar dos muestras x i y x j cuando
trabajamos en R2:
2
K ( x i , x j ) =( x i ⋅ x j ) =( x i ,1 ⋅ x j ,1 + x i ,2 ⋅ x j ,2 ) =x i ,1 ⋅ x j ,1 + x i ,2 ⋅ x j ,2 +2 ⋅ x i ,1 ⋅ x j ,1 ⋅ xi , 2 ⋅ x j , 2
t 2 2 2 2 2
Esta función de Kernel es equivalente a:
2
K ( x i , x j ) =( x i ⋅ x j ) =( x i ,1 , x i , 2 , √ 2⋅ x i ,1 ⋅ x i ,2 ) ⋅ ( x j ,1 , x j , 2 , √ 2⋅ x j ,1 ⋅ x j ,2 )
t 2 2 2 2
2
O dicho de otro modo, usar la función de Kernel K ( x i , x j ) =( x i ⋅ x j ) equivale a transformar
t
nuestro espacio en R2 a un espacio en R3 en el que trabajar mediante la siguiente
transformación:
( x i ,1 , x i , 2 ) → ( x 2i ,1 , x2i , 2 , √ 2⋅ x i , 1 ⋅ x i ,2 )
¿Y por qué es mejor trabajar en R3 que en R2? Veámoslo gráficamente.
Supongamos que tenemos el siguiente conjunto de datos:
import [Link] as plt
from [Link] import make_circles
X, y = make_circles(n_samples=50, noise=0.1, factor=0.4,
random_state=42)
[Link](X[:,0], X[:,1], c=y, cmap=[Link])
[Link]()
Su transformación a R3 generaría el siguiente conjunto de datos:
import plotly.graph_objects as go
import numpy as np
from math import sqrt
fig = [Link](data=[go.Scatter3d(
x = X[:,0]**2,
y = X[:,1]**2,
z = sqrt(2)*X[:,0]*X[:,1],
mode = 'markers',
marker = dict(
size = 6,
color = -y,
colorscale = 'RdBu',
opacity = 0.8
)
)])
fig.update_layout(margin=dict(l=0, r=0, b=0, t=0),
scene = dict(
xaxis = dict(title='x1^2'),
yaxis = dict(title='x2^2'),
zaxis = dict(title='sqrt(2)*x1*x2')
))
[Link]()
El cual es separable linealmente.
Existen infinidad de funciones de Kernel, denotadas por K ( x i , x j ) :
t
• Lineal: K ( x i , x j ) =x i ⋅ x j
d
Polinomial: K ( x i , x j ) =( x i ⋅ x j +c ) donde c >0
t
•
Sigmoidal: K ( x i , x j ) =t a n h ( a ⋅ ( x i ⋅ x j) +b ) donde a , b> 0
t
•
Sin embargo, la función de Kernel más popular es la Gaussian Kernel, que se encuentra
implementada en sklearn bajo el acrónimo 'rbf' (Radial Basis Fuction Kernel). Esta función
determina la distancia euclídea entre dos vectores suavizándose mediante una distribución
normal de desviación típica σ :
( )
2
|)x i − x j|)
K ( x i , x j ) =e x p −
2 σ2
Independientemente de la función de Kernel a utilizar, f ( ⃗x i ) devolverá un vector con el resultado
de la función de Kernel (i.e. la distancia) de la muestra i con respecto a todas las demás.
\begin{equation} f(\vec{x}_i) = \begin{pmatrix} K(x_i,x_1) \ K(x_i,x_2) \ \vdots \ K(x_i,x_i) \ \vdots \
K(x_i,x_n) \end{pmatrix} \end{equation}
Intuitivamente, las muestras cercanas a i tendrán K ( x i , x j ) ≈ 1 y las muestras lejanas tendrá
K ( x i , x j ) ≈ 0.
Por consiguiente, θ pasará a ser un vector de tamaño n (número de muestras) con los
parámetros que el modelo debe aprender.
Este clasificador se encuentra implementado en la clase [Link] de la librería
sklearn.
Se destacan dos parámetros tres parámetros de su implementación:
• C que indica la no-regularización como en LinearSVC.
• Kernel que permite modificar la función de Kernel. Por defecto 'rbf'.
• gamma que modifica los parámetros de la función de Kernel. En el caso de 'rbf', valores
altos de gamma indican una baja desviación típica y, por tanto, prestar más atención a las
muestras más cercanas, mientras que valores bajos de gamma indican lo apuesto.
Analicemos la importancia de estos parámetros con un ejemplo gráfico:
import [Link] as plt
from [Link] import make_moons
from [Link] import MinMaxScaler
from sklearn import svm
import numpy as np
X, y = make_moons(n_samples=100, noise=0.08, random_state=10)
min = [Link](X, axis=0)
max = [Link](X, axis=0)
diff = max - min
min = min - 0.1 * diff
max = max + 0.1 * diff
fig, axs = [Link](4,4, figsize=(20,20))
fig.tight_layout(pad=4.0)
for i, C in enumerate([0.01, 0.04, 0.08, 0.12]):
for j, gamma in enumerate([0.1,2,3,10]):
clf = [Link](kernel='rbf', gamma=gamma, C=C, probability=True)
[Link](X, y)
axs[i,j].set_title('SVM, C: ' + format(C) + ', gamma: '+
str(gamma))
axs[i,j].set_xlabel('X1')
axs[i,j].set_ylabel('X2')
axs[i,j].set_xlim(min[0], max[0])
axs[i,j].set_ylim(min[1], max[1])
x1, x2 = [Link]([Link](min[0], max[0]),
[Link](min[1], max[1]))
proba = clf.predict_proba(np.c_[[Link](), [Link]()])[:,0]
proba = [Link]([Link])
axs[i,j].pcolormesh(x1, x2, proba, cmap='bwr_r', vmin=0, vmax=1)
axs[i,j].scatter(X[:,0], X[:,1], c=y, edgecolor='black',
cmap='bwr')
import [Link] as plt
from [Link] import MinMaxScaler
from sklearn import svm
from make_spirals import make_spirals
import numpy as np
X, y = make_spirals(n_samples=1000, random_state=10)
min = [Link](X, axis=0)
max = [Link](X, axis=0)
diff = max - min
min = min - 0.1 * diff
max = max + 0.1 * diff
fig, axs = [Link](4,4, figsize=(20,20))
fig.tight_layout(pad=4.0)
for i, C in enumerate([0.01, 0.04, 0.08, 0.12]):
for j, gamma in enumerate([0.01,0.05,0.1,5]):
clf = [Link](kernel='rbf', gamma=gamma, C=C, probability=True)
[Link](X, y)
axs[i,j].set_title('SVM, C: ' + format(C) + ', gamma: '+
str(gamma))
axs[i,j].set_xlabel('X1')
axs[i,j].set_ylabel('X2')
axs[i,j].set_xlim(min[0], max[0])
axs[i,j].set_ylim(min[1], max[1])
x1, x2 = [Link]([Link](min[0], max[0]),
[Link](min[1], max[1]))
proba = clf.predict_proba(np.c_[[Link](), [Link]()])[:,0]
proba = [Link]([Link])
axs[i,j].pcolormesh(x1, x2, proba, cmap='bwr_r', vmin=0, vmax=1)
axs[i,j].scatter(X[:,0], X[:,1], c=y, edgecolor='black',
cmap='bwr')
import [Link] as plt
from [Link] import MinMaxScaler
from [Link] import make_circles
from sklearn import svm
import numpy as np
X, y = make_circles(n_samples=300, noise=0.1, factor=0.5,
random_state=42)
min = [Link](X, axis=0)
max = [Link](X, axis=0)
diff = max - min
min = min - 0.1 * diff
max = max + 0.1 * diff
fig, axs = [Link](4,4, figsize=(20,20))
fig.tight_layout(pad=4.0)
for i, C in enumerate([0.001, 0.02, 0.04, 0.10]):
for j, gamma in enumerate([0.25,0.50,0.75,1.00]):
clf = [Link](kernel='rbf', gamma=gamma, C=C, probability=True)
[Link](X, y)
axs[i,j].set_title('SVM, C: ' + format(C) + ', gamma: '+
str(gamma))
axs[i,j].set_xlabel('X1')
axs[i,j].set_ylabel('X2')
axs[i,j].set_xlim(min[0], max[0])
axs[i,j].set_ylim(min[1], max[1])
x1, x2 = [Link]([Link](min[0], max[0]),
[Link](min[1], max[1]))
proba = clf.predict_proba(np.c_[[Link](), [Link]()])[:,0]
proba = [Link]([Link])
axs[i,j].pcolormesh(x1, x2, proba, cmap='bwr_r', vmin=0, vmax=1)
axs[i,j].scatter(X[:,0], X[:,1], c=y, edgecolor='black',
cmap='bwr')
Creado por Fernando Ortega ([Link]@[Link])