Nombre: Andrés Noboa

Cargamos las librerias

library(ggplot2)

Cargamos los data representantes de grupos que no son linealmente separables.

set.seed(10111)
coordinate <- matrix(rnorm(40), 20, 2)
colnames(coordinate) <- c("x1","x2")
y <- c(rep(-1,10), rep(1,10))
coordinate[y == 1, ] <- coordinate[y == 1, ] + 1
df <- data.frame(coordinate, y)
ggplot(data = df, aes(x = x1, y = x2, color = as.factor(y))) +
  geom_point(size = 6) +
  theme_bw() +
  theme(legend.position = "none")

la funcion svm(), nos indica automáticamente si se trata de un problema de clasificación, la variable respuesta es de tipo factor, o de regresión, la variable respuesta es tipo numérico

df$y <- as.factor(df$y)

modelo_svm <- svm(formula = y ~ x1 + x2, data = df, kernel = "linear",
                  cost = 10, scale = FALSE)

El summary del modelo muestra que hay un total de 6 vectores soporte, 3 pertenecen a una clase y 3 a la otra.

summary(modelo_svm)

Call:
svm(formula = y ~ x1 + x2, data = df, kernel = "linear", cost = 10, scale = FALSE)


Parameters:
   SVM-Type:  C-classification 
 SVM-Kernel:  linear 
       cost:  10 

Number of Support Vectors:  6

 ( 3 3 )


Number of Classes:  2 

Levels: 
 -1 1

Empleando la función plot() con un objeto de tipo svm junto con las observaciones de entrenamiento, se obtiene una representación del límite de decisión:

plot(modelo_svm, df)

Alternativa basada en ggplot2 para obtener una representación de los resultados de svm() :

# range de los predictores
range_x1 <- range(data$x1)
range_x2 <- range(data$x2)

# Interpolación de puntos
new_x1 <- seq(from = range_x1[1], to = range_x1[2], length = 75)
new_x2 <- seq(from = range_x2[1], to = range_x2[2], length = 75)
nuevos_puntos <- expand.grid(x1 = new_x1, x2 = new_x2)

# Predicción según el modelo
predicciones <- predict(object = modelo_svm, newdata = nuevos_puntos)

# Se almacenan los puntos predichos para dar color a las regiones
color_regiones <- data.frame(nuevos_puntos, y = predicciones)

# Para extraer la ecuación del hiperplano y del margen es necesario aplicar 
# algebra lineal.
beta <- drop(t(modelo_svm$coefs) %*% as.matrix(data[,c("x1","x2")])[modelo_svm$index,])
beta0 <- modelo_svm$rho


ggplot() +
  # Representación de las 2 regiones empleando los puntos y coloreándolos
  # según la clase predicha por el modelo
  geom_point(data = color_regiones, aes(x = x1, y = x2, color = as.factor(y)),
             size = 0.5) +
  # Se añaden las observaciones
  geom_point(data = data, aes(x = x1, y = x2, color = as.factor(y)),
             size = 6) +
  # Se identifican aquellas observaciones que son vectores soporte del modelo
  geom_point(data = data[modelo_svm$index, ],
             aes(x = x1, y = x2, color = as.factor(y)),
             shape = 21, colour = "black",
             size = 6) +
  # Se añaden las rectas del hiperplano y los márgenes
  geom_abline(intercept = beta0/beta[2], slope = -beta[1]/beta[2]) +
  geom_abline(intercept = (beta0 - 1)/beta[2], slope = -beta[1]/beta[2],
              linetype = "dashed") +    
  geom_abline(intercept = (beta0 + 1)/beta[2], slope = -beta[1]/beta[2],
              linetype = "dashed") +
  theme_bw() +
  theme(legend.position = "none")

Podemos realizar cross-validation mediante el paquete e1071 de R, con tune() podemos especificar un 10-fold cross validation

set.seed(1) # semilla en 1
svm_cv <- tune("svm", y ~ x1 + x2, data = df,
               kernel = 'linear',
               ranges = list(cost = c(0.001, 0.01, 0.1, 1, 5, 10, 20, 50, 100,
                                      150, 200)))
summary(svm_cv)

Parameter tuning of ‘svm’:

- sampling method: 10-fold cross validation 

- best parameters:

- best performance: 0.1 

- Detailed performance results:
NA

Gráfico del error de clasificación vs hiperparámetro C

ggplot(data = svm_cv$performances, aes(x = cost, y = error)) +
  geom_line() +
  geom_point() +
  labs(title = "Error de clasificación vs hyperparameter C") +
  theme_bw()

Obtenemos el mejor modelo de los hiperparameters

Test dataset simulado

# Datos de test simulados
set.seed(19)
coordenadas <- matrix(rnorm(40), 20, 2)
colnames(coordenadas) <- c("x1","x2")
y <- sample(c(-1,1), 20, rep = TRUE)
coordenadas[y == 1, ] <- coordenadas[y == 1, ] + 1
test <- data.frame(coordenadas, y)

Predicciones

paste("Error de test:", 100*mean(test$y != predicciones),"%")
[1] "Error de test: 15 %"

Matriz de confusion de las predicciones

table(predicción = predicciones, valor_real = test$y)
          valor_real
predicción -1  1
        -1 10  0
        1   3  7
LS0tCnRpdGxlOiAiTcOhcXVpbmFzIGRlIFZlY3RvciBTb3BvcnRlIChTdXBwb3J0IFZlY3RvciBNYWNoaW5lcywgU1ZNcykiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCk5vbWJyZTogQW5kcsOpcyBOb2JvYSAKCkNhcmdhbW9zIGxhcyBsaWJyZXJpYXMgCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZTEwNzEpCgpgYGAKCkNhcmdhbW9zIGxvcyBkYXRhIHJlcHJlc2VudGFudGVzIGRlIGdydXBvcyBxdWUgbm8gc29uIGxpbmVhbG1lbnRlIHNlcGFyYWJsZXMuCgpgYGB7cn0Kc2V0LnNlZWQoMTAxMTEpCmNvb3JkaW5hdGUgPC0gbWF0cml4KHJub3JtKDQwKSwgMjAsIDIpCmNvbG5hbWVzKGNvb3JkaW5hdGUpIDwtIGMoIngxIiwieDIiKQp5IDwtIGMocmVwKC0xLDEwKSwgcmVwKDEsMTApKQpjb29yZGluYXRlW3kgPT0gMSwgXSA8LSBjb29yZGluYXRlW3kgPT0gMSwgXSArIDEKZGYgPC0gZGF0YS5mcmFtZShjb29yZGluYXRlLCB5KQpnZ3Bsb3QoZGF0YSA9IGRmLCBhZXMoeCA9IHgxLCB5ID0geDIsIGNvbG9yID0gYXMuZmFjdG9yKHkpKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDYpICsKICB0aGVtZV9idygpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpgYGAKbGEgZnVuY2lvbiBgc3ZtKClgLCBub3MgaW5kaWNhICBhdXRvbcOhdGljYW1lbnRlIHNpIHNlIHRyYXRhIGRlIHVuIHByb2JsZW1hIGRlIGNsYXNpZmljYWNpw7NuLCBsYSB2YXJpYWJsZSByZXNwdWVzdGEgZXMgZGUgdGlwbyBmYWN0b3IsIG8gZGUgcmVncmVzacOzbiwgbGEgdmFyaWFibGUgcmVzcHVlc3RhIGVzIHRpcG8gbnVtw6lyaWNvCgpgYGB7cn0KZGYkeSA8LSBhcy5mYWN0b3IoZGYkeSkKCm1vZGVsb19zdm0gPC0gc3ZtKGZvcm11bGEgPSB5IH4geDEgKyB4MiwgZGF0YSA9IGRmLCBrZXJuZWwgPSAibGluZWFyIiwKICAgICAgICAgICAgICAgICAgY29zdCA9IDEwLCBzY2FsZSA9IEZBTFNFKQpgYGAKCgpFbCBzdW1tYXJ5IGRlbCBtb2RlbG8gbXVlc3RyYSBxdWUgaGF5IHVuIHRvdGFsIGRlIDYgdmVjdG9yZXMgc29wb3J0ZSwgMyBwZXJ0ZW5lY2VuIGEgdW5hIGNsYXNlIHkgMyBhIGxhIG90cmEuCmBgYHtyfQpzdW1tYXJ5KG1vZGVsb19zdm0pCgpgYGAKCkVtcGxlYW5kbyBsYSBmdW5jacOzbiBwbG90KCkgY29uIHVuIG9iamV0byBkZSB0aXBvIHN2bSBqdW50byBjb24gbGFzIG9ic2VydmFjaW9uZXMgZGUgZW50cmVuYW1pZW50bywgc2Ugb2J0aWVuZSB1bmEgcmVwcmVzZW50YWNpw7NuIGRlbCBsw61taXRlIGRlIGRlY2lzacOzbjoKCmBgYHtyfQpwbG90KG1vZGVsb19zdm0sIGRmKQoKYGBgCkFsdGVybmF0aXZhIGJhc2FkYSBlbiBnZ3Bsb3QyIHBhcmEgb2J0ZW5lciB1bmEgcmVwcmVzZW50YWNpw7NuIGRlIGxvcyByZXN1bHRhZG9zIGRlIHN2bSgpIDoKCmBgYHtyfQojIHJhbmdlIGRlIGxvcyBwcmVkaWN0b3JlcwpyYW5nZV94MSA8LSByYW5nZShkYXRhJHgxKQpyYW5nZV94MiA8LSByYW5nZShkYXRhJHgyKQoKbmV3X3gxIDwtIHNlcShmcm9tID0gcmFuZ2VfeDFbMV0sIHRvID0gcmFuZ2VfeDFbMl0sIGxlbmd0aCA9IDc1KQpuZXdfeDIgPC0gc2VxKGZyb20gPSByYW5nZV94MlsxXSwgdG8gPSByYW5nZV94MlsyXSwgbGVuZ3RoID0gNzUpCm51ZXZvc19wdW50b3MgPC0gZXhwYW5kLmdyaWQoeDEgPSBuZXdfeDEsIHgyID0gbmV3X3gyKQoKIyBQcmVkaWNjacOzbiBzZWfDum4gZWwgbW9kZWxvCnByZWRpY2Npb25lcyA8LSBwcmVkaWN0KG9iamVjdCA9IG1vZGVsb19zdm0sIG5ld2RhdGEgPSBudWV2b3NfcHVudG9zKQoKY29sb3JfcmVnaW9uZXMgPC0gZGF0YS5mcmFtZShudWV2b3NfcHVudG9zLCB5ID0gcHJlZGljY2lvbmVzKQoKIyBFeHRyYXllbmRvIGxhIGVjIGRlbCBoaXBlcnBsYW5vCmJldGEgPC0gZHJvcCh0KG1vZGVsb19zdm0kY29lZnMpICUqJSBhcy5tYXRyaXgoZGF0YVssYygieDEiLCJ4MiIpXSlbbW9kZWxvX3N2bSRpbmRleCxdKQpiZXRhMCA8LSBtb2RlbG9fc3ZtJHJobwoKCmdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSBjb2xvcl9yZWdpb25lcywgYWVzKHggPSB4MSwgeSA9IHgyLCBjb2xvciA9IGFzLmZhY3Rvcih5KSksCiAgICAgICAgICAgICBzaXplID0gMC41KSArCiAgIyBTZSBhw7FhZGVuIGxhcyBvYnNlcnZhY2lvbmVzCiAgZ2VvbV9wb2ludChkYXRhID0gZGF0YSwgYWVzKHggPSB4MSwgeSA9IHgyLCBjb2xvciA9IGFzLmZhY3Rvcih5KSksCiAgICAgICAgICAgICBzaXplID0gNikgKwogICMgU2UgaWRlbnRpZmljYW4gYXF1ZWxsYXMgb2JzZXJ2YWNpb25lcyBxdWUgc29uIHZlY3RvcmVzIHNvcG9ydGUgZGVsIG1vZGVsbwogIGdlb21fcG9pbnQoZGF0YSA9IGRhdGFbbW9kZWxvX3N2bSRpbmRleCwgXSwKICAgICAgICAgICAgIGFlcyh4ID0geDEsIHkgPSB4MiwgY29sb3IgPSBhcy5mYWN0b3IoeSkpLAogICAgICAgICAgICAgc2hhcGUgPSAyMSwgY29sb3VyID0gImJsYWNrIiwKICAgICAgICAgICAgIHNpemUgPSA2KSArCiAgIyBTZSBhw7FhZGVuIGxhcyByZWN0YXMgZGVsIGhpcGVycGxhbm8geSBsb3MgbcOhcmdlbmVzCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gYmV0YTAvYmV0YVsyXSwgc2xvcGUgPSAtYmV0YVsxXS9iZXRhWzJdKSArCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gKGJldGEwIC0gMSkvYmV0YVsyXSwgc2xvcGUgPSAtYmV0YVsxXS9iZXRhWzJdLAogICAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIpICsgICAgCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gKGJldGEwICsgMSkvYmV0YVsyXSwgc2xvcGUgPSAtYmV0YVsxXS9iZXRhWzJdLAogICAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICB0aGVtZV9idygpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKClBvZGVtb3MgcmVhbGl6YXIgYGNyb3NzLXZhbGlkYXRpb25gIG1lZGlhbnRlIGVsIHBhcXVldGUgYGUxMDcxYCBkZSBSLCBjb24gYHR1bmUoKWAgcG9kZW1vcyBlc3BlY2lmaWNhciB1biAxMC1mb2xkIGNyb3NzIHZhbGlkYXRpb24KYGBge3J9CnNldC5zZWVkKDEpICMgc2VtaWxsYSBlbiAxCnN2bV9jdiA8LSB0dW5lKCJzdm0iLCB5IH4geDEgKyB4MiwgZGF0YSA9IGRmLAogICAgICAgICAgICAgICBrZXJuZWwgPSAnbGluZWFyJywKICAgICAgICAgICAgICAgcmFuZ2VzID0gbGlzdChjb3N0ID0gYygwLjAwMSwgMC4wMSwgMC4xLCAxLCA1LCAxMCwgMjAsIDUwLCAxMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMTUwLCAyMDApKSkKc3VtbWFyeShzdm1fY3YpCmBgYAoKCgoKIyMjIEdyw6FmaWNvIGRlbCBlcnJvciBkZSBjbGFzaWZpY2FjacOzbiB2cyBoaXBlcnBhcsOhbWV0cm8gQyAKYGBge3J9CmdncGxvdChkYXRhID0gc3ZtX2N2JHBlcmZvcm1hbmNlcywgYWVzKHggPSBjb3N0LCB5ID0gZXJyb3IpKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21fcG9pbnQoKSArCiAgbGFicyh0aXRsZSA9ICJFcnJvciBkZSBjbGFzaWZpY2FjacOzbiB2cyBoeXBlcnBhcmFtZXRlciBDIikgKwogIHRoZW1lX2J3KCkKYGBgCk9idGVuZW1vcyBlbCBtZWpvciBtb2RlbG8gZGUgbG9zIGhpcGVycGFyYW1ldGVycwoKYGBge3J9CmJlc3RfbW9kZWwgPC0gc3ZtX2N2JGJlc3QubW9kZWwKYGBgCgpUZXN0IGRhdGFzZXQgc2ltdWxhZG8KYGBge3J9CnNldC5zZWVkKDE5KQpjb29yZGVuYWRhcyA8LSBtYXRyaXgocm5vcm0oNDApLCAyMCwgMikKY29sbmFtZXMoY29vcmRlbmFkYXMpIDwtIGMoIngxIiwieDIiKQp5IDwtIHNhbXBsZShjKC0xLDEpLCAyMCwgcmVwID0gVFJVRSkKY29vcmRlbmFkYXNbeSA9PSAxLCBdIDwtIGNvb3JkZW5hZGFzW3kgPT0gMSwgXSArIDEKdGVzdCA8LSBkYXRhLmZyYW1lKGNvb3JkZW5hZGFzLCB5KQpgYGAKCiMjIFByZWRpY2Npb25lcyAKYGBge3J9CnByZWRpY2Npb25lcyA8LSBwcmVkaWN0KG9iamVjdCA9IGJlc3RfbW9kZWwsIHRlc3QpCnBhc3RlKCJFcnJvciBkZSB0ZXN0OiIsIDEwMCptZWFuKHRlc3QkeSAhPSBwcmVkaWNjaW9uZXMpLCIlIikKYGBgCgojIyBNYXRyaXogZGUgY29uZnVzaW9uIGRlICBsYXMgcHJlZGljY2lvbmVzCmBgYHtyfQp0YWJsZShwcmVkaWNjacOzbiA9IHByZWRpY2Npb25lcywgdmFsb3JfcmVhbCA9IHRlc3QkeSkKYGBgCgo=