PDF: https://github.com/CristinaGil/Ciencia-de-Datos-R


INTRODUCCIÓN


Las máquinas de vector soporte o Support Vector Machines (SVM) son otro tipo de algoritmo de machine learning supervisado aplicable a problemas de regresión y clasificación, aunque se usa más comúnmente como modelo de clasificación. Las máquinas de vector soporte suponen una generalización de un clasificador simple denominado maximal margin classifier. Sin embargo, este clasificador no puede aplicarse a sets de datos donde las clases de la variable respuesta no son separables mediante un límite lineal. Una extensión del mismo, el support vector classifier es aplicable en un mayor rango de casos. El support vector machine supone una extensión más del support vector classifier para casos con límites no lineales entre clases (clasificación binaria o de más clases).


MAXIMAL MARGIN CLASSIFIER


Concepto de hiperplano


En un espacio euclídeo \(\small{p}\)-dimensional, un hiperplano es un subespacio plano y afín (no tiene por qué pasar por el origen) de dimensión \(\small{p-1}\), que divide el espacio en dos mitades. Por ejemplo, en un espacio de 2 dimensiones un hiperplano es un subespacio plano de una sola dimensión, o lo que es lo mismo, una línea, definida por la ecuación lineal


donde cualquier \(\small{X = X_1, X_2}\) para los que se cumple la ecuación es un punto en el hiperplano.


En un espacio de 3 dimensiones el hiperplano se corresponde con un sub-espacio de dos dimensiones, es decir, un plano. Si \(\small{p>3}\) puede resultar difícil visualizar el hiperplano. Para escenarios \(\small{p}\)-dimensionales, la ecuación anterior puede ser extendida a


donde, igualmente, los puntos \(\small{X = X_1, X_2, ..., X_p}\) que cumplen la ecuación pertenecen al hiperplano.


En el supuesto que \(\small{X}\) no satisfazca la ecuación, dándose uno de estos dos casos


el punto \(\small{X}\) estará situado a uno u otro lado del hiperplano, no sobre él.


Uso del hiperplano para clasificación binaria


Suponiendo que contamos con una matriz de datos \(\small{n×p}\) con \(\small{n}\) observaciones y \(\small{p}\) predictores (donde en la variable respuesta son distinguibles dos clases distintas \(\small{y_1, ..., y_n ∈ (-1, 1)}\)), el objetivo será el de desarrollar un clasificador en base al subgrupo de datos de entrenamiento que clasifique correctamente nuevas observaciones en base a los valores de los predictores en función de un hiperplano de separación con la propiedad

o equivalentemente

para cada \(\small{i = 1, ..., n}\).


Siendo \(\small{β_0, β_1, ..., β_p}\) los coeficientes del hiperplano, una nueva observación \(\small{x^*}\) se asignará a un grupo u otro dependiendo de en qué lado del hiperplano se localice (en función del signo de \(\small{f(x^*) = β_0 + β_1x^*_1 + ... + β_px^*_p}\)). Si \(\small{f(x^*)}\) es positiva, se asigna la nueva observación a la clase 1, y si es negativa, a la clase -1. La magnitud de \(\small{f(x^*)}\) también es informativa: si \(\small{f(x^*)}\) tiene un valor muy lejano a 0, significa que \(\small{x^*}\) se encuentra muy lejos del hiperplano, lo cual aporta más seguridad a la clasificación de dicha observación. Por el contrario, un valor de \(\small{f(x^*)}\) próximo a 0 significa que la observación está cerca del hiperplano, con lo que estaremos menos seguros acerca de la clase asignada a esta observación.

NOTA: Otros métodos aplicables a este problema son LDA, regresión logística y árboles de clasificación.


Clasificación binaria para casos separables linealmente


Si nuestros datos son perfectamente separables mediante un hiperplano, entonces existirá un número infinito de tales hiperplanos (debido a la posibilidad de moverlos o rotarlos sin entrar en contacto con las observaciones). Esto hace que necesitemos un modo de decidir cuál de todos los hiperplanos posibles utilizar. La solución es escoger el hiperplano de separación que se encuentre más alejado de las observaciones de entrenamiento, al que se conoce como maximal margin hyperplane o hiperplano óptimo de separación. Este se obtiene calculando las distancias perpendiculares de cada observación a un hiperplano dado, donde la distancia más pequeña se corresponde con la distancia mínima de las observaciones al hiperplano, espacio conocido como margen. Con esto, el hiperplano óptimo de separación será aquel que tenga la mayor distancia mínima de las observaciones al hiperplano, o lo que es lo mismo, el mayor margen (\(\small{M}\)). Por tanto, los parámetros del hiperplano \(\small{X = β_0, β_1, ..., β_p}\) se optimizan para maximizar \(\small{M}\).


(Imagen obtenida del libro ISLR)


En la imagen, la línea negra continua representa el hiperplano óptimo de separación y el margen en líneas discontinuas. En ella, hay tres observaciones (dos de la clase azul y una de la morada) que son equidistantes al hiperplano. A estas observaciones que son las que se encuentran sobre el margen, y por tanto, más próximas al hiperplano, se conocen como support vectors o vectores soporte, pues son vectores en el espacio \(\small{p}\)-dimensional (en la imagen, \(\small{p=2}\)) que “soportan” al hiperplano óptimo de separación en el sentido de que si estas observaciones cambiaran ligeramente, el hiperplano lo haría también. El movimiento del resto de observaciones no tendría impacto en el hiperplano, siempre y cuando no cruzaran el límite establecido por el margen.

Se espera que un clasificador con un margen alto para las observaciones de entrenamiento lo tenga también para las observaciones de test, y pueda por tanto clasificarlas correctamente.

Un problema que puede surgir es que este clasificador sufra de overfitting cuando el número de dimensiones (\(\small{p}\)) es alto.


SUPPORT VECTOR CLASSIFIER


Clasificación binaria para casos cuasi-separables linealmente


Observaciones pertenecientes a dos clases no tienen por qué ser necesariamente separables mediante el uso de un hiperplano, con lo que el uso de este tipo de clasificador puede no ser deseable. Un clasificador basado en un hiperplano clasificará todas las observaciones de entrenamiento perfectamente, lo cual conlleva a una sensibilidad a nuevas observaciones (signo de overfitting): la adición de una sola observación tiene la capacidad de cambiar drásticamente el hiperplano óptimo de separación, tal y como se muestra en la siguiente imagen:

(Imagen obtenida del libro ISLR)


El cambio en el hiperplano causado por una sola observación en la imagen superior, hace que éste deje de ser óptimo, ya que el margen se ha reducido drásticamente, y esta distancia supone una medida de la confianza con la que una observación es correctamente clasificada.

En estos casos, puede ser útil considerar el clasificador denominado soft margin classifier o support vector classifier, que, aún basado en un hiperplano, no separe perfectamente las dos clases, con el interés de obtener:

• Mayor robustez a observaciones individuales

• Mejor clasificación de la mayoría de las observaciones de entrenamiento y test.

Con este clasificador, se permite que algunas observaciones se encuentren en el lado incorrecto del margen (de ahí el término soft o blando), o incluso en el lado incorrecto del hiperplano. Estas serán las observaciones de entrenamiento mal clasificadas por el modelo.

De nuevo, el support vector classifier clasifica cada nueva observación en función de a qué lado de hiperplano pertenezca, es decir, en función del signo de \(\small{f(x^*) = β_0 + β_1x^*_1 + ... + β_px^*_p}\)), y al igual que con el maximal margin classifier, solo las observaciones que se encuentran sobre el margen o que lo violan (vectores soporte) afectarán al hiperplano y, por lo tanto, al clasificador obtenido.

Nota: en un support vector classifier los vectores soporte se corresponden con las observaciones que se encuentran sobre el margen y también las que lo violan.

El proceso de optimización del hiperplano en este caso incorpora un parámetro de regularización o tuning parameter C, el cual controla la severidad permitida de las violaciones de las n observaciones sobre el margen e hiperplano, y a la vez, el equilibrio bias-varianza. Sin entrar en detalles matemáticos, si \(\small{C > 0}\), no más de \(\small{C}\) observaciones pueden encontrarse en el lado incorrecto del hiperplano. Si \(\small{C}\) es pequeño, los márgenes serán estrechos pues muy pocas observaciones podrán estar en el lado incorrecto del mismo (esto equivale a un modelo bastante bien ajustado a los datos, el cual puede tener poco bias pero mucha varianza). Conforme incrementamos \(\small{C}\), mayor es la toleración a las violaciones sobre el margen, con lo que el margen será más ancho y habrá más vectores soporte (esto equivale a un modelo más flexible y con mayor bias pero menor varianza). Si \(\small{C = 0}\), el clasificador es equivalente al maximal margin classifier, pues no están permitidas violaciones sobre el margen (todas las observaciones deben estar correctamente clasificadas). En la práctica el parámetro \(\small{C}\) se escoge u optimiza por validación cruzada.

En resumen:

Poca varianza y alto bias: márgenes anchos y mayor número de vectores soporte

Mucha varianza y bajo bias: márgenes estrechos y menor número de vectores soporte


SUPPORT VECTOR MACHINES


Clasificación binaria para casos no separables linealmente


Anteriormente se ha explicado como los hiperplanos de separación son buenos clasificadores cuando las clases son perfectamente separables o cuasi-perfectamente separables. Sin embargo, la aplicación de un support vector classifier para casos claramente no separables linealmente (la mayoría de problemas reales) carece de interés práctico.

Una posibilidad para tratar con límites no lineales entre clases consiste en aumentar manualmente el espacio de los predictores mediante funciones polinómicas o con términos de interacción. Aun así, corremos el riesgo de acabar con demasiados predictores. Como alternativa está el uso de los support vector machines o máquinas de vector soporte (SVM), que suponen una extensión de los support vector classifiers que aumenta la dimensionalidad de una manera específica, mediante el uso de kernels, un enfoque computacionalmente más eficiente. Los kernels son funciones que transforman un espacio de pocas dimensiones en un espacio de dimensiones mayores mediante transformaciones complejas de los datos. También puede definirse como una función que cuantifica la similitud entre dos observaciones en un nuevo espacio dimensional.

Entre los kernels más populares para usar con SVMs se encuentran:


Kernel lineal


El kernel lineal cuantifica la similitud de un par de observaciones usando la correlación de Pearson. Con un kernel lineal, el clasificador obtenido es equivalente a un support vector classifier.


Kernel polinómico


Un kernel polinómico de grado d (siendo \(\small{d>1}\)) permite un límite de decisión mucho más flexible. Cuando un support vector classifier se combina con un kernel no lineal, se obtiene un support vector machine.


Kernel radial


donde 𝛾 es una constante positiva que cuanto mayor sea, mayor la flexibilidad del SVM. Suponiendo que una observación de test \(\small{x^* = x^*_1 ... x^*_p}\) se encuentra alejada de una observación de entrenamiento \(\small{x_i}\) en términos de distancia Euclídea, entonces \(\small{K = x^*,x_i}\) será muy pequeño, lo que significa que \(\small{x_i}\) no influirá en \(\small{f(x^*)}\). El kernel radial tiene un comportamiento muy local, en el sentido de que solo las observaciones de entrenamiento cercanas a una observación de test tendrán efecto sobre su clasificación.



Es importante tener en cuenta que una mayor flexibilidad no tiene por qué mejorar las predicciones, ya que un modelo muy flexible puede ajustarse demasiado a los datos de entrenamiento.


Clasificación con más de dos clases


Existen varias extensiones de los SVMs para problemas de clasificación con más de dos clases (\(\small{K > 2}\)), siendo dos de las más populares:

One-versus-one

One-versus-all


Clasificación one-vs-one


Este método construye \(\small{k \choose 2}\) SVMs, correspondiente a \(\small{K(K – 1)/2}\), cada uno comparando un par de clases. Una observación de test se clasifica usando cada uno de los SVMs, contando el número de veces que esta observación es asignada a cada una de las \(\small{K}\) clases. La clase final predicha será aquella a la que la observación ha sido asignada en la mayoría de los SVMs.


Clasificación one-vs-all


Se ajustan \(\small{K}\) SVMs, cada vez comparándose una de las K clases (codificada como +1) con el resto de \(\small{K-1}\) clases (codificadas como -1). Siendo \(\small{β_{0k}, β_{1k}, β_{pk}}\) los parámetros resultantes del ajuste de un SVM y \(\small{x^*}\) una observación de test, la observación será asignada a la clase para la que \(\small{β_{0k} + β_{1k}x^*_1 +...+ β_{pk}x^*_p}\) sea mayor. Es decir, la magnitud de \(\small{f(x^*)}\) indica como de lejos está \(\small{x^*}\) del hiperplano de separación, y cuanto más lejos esté, mayor será el nivel de confianza de que la observación \(\small{x^*}\) ha sido correctamente clasificada.


SUPPORT VECTOR REGRESSION


SVM también puede utilizarse como un método de regresión (support vector regression o SVR). SVR sigue los mismos principios que el SVM para clasificación, con alguna diferencia en cuanto al algoritmo (se establece un margen de tolerancia para las predicciones, épsilon).


SVM vs REGRESIÓN LOGÍSTICA


Una característica interesante de los support vector classifiers es que solo los vectores soporte juegan un papel importante en la clasificación final obtenida: observaciones en el lado correcto del margen no afectan a las predicciones. En contraposición, el término de penalización en regresión logística es muy pequeño para observaciones lejanas al límite de decisión, pero nunca es exactamente 0. Aun así, dadas las similitudes en cuanto al término de penalización, ambos métodos estadísticos pueden dar con frecuencia resultados similares.

Cuando las clases son fácilmente separables, los SVMs suelen superar a la regresión logística. En situaciones donde las clases son más solapantes, la regresión logística suele ser la opción escogida.

El uso de kernels para aumentar la dimensionalidad no está limitado sólo al uso de SVMs (podrían usarse también para regresión logística), pero su uso está más extendido en estos casos.


EJEMPLO EN R


Support vector classifier/machine: library(e1071)

svm() -> Ajuste de modelo support vector classifier (kernel = “linear”) y support vector machine (kernel = “polinomial”, “radial”…). Si la variable respuesta contiene más de dos niveles, la función lleva a cabo la clasificación usando el método one-vs-one

tune() -> Función genérica para optimización de hiperparámetros mediante validación cruzada

plot.svm()


Para este ejemplo de clasificación utilizaremos el set de datos OJ, del paquete ISLR. Contiene información sobre compra de dos tipos de bebida (Citrus Hill y Minute Maid Orange Juice) por parte de 1070 clientes (las variables registran distintas características del cliente y el producto). Generaremos modelos basados en SVM con tres tipos de kernel: lineal, polinómico y radial, que predigan qué tipo de bebida (Purchase) compra el consumidor, en función del conjunto de predictores.

NOTA: La variable respuesta ha de estar codificada como factor.

NOTA: Un número alto de variables en relación al número de observaciones implicaría que es fácil encontrar un hiperplano que separe completamente las clases.

(Ver ejemplo de árbol de clasificación aplicado a este set de datos en Árboles de decisión. Como en este caso contamos con un predictor con K = 2, la regresión logística también sería una opción).

library(ISLR)

# Estructura de los datos
str(OJ)
## 'data.frame':    1070 obs. of  18 variables:
##  $ Purchase      : Factor w/ 2 levels "CH","MM": 1 1 1 2 1 1 1 1 1 1 ...
##  $ WeekofPurchase: num  237 239 245 227 228 230 232 234 235 238 ...
##  $ StoreID       : num  1 1 1 1 7 7 7 7 7 7 ...
##  $ PriceCH       : num  1.75 1.75 1.86 1.69 1.69 1.69 1.69 1.75 1.75 1.75 ...
##  $ PriceMM       : num  1.99 1.99 2.09 1.69 1.69 1.99 1.99 1.99 1.99 1.99 ...
##  $ DiscCH        : num  0 0 0.17 0 0 0 0 0 0 0 ...
##  $ DiscMM        : num  0 0.3 0 0 0 0 0.4 0.4 0.4 0.4 ...
##  $ SpecialCH     : num  0 0 0 0 0 0 1 1 0 0 ...
##  $ SpecialMM     : num  0 1 0 0 0 1 1 0 0 0 ...
##  $ LoyalCH       : num  0.5 0.6 0.68 0.4 0.957 ...
##  $ SalePriceMM   : num  1.99 1.69 2.09 1.69 1.69 1.99 1.59 1.59 1.59 1.59 ...
##  $ SalePriceCH   : num  1.75 1.75 1.69 1.69 1.69 1.69 1.69 1.75 1.75 1.75 ...
##  $ PriceDiff     : num  0.24 -0.06 0.4 0 0 0.3 -0.1 -0.16 -0.16 -0.16 ...
##  $ Store7        : Factor w/ 2 levels "No","Yes": 1 1 1 1 2 2 2 2 2 2 ...
##  $ PctDiscMM     : num  0 0.151 0 0 0 ...
##  $ PctDiscCH     : num  0 0 0.0914 0 0 ...
##  $ ListPriceDiff : num  0.24 0.24 0.23 0 0 0.3 0.3 0.24 0.24 0.24 ...
##  $ STORE         : num  1 1 1 1 0 0 0 0 0 0 ...
# Comprobamos valores faltantes en la variable respuesta
sum(is.na(OJ$Purchase))
## [1] 0
# Distribución variable respuesta
library(ggplot2)

ggplot(data = OJ, aes(x = Purchase, y = ..count.., fill = Purchase)) +
geom_bar() +
labs(title = "Distribución de 'Purchase'") +
scale_fill_manual(values = c("darkgreen", "orangered2"), 
                  labels = c("Citrus Hill", "Orange Juice")) +
theme_bw() + theme(plot.title = element_text(hjust = 0.5))

# Tabla frecuencias variable respuesta
table(OJ$Purchase)
## 
##  CH  MM 
## 653 417
# Tabla proporciones variable respuesta
library(dplyr)
prop.table(table(OJ$Purchase)) %>% round(digits = 2)
## 
##   CH   MM 
## 0.61 0.39


Para que los modelos generados sean útiles, el porcentaje de aciertos en cuanto a la clasificación de las observaciones ha de superar un nivel mínimo, en este caso, el que se obtendría si la predicción de todas las observaciones se correspondiera con la clase mayoritaria. La clase mayoritaria (moda) en este caso es la bebida CH con el 61% de las compras. Este será el nivel basal a superar por el modelo (este es el porcentaje mínimo de aciertos si siempre se predijera CH). (Recalcular este valor con los datos de entrenamiento).

Antes de proceder a generar los modelos, dividimos el set de datos en un grupo de entrenamiento (para el ajuste de los modelos) y otro de test (para la evaluación de los mismos). Esta división dependerá de la cantidad de observaciones con las que contemos y la seguridad con la que queramos obtener la estimación del test error. En este ejemplo se opta por una división 80%-20%.

library(caret)

# Índices observaciones de entrenamiento
set.seed(123)
train <- createDataPartition(y = OJ$Purchase, p = 0.8, list = FALSE, times = 1)

# Datos entrenamiento
datosOJ_train <- OJ[train, ]
dim(datosOJ_train)
## [1] 857  18
# Datos test
datosOJ_test <- OJ[-train, ]
dim(datosOJ_test)
## [1] 213  18


Support vector classifier


Ajuste del modelo

Paquete e1071


A la hora de ajustar un support vector classifier, es importante tener en cuenta que el hiperparámetro \({\small{C}}\) (cost) controla el equilibrio bias-varianza y la capacidad predictiva del modelo, ya que determina la severidad permitida respecto a las violaciones sobre el margen. En otras palabras, necesitamos fijar un margen de separación entre observaciones a priori. Por ello es recomendable evaluar distintos valores del mismo mediante validación cruzada y escoger el valor óptimo.

IMPORTANTE: Estandarizar los predictores cuando no estén medidos en la misma escala, para que los de mayor magnitud no tengan mayor influencia que el resto. Un argumento disponible en la función svm() para ello es scale = TRUE).

Para ajustar un support vector classifier, el kernel indicado en la función svm() ha de ser lineal. Obtendremos un valor de coste óptimo mediante validación cruzada utilizando la función tune() del paquete e1071:

library(e1071)

# Optimización de hiperparámetros mediante validación cruzada 10-fold
set.seed(325)
tuning <- tune(svm, Purchase ~ ., data = datosOJ_train, 
               kernel = "linear", 
               ranges = list(cost = c(0.001, 0.01, 0.1, 1, 5, 10, 15, 20)), 
               scale = TRUE)

Podemos acceder a los errores de validación con cada valor de coste con la función summary():

summary(tuning)
## 
## Parameter tuning of 'svm':
## 
## - sampling method: 10-fold cross validation 
## 
## - best parameters:
##  cost
##    15
## 
## - best performance: 0.1656772 
## 
## - Detailed performance results:
##     cost     error dispersion
## 1  0.001 0.3009850 0.05276636
## 2  0.010 0.1691382 0.04602764
## 3  0.100 0.1679754 0.04037149
## 4  1.000 0.1703010 0.04099747
## 5  5.000 0.1703010 0.03755497
## 6 10.000 0.1668263 0.03560699
## 7 15.000 0.1656772 0.03338668
## 8 20.000 0.1668399 0.03479758
names(tuning)
## [1] "best.parameters"  "best.performance" "method"           "nparcomb"        
## [5] "train.ind"        "sampling"         "performances"     "best.model"
ggplot(data = tuning$performances, aes(x = cost, y = error)) +
  geom_line() +
  geom_point() +
  labs(title = "Error de validación ~ hiperparámetro C") +
  theme_bw() +
  theme(plot.title = element_text(hjust = 0.5))

El valor de coste que resulta en el menor error de validación (0,165) es 15.

# Almacenamos el modelo optimo obtenido y accedemos a su información
modelo_svc <- tuning$best.model
summary(modelo_svc)
## 
## Call:
## best.tune(method = svm, train.x = Purchase ~ ., data = datosOJ_train, 
##     ranges = list(cost = c(0.001, 0.01, 0.1, 1, 5, 10, 15, 20)), 
##     kernel = "linear", scale = TRUE)
## 
## 
## Parameters:
##    SVM-Type:  C-classification 
##  SVM-Kernel:  linear 
##        cost:  15 
## 
## Number of Support Vectors:  345
## 
##  ( 173 172 )
## 
## 
## Number of Classes:  2 
## 
## Levels: 
##  CH MM

El número de vectores soporte es de 345, 173 de la clase CH y 172 de la clase MM. Podemos obtener los índices de las observaciones que se corresponden con los vectores soporte:

# Muestra de 50 de los 345
head(modelo_svc$index)
## [1]  1 15 24 28 32 45


El mejor modelo obtenido sería equivalente a ajustar:

modelo_svc <- svm(Purchase ~ ., data = datosOJ_train, 
                  kernel = "linear", 
                  cost = 15, 
                  scale = TRUE)


Al tratarse de un problema con más de dos predictores, podemos representar el modelo usando la función plot(), pero creando representaciones entre pares de predictores (teniendo en cuenta que plot.svm solo representa predictores continuos). Ejemplos:

plot(modelo_svc, datosOJ_test, SalePriceCH ~ PriceCH)

plot(modelo_svc, datosOJ_test, SalePriceCH ~ SalePriceMM)

Evaluación del modelo

# Error de test
predicciones = predict(modelo_svc, datosOJ_test)
table(prediccion = predicciones, real = datosOJ_test$Purchase)
##           real
## prediccion  CH  MM
##         CH 108  17
##         MM  22  66
paste("Observaciones de test mal clasificadas:", 
      100 * mean(datosOJ_test$Purchase != predicciones) %>% 
        round(digits = 4), "%")
## [1] "Observaciones de test mal clasificadas: 18.31 %"


Paquete caret


SVM lineal: method = “svmLinear”

SVM polinómico: method = “svmPoly”

SVM radial: method = “svmRadial”

Si no se especifica, la métrica para la evaluación es el Accuracy. Podría emplearse otra como por ejemplo “ROC”.

# AJUSTE DEL MODELO
# -----------------------------------------------------------------------------
# Configuración del proceso de selección del modelo
fitControl <- trainControl(method = "cv", 
                           number = 10, 
                           classProbs = TRUE, 
                           search = "grid")

# Parametros del modelo disponibles
getModelInfo(model = "svmLinear")[[2]]$parameters
##   parameter   class label
## 1         C numeric  Cost
# Valores del hiperparámetro C a evaluar
grid_C <- data.frame(C = c(0.001, 0.01, 0.1, 1, 5, 10, 15, 20))

# Entrenamiento del SVM con un kernel lineal y optimización del hiperparámetro C
set.seed(325) # misma semilla que en el ejemplo con el paquete e1071
modelo_svc <- train(Purchase ~ ., data = datosOJ_train, 
                    method = "svmLinear", 
                    trControl = fitControl, 
                    preProc = c("center", "scale"), #estandarizacion de los datos
                    tuneGrid = grid_C)

# Resultado del entrenamiento
modelo_svc
## Support Vector Machines with Linear Kernel 
## 
## 857 samples
##  17 predictor
##   2 classes: 'CH', 'MM' 
## 
## Pre-processing: centered (17), scaled (17) 
## Resampling: Cross-Validated (10 fold) 
## Summary of sample sizes: 771, 771, 771, 771, 772, 772, ... 
## Resampling results across tuning parameters:
## 
##   C       Accuracy   Kappa    
##    0.001  0.7760250  0.5542394
##    0.010  0.8296937  0.6381732
##    0.100  0.8367121  0.6530778
##    1.000  0.8331827  0.6447255
##    5.000  0.8308298  0.6400809
##   10.000  0.8331690  0.6451533
##   15.000  0.8320199  0.6424716
##   20.000  0.8331961  0.6450391
## 
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was C = 0.1.
# Evolución del accuracy en funcion del valor de coste en validacion cruzada
plot(modelo_svc)

# EVALUACIÓN DEL MODELO
# -----------------------------------------------------------------------------

confusionMatrix(predict(modelo_svc, datosOJ_test), datosOJ_test$Purchase)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  CH  MM
##         CH 107  17
##         MM  23  66
##                                           
##                Accuracy : 0.8122          
##                  95% CI : (0.7532, 0.8623)
##     No Information Rate : 0.6103          
##     P-Value [Acc > NIR] : 1.758e-10       
##                                           
##                   Kappa : 0.6103          
##                                           
##  Mcnemar's Test P-Value : 0.4292          
##                                           
##             Sensitivity : 0.8231          
##             Specificity : 0.7952          
##          Pos Pred Value : 0.8629          
##          Neg Pred Value : 0.7416          
##              Prevalence : 0.6103          
##          Detection Rate : 0.5023          
##    Detection Prevalence : 0.5822          
##       Balanced Accuracy : 0.8091          
##                                           
##        'Positive' Class : CH              
## 


Support vector machine


Además del hiperparámetro de penalización \(\small{C}\), en los modelos SVM es necesario especificar el hiperparámetro gamma (para kernel radial) o el grado de polinomio (para kernel polinómico), los cuales también son importante optimizar mediante validación cruzada.


Kernel polinómico


Ajuste del modelo

set.seed(325)
tuning <- tune(svm, Purchase ~ ., data = datosOJ_train, 
               kernel = "polynomial", 
               ranges = list(cost = c(0.001, 0.01, 0.1, 1, 5, 10, 15), 
                             degree = c(2, 3)), 
               scale = TRUE)

summary(tuning)
## 
## Parameter tuning of 'svm':
## 
## - sampling method: 10-fold cross validation 
## 
## - best parameters:
##  cost degree
##    15      2
## 
## - best performance: 0.1761696 
## 
## - Detailed performance results:
##      cost degree     error dispersion
## 1   0.001      2 0.3896990 0.02807724
## 2   0.010      2 0.3908618 0.03010963
## 3   0.100      2 0.3302873 0.04089620
## 4   1.000      2 0.1925308 0.03844859
## 5   5.000      2 0.1797264 0.02985789
## 6  10.000      2 0.1820246 0.03444009
## 7  15.000      2 0.1761696 0.03362081
## 8   0.001      3 0.3896990 0.02807724
## 9   0.010      3 0.3710534 0.03486570
## 10  0.100      3 0.2952804 0.05102072
## 11  1.000      3 0.1832148 0.02660863
## 12  5.000      3 0.1855404 0.03629834
## 13 10.000      3 0.1936936 0.04189787
## 14 15.000      3 0.1971956 0.04512246

Con un kernel polinómico, los hiperparámetros óptimos que reducen el error de validación son coste = 15, grado = 2.

ggplot(data = tuning$performances, aes(x = cost, y = error, col = as.factor(degree))) +
  geom_line() +
  geom_point() +
  labs(title = "Error de validación ~ hiperparámetro C y polinomio") +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme_bw() + theme(legend.position = "bottom")

# Modelo SVM kernel polinómico
modelo_svmP <- svm(Purchase ~ ., data = datosOJ_train, 
                   kernel = "polynomial", 
                   cost = 15, 
                   degree = 2, 
                   scale = TRUE)

summary(modelo_svmP)
## 
## Call:
## svm(formula = Purchase ~ ., data = datosOJ_train, kernel = "polynomial", 
##     cost = 15, degree = 2, scale = TRUE)
## 
## 
## Parameters:
##    SVM-Type:  C-classification 
##  SVM-Kernel:  polynomial 
##        cost:  15 
##      degree:  2 
##      coef.0:  0 
## 
## Number of Support Vectors:  348
## 
##  ( 176 172 )
## 
## 
## Number of Classes:  2 
## 
## Levels: 
##  CH MM


Evaluación del modelo

confusionMatrix(predict(modelo_svmP, datosOJ_test), datosOJ_test$Purchase)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  CH  MM
##         CH 111  20
##         MM  19  63
##                                           
##                Accuracy : 0.8169          
##                  95% CI : (0.7583, 0.8664)
##     No Information Rate : 0.6103          
##     P-Value [Acc > NIR] : 6.226e-11       
##                                           
##                   Kappa : 0.6142          
##                                           
##  Mcnemar's Test P-Value : 1               
##                                           
##             Sensitivity : 0.8538          
##             Specificity : 0.7590          
##          Pos Pred Value : 0.8473          
##          Neg Pred Value : 0.7683          
##              Prevalence : 0.6103          
##          Detection Rate : 0.5211          
##    Detection Prevalence : 0.6150          
##       Balanced Accuracy : 0.8064          
##                                           
##        'Positive' Class : CH              
## 
paste("Observaciones de test mal clasificadas:", 
      100 * mean(datosOJ_test$Purchase != predict(modelo_svmP, datosOJ_test)) %>%
        round(digits = 4), "%")
## [1] "Observaciones de test mal clasificadas: 18.31 %"


Kernel radial


Ajuste del modelo

set.seed(325)
tuning <- tune(svm, Purchase ~ ., data = datosOJ_train, 
               kernel = "radial", 
               ranges = list(cost = c(0.001, 0.01, 0.1, 1, 5, 10, 15), 
                             gamma = c(0.01, 0.1, 1, 5, 10)), 
               scale = TRUE)

summary(tuning)
## 
## Parameter tuning of 'svm':
## 
## - sampling method: 10-fold cross validation 
## 
## - best parameters:
##  cost gamma
##     5  0.01
## 
## - best performance: 0.1645554 
## 
## - Detailed performance results:
##      cost gamma     error dispersion
## 1   0.001  0.01 0.3896990 0.02807724
## 2   0.010  0.01 0.3896990 0.02807724
## 3   0.100  0.01 0.1878933 0.02846851
## 4   1.000  0.01 0.1657045 0.03115640
## 5   5.000  0.01 0.1645554 0.02632608
## 6  10.000  0.01 0.1692066 0.03088839
## 7  15.000  0.01 0.1727086 0.03067519
## 8   0.001  0.10 0.3896990 0.02807724
## 9   0.010  0.10 0.3896990 0.02807724
## 10  0.100  0.10 0.1890834 0.05364587
## 11  1.000  0.10 0.1774008 0.02768103
## 12  5.000  0.10 0.1867305 0.03786742
## 13 10.000  0.10 0.1890698 0.03973010
## 14 15.000  0.10 0.1925445 0.03358402
## 15  0.001  1.00 0.3896990 0.02807724
## 16  0.010  1.00 0.3896990 0.02807724
## 17  0.100  1.00 0.3430506 0.03950313
## 18  1.000  1.00 0.2042134 0.04046699
## 19  5.000  1.00 0.2169357 0.04204666
## 20 10.000  1.00 0.2227497 0.05107098
## 21 15.000  1.00 0.2250616 0.05621604
## 22  0.001  5.00 0.3896990 0.02807724
## 23  0.010  5.00 0.3896990 0.02807724
## 24  0.100  5.00 0.3827086 0.03008126
## 25  1.000  5.00 0.2321614 0.04716190
## 26  5.000  5.00 0.2461696 0.05457010
## 27 10.000  5.00 0.2519699 0.06920434
## 28 15.000  5.00 0.2507934 0.06874949
## 29  0.001 10.00 0.3896990 0.02807724
## 30  0.010 10.00 0.3896990 0.02807724
## 31  0.100 10.00 0.3896990 0.02807724
## 32  1.000 10.00 0.2473324 0.05124194
## 33  5.000 10.00 0.2590014 0.06403488
## 34 10.000 10.00 0.2590150 0.06409830
## 35 15.000 10.00 0.2625171 0.06606382

Con un kernel radial, los hiperparámetros que reducen el error de validación son coste = 5, gamma = 0,01.

ggplot(data = tuning$performances, aes(x = cost, y = error, color = factor(gamma))) +
  geom_line() +
  geom_point() +
  labs(title = "Error de validación ~ hiperparámetro C y gamma") +
  theme_bw() +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(legend.position = "bottom")

# Modelo SVM con kernel radial
modelo_svmR <- svm(Purchase ~ . , data = datosOJ_train, 
                   kernel = "radial", 
                   cost = 5, 
                   gamma = 0.01, 
                   scale = TRUE)


Evaluación del modelo

# Matriz de confusion y métricas en test
confusionMatrix(predict(modelo_svmR, datosOJ_test), datosOJ_test$Purchase)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  CH  MM
##         CH 109  17
##         MM  21  66
##                                           
##                Accuracy : 0.8216          
##                  95% CI : (0.7635, 0.8705)
##     No Information Rate : 0.6103          
##     P-Value [Acc > NIR] : 2.139e-11       
##                                           
##                   Kappa : 0.6282          
##                                           
##  Mcnemar's Test P-Value : 0.6265          
##                                           
##             Sensitivity : 0.8385          
##             Specificity : 0.7952          
##          Pos Pred Value : 0.8651          
##          Neg Pred Value : 0.7586          
##              Prevalence : 0.6103          
##          Detection Rate : 0.5117          
##    Detection Prevalence : 0.5915          
##       Balanced Accuracy : 0.8168          
##                                           
##        'Positive' Class : CH              
## 
paste("Observaciones de test mal clasificadas:", 
       100 * mean(datosOJ_test$Purchase != predict(modelo_svmR, datosOJ_test)) %>%
           round(digits = 4), "%")
## [1] "Observaciones de test mal clasificadas: 17.84 %"


EJEMPLO EN PYTHON


Para ver la aplicación de estos algoritmos sobre los mismos datos utilizando código en lenguaje Python y la libreríascikit-learn, visitar mi repositorio GitHub.


BIBLIOGRAFÍA


An Introduction to Statistical Learning: with Applications in R (Springer Texts in Statistics)

https://topepo.github.io/caret/model-training-and-tuning.html

https://www.analyticsvidhya.com/blog/2017/09/understaing-support-vector-machine-example-code/


CC BY 4.0

This work by Cristina Gil Martínez is licensed under a Creative Commons Attribution 4.0 International License.

CC BY 4.0