Clasificación - 2da Parte

Como vimos la clase anterior, hay muchos enfoques para la clasificación; En esta segunda clases veremos otros de los modelos más utilizados. Cada modelo de clasificación tiene una intuición diferente detrás. Es importante conocer esta intuición a la hora de elegir el clasificador adecuado para nuestros datos. Veremos cómo funciona internamente cada modelo de clasificación para obtener esta comprensión. Pero antes de empezar con los modelos cargaremos el conjunto de datos:

data1 <- read.csv("Clase 11.csv")
data1$CreditCard <- as.factor(data1$CreditCard)
data1$Personal.Loan <- as.factor(data1$Personal.Loan)
data1$Education <- as.factor(data1$Education)

Como podrán notar, usaremos una base de datos nueva pero seguiremos con áreas bancarias. Esta base de datos incluye 5000 usuarios de un banco a los cuales les ofrecieron tarjetas de crédito. La variable que queremos predecir es si la persona tomará la tarjeta o no. Nuestro objetivo será realizar una predicción utilizando estos features. Para obtener los datos el link es el siguiente: https://www.dropbox.com/s/yg101amd5of01gb/Clase%2011.csv?dl=0.

Árboles de decisión

Árboles de decisión con rpart

Empezaremos por instalar nuestro paquete, el cual nos permitirá realizar los ejercicios de clase:

library(rpart)

Al igual que en los métodos anteriores, tendremos que hacer una división de los datos:

data2 <- data1[,c(2,3,4,6,7,8,9,10)]
set.seed(1234)
proba <- 0.7
trainIndex1 <- sample(nrow(data2), nrow(data2)*proba)
train1 <- data2[trainIndex1,]
test1 <- data2[-trainIndex1,]

Nuevamente, para este caso el comando es rpart():

mod1 <- rpart(Personal.Loan ~ ., data=train1)
mod1
## n= 3500 
## 
## node), split, n, loss, yval, (yprob)
##       * denotes terminal node
## 
##  1) root 3500 329 0 (0.90600000 0.09400000)  
##    2) Income< 113.5 2815  52 0 (0.98152753 0.01847247) *
##    3) Income>=113.5 685 277 0 (0.59562044 0.40437956)  
##      6) Education=1 442  46 0 (0.89592760 0.10407240)  
##       12) Family< 2.5 396   0 0 (1.00000000 0.00000000) *
##       13) Family>=2.5 46   0 1 (0.00000000 1.00000000) *
##      7) Education=2,3 243  12 1 (0.04938272 0.95061728) *
plot(mod1)
text(mod1,pretty=T,minlength=10, cex=0.5)

Al revisar los resultados vemos que las variables importantes son 2,4,10 y 11. Esto confirma nuestros resultados del modelo anterior. Ahora deberemos hacer uso de nuestro comando predict() para realizar nuestras pruebas del modelo:

pred1 <- predict(mod1, test1)
pred1_1 <- ifelse(pred1[,2]>0.75, yes=1, no=0)

Finalmente evaluaremos el modelo de acuerdo con nuestra matriz de confusión:

table(test1$Personal.Loan, pred1_1)
##    pred1_1
##        0    1
##   0 1344    5
##   1   32  119

Con respecto a los modelos anteriores vemos que hay una mejoría en el nivel de predicción, lo que nos dice que este modelo funciona mejor para este propósito.

Árboles de decisión con caret

Una vez más, les mostraré la aplicación con el paquete caret(). Realizaremos nuevamente la división de los datos y el ajuste de los controles:

library(caret)
## Loading required package: lattice
## Loading required package: ggplot2
set.seed(123)
trainIndex2<- createDataPartition(data2$Personal.Loan, p=0.7, list=FALSE)
train2 <- data2[trainIndex2,]
test2 <- data2[-trainIndex2,]
fitControl <- trainControl(method = "cv", number = 10, savePredictions = "final", classProbs = FALSE)

Ahora pasaremos a la realización del modelo. En este caso el método será rpart():

mod2 <- train(train2[,1:7],train2$Personal.Loan, method="rpart", trControl=fitControl)

Ahora iremos a revisar los resultados con nuestra base de datos de test:

pred2 <- predict(mod2, test2)
confusionMatrix(test2$Personal.Loan,pred2)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction    0    1
##          0 1332   24
##          1   16  128
##                                           
##                Accuracy : 0.9733          
##                  95% CI : (0.9639, 0.9809)
##     No Information Rate : 0.8987          
##     P-Value [Acc > NIR] : <2e-16          
##                                           
##                   Kappa : 0.8501          
##                                           
##  Mcnemar's Test P-Value : 0.2684          
##                                           
##             Sensitivity : 0.9881          
##             Specificity : 0.8421          
##          Pos Pred Value : 0.9823          
##          Neg Pred Value : 0.8889          
##              Prevalence : 0.8987          
##          Detection Rate : 0.8880          
##    Detection Prevalence : 0.9040          
##       Balanced Accuracy : 0.9151          
##                                           
##        'Positive' Class : 0               
## 

El uso automático del modelo no nos ha generado mejores resultados (uno de los riesgos de este tipo de modelos). La pregunta es ¿cómo podemos hacerlo incluso mejor? La mejor forma es hacer muchos árboles. Sin embargo, esto es costoso en términos de programación y no sabremos si tenemos una buena combinación. Para solucionar este problema se diseñaron los bosques aleatorios.

Bosques aleatorios

Si aplicamos de manera iterativa el algoritmo que crea árboles de decisión con diferentes parámetros sobre los mismos datos, obtenemos lo que denominamos un bosque aleatorio de decisión (random forest). Este algoritmo es uno de los métodos más eficientes de predicción y más usados hoy día para big data, pues promedia muchos modelos con ruido e imparciales reduciendo la variabilidad final del conjunto.

En realidad lo que se hace es construir diferentes conjuntos de entrenamiento y de test sobre los mismos datos, lo que genera diferentes árboles de decisión sobre los mismos datos. La unión de estos árboles de diferentes complejidades y con datos de origen distinto aunque del mismo conjunto resulta un bosque aleatorio, cuya principal característica es que crea modelos más robustos de los que se obtendrían creando un solo árbol de decisión complejo sobre los mismos datos.

El ensamblado de modelos (arboles de decisión) distintos genera predicciones mas robustas. Los grupos de árboles de clasificación se combinan y se deduce una única predicción votada en democracia por la población de árboles.El paquete randomForest() nos permite crear este tipo de modelos de manera muy sencilla.

Bosques aleatorios con randomForest

Empezaremos por llamar al paquete randomForest:

library(randomForest)
## Warning: package 'randomForest' was built under R version 4.1.1
## randomForest 4.6-14
## Type rfNews() to see new features/changes/bug fixes.
## 
## Attaching package: 'randomForest'
## The following object is masked from 'package:ggplot2':
## 
##     margin

Al igual que en los métodos anteriores, tendremos que hacer una división de los datos y una selección de las variables. Por términos del trabajo nos ahorraremos estos pasos e iremos directamente a la realización del modelo. Para este caso el comando es randomTree(). Es importante notar que debemos indicar la cantidad de árboles a generar. En este caso se recomiendo que se tengan 5 árboles por cada observación para bases pequeñas y 2 para bases grandes. En este caso decidimos usar 3000 árboles:

set.seed(1234)
mod3 <- randomForest(Personal.Loan ~ ., data=train1, ntree=3000)

Tengan en cuenta que dada la cantidad de árboles el tiempo de procesamiento será grande. Ahora debemos realizar la predicción haciendo uso del comando predict():

pred3 <- predict(mod3, test1)

Una vez hemos hecho nuestra predicción veremos que tan bueno es el resultado usando la matriz de confusión:

table(test1$Personal.Loan, pred3)
##    pred3
##        0    1
##   0 1348    1
##   1   21  130

Aunque no es un modelo excelente, mejora con respecto a los modelos anteriores. Finalmente veremos si este modelo funciona bien con el paquete caret

Bosques aleatorios con caret

Nuevamente, omitiremos los pasos de selección de variables, diseño de los controles y división de bases de datos e iremos directamente a la predicción con el método rf:

mod4 <- train(train2[,1:7], train2$Personal.Loan, method="rf", trControl = fitControl)

Al igual que antes, tengan en cuenta que las operaciones pueden ser lentas. Una vez tenemos nuestro modelo iremos a la predicción:

pred4 <- predict(mod4, test2)

Una vez tenemos nuestro modelo correremos la matriz de confusión de este modelo:

confusionMatrix(test2$Personal.Loan, pred4)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction    0    1
##          0 1349    7
##          1   13  131
##                                           
##                Accuracy : 0.9867          
##                  95% CI : (0.9795, 0.9918)
##     No Information Rate : 0.908           
##     P-Value [Acc > NIR] : <2e-16          
##                                           
##                   Kappa : 0.9217          
##                                           
##  Mcnemar's Test P-Value : 0.2636          
##                                           
##             Sensitivity : 0.9905          
##             Specificity : 0.9493          
##          Pos Pred Value : 0.9948          
##          Neg Pred Value : 0.9097          
##              Prevalence : 0.9080          
##          Detection Rate : 0.8993          
##    Detection Prevalence : 0.9040          
##       Balanced Accuracy : 0.9699          
##                                           
##        'Positive' Class : 0               
## 

Aunque hemos mejorado a lo largo de estos modelos, parece que todavía podemos encontrar modelos adecuados. Ahora pasaremos a otro modelo adicional, la máquina de vectores de soporte.

Máquinas de vectores de soporte

A continuación, exploraremos un método de clasificación desarrollado en los años 90. El método de máquinas de vectores de soporte (Vector Support Machines, SVMs) surgió inicialmente como un método de clasificación binaria pero su uso ha sido extendido a problemas de diferente naturaleza como la regresión y la clasificación múltiple.

Este tipo de algoritmo de ML se clasifica como supervisado y es comúnmente usado para clasificación. Las maquinas de vector soporte se basan en una generalización del maximal margin classifier y son aplicables a sistemas cuyas variables respuesta pueden separarse por un límite lineal.

El objetivo principal de un método basado en SVMs es tomar grupos de observaciones y construir lazos para predecir qué grupo de observaciones futuras “pertenecen” basándose en sus mediciones. Los diferentes grupos separados se llamarán “clases”. Los métodos SVM son muy flexibles ya que pueden soportar cualquier cantidad de clases, observaciones de cualquier dimensión y sistemas con diferentes formas (lineales, radiales, polinomiales y otras) Nota: cuando hay limites no lineales se usan los support vector classifier.

En R las librerías que contienen los algoritmos y funciones necesarias para obtener modelos SVMs de clasificación simple, múltiple y regresión son:

  • e1070
  • LiblineaR

Hiperplanos

En un espacio N-dimensional, un hiperplano se define como un subespacio plano y afín de dimensiones N−1. El término afín significa que el subespacio no tiene por qué pasar por el origen. Por ejemplo, si estamos trabajando en un espacio dos dimensiones, un hiperplano sería un subespacio de 1 dimensión, es decir, una recta ya que sobre ella se agruparía o distribuiría cierta cantidad de información. Para dimensiones p>3 no es intuitivo visualizar un hiperplano, pero el concepto de subespacio con p−1 dimensiones se mantiene.

Figura 1: Hiperplano

Los hiperplanos pueden estar ubicados en cualquier sector de datos, aunque no resulte conveniente en la mayoría de casos. En SVM se busca el hiperplano óptimo de separación, en el que la distancia entre los puntos y el hiperplano sea la mínima.

En la imagen podemos apreciar datos separados en dos grupos de colores (cada color representa una clase), un hiperplano de separación, que se supondrá optimo y alrededor una margen equidistante sobre la cual se encuentran 3 puntos que serán llamados ahora “vectores de soporte”, reciben este nombre debido a que estos vectores en el espacio p-dimensional (en la imagen, p=2) son los 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.

Figura 2: Vectores de soporte

Maximal Margin classifier

Si las clases son separables por un hiperplano lineal podemos usar el método maximal margin classifier. El objetivo de este método es identificar la relación linear que maximice la distancia entre los puntos más cercanos de cada clase. Para ello se usa la función svm() de e1071;

library(e1071)
x <- matrix(rnorm(20*2), ncol = 2)
y <- c(rep(-1,10), rep(1,10))
x[y==1,] <- x[y==1,] + 3/2
dat <- data.frame(x=x, y=as.factor(y))

svmfit <- svm(y~., data = dat, kernel = "linear", scale = FALSE)
plot(svmfit, dat)

Los puntos marcados con “x” son los vectores de soporte, los cuales afectarán directamente la línea de clasificación. ### Casos cuasi-separables linealmente: (o support vector classifier)

El caso descrito con el maximal margin hyperplane es en la mayoría de los casos aplicable en casos ideales en los que el hiperplano óptimo de separación existe. Cuando tratamos casos con datos reales en la mayoría de las ocasiones los datos no tienen separaciones lineales perfectas por lo que no puede obtenerse un hiperplano de separación ni un maximal margin hyperplane. Incluso cuando se cumplen las condiciones ideales, el uso de maximal margin hyperplane tiene dos grandes problemas:

  • Dado que el hiperplano tiene que separar perfectamente las observaciones, es muy sensible a variaciones en los datos. Incluir una nueva observación puede suponer cambios muy grandes en el hiperplano de separación (poca robustez).

  • Que el maximal margin hyperplane se ajuste perfectamente a las observaciones de entrenamiento para separarlas todas correctamente suele conllevar problemas de overfitting.

train3 <- train1[,c(3,4,8)]
mod5 <- svm(Personal.Loan ~ ., data=train3, kernel="linear")
plot(mod5, data=train3)

En este tipo de casos podemos definir un hiperplano que permita una separación parcial de las clases pero con un margen definido para errores. A este tipo de hiperplano se le conoce como support vector classifier o soft margin. Este clasificador es esencialmente más robusto que un maximal margin hyperplane debido a que, aunque puede cometer errores, su capacidad predictiva es superior. Este método no busca la mayor distancia entre los vectores de soporte y el hiperplano, en su lugar busca ubicar correctamente la mayor cantidad de puntos en referencia a él, permitiendo que ciertas observaciones estén en el lado incorrecto del hiperplano lo que representa un problema de optimización convexa.

Maquinas vector de soporte

El Support Vector Classifier descrito en los apartados anteriores consigue buenos resultados cuando los limites tienen comportamiento aproximadamente lineal. Si no lo es, su capacidad predictiva decae drásticamente.

Una estrategia para enfrentarse a escenarios en los que la separación de los grupos es de tipo no lineal consiste en expandir las dimensiones del espacio original.

El hecho de que los grupos no sean linealmente separables en el espacio original no significa que no lo sean en un espacio de mayores dimensiones. En la siguiente imagen se ilustra un ejemplo:

Figura 3: Distribución de datos

Figura 4: Distribución de datos

Realizar este procedimiento requiere definir una dimensión adecuada. El concepto de aumento de la dimensión del sistema está relacionado con el de transformación lineal, en la que cada nueva dimensión debe ser adecuada para poder aplicar correctamente en ella un MVS.

Un kernel es una función que devuelve el resultado del producto punto entre dos vectores realizado en un nuevo espacio dimensional, diferente a los espacios origen de los vectores. Si se sustituye el producto punto por un kernel se obtiene directamente el grupo de vectores soporte y el hiperplano en la dimensión correspondiente al kernel.

Encontraremos algunas variaciones con el kernel lineal (igual al maximal margin classifier), kernel polinómico o kernel gaussiano, entre otros.

mod5 <- svm(Personal.Loan ~ ., data=train3, kernel="polynomial")
plot(mod5, data=train3)

Ejemplos con SVM

Ahora haremos el método de clasificación usando todos los datos usando el comando e1071:

mod5 <- svm(Personal.Loan ~ ., data=train1, kernel="linear")

Ahora probaremos el modelo con nuestros datos de prueba:

pred5 <- predict(mod5, test1)

Y lo probaremos con nuestra matriz de confusión:

table(test1$Personal.Loan, pred5)
##    pred5
##        0    1
##   0 1343    6
##   1   69   82

A diferencia de los otros modelos parece que su poder es menor, así que cambiaremos el kernel para revisar si mejora el resultado:

mod5 <- svm(Personal.Loan ~ ., data=train1, kernel="polynomial")
pred5 <- predict(mod5, test1)
table(test1$Personal.Loan, pred5)
##    pred5
##        0    1
##   0 1349    0
##   1   52   99

Con este nuevo kernel vemos que se mejoran los resultados, por lo que esta configuración es mejor que las previas.