Métodos de consenso

Aprendizaje Supervisado vs Aprendizaje no supervisado.

La mayoría de problemas de aprendizaje estadístico pueden ser clasificados dos categorías: Aprendizaje Supervisado y Aprendizaje No Supervisado.

Aprendizaje supervisado.

Para cada observación de las variables predictoras \(x_i~;~i=1,..,n\) existe una variable respuesta \(y_i\). Se desea ajustar un modelo de datos que relacione la variable respuesta \(y_i\) con las variables predictoras.

  • Predicción: para futuras observaciones de individuos tratar de pronosticar con exactitud la respuesta.

  • Inferencia: comprender mejor la relación entre la respuesta y las predictoras.

Algunos ejemplos de métodos de solución de problemas asociados con aprendizaje supervisado:

  • Regresión lineal
  • Regresión polinómica.
  • Regresión Ridge.
  • Regresión Lasso.
  • Regresión logística.
  • Árboles de decisión.
  • Máquinas de soporte vectorial.
  • Random Forest.
  • K-Nearest Neighbors (KNN).
  • Naive Bayes. …

Aprendizaje no supervisado.

Para cada observación \(i=1,..., n\), se tiene un vector \(x_i\), sin embargo, no se tiene asociado una variable respuesta \(y_i\) que permita supervisar algún ajuste.

Cuando se utiliza aprendizaje no supervisado se puede tratar de entender las relaciones entre variables o las relaciones entre las observaciones. Una herramienta de aprendizaje estadístico que podemos utilizar en este contexto es el análisis de conglomerados, o clustering. El objetivo del análisis de conglomerados es determinar, a partir del análisis de x1, . , xn , si las observaciones pertenecen a grupos relativamente distintos. Por ejemplo, en un estudio de segmentación de mercado podríamos observar múltiples características (variables) de los clientes potenciales, como el código postal, los ingresos familiares y los hábitos de compra. Podríamos pensar que los clientes pertenecen a grupos diferentes, como los que gastan mucho o los que gastan poco. Si se dispusiera de información sobre los patrones de gasto de cada cliente, sería posible realizar un análisis supervisado. Sin embargo, esta información no está disponible, es decir, no sabemos si cada cliente potencial es un gran gastador o no. En este caso, podemos intentar agrupar a los clientes en función de las variables medidas, con el fin de identificar distintos grupos de clientes potenciales. La identificación de estos grupos puede ser interesante porque es posible que difieran en alguna propiedad de interés, como los hábitos de gasto. Algunos métodos de aprendizaje no supervisado:

  • K-means
  • Clustering jerárquico.
  • Análisis de componentes principales.
  • Análisis discriminante no supervizado. …

Problemas de regresión vs Problemas de clasificación

Las variables pueden ser clasificadas como cuantitativas o cualitativas. Las variables cuantitativas toman valores numéricos: edad, estatura, ingreso, precio de una materia prima, costo de una vivienda. En contraste, las variables cualitativas toman valores en una de \(k\) clases o categorías diferentes: estado civil, marca de producto comprado, tipo de diagnóstico de una enfermedad.

En análisis de datos suele referirse a los problemas con variables respuesta cuantitativas como problemas de regresión, mientras que, a los problemas con variable respuesta cualitativa se consideran problemas de clasificaicón

Métodos de consenso

Los árboles de decisión presentan una gran desventaja, poseen alta varianza. Lo anterior significa que, si dividimos la base de datos en dos partes, de manera aleatoria, y aplicamos un árbol de decisión a ambas mitades, los resultados que se van a obtener son posiblemente muy diferentes. Por otro lado, si tuviéramos un procedimiento o técnica con baja varianza los resultados producidos serías similares si se aplica repetidamente a un conjunto de datos distinto.

Bootstrap aggregation, Bagging, o métodos de consenso es una técnica cuyo propósito general consiste en disminuir la varianza en un método de aprendizaje estadístico. Los métodos de consenso son usualmente utilizados en el contexto de árboles de decisión.

La idea es tomar \(m\) muestras aleatorias con reemplazo (BOOSTRAPS) de los datos originales y luego aplicas a cada una de ellas un método predictivo, para luego, con algún criterio estableces un consenso de todos los resultados.

El consenso podría ser un promedio, un promedio ponderado básado en cuál método se obtuvo un mejor resultado.

El consenso más usado es el que obtenga mayor cantidad de votos

Funcionamiento del método de consenso.

El funcionamiento gráfico del método se observa en la Figura 1

flowchart TD
  A[D] --> B[D1]
  A --> C[D2]
  A --> D[Dt-1]
  A --> E[Dt]
  
  B --> F[C1 ó R1]
  C --> G[C2 ó R2]
  D --> H[Ct-1 ó Rt-1]
  E --> I[Ct ó Rt]

  F --> J[C* ó R*]
  G --> J[C* ó R*]
  H --> J[C* ó R*]
  I --> J[C* ó R*]


  style A fill:#b6daf6
  
Figura 1: Métodos de consenso

\(D\) corresponde a la base de datos para entrenamiento original.

\(D1, D2, D_{t-1}, D_{t}\) corresponden a los múltiples conjuntos de datos creados de manera aleatoria con reemplazo.

\(C1, C2, C_{t-1}, C_{t}\) corresponden a los modelos de clasificación o regresión usado para cada conjunto de dato \(D\).

\(C^*, R^*\) representan el consenso para el pronóstico ya sea para el clasificador o el regresor.

Una ventaja adicional del bagging es que permite estimar el error de la predicción de forma directa, sin necesidad de utilizar una muestra de test o de aplicar validación cruzada u, otra vez, remuestreo, y se obtiene un resultado similar al que obtendríamos con estos métodos. Es bien sabido que una muestra bootstrap va a contener muchas observaciones repetidas y que, en promedio, solo utiliza aproximadamente dos tercios de los datos (para ser más precisos, \(1-(1-\frac{1}{n})^n\) que, a medida que el tamaño del conjunto de datos de entrenamiento aumenta, es aproximadamente \(1-e^{-1} = 0.6321\)). Un dato que no es utilizado para construir un árbol se denomina un dato out-of-bag (OOB). De este modo, para cada observación se pueden utilizar los árboles para los que esa observación es out-of-bag (aproximadamente una tercera parte de los árboles construidos) para generar una única predicción para ella. Repitiendo el proceso para todas las observaciones se obtiene una medida del error.

Una decisión que hay que adoptar es cuántas muestras bootstrap se toman (o lo que es lo mismo, cuántos árboles se construyen). En la práctica, es habitual realizar un estudio gráfico de la convergencia del error OOB a medida que se incrementa el número de árboles. Si aparentemente hay convergencia con unos pocos cientos de árboles, no va a variar mucho el nivel de error al aumentar el número de remuestras. Por tanto, aumentar mucho el número de árboles no mejora las predicciones, aunque tampoco aumenta el riesgo de sobreajuste. Los costes computacionales aumentan con el número de árboles, pero la construcción y evaluación del modelo son fácilmente paralelizables (aunque pueden llegar a requerir mucha memoria si el conjunto de datos es muy grande). Por otra parte, si el número de árboles es demasiado pequeño puede que se obtengan pocas (o incluso ninguna) predicciones OOB para alguna de las observaciones de la muestra de entrenamiento.

Bagging para árboles de deciciones de clasificación enR usando la librería ramdonForest

Usaremos una base de datos que contiene la siguiente información fisico-química y de calidad:

  • fixed.acidity: acidez fija.

  • volatile.acidity:acidez volátil.

  • Citric.acid: ácido cítrico.

  • residual.sugar: azúcar residual.

  • chlorides: cloruros.

  • free.sulfur.dioxide : dióxido de azufre libre.

  • total.sulfur.dioxide: dióxido de azufre total.

  • density: densidad,

  • pH: PH.

  • sulphates: sulfatos

  • alcohol: alcohol)

  • taste: sabor, que indica la calidad del vino (se considera bueno si la mediana de las evaluaciones de la calidad del vino, realizadas por expertos, que los evaluaron entre 0 = muy malo y 10 = muy excelente, no es inferior a 6. Factor con niveles «bueno» y «malo» que indican la calidad del vino.

Librerías

library(randomForest) #Para bagging
library(mpae) #Contiene la base de datos que se utilizará como ejemplo
library(caret) #Matriz de confusión
library(pROC) #Curva ROC y AUC

Creación del data.frame a partir de la base de datos winetaste de mpae

datos <- winetaste

Limpieza básica de datos N/A

datos<- na.omit(datos)

Definición de la base de datos en entrenamiento y prueba

set.seed(1) #Semilla para replicabilidad del ejemplo
numero_obs <- nrow(datos)
base_entrenamiento<- sample(numero_obs, 0.8*numero_obs, replace=FALSE) 
datos_entrenamiento <- datos[base_entrenamiento,]
datos_prueba <- datos[-base_entrenamiento,]

Planteamiento del modelo

set.seed(4) #Semilla para replicabilidad del ejemplo
bagging <- randomForest(taste~., data=datos_entrenamiento,  mtry = ncol(datos_entrenamiento) - 1)
bagging

Call: randomForest(formula = taste ~ ., data = datos_entrenamiento, mtry = ncol(datos_entrenamiento) - 1) Type of random forest: classification Number of trees: 500 No. of variables tried at each split: 11

    OOB estimate of  error rate: 23.5%

Confusion matrix: good bad class.error good 565 97 0.1465257 bad 138 200 0.4082840

Evaluación del modelo con matriz de confusión

prediccion <- predict(bagging, newdata=datos_prueba)
matriz_confusion <- confusionMatrix(data = prediccion, reference = datos_prueba$taste)
matriz_confusion

Confusion Matrix and Statistics

      Reference

Prediction good bad good 145 42 bad 21 42

           Accuracy : 0.748           
             95% CI : (0.6894, 0.8006)
No Information Rate : 0.664           
P-Value [Acc > NIR] : 0.002535        
                                      
              Kappa : 0.3981          
                                      

Mcnemar’s Test P-Value : 0.011743

        Sensitivity : 0.8735          
        Specificity : 0.5000          
     Pos Pred Value : 0.7754          
     Neg Pred Value : 0.6667          
         Prevalence : 0.6640          
     Detection Rate : 0.5800          

Detection Prevalence : 0.7480
Balanced Accuracy : 0.6867

   'Positive' Class : good            
                                      

Para recordar

\[\begin{align} Accuracy = \frac{TP+TN}{TP+TN+FP+FN} \end{align}\]

\[\begin{align} Accuracy = \frac{145+42}{145+42+42+21} =0.748 \end{align}\]

\[\begin{align} Sensitivity = \frac{TP}{TP+FN} \end{align}\]

\[\begin{align} Sensitivity = \frac{145}{145+21} = 0.873494 \end{align}\]

\[\begin{align} Specificity = \frac{TN}{TN+FP} \end{align}\]

\[\begin{align} Specificity = \frac{42}{42+42} = 0.5 \end{align}\]

Evaluación del modelo con ROC y AUC

#Predicción como probabilidades
prob_prediccion <- predict(bagging,datos_prueba, type="prob")

#Probabilidad "Positiva" o "Buena"
prob_positiva <- prob_prediccion[, "good"]

# Variable binaria con la clase "verdadera"
clase_verdadera <- datos_prueba$taste

#Curva ROC
curva_ROC <- roc(clase_verdadera, prob_positiva)
plot(curva_ROC, main = "Curva ROC para el modelo Bagging")

#AUC
valor_AUC <- auc(curva_ROC)
print(valor_AUC)

Area under the curve: 0.8026

Se puede examinar la convergencia del error en las muestras OOB

plot(bagging, main = "")
legend("right", colnames(bagging$err.rate), lty = 1:5, col = 1:6)

Bagging para árboles de deciciones de regresión enR usando la librería ramdonForest

Librerías

library(randomForest) #Para bagging
library(ISLR2) #Contiene la base de datos que se utilizará como ejemplo
library(tidyverse)
library(caret) #Matriz de confusión

Creación del data.frame a partir de la base de datos winetaste de mpae

datos <- Hitters

Limpieza básica de datos N/A

datos<- na.omit(datos)

Establecemos como factores las variables cualitativas.

datos$League <- as.factor(datos$League)
datos$Division <- as.factor(datos$Division)
datos$NewLeague <- as.factor(datos$NewLeague)

Definición de la base de datos en entrenamiento y prueba

set.seed(1) #Semilla para replicabilidad del ejemplo
numero_obs <- nrow(datos)
base_entrenamiento<- sample(numero_obs, 0.8*numero_obs, replace=FALSE) 
datos_entrenamiento <- datos[base_entrenamiento,]
datos_prueba <- datos[-base_entrenamiento,]

Planteamiento del modelo

set.seed(4) #Semilla para replicabilidad del ejemplo
bagging <- randomForest(Salary~., data=datos_entrenamiento,  mtry = ncol(datos_entrenamiento) - 1)
bagging

Call: randomForest(formula = Salary ~ ., data = datos_entrenamiento, mtry = ncol(datos_entrenamiento) - 1) Type of random forest: regression Number of trees: 500 No. of variables tried at each split: 19

      Mean of squared residuals: 75677.13
                % Var explained: 60.67

Evaluación del modelo

predicciones <- predict(bagging, newdata = datos_prueba)
mse <- mean((predicciones - datos_prueba$Salary)^2)
print(mse)

[1] 105794.2

rmse <- sqrt(mse)
print(rmse)

[1] 325.2602

Análisis de la convergencia

plot(bagging, main = "")

Random Forest

Los bosques aleatorios (random forest) son una variante de bagging específicamente diseñados para trabajar con árboles de decisión. Las muestras bootstrap que se generan al hacer bagging introducen un elemento de aleatoriedad que en la práctica provoca que todos los árboles sean distintos, pero en ocasiones no son lo suficientemente distintos. Es decir, suele ocurrir que los árboles tengan estructuras muy similares, especialmente en la parte alta, aunque después se vayan diferenciando según se desciende por ellos. Esta característica se conoce como correlación entre árboles y se da cuando el árbol es un modelo adecuado para describir la relación entre los predictores y la respuesta, y también cuando uno de los predictores es muy fuerte, es decir, es especialmente relevante, con lo cual casi siempre va a estar en el primer corte. Esta correlación entre árboles se va a traducir en una correlación entre sus predicciones (más formalmente, entre los predictores).

Promediar variables altamente correlacionados produce una reducción de la varianza mucho menor que si promediamos variables incorrelacionadas. La solución pasa por añadir aleatoriedad al proceso de construcción de los árboles, para que estos dejen de estar correlacionados. En la construcción de cada uno de los árboles que finalmente constituirán el bosque, se van haciendo cortes binarios, y para cada corte hay que seleccionar una variable predictora. La modificación introducida fue que antes de hacer cada uno de los cortes, de todas las
\(p\) variables predictoras, se seleccionan al azar \(m<p\) predictores que van a ser los candidatos para el corte.

El hiperparámetro de los bosques aleatorios es \(m\). Como puntos de partida razonables se pueden considerar \(m= \sqrt{p}\) para problemas de clasificación) y $m= para los problemas de regresión. El número de árboles que van a constituir el bosque también puede tratarse como un hiperparámetro, aunque es más frecuente tratarlo como un problema de convergencia. En general, van a hacer falta más árboles que en bagging. En ocasiones, también se trata como hiperparámetro la selección del tamaño mínimo de los nodos terminales de los árboles.

Los bosques aleatorios son computacionalmente más eficientes que el bagging porque, aunque como acabamos de decir requieren más árboles, la construcción de cada árbol es mucho más rápida al evaluarse solo unos pocos predictores en cada corte.

Random Forest para árboles de deciciones de clasificación enR usando la librería ramdonForest

Librerías

library(randomForest) #Para bagging
library(mpae) #Contiene la base de datos que se utilizará como ejemplo
library(caret) #Matriz de confusión
library(pROC) #Curva ROC y AUC

Creación del data.frame a partir de la base de datos winetaste de mpae

datos <- winetaste

Limpieza básica de datos N/A

datos<- na.omit(datos)

Definición de la base de datos en entrenamiento y prueba

set.seed(1) #Semilla para replicabilidad del ejemplo
numero_obs <- nrow(datos)
base_entrenamiento<- sample(numero_obs, 0.8*numero_obs, replace=FALSE) 
datos_entrenamiento <- datos[base_entrenamiento,]
datos_prueba <- datos[-base_entrenamiento,]

Planteamiento del modelo

set.seed(1) #Semilla para replicabilidad del ejemplo
random_forest <- randomForest(taste~., data=datos_entrenamiento)
random_forest

Call: randomForest(formula = taste ~ ., data = datos_entrenamiento) Type of random forest: classification Number of trees: 500 No. of variables tried at each split: 3

    OOB estimate of  error rate: 22%

Confusion matrix: good bad class.error good 578 84 0.1268882 bad 136 202 0.4023669

Evaluación del modelo con matriz de confusión

prediccion <- predict(random_forest, newdata=datos_prueba)
matriz_confusion <- confusionMatrix(data = prediccion, reference = datos_prueba$taste)
matriz_confusion

Confusion Matrix and Statistics

      Reference

Prediction good bad good 153 43 bad 13 41

           Accuracy : 0.776           
             95% CI : (0.7192, 0.8261)
No Information Rate : 0.664           
P-Value [Acc > NIR] : 7.227e-05       
                                      
              Kappa : 0.4494          
                                      

Mcnemar’s Test P-Value : 0.0001065

        Sensitivity : 0.9217          
        Specificity : 0.4881          
     Pos Pred Value : 0.7806          
     Neg Pred Value : 0.7593          
         Prevalence : 0.6640          
     Detection Rate : 0.6120          

Detection Prevalence : 0.7840
Balanced Accuracy : 0.7049

   'Positive' Class : good            
                                      

Evaluación del modelo con ROC y AUC

#Predicción como probabilidades
prob_prediccion <- predict(random_forest,datos_prueba, type="prob")

#Probabilidad "Positiva" o "Buena"
prob_positiva <- prob_prediccion[, "good"]

# Variable binaria con la clase "verdadera"
clase_verdadera <- datos_prueba$taste

#Curva ROC
curva_ROC <- roc(clase_verdadera, prob_positiva)
plot(curva_ROC, main = "Curva ROC para el modelo Bagging")

#AUC
valor_AUC <- auc(curva_ROC)
print(valor_AUC)

Area under the curve: 0.8159

Se puede examinar la convergencia del error en las muestras OOB

plot(random_forest, main = "")
legend("right", colnames(random_forest$err.rate), lty = 1:5, col = 1:6)

Podemos mostrar la importancia de las variables predictoras (utilizadas en el bosque aleatorio y sus sustitutas) con la función importance() o representarlas con varImpPlot()

importance(random_forest)
                 MeanDecreaseGini

fixed.acidity 37.77155 volatile.acidity 43.99769 citric.acid 41.50069 residual.sugar 36.79932 chlorides 33.62100 free.sulfur.dioxide 42.29122 total.sulfur.dioxide 39.63738 density 45.38724 pH 32.31442 sulphates 30.32322 alcohol 63.89185

Se puede graficar la importancia de las variables regresoras usando la sentencia varImpPlot()

varImpPlot(random_forest)

Random Forest para árboles de deciciones de regresión enR usando la librería ramdonForest

Librerías

library(randomForest) #Para bagging
library(ISLR2) #Contiene la base de datos que se utilizará como ejemplo
library(tidyverse)
library(caret) #Matriz de confusión

Creación del data.frame a partir de la base de datos winetaste de mpae

datos <- Hitters

Limpieza básica de datos N/A

datos<- na.omit(datos)

Establecemos como factores las variables cualitativas.

datos$League <- as.factor(datos$League)
datos$Division <- as.factor(datos$Division)
datos$NewLeague <- as.factor(datos$NewLeague)

Definición de la base de datos en entrenamiento y prueba

set.seed(1) #Semilla para replicabilidad del ejemplo
numero_obs <- nrow(datos)
base_entrenamiento<- sample(numero_obs, 0.8*numero_obs, replace=FALSE) 
datos_entrenamiento <- datos[base_entrenamiento,]
datos_prueba <- datos[-base_entrenamiento,]

Planteamiento del modelo

set.seed(4) #Semilla para replicabilidad del ejemplo
random_forest <- randomForest(Salary~., data=datos_entrenamiento)
random_forest

Call: randomForest(formula = Salary ~ ., data = datos_entrenamiento) Type of random forest: regression Number of trees: 500 No. of variables tried at each split: 6

      Mean of squared residuals: 75557.1
                % Var explained: 60.73

Evaluación del modelo

predicciones <- predict(random_forest, newdata = datos_prueba)
mse <- mean((predicciones - datos_prueba$Salary)^2)
print(mse)

[1] 97867.42

rmse <- sqrt(mse)
print(rmse)

[1] 312.8377

Análisis de la convergencia

plot(random_forest, main = "")

Podemos mostrar la importancia de las variables predictoras (utilizadas en el bosque aleatorio y sus sustitutas) con la función importance() o representarlas con varImpPlot()

importance(random_forest)
      IncNodePurity

AtBat 1750397.77 Hits 2335167.30 HmRun 1385377.74 Runs 2130302.32 RBI 2421192.53 Walks 2833417.29 Years 762955.96 CAtBat 4309096.75 CHits 4625552.04 CHmRun 1916508.10 CRuns 4040930.22 CRBI 4812676.58 CWalks 2041337.78 League 88378.86 Division 221348.16 PutOuts 2933455.42 Assists 573884.60 Errors 390749.88 NewLeague 68637.34

Se puede graficar la importancia de las variables regresoras usando la sentencia varImpPlot()

varImpPlot(random_forest)

Argumentos interés de randomForest

formula y data (opcional): permiten especificar la respuesta y las variables predictoras de la forma habitual (típicamente respuesta ~ .), aunque si el conjunto de datos es muy grande puede ser preferible emplear una matriz o un data.frame para establecer los predictores y un vector para la respuesta (sustituyendo estos argumentos por x e y). Si la variable respuesta es un factor asumirá que se trata de un problema de clasificación, y en caso contrario de regresión.

ntree: número de árboles; por defecto \(500\).

mtry: número de predictores seleccionados al azar en cada división; por defecto max(floor(p/3), 1) en el caso de regresión y floor(sqrt(p)) en clasificación, siendo p = ncol(x) = ncol(data) - 1 el número de predictores.

nodesize: número mínimo de observaciones en un nodo terminal; por defecto 1 en clasificación y 5 en regresión. Si el conjunto de datos es muy grande, es recomendable incrementarlo para evitar problemas de sobreajuste, disminuir el tiempo de computación y los requerimientos de memoria. Puede ser tratado como un hiperparámetro.

Otros argumentos que pueden ser de interés son:

maxnodes: número máximo de nodos terminales. Puede utilizarse como alternativa para controlar la complejidad del modelo.

importance = TRUE: permite obtener medidas adicionales de la importancia de las variables predictoras.

proximity = TRUE: permite obtener una matriz de proximidades (componente proximity) entre las observaciones (frecuencia con la que los pares de observaciones están en el mismo nodo terminal).

na.action = na.fail: por defecto, no admite datos faltantes con la interfaz de fórmulas. Si los hubiese, se podrían imputar estableciendo na.action = na.roughfix (empleando medias o modas) o llamando previamente a rfImpute() (que emplea proximidades obtenidas con un bosque aleatorio).