Aprendizaje de maquinas -- R -- Metodos de ensamble.

Notas de clase sobre aprendizaje de maquinas usando R

Juan David Velásquez Henao
jdvelasq@unal.edu.co
Universidad Nacional de Colombia, Sede Medellín
Facultad de Minas
Medellín, Colombia

[Licencia]

[Readme]

Software utilizado.

Este es un documento interactivo escrito como un notebook de Jupyter, en el cual se presenta un tutorial sobre regresión logistica usando R en el contexto de aprendizaje de maquinas. Los notebooks de Jupyter permiten incoporar simultáneamente código, texto, gráficos y ecuaciones. El código presentado en este notebook puede ejecutarse en los sistemas operativos Linux y OS X.

Haga click aquí para obtener instrucciones detalladas sobre como instalar Jupyter en Windows y Mac OS X.

Haga clic [aquí] para ver la última versión de este documento en nbviewer.

Descargue la última versión de este documento a su disco duro; luego, carguelo y ejecutelo en línea en Try Jupyter!

Haga clic aquí para ver el tutorial de visualización y gráficas.

Contenido

Bibliografía.

Material complementario.

Webinar RStudio Getting your data into R


Introducción

Contenido

Para producir un modelo combinado que prediga de forma más precisa que cada modelo individual separado. En este notebook se abordarán tres métodos: Bagging, Boosting y Random Forest. Para esto se utilizarán modelos vistos en clases anteriores y cómo estos métodos mejoran el poder predictivo de estos.

Bagging-Agregación Bootstraping

Contenido

Este primer método de ensamble conocido como Bagging se basa en utilizar diferentes muestras de todo el conjunto de observaciones para entrenar múltiples versiones del mismo modelo. Cada uno de estos modelos votan (arrojan su predicción) por la respuesta correcta donde gana o se escoje el valor de la predicción con mayores "votos" (es decir, por mayoría) cuando se trata de una variable de respuesta categórica. Análogamente para una variable de respuesta continua el valor de predicción es el promedio de todos los valores arrojados por los modelos.

Básicamente el algoritmo conlleva a entrenar el mismo modelo $M$ veces cada una con diferentes datos de entrenamiento, a través de muestreo con reemplazo, y promediar los resultados de cada uno para tener una salida final. Esta técnica permite inferir que en promedio se obtiene el 63% de observaciones distintas cada vez que se realiza la muestra utilizando reemplazo.

Esquema de Pseudo-Algoritmo Bagging en Clasificación Binaria

Contenido

Dentro de este algoritmo las variables de entrada son la base completa de observaciones y la cantidad de modelos a entrenar $M$.

  1. Crear una muestra aleatoria de tamaño $n$ (tamaño de toda la base de datos). Es decir que quedaran observaciones repetidas y otras por fuera. Este proceso se conoce como bootstraping.

  2. Entrenar un modelo de clasificacion usando esta muestra de los datos. Usualmente, no es recomendable utilizar algortimos de regularización o modelos reducidos diseñados para combatir el sobre-ajuste ya que el proceso de agregación es usado al final es capaz de suavizar el ajuste.

  3. Para cada observacion en la muestra de los datos, se almacena la clase asignada por el modelo (0 o 1).

  4. Repertir este proceso $M$ veces para entrenar $M$ modelos.

  5. Para cada observacion en la base de datos original (sin muestreo), se calcula la predicción final de la clase a través del conteo de los resultados de cada modelo donde se escoge aquella con mayor número. Es decir, que si se entrenaron $M=25$, donde 20 de estos dicen que la clase es 0 y los otros 5 dicen que la clase es 1, la predicción final de dicha observación es 0.

  6. Calcular la precisión del modelo.

Aplicación Bagging CART-SkillCraft

Contenido

Para el ejemplo con CART se utiliza el árbol que se generó en el notebook XXXX (Métodos Basados en Árboles), el cual utiliza la base de datos SkillCraft.


In [5]:
## Instale y cargue las siguientes librerías

library(rpart)
library(caret)
library(ipred)


Warning message:
: package 'ipred' was built under R version 3.3.2

In [3]:
## Codigos del capítulo 6. (Para obtener la descripcion de los codigos dirigirese al capitulo)

set.seed(266)

link                       <-"https://archive.ics.uci.edu/ml/machine-learning-databases/00272/SkillCraft1_Dataset.csv"
skillcraft                 <-read.csv(url(link)) 
skillcraft                 <- skillcraft[-1] 
skillcraft$TotalHours      <- as.numeric(levels(skillcraft$TotalHours))[skillcraft$TotalHours]
skillcraft$HoursPerWeek    <- as.numeric(levels(skillcraft$HoursPerWeek))[skillcraft$HoursPerWeek]
skillcraft$Age             <- as.numeric(levels(skillcraft$Age))[skillcraft$Age]
skillcraft                 <- skillcraft[complete.cases(skillcraft),]
skillcraft_sampling_vector <- createDataPartition(skillcraft$LeagueIndex, p = 0.80, list = FALSE)
skillcraft_train           <- skillcraft[skillcraft_sampling_vector,]
skillcraft_test            <- skillcraft[-skillcraft_sampling_vector,]


Warning message:
In eval(expr, envir, enclos): NAs introducidos por coerciónWarning message:
In eval(expr, envir, enclos): NAs introducidos por coerciónWarning message:
In eval(expr, envir, enclos): NAs introducidos por coerción

In [6]:
## Bagging al árbol CART
baggedtree <- bagging(LeagueIndex ~ .,                       # Formula del arbol
                      data = skillcraft_train,               # Data de entrenamiento
                      nbagg = 100,                           # Numero de replicas en bootstrapping 
                      coob = T)                              # calculo estimado out-of-bag estimate de la tasa de error

## Predicción con el bagged tree
baggedtree_predictions <- predict(baggedtree,                # Modelo CART bagged
                                  skillcraft_test)           # Datos de validación

## Función para calcular el SSE
compute_SSE <- function(correct, predictions) { 
return(sum((correct - predictions) ^ 2))                     # Suma de los errores al cuadrado.
}

## Calculo error SSE
(baggedtree_SSE <- compute_SSE(baggedtree_predictions,       # Valores pronosticados
                               skillcraft_test$LeagueIndex)) # Valores de validacion


684.919718694183

El valor de $SSE$ es de 684.92 el cual es menor que el arbol con mejoramiento de parámetros estimado en el capitlo 6 (701.33)

Aplicación Bagging Regresión Logística-Enfermedades del Corazón

Contenido

El método de Bagging también se puede utilizar para una variedad de modelos. Para ilustrar esto, se utilizará el modelo de regresión logística que se realizo en libro 01 y evaluar la mejora del desempeño.


In [ ]:
## Instale y cargue las siguientes
library(caret)

In [7]:
## Codigos del notebook - Regresión Logística (Para obtener la descripcion de los codigos dirigirese al notebook)

heart                    <- read.table("http://archive.ics.uci.edu/ml/machine-learning-databases/statlog/heart/heart.dat",
                                           quote="\"")

names(heart)             <- c("AGE", "SEX", "CHESTPAIN", "RESTBP", "CHOL",
                              "SUGAR", "ECG", "MAXHR", "ANGINA", "DEP", 
                              "EXERCISE", "FLUOR","THAL", "OUTPUT")

heart$CHESTPAIN          <- factor(heart$CHESTPAIN)
heart$ECG                <- factor(heart$ECG)
heart$THAL               <- factor(heart$THAL)
heart$EXERCISE           <- factor(heart$EXERCISE)
heart$OUTPUT             <- heart$OUTPUT - 1


set.seed(987954)

heart_sampling_vector    <- createDataPartition(heart$OUTPUT, p = 0.85, list = FALSE)   
heart_train              <- heart[heart_sampling_vector,]
heart_train_labels       <- heart$OUTPUT[heart_sampling_vector] 
heart_test               <- heart[-heart_sampling_vector,]
heart_test_labels        <- heart$OUTPUT[-heart_sampling_vector]

Ya con la base de datos cargada y depurada, se configura los parámetros de modelo de ensamble empezando por la obtención de los vectores de muestro aleatorio con reemplazo bootstraping


In [8]:
## Parámetros del modelo

M     <- 11                                                          # Numero de modelos a entrenar
seeds <- 70000 : (70000 + M - 1)                                     # Semillas de cada modelo
n     <- nrow(heart_train)                                           # Número de observaciones

## Generación de las posiciones del muestreo con reemplazo Bagging

sample_vectors <- sapply(seeds,                                      # Para cada semilla hacemos
                         function(x) {set.seed(x);                   # Establecer la semilla aleatoria
                                      return(sample(n,               # Devolver el muestreo del 1 al n
                                                    n,               # Una muestra de n elementos
                                                    replace = T))    # Con reemplazo
                                     }
                        )
head(sample_vectors)                                                 # Primeros valores


10 75104121 14219229189 84155162
65168132111139126 58 95101216 74
25203130 93225 93219139185186 94
151 93155188127110152105191 14106
85 49 89135105192 74220126192127
181200212182169165 51 68 55 13 23

Una vez con la matriz de muestreo, se procede a entrenar los $M$ modelos.


In [9]:
## Funcion para realizar el modelo logístico
train_1glm <- function(sample_indices) {                             # Funcion "train_1glm" que depende de las posiciones del muestreo
    data   <- heart_train[sample_indices,];                          # subset la data con las posiciones de la muestra
    model  <- glm(OUTPUT ~ .,                                        # Modelo con variable respuesta "OUTPUT"
                 data = data,                                        # Datos de entrada
                 family = binomial("logit"));                        # Regresion Logistica
    return(model)                                                    # La función devuelve el modelo
}

## Estimación los M modelos
models    <- apply(sample_vectors,                                   # A cada vector de muestreo le aplicamos la función 
                2,                                                   # Indicador de aplicación por columnas
                train_1glm)                                          # Función de modelo logístico

head(models)                                                         # Primeros valores


[[1]]

Call:  glm(formula = OUTPUT ~ ., family = binomial("logit"), data = data)

Coefficients:
(Intercept)          AGE          SEX   CHESTPAIN2   CHESTPAIN3   CHESTPAIN4  
  -7.594241    -0.020559     1.349963     1.731942     0.969115     3.077225  
     RESTBP         CHOL        SUGAR         ECG1         ECG2        MAXHR  
   0.028977     0.005318    -1.252289     0.533759    -0.197845    -0.021618  
     ANGINA          DEP    EXERCISE2    EXERCISE3        FLUOR        THAL6  
   1.292296     0.213861     1.745369     0.260860     1.592797    -0.782992  
      THAL7  
   2.201760  

Degrees of Freedom: 229 Total (i.e. Null);  211 Residual
Null Deviance:	    314.9 
Residual Deviance: 104.2 	AIC: 142.2

[[2]]

Call:  glm(formula = OUTPUT ~ ., family = binomial("logit"), data = data)

Coefficients:
(Intercept)          AGE          SEX   CHESTPAIN2   CHESTPAIN3   CHESTPAIN4  
   -5.25155     -0.03049      2.04531     -0.29309     -1.81503      2.42040  
     RESTBP         CHOL        SUGAR         ECG1         ECG2        MAXHR  
    0.04817      0.01083     -0.82672      1.09534      0.19889     -0.05267  
     ANGINA          DEP    EXERCISE2    EXERCISE3        FLUOR        THAL6  
    2.22879      0.37747     -0.17252      1.72625      2.01710      1.36969  
      THAL7  
    2.41236  

Degrees of Freedom: 229 Total (i.e. Null);  211 Residual
Null Deviance:	    314.4 
Residual Deviance: 82.88 	AIC: 120.9

[[3]]

Call:  glm(formula = OUTPUT ~ ., family = binomial("logit"), data = data)

Coefficients:
(Intercept)          AGE          SEX   CHESTPAIN2   CHESTPAIN3   CHESTPAIN4  
   -6.65939     -0.07101      3.01732      2.92833      2.00574      3.50170  
     RESTBP         CHOL        SUGAR         ECG1         ECG2        MAXHR  
    0.04140      0.01063     -2.51102      0.80422      0.34057     -0.03731  
     ANGINA          DEP    EXERCISE2    EXERCISE3        FLUOR        THAL6  
    1.73266      0.52395      0.64481      0.16485      1.63687      0.46386  
      THAL7  
    1.22768  

Degrees of Freedom: 229 Total (i.e. Null);  211 Residual
Null Deviance:	    314.9 
Residual Deviance: 121.2 	AIC: 159.2

[[4]]

Call:  glm(formula = OUTPUT ~ ., family = binomial("logit"), data = data)

Coefficients:
(Intercept)          AGE          SEX   CHESTPAIN2   CHESTPAIN3   CHESTPAIN4  
  -1.695002    -0.040329     1.048970     1.851488     1.694622     3.403773  
     RESTBP         CHOL        SUGAR         ECG1         ECG2        MAXHR  
   0.003866     0.005311    -0.083538     0.960724    -0.011692    -0.024451  
     ANGINA          DEP    EXERCISE2    EXERCISE3        FLUOR        THAL6  
   0.746202     0.665192    -0.204678    -1.098114     0.847238     2.829046  
      THAL7  
   1.568876  

Degrees of Freedom: 229 Total (i.e. Null);  211 Residual
Null Deviance:	    313.2 
Residual Deviance: 137.6 	AIC: 175.6

[[5]]

Call:  glm(formula = OUTPUT ~ ., family = binomial("logit"), data = data)

Coefficients:
(Intercept)          AGE          SEX   CHESTPAIN2   CHESTPAIN3   CHESTPAIN4  
 -10.129915    -0.036380     2.652578     2.411853    -0.521962     3.804546  
     RESTBP         CHOL        SUGAR         ECG1         ECG2        MAXHR  
   0.021622     0.023483    -3.590200     0.916767    -0.319370    -0.026799  
     ANGINA          DEP    EXERCISE2    EXERCISE3        FLUOR        THAL6  
   1.018611    -0.007493     2.972216     1.772404     1.527764    -2.737567  
      THAL7  
   2.034268  

Degrees of Freedom: 229 Total (i.e. Null);  211 Residual
Null Deviance:	    314.9 
Residual Deviance: 108.2 	AIC: 146.2

[[6]]

Call:  glm(formula = OUTPUT ~ ., family = binomial("logit"), data = data)

Coefficients:
(Intercept)          AGE          SEX   CHESTPAIN2   CHESTPAIN3   CHESTPAIN4  
 -13.674601    -0.002809     2.239275     2.558059    -0.146453     3.473136  
     RESTBP         CHOL        SUGAR         ECG1         ECG2        MAXHR  
   0.020230     0.006904    -1.562047     2.887265     0.687904     0.014562  
     ANGINA          DEP    EXERCISE2    EXERCISE3        FLUOR        THAL6  
   1.769353     0.413417     0.751045     1.608631     1.209291    -0.119684  
      THAL7  
   2.161520  

Degrees of Freedom: 229 Total (i.e. Null);  211 Residual
Null Deviance:	    316.3 
Residual Deviance: 109.6 	AIC: 147.6

Una vez entrenados los modelos, se extrae la data de entrenamiento de cada modelo pero sin repeticiones lo cual servierá para hacer medidas de ajuste.


In [15]:
## Funcion para obtener los valores de entrenamiento para cada modelo sin repetición

get_1bag  <- function(sample_indices) {                              # Funcion "get_1bag" que depende de las posiciones del muestreo
    unique_sample <- unique(sample_indices);                         # Posiciones unicas del muestreo
    df    <- heart_train[unique_sample, ];                           # Observaciones de la muestra sin repetirse.
    df$ID <- unique_sample;                                          # Agregar columna de identificador de la posisión de la muestra
    return(df)                                                       # Mostrar el data.frame
}

## Se obtine los bags
bags <- apply(sample_vectors,                                        # Vector de posiciones
              2,                                                     # Aplicamos por columnas
              get_1bag)                                              # Función de obtener los bags únicos

#head(bags)

Predicción con los modelos.


In [17]:
## Función para predecir con el modelo
glm_predictions   <- function(model, data, model_index) {
    colname       <- paste("PREDICTIONS", 
                     model_index);
    data[colname] <- as.numeric(
        predict(model, 
                data, 
                type = "response") > 0.5);
    return(data[,c("ID", colname), 
                drop = FALSE])
}

## Predicciones de cada modelo
training_predictions <- mapply(glm_predictions,                     # Función a aplicar
                               models,                              # Cada uno de los 11 modelos 
                               bags,                                # Bags (Registros unicos de entrenamiento)
                               1 : M,                               # Indice de cada modelo
                               SIMPLIFY = F)                        # Devuelve una lista

#head (training_predictions)

Se agrupan todas las predicciones de cada modelo para cada registro. Esto se realiza haciendo un join entre cada predicción con la llave del ID de la fila, luego se reducen las filas y se coloca NA donde no haya predicción.


In [18]:
## Agrupación de todas las predicciones en un data_frame
train_pred_df <- Reduce(function(x, y) merge(x, y, by = "ID", all = T),   # Reducción de la matriz a unicamente el merge (JOIN)
                        training_predictions)                             # Matriz a aplicar el Join y el Reduce
    
head(train_pred_df)


IDPREDICTIONS 1PREDICTIONS 2PREDICTIONS 3PREDICTIONS 4PREDICTIONS 5PREDICTIONS 6PREDICTIONS 7PREDICTIONS 8PREDICTIONS 9PREDICTIONS 10PREDICTIONS 11
1 1NA 1NANA 1 1NA 1 1 1
2 0NANA 0NA 0 1NANA 0NA
3 NA 0 0NANA 0 0 0NANANA
4 NA 1 1 1NANANANA 1 1 1
5 0 0 0NA 0 0 0 0 0 0NA
6 0 1 0 0 0NANANA 0NA 0

In [19]:
## Resultado final del modelo Bagging
train_pred_vote <- apply(train_pred_df,                                        # Data frame al cual le vamos a aplicar                                     
                         1,                                                    # Aplicar por filas
                         function(x) as.numeric(mean(x, na.rm = TRUE) > 0.5))  # Calcular la media de cada fila. Si la media es mayor que 0.5, la predicción es 1, de lo contrario 0.
                             
head(train_pred_vote)


  1. 1
  2. 0
  3. 0
  4. 1
  5. 0
  6. 1

In [21]:
## calculo de la precisión media del algoritmo
(training_accuracy <- mean(train_pred_vote ==                                         # Valores predichos
                           heart_train$OUTPUT[as.numeric(train_pred_df$ID)]))         # Valores reales


0.447826086956522

Ejercicio.-- A partir de la base de datos de clasificacion de vinos utilice el bagging para entrenar una regresión logística y mejorar el desempeño de los resultados del modelo individual en categorizar cada uno dentro de las 3 clases. Parametrice un porcentaje de entramiento-validación de 90-10; tenga cuidad de no desbalancear las muestras.

Las características de los vinos son:

  1. Alcohol
  2. Malic acid
  3. Ash
  4. Alcalinity of ash
  5. Magnesium
  6. Total phenols
  7. Flavanoids
  8. Nonflavanoid phenols
  9. Proanthocyanins
  10. Color intensity
  11. Hue
  12. OD280/OD315 of diluted wines
  13. Proline
## Datos url <- "https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data"

In [ ]:


Ejercicio.-- Utilice la base de datos de detección de graude en tarjetas de credito en Septiembre de 2013. La descripción de la base de datos se puede enconrtar en la página de Kaggle

Notas a la base de datos: "El conjunto de datos es altamente desequilibrado, la clase positiva (fraudes) representa el 0,172% de todas las transacciones. Contiene sólo variables numéricas de entrada que son el resultado de una transformación PCA. Lamentablemente, debido a problemas de confidencialidad, no podemos proporcionar las características originales y más información de fondo sobre los datos. Las características V1, V2, ... V28 son los componentes principales obtenidos con PCA, las únicas características que no han sido transformadas con PCA son 'Tiempo' y 'Cantidad'. La característica 'Tiempo' contiene los segundos transcurridos entre cada transacción y la primera transacción en el conjunto de datos. El campo 'Import' es la cantidad de la transacción. La característica 'Class' es la variable de respuesta y toma el valor 1 en caso de fraude y 0 en caso contrario. Dada la relación de desequilibrio de clase, recomendamos medir la precisión usando el área bajo la curva Precision-Recall (AUPRC). La precisión de la matriz de confusión no es significativa para la clasificación desequilibrada."

Para este ejercicio, se recomienda balancear la base de datos a un 1:5 o 1:6 (por cada 6 transacciones no fraudlentas, existe 1 fraudlenta). Puede utilizar cualquiera de las técnicas ya vistas en clase (regresión logística, redes neuronales, árboles de decisión). Lo importante es que utilice el concepto de bagging para mostrar la mejora en el desempeño de los modelos individuales.

Datos


In [ ]:


Boosting

Contenido

El Boosting consiste en entrenar una cadena de modelos y asignarle pesos a las observaciones que fueron clasificadas incorrectamente o cayeron muy lejos de su valor esperado, de tal forma que los modelos siguientes estén forzados a priorizar dichas observaciones.

Este metodo aforce una alternativa especialmente para aquellos algoritmos que son "débiles", es decir, que producen una predicción un poco mejor que elección al azar. En estos modelos usualmente la complejidad es baja, no obstante, se pueden entrenar modelos cuyo parámetro de complejidad sea configurable como las redes neuronales o árbole de decisión.

Una de las diferencias del Boosting respecto al bagging es que no existe un componente aleatorio al momento de elegir los datos de entranmiento. Todos los modelos se entrenan con la misma base de entrenamiento original. Otra diferencia es la cantidad de existente de tecnicas de boosting para abordar los problemas (AdaBoost, BrownBoost, Stochastic Gradient Boost, CoBoost) mientras que el bagging no presenta técnicas con cambios importantes dentro de su implementación.

De forma general, el boosting parte construyendo un modelo con las observaciones de entrenamiento y midiendo la precisión en los mismos datos. Cada una de las observaciones que fueron erroneamente identificadas por el modelo se les da un peso más grande de aquuellas que fueron correctamente clasificadas. De esta forma, el modelo se re-entrena usando estos pesos. Estos pasos se repiten multiples veces, ajustando nuevamente los pesos de los datos erroneamente categorizados en la iteración anterior.

Lógicamente, si este proceso sigue indefinidamente el modelo de la iteración final estará sobre-ajustado. Por lo tanto, para evitar este inconveniente, el modelo ensamblado se construye a partir de el promedio ponderado (usualmente son proporcionales a la precisión) de todos los modelos entrenados en el proceso. Es decir, desde el modelo inicial hasta el modelo final de todo el proceso de ajuste de pesos. Usualmente, en modelos de regresión se ajustan los pesos de las observaciones en base a alguna médida de distancia entre el valor predicho y el valor real.

Algoritmo AdaBoost en Clasificación Binaria

Contenido

Existen dos tipos Adaptative Boost (AdaBoost): Discreta (binaria) y Real (multinomial). No obstante también existen extensiones para problemas de regresión. El input de este tipo de algoritmos son los mismos que en el bagging, la base completa de observaciones y la cantidad de modelos a entrenar M.

  1. Se inicia el vector de pesos de cada observación, $w$, de longitud $n$ (numero de observaciones) con el valor de $ w_i = \frac {1}{n} $. Estos valores son actualizados en cada iteración

  2. Se usa el vector actual de pesos y todos los datos para entrenar un modelo $ G_m $.

  3. Se calcula la tasa de error ponderado como la suma de todas las observaciones mal clasificadas multiplicada por su peso, dividido por la suma del vector de pesos. Esto se expresa como:

$$ err_m = \frac {\sum_{i=1}^{n} w_i · I(y_i \neq G_m (x))}{\sum_{i=1}^{n} w_i} $$
  1. Luego se transforma el peso para este modelo, $ a_m $, como el logaritmo de la división entre la la precisión y el error. Lo anterior se expresa:
$$ a_m = \frac {1}{2} · log_e (\frac {1-err_m}{err_m}) $$
  1. Se actualizan los pesos de las observaciones $w$ para la próxima iteracion. Los eventos de observaciones mal clasificadas se multiplican su peso actual por $e^{a_m}$, lo que hace que incremente dicho peso. Por el contrario, aquellas que fueron bien clasificadas, se multiplican por $e^{-a_m}$, reduciendo su peso dentro del modelo.

  2. Se renormaliza los pesos del vector de tal forma que la suma de ellos den 1.

  3. Repetir los pasos del 2 al 6, M veces para producir M modelos.

  4. Se define el modelo final como la función signo de la suma ponderada de las salidas de todos los modelos $ G_m $, luego:

$$ G(x) = sign(\sum_{m=1}^{M} a_m · G_m(x) )$$

Limitaciones del Boosting

Contenido

  • En ocasiones la agregación de algoritmos débiles puede generar un sobre ajuste en el modelo.
  • La mayoría de los algoritmos tienen una función de ajuste de pesos simétrica para el error, donde en cierto tipos de problemas puede ser más importante el error tipo I que el error tipo II, o viceversa.

Aplicación Boosting-AdaBoost

Contenido

En este ejemplo se utiliza los datos del telescopio de rayos gamma donde se analiza los patrones de onda para predecir si un patron en particular viene de rayos gamas que se filtran en la atmosfera o vienen de radiación de fondo normal.


In [25]:
## Instale y cargue las siguientes librerías
library(caret)
library(nnet)

In [22]:
## Lectura de datos
magic        <- read.csv("http://archive.ics.uci.edu/ml/machine-learning-databases/magic/magic04.data",   
                   header=FALSE)                                                  # Sin encabezados
names(magic) <- c("FLENGTH", "FWIDTH", "FSIZE", "FCONC", "FCONC1","FASYM",
                  "FM3LONG", "FM3TRANS", "FALPHA", "FDIST", "CLASS")              # Nombres columnas

magic$CLASS  <- as.factor(ifelse(magic$CLASS =='g', 1, -1))                       # Conversion CLASS a factor (-1,1)

head(magic)


FLENGTHFWIDTHFSIZEFCONCFCONC1FASYMFM3LONGFM3TRANSFALPHAFDISTCLASS
28.7967 16.00212.6449 0.3918 0.1982 27.7004 22.0110 -8.202740.0920 81.88281
31.6036 11.72352.5185 0.5303 0.3773 26.2722 23.8238 -9.9574 6.3609 205.26101
162.0520136.03104.0612 0.0374 0.0187 116.7410-64.8580-45.216076.9600 256.78801
23.8172 9.57282.3385 0.6147 0.3922 27.2107 -6.4633 -7.151310.4490 116.73701
75.1362 30.92053.1611 0.3168 0.1832 -5.5277 28.5525 21.8393 4.6480 356.46201
51.6240 21.15022.9085 0.2420 0.1340 50.8761 43.1887 9.8145 3.6130 238.09801

Creación datasets de entrenamiento y validación.


In [23]:
## Datos de training y test 

set.seed(33711209)                                            # Semilla de aleatoriedad

magic_sampling_vector <- createDataPartition(magic$CLASS,     # Posiciones de variable CLASS
                                             p = 0.80,        # Proporcion de set de training
                                             list = FALSE)    # Devolver como lista es FALSO

magic_train           <- magic[magic_sampling_vector, 1:10]   # Subset de la data de training sin CLASS (Predictoras)
magic_train_output    <- magic[magic_sampling_vector, 11]     # Subset de la data de training de CLASS (Respuesta)
magic_test            <- magic[-magic_sampling_vector, 1:10]  # Subset de la data de test sin CLASS (Predictoras)
magic_test_output     <- magic[-magic_sampling_vector, 11]    # Subset de la data de test de CLASS (Respuesta)

Normalización de los datos con media igual a cero (0) y desviación estándar igual a uno (1)


In [24]:
## Transoformacion de los datos
magic_pp            <- preProcess(magic_train,           # Generar un modelo de normalización estándar.
                       method = c("center", "scale"))    # Center = Media 0, Scale = Division por la desviación

magic_train_pp      <- predict(magic_pp,                 # Normalización de la data de entrenamiento
                          magic_train)

magic_train_df_pp   <- cbind(magic_train_pp,             # Juntar data de entrenamiento normalizada y la variable respuesta
                           CLASS = magic_train_output)

magic_test_pp       <- predict(magic_pp,                 # Normalización de la data de test
                         magic_test)

Entrenamiento de una red neuronal con una capa oculta.


In [26]:
## Entrenamos un modelo de red neural con una capa de la misma forma que el capitulo 4.
                                                             
n_model            <- nnet(CLASS ~ .,                                     # Variable respuesta CLASS, predictoras el resto
                            data = magic_train_df_pp,                     # Data de entrenamiento normalizada
                            size = 1)                                     # Tamaño de las capas ocultas

n_test_predictions <- predict(n_model,                                     # Predecimos con la red neuronal
                              magic_test_pp,                               # Data de test
                              type = "class")                              # Predicción de CLASS (NO PROBABILIDAD)

## calculo de la precisión media del algoritmo
(n_test_accuracy <- mean(n_test_predictions == magic_test_output))


# weights:  13
initial  value 9853.413883 
iter  10 value 7272.941211
iter  20 value 7099.756392
iter  30 value 7006.837619
iter  40 value 6999.147893
iter  50 value 6984.058978
iter  60 value 6983.258778
iter  70 value 6981.254951
iter  80 value 6979.846315
iter  90 value 6979.678059
iter 100 value 6979.312668
final  value 6979.312668 
stopped after 100 iterations
0.795424664738364

Creación de las propias funciones personalizadas para implementar AdaBoost


In [27]:
## Creamos la función AdaBoost
AdaBoostNN <- function(training_data, output_column, M, hidden_units) {  # Funcion AdaBoost (Datos entrenamiento, Variable respuesta, Numero modelos, numero capas escondidas)
    
    require("nnet")                                                         # Libreria
    models <- list()                                                        # lista vacia de modelos
    alphas <- list()                                                        # lista vacia de alphas
    n <- nrow(training_data)                                                # número de observaciones para entrenar
    model_formula <- as.formula(paste(output_column, '~ .', sep = ''))      # fórmula (Variable respuesta ~ Variables Predictoras)
    w <- rep((1/n), n)                                                      # Vector de pesos iniciales proporcionales 
    
## Ciclo para los M modelos
    for (m in 1:M) {                                                        # Ciclo for de 1 a M (numero modelos)
## Generar el modelo y sus predicciones
        model <- nnet(model_formula,                                        # Formula
                      data = training_data,                                 # Datos de training
                      size = hidden_units,                                  # Capas ocultas
                      weights = w)                                          # Pesos
        models[[m]] <- model                                                # Guardar modelo en la lista
        predictions <- as.numeric(predict(model,                            # Forzar a numerico la predicción delo modelo
            training_data[, -which(names(training_data) ==output_column)],  # Data training (Sin la variable respuesta)
                                          type = "class"))                  # Prediccion de clase (no de probabilidad)
## calculo de los errores para ajuste y su corrección     
        errors <- predictions != training_data[, output_column]             # Comparación de las predicciones con los reales
        error_rate <- sum(w * as.numeric(errors)) / sum(w)                  # Calculo de la tasa de error
        alpha <- 0.5 * log((1 - error_rate) / error_rate)                   # Calculo de los alpha de correccion
        alphas[[m]] <- alpha                                                # Se almace el alpha
        temp_w <- mapply(                                                   # Aplicación de la funcion
            function(x, y) if (y) { x * exp(alpha) }                        # Si esta bien clasificada, su peso es con alpha positivo
                else { x * exp(-alpha)},                                    # De lo contrario, su peso es con alpha negativo
            w,                                                              # Use los pesos actuales como x
            errors)                                                         # Use los errores como y
        w <- temp_w / sum(temp_w)                                           # Normalice los pesos
    }
## Termina el for
            
return(list(models = models, alphas = unlist(alphas)))                      # Devuelva los modelos, los alphas.
}

## Creacion función para predecir con AdaBoost
AdaBoostNN.predict <- function(ada_model, test_data) {                      # Función recibe modelo y data test
    models <- ada_model$models                                              # Asignación modelos
    alphas <- ada_model$alphas                                              # Asignacion alphas
    
## Creacion matriz de predicciones
    prediction_matrix <- sapply(models,                                     # Para cada modelo
                                function (x) as.numeric(predict(x,          # Forzar a numerico las predicciones 
                                                                test_data,  # Con la data de validacion
                                                                type = "class")))  # Forzar a salida clase
                                    
## Calculo de predicciones ponderadas
    weighted_predictions <- t(apply(prediction_matrix,                           # Transponer el apply de la matriz de prediccion = x 
                                    1,                                           # Por filas
                                    function(x) mapply(function(y, z) y * z,     # de la multiplicación y * z
                                        x,                                       # Donde y son las predicciones
                                        alphas)))                                # z son los alphas
## Aplicacion de la función signo (forza -1 o 1)                                       
    final_predictions <- apply(weighted_predictions,                             # Aplicacion a las predicciones ponderadas
                               1,                                                # Por filas
                               function(x) sign(sum(x)))                         # La función signo
                                   
    return(final_predictions)                                                    # Devolver las predicciones
}

Se utiliza las funciones anteriores para entrenar la red neuronal con boosting y predecir.


In [28]:
## Ejecutar el modelos con Boosting y predecimos
ada_model <- AdaBoostNN(magic_train_df_pp,                   # Datos de entrenamiento
                        'CLASS',                             # Variable respuesta
                        10,                                  # Numero de modelos a entrenar
                        1)                                   # Numero de capas ocultas de la red

predictions <- AdaBoostNN.predict(ada_model,                 # Prediccion con el modelo
                                  magic_test_pp,             # Datos de testing
                                  'CLASS')                   # Variable respuesta

## Calculo la precisión media del algoritmo
mean(predictions == magic_test_output)


# weights:  13
initial  value 0.653043 
iter  10 value 0.490532
iter  20 value 0.472345
iter  30 value 0.467543
iter  40 value 0.465317
iter  50 value 0.462195
iter  60 value 0.461556
iter  70 value 0.461182
iter  80 value 0.460487
iter  90 value 0.460309
iter 100 value 0.460104
final  value 0.460104 
stopped after 100 iterations
# weights:  13
initial  value 0.691335 
iter  10 value 0.682753
iter  20 value 0.673642
iter  30 value 0.669705
iter  40 value 0.668436
iter  50 value 0.667255
iter  60 value 0.665946
iter  70 value 0.665716
iter  80 value 0.665105
iter  90 value 0.664161
iter 100 value 0.663563
final  value 0.663563 
stopped after 100 iterations
# weights:  13
initial  value 0.694629 
iter  10 value 0.692252
iter  20 value 0.677093
iter  30 value 0.663049
iter  40 value 0.661402
iter  50 value 0.652622
iter  60 value 0.643111
iter  70 value 0.636908
iter  80 value 0.636534
iter  90 value 0.635478
iter 100 value 0.635054
final  value 0.635054 
stopped after 100 iterations
# weights:  13
initial  value 0.690733 
iter  10 value 0.682223
iter  20 value 0.667209
iter  30 value 0.653828
iter  40 value 0.642063
iter  50 value 0.634364
iter  60 value 0.631926
iter  70 value 0.631224
iter  80 value 0.629839
iter  90 value 0.629304
iter 100 value 0.629026
final  value 0.629026 
stopped after 100 iterations
# weights:  13
initial  value 0.689411 
iter  10 value 0.675165
iter  20 value 0.665723
iter  30 value 0.665082
iter  40 value 0.661048
iter  50 value 0.652168
iter  60 value 0.649418
iter  70 value 0.646686
iter  80 value 0.644873
iter  90 value 0.644294
iter 100 value 0.644020
final  value 0.644020 
stopped after 100 iterations
# weights:  13
initial  value 0.696713 
iter  10 value 0.692991
iter  20 value 0.691617
iter  30 value 0.690348
iter  40 value 0.689315
iter  50 value 0.674067
iter  60 value 0.668855
iter  70 value 0.665634
iter  80 value 0.661327
iter  90 value 0.660405
iter 100 value 0.659419
final  value 0.659419 
stopped after 100 iterations
# weights:  13
initial  value 0.737065 
iter  10 value 0.687170
iter  20 value 0.681067
iter  30 value 0.671485
iter  40 value 0.666182
iter  50 value 0.662185
iter  60 value 0.657805
iter  70 value 0.656860
iter  80 value 0.655014
iter  90 value 0.653984
iter 100 value 0.653356
final  value 0.653356 
stopped after 100 iterations
# weights:  13
initial  value 0.723157 
iter  10 value 0.683728
iter  20 value 0.681521
iter  30 value 0.680551
iter  40 value 0.679805
iter  50 value 0.678369
iter  60 value 0.677566
iter  70 value 0.674697
iter  80 value 0.670483
iter  90 value 0.667999
iter 100 value 0.667133
final  value 0.667133 
stopped after 100 iterations
# weights:  13
initial  value 0.736406 
iter  10 value 0.684576
iter  20 value 0.678103
iter  30 value 0.675963
iter  40 value 0.673703
iter  50 value 0.666030
iter  60 value 0.664828
iter  70 value 0.663762
iter  80 value 0.663533
iter  90 value 0.663429
iter 100 value 0.663391
final  value 0.663391 
stopped after 100 iterations
# weights:  13
initial  value 0.687378 
iter  10 value 0.684246
iter  20 value 0.684055
iter  30 value 0.680901
iter  40 value 0.680098
iter  50 value 0.679991
iter  60 value 0.679894
iter  70 value 0.679710
iter  80 value 0.679645
iter  90 value 0.678764
iter 100 value 0.675372
final  value 0.675372 
stopped after 100 iterations
Error in AdaBoostNN.predict(ada_model, magic_test_pp, "CLASS"): unused argument ("CLASS")
Traceback:

1. AdaBoostNN.predict(ada_model, magic_test_pp, "CLASS")

Ejercicio.-- Utilice la base de datos de sobrevientes del Titanic donde ajuste un modelo de clasificación para predecir si una persona sobrevivió al Titanic a partir de sus características. Mediante estas técnicas mas avanzadas, que modelo tiene mejor desempeño respecto al modelo de regresión logistica realizado al inicio del curso?

Datos


In [ ]:


Random Forest

Contenido

Los bosques aleatorios (Random Forest) es una técnica de ensamble basada en el concepto de árbol que se estudió en el notebook XXXX. Básicamente, la idea se deriva de una situación particular en bagged trees (árboles con bagging). Si se supone que la relación entre las variables predictoras y la respuesta se puede modelar correctamente con un árbol de decisión, es muy probable que dentro del proceso de bagging sigamos escogiendo las mismas variables para particionar las observaciones en todos los modelos. Esto conlleva a que todos los árboles no sean independientes uno de los otros porque tendrán los mismos nodos y valores, por lo tanto el promedio de los resultados será menos exitoso al tratar de reducir la vairanza en el ensamblaje.

Para atacar esto, el algoritmo de bosques aleatorios sigue con el concepto de bagged trees e introduce un elemento de aleatoriedad en el proceso de construcción del arboles imponiendo una restricción. Para cada nodo en el arbol, se extrae una muesta aleatoria de tamaño $ m_{try} $ del total de variables predictoras (se quitan variables) y se determina la partición con esta muestra. Lo anterior asegura que las variables más importantes son muestreadas de forma suficiente.

Aplicación Random Forest

Contenido

Dentro del ejemplo de Random Forest,se la base de datos de SkillCraft de la clase anterior.


In [29]:
## Instale y cargue las siguiente librerias
library(randomForest)
library(e1071)


Warning message:
: package 'randomForest' was built under R version 3.3.2randomForest 4.6-12
Type rfNews() to see new features/changes/bug fixes.

Attaching package: 'randomForest'

The following object is masked from 'package:ggplot2':

    margin

Warning message:
: package 'e1071' was built under R version 3.3.2

In [ ]:
## calibracion del modelo
rf_ranges <- list(ntree = c(500, 1000, 1500, 2000),       # Busqueda del mejor número de arboles en el modelo 
                  mtry = 3:8)                             # Intentos para cada número de arboles

rf_tune   <- tune(randomForest,                           # Metodo a calibrar, en este caso RandomForest
                LeagueIndex ~ .,                          # variable respuesta ~ Variable predictoras
                data =skillcraft_train,                   # Datos de entrenamiento
                ranges = rf_ranges)                       # Rangos de búsqueda

rf_tune$best.parameters

## Modelo final calibrado y predicciones

rf_best             <- rf_tune$best.model                 # Extraer el mejor modelo

rf_best_predictions <- predict(rf_best,                   # Predecimos con el mejor modelo
                               skillcraft_test)           # Data de testing

## Calculo SSE e importancia
(rf_best_SSE <- compute_SSE(rf_best_predictions,          # Calculo del SSE (Valores predichos vs Valores reales)
                            skillcraft_test$LeagueIndex))

importance(rf_tune)                                       # Grafico la importancia de cada variable

Ejercicio.-- Se busca predecir el precio de la gasolina dadas las características en la base de datos gasolina. Utilice el método de Random Forest para abarcar dicho problema.

El desarrollo y resultado del ejercicio busca responder:

  • ¿En qué zonas del país la gasolina es más cara y más barata?
  • ¿Qué gasolina (Porducto) son más baratas para comprar combustible?
  • ¿Cuales son los distribuidores de gasolina (bandera) con precios más competitivos?

In [ ]: