Machine Learning usando caret y caretEnsemble

Un flujo de trabajo integral de Machine Learning con modelado múltiple utilizando caret y caretEnsemble en R

¡Hola, mundo! Este es mi primer artículo y no podría estar más feliz. Empecé a aprender data science hace ocho meses y estaba tan cautivado por este campo que decidí dedicarme al 100% para estudiarlo si realmente quería hacer una carrera de esto. Y eso es exactamente lo que hice, así que terminé dejando mi puesto de controlador financiero para pasar 8-9 horas diarias con el fin de aprender todo lo que podía sobre este fascinante mundo.

Estoy ahora en una etapa en la que he terminado todos mis cursos, leo todos mis libros (¡más de lo que puedes imaginar!) y he practicado mucho, pero siento que tengo que esforzarme más para trabajar en problemas reales, escribir sobre ellos y más importante aún, ¡compártalos y aprende de los comentarios!

Así que este es el comienzo de la esperanza de que llegarán muchos más artículos, y para empezar, voy a ir con uno largo. El conjunto de datos es de uno de los libros que leí, así que no es un verdadero “ problema real “, pero es una buena idea para empezar.

¡Demasiado dicho! ¡Vamos a trabajar!

Introducción

Usaré un conjunto de datos muy interesante presentado en el libro Aprendizaje automático con R de Packt Publishing escrito por Brett Lantz. Mi intención es ampliar el análisis en este conjunto de datos mediante la ejecución de un flujo de trabajo de aprendizaje automático supervisado que he estado diseñando desde hace un tiempo para ayudarme a atacar cualquier problema similar con un enfoque sistemático y metódico.

Si están pensando que esto no es nada nuevo, ¡entonces tienes toda la razón! No se me ocurre nada nuevo aquí, solo me aseguro de tener todas las herramientas necesarias para seguir un proceso completo sin dejar ningún detalle grande. Espero que algunos de ustedes lo encuentren útil también y estén seguros de que van a encontrar algunos errores de juicio de mi parte y / o cosas que harían de manera diferente. ¡Siéntete libre de dejarme un comentario y ayudarme a mejorar!

Avancemos y comencemos a entender con qué información vamos a trabajar:

Modelando la fuerza del concreto

Del libro:

“En el campo de la ingeniería, es crucial tener estimaciones precisas del rendimiento de los materiales de construcción. Estas estimaciones son necesarias para desarrollar pautas de seguridad que rigen los materiales utilizados en la construcción de edificios, puentes y carreteras.
Estimar la resistencia del concreto es un desafío de particular interés. Aunque se usa en casi todos los proyectos de construcción, el rendimiento del hormigón varía mucho debido a una amplia variedad de ingredientes que interactúan de manera compleja. Como resultado, es difícil predecir con precisión la fuerza del producto final. Un modelo que pueda predecir confiablemente la resistencia del concreto dada una lista de la composición de los materiales de entrada podría resultar en prácticas de construcción más seguras.

Para este análisis, utilizaremos datos sobre la resistencia a la compresión del concreto donado a el Repositorio de Datos de Aprendizaje Automático UCI ( http://archive.ics.uci.edu/ml ) por I-Cheng Yeh.

Según el sitio web, el conjunto de datos concreto contiene 1.030 ejemplos de hormigón con ocho características que describen los componentes utilizados en la mezcla. Se cree que estas características están relacionadas con la resistencia a la compresión final e incluyen la cantidad (en kilogramos por metro cúbico) de cemento, escoria, ceniza, agua, superplastificante, agregado grueso y agregado fino utilizado en el producto además del envejecimiento. tiempo (medido en días). “

Un flujo de trabajo de aprendizaje automático

He descubierto que dividir el flujo de trabajo en 6 fases funciona mejor para mí. En ese sentido, describiré estas instancias como:
1) Configuración
2) Análisis de datos exploratorios
3) Ingeniería de características
4) Preparación de datos
5) Modelado
6 ) Conclusión

En la práctica, termino saltando de uno a otro, muchas veces sin importar el orden y, a menudo, en el ciclo. Así que piense en esto como una estructura inicial y más de una lista de verificación en lugar de una guía paso a paso.

La lista de verificación es bastante comprensiva y cubre la mayor parte de lo que seguiría en supervisado aprendiendo ambos para clasificación y regresión problemas. En la práctica, lo más probable es que termine omitiendo algunos de los puntos de la lista porque hay cosas que hace para un problema de clasificación que no hace para un problema de regresión viceversa. Incluso algunos de ellos a veces son un poco redundantes (aunque la verificación doble siempre es útil en mi opinión). Estoy seguro de que con más práctica y experiencia, esta lista se pondrá a punto y espero que también pueda compartir eso nuevamente en el futuro.

Me limitaré a dar respuestas directas y ampliaré cada vez que sea necesario. . ¡De lo contrario, me temo que podría alargar demasiado mi vida!

Verá que, aunque incluyo la lista de verificación completa, en muchos casos no tenemos que hacer absolutamente nada. Esto se debe a que este conjunto de datos es bastante sencillo. Se siente un poco como hacer trampa, sin embargo, me aseguraré de mencionar por qué no tenemos que hacer nada y la idea detrás de ese punto de control.

Lo último que quería mencionar es que cuando llegue al fase de modelado cambiaré mi enfoque y mostraré algunas herramientas de modelado útiles con paquetes R caret y caretEnsemble . Así que en lugar de ir paso a paso probando algunos modelos de línea de base y algunos modelos más avanzados con ajuste y regularización de hiperparámetros para reducir el sobreajuste, voy a entrenar un montón de modelos a la vez, ya que me permite hacer esto última fase, mucho más conciso y directo para la interpretación. También porque la fase de modelado es donde a uno le gusta hacer lo propio y ser creativo usando diferentes enfoques. En mi opinión, esta lista de verificación es muy importante para las fases 1 a 4, pero luego, al comenzar la fase 5, puede volverse menos estricta.

Suficientemente dicho, comencemos:

Encuentre los archivos de trabajo y códigos aquí: https://github.com/gabrielpierobon/ML-workflow-caret

1) SETTING

1.1) ¿Qué estamos tratando de predecir?

Necesitamos estimar con precisión el rendimiento de materiales de construcción para el campo de la ingeniería.

1.2) ¿Qué tipo de problema es? ¿Aprendizaje supervisado o no supervisado? Clasificación o Regresión? Binario o Multi-clase? Uni-variate or Multi-variate?

Este es un problema de aprendizaje automático supervisado en el que tenemos que predecir los resultados numéricos, por lo tanto, vamos a utilizar técnicas de regresión.

1.3) ¿Qué tipo de de los datos que tenemos?

Nuestros datos están en formato “csv”. Presenta una fila de encabezado con los nombres de columna. Parece contener solo información numérica.

1.4) Importar el conjunto de datos

Simplemente podemos cargarlo con este código:

 concrete <- read.csv ("/ Users / Gabriel Pierobon / Desktop / Datasets para cargar /concrete.csv")
#use your own directory!

Este es el tamaño de nuestro conjunto de datos:

 dim (hormigón) 
[1]  1030 9

1.030 filas y 9 columnas, una de las cuales es nuestra respuesta / variable objetivo.

Podemos ver que se importó como un marco de datos que es el formato que necesitamos para trabajar con estos datos:

clase (concreta) 
[1]  "data.frame" [19659047] También podemos verificar que los nombres de nuestras columnas fueron importados correctamente.
 nombres (concretos)

De esa lista, identificamos la fuerza como nuestra respuesta / variable objetivo .

Nosotros Podemos concluir que nuestros datos se importaron correctamente y así finalizar nuestra fase s etting .

Antes de hacer eso, me gustaría solicitar todas nuestras bibliotecas por completo. Es útil hacerlo todo de una vez al principio, para mejorar la legibilidad de este trabajo:

 biblioteca (dplyr) 
 biblioteca (ggplot2) 
 biblioteca (PerformanceAnalytics) 
 biblioteca (ggthemes) 
 biblioteca (corrplot) 
 biblioteca (coche) 
 biblioteca (psych) 
 biblioteca (caret) 
 biblioteca (caretEnsemble) 
 biblioteca (doParallel)

Con eso cuidado, vamos a movernos en el análisis de datos exploratorios:

2) Análisis de datos exploratorios (EDA)

2.1) Ver datos (vista de str o dplyr). Primera vista. ¿Algo extraño?

La primera manera en que queremos verificar lo que hicimos en la fase es ver rápidamente nuestro conjunto de datos completo. Lo hacemos así:

 Ver (concreto)

Esto mostrará una ventana con el conjunto de datos. Me gusta mirarlo de arriba a abajo y de izquierda a derecha para reafirmar el hecho de que los datos se cargaron correctamente. También es una manera rápida y sucia de detectar problemas que podrían observarse a primera vista. No desea hacer esto para conjuntos de datos significativamente grandes.

A continuación, tomaremos un vistazo a nuestros datos y observaremos solo las primeras filas del marco de datos:

 vislumbre (hormigón)
 cabeza (hormigón)

2.2) ¿Es un conjunto de datos “ordenado”? ¿Necesita “reunir” o “difundir”? ¿Se presenta de una manera que podamos trabajar?

Necesitamos que nuestros datos se presenten con observaciones individuales como filas y características como columnas. Afortunadamente para nosotros, este es el caso de este conjunto de datos, por lo que no tendremos que transformarlo. De lo contrario, tendríamos que usar algún tipo de función para pivote el marco de datos para acomodarnos a nuestras necesidades ( consulte este sitio para obtener más información sobre eso )

2.3) nombres de fila y colnames ok? ¿Deberíamos cambiarlos?

Ya hemos comprobado cómo se cargaron. ¿Necesitamos actualizar / cambiar algo para tener una comprensión más clara de nuestras variables? No creo que este sea el caso, podemos seguir adelante. La idea aquí es asegurarnos de avanzar cómodamente con nuestros nombres de funciones, evitando nombres largos innecesarios o cualquier otro tipo de situación confusa.

2.4) Verifique los tipos de datos. ¿Estan bien? De lo contrario, convertir

Como hemos visto, todas nuestras variables son del tipo double excepto por la edad variable que es entero . ¡Afortunadamente, no tenemos que convertir nada! Es muy importante que verifiquemos esto para evitar algún tipo de error al cargar los datos. A veces, un solo carácter en una columna numérica puede dar como resultado que toda la columna se cargue como carácter .

2.5) ¿Cuál es nuestra respuesta / variable objetivo? ¿Desequilibrio de clase? Estudielo

Nuestra variable de respuesta es la fuerza. Veamos algunas estadísticas útiles al respecto:

 summary (concrete $ strength)

Podemos ver que va de 2.3 a 82.6. La mediana y la media son ​​muy similares, pero desde la mediana es en realidad más pequeña, esto da como resultado un ligero desviación de la distribución variable a la derecha.

Podemos observar que con una gráfica usando ggplot2 :

Como podemos ver, la distribución de la variable de resistencia no es perfecta normal pero van a proceder independientemente Esto no debería ser un problema porque está cerca. A qqplot también ayuda con el análisis visual de la normalidad (se lo dejo a usted)

Por último, ya que este es un problema de regresión, no tenemos que preocuparnos por el desequilibrio de clase. En la clasificación, quiere tener clases balanceadas en su variable de respuesta ( aquí hay una buena publicación sobre el desequilibrio de clase )

2.6) Resto de las características. Resumen estadístico. Comprenda sus datos

Aquí ampliamos el análisis estadístico a nuestras otras variables / características. Queremos prestar atención al mínimos y máximos (este es un primer control para posibles valores atípicos). Además, significa y mediana la diferencia es algo de lo que preocuparse. Nos gustaría que todas nuestras variables sigan la distribución normal tanto como sea posible.

 resumen (concreto)

Podemos seguir nuestro análisis con una gráfica de correlación. Esto nos presentará un gráfico que muestra la correlación entre todas las variables. También nos permitirá pensar por primera vez si necesitamos todas nuestras variables en nuestro modelo. No queremos que nuestras variables de características presenten una alta correlación entre ellas. Nos ocuparemos de esto más adelante.

 corrplot (cor (concrete), method = "square")

Otra buena manera de ver todo esto junto es con este diagrama de correlación:

 chart.Correlation ( concreto)

En una de las diagonales, podemos ver la distribución de cada característica. No todos se parecen a la normalidad, así que trataremos esto más adelante.

En este punto, mi primera conclusión es que la ceniza variable tiene baja correlación con nuestra fuerza variable de respuesta y una alta correlación con cuatro de las otras ocho características. Por lo tanto, es un candidato fuerte para eliminar, también lo abordaremos más adelante.

2.7) Datos / factores categóricos: crear tablas de recuento para comprender las diferentes categorías. Verifíquelos todos.

En este caso, no estamos trabajando con funciones categóricas. Desea asegurarse de que comprende sus categorías, a veces las faltas de ortografía pueden introducir problemas para factor variables.

Podemos seguir adelante.

2.8) ¿Columnas innecesarias? Columnas que podemos entender rápidamente que no necesitamos. Déjalos

Aquí queremos buscar columnas que son totalmente inútiles. Todo lo que vendría en el conjunto de datos realmente no informativo y podemos determinar que deberíamos dejarlo. Columnas de índice adicionales, columnas de cadena no informativas, etc. Este no es el caso para este conjunto de datos. Puede hacer esto perfectamente tan pronto como importe el conjunto de datos, sin necesidad de hacerlo específicamente en este momento.

2.9) Compruebe si faltan valores. ¿Cuántos? ¿Dónde? ¿Borra los? ¿Impumpirlos?

Primero hagamos un control general para las NA:

 anyNA (concreto) 
[1]  FALSO

¡Maravilloso! ¡No faltan valores en todo el conjunto! Permítame también mostrarle una manera en que podríamos haber detectado esto para cada columna:

 sapply (concrete, {function (x) any (is.na (x))})

( Algunas herramientas para tratar con valores perdidos )

2.10) Verifique si hay valores atípicos y otros puntos de datos incoherentes. Diagramas de caja. DBSCAN para la detección de valores atípicos?

La detección de valores atípicos es más una habilidad que cualquier otra cosa, en mi opinión. Realmente me gusta el enfoque del uso de clústeres DBSCAN para la detección de valores atípicos, pero no voy a proceder con esto, así que no extiendo demasiado este análisis. DBSCAN es un algoritmo de agrupamiento que puede detectar ruido puntos en los datos y no asignarlos a ningún clúster. Me parece muy convincente para la detección de valores atípicos ( más sobre DBSCAN aquí )

En su lugar, seguiré adelante con un diagrama de caja y trataré de trabajar con los puntos que considero relevantes con solo ver: [19659045] boxplot (concrete [-9]col = “orange”, main = “Features Boxplot”)

Vemos que hay varios outliers potenciales, sin embargo, considero que la característica de edad podría ser la más problemática. Veámoslo aislado:

 boxplot (concrete $ age, col = "red")

¿Son solo 4 valores atípicos? Si es así, podríamos deshacernos de ellos. ¿O no deberíamos? Vamos a averiguar cuáles son estos valores y cuántos de ellos están allí.

 age_outliers  100) 
 concrete [age_outliers, “age”]

¡Ay! ¡entonces hubo 62 de ellos en lugar de solo 4! Obviamente, esto es simplemente porque los mismos números se repiten varias veces. Esto me hace pensar que estos puntos de edad son realmente relevantes y no queremos deshacernos de ellos. 62 puntos de datos de nuestro conjunto de datos de 1.030 parece un número demasiado alto como para eliminarlo (estaríamos perdiendo mucha información).

2.11) Verifique la multicolinealidad en los datos numéricos. Variance Inflation Factors (VIF)

Ya hemos visto en la gráfica de correlación presentada anteriormente que parece haber una correlación significativa entre algunas características. Queremos asegurarnos de que multicolinealidad no sea un problema que nos impida avanzar. Para hacer esto, calcularemos un puntaje llamado Factor de Inflación de la Varianza (VIF) que mide cuánto se infla la varianza de un coeficiente de regresión debido a la multicolinealidad en el modelo. Si la puntuación VIF es superior a 10, se sugiere fuertemente la multicolinealidad y deberíamos tratar de deshacernos de las características que la causan.

Primero, generamos un modelo de regresión lineal simple de nuestra variable objetivo explicada por cada otra característica. Después, llamamos a la función vif () en el objeto modelo y echamos un vistazo a la salida de la lista nombrada:

 simple_lm <- lm (strength ~., Data = concrete) 
 vif (simple_lm)

Incluso aunque hay muchas variables con una puntuación de 5 y superior, ninguna de ellas supera el umbral de 10, por lo que consideraremos que la multicolinealidad no es un gran problema. Sin embargo, algunos argumentarían que realmente podría ser un problema tener tantas características con puntajes de 7. No nos preocuparemos por eso en este momento.

Con esto, consideramos el Análisis de Datos Exploratorios (EDA) fase y pasamos a la Ingeniería de funciones. ¡Siéntase libre de estar en desacuerdo con esto terminado! Estoy seguro de que todavía queda mucho por explorar.

3) INGENIERÍA DE CARACTERÍSTICAS

La ingeniería de rasgos también podría considerarse una obra muy importante para un científico de datos. Implica la producción de nuevas características obtenidas a partir de las características presentes en el conjunto de datos. Esto podría ser tan simple como extraer algunas fechas información de cadenas columnas, o producir términos de interacción . Además, sin duda requerirá cierto grado de experiencia en la materia y conocimiento del dominio, ya que idear nuevas características informativas es algo intrínseco a la naturaleza de los datos y al campo de aplicación general.

Aquí, vamos a ir muy rápido, ya que nuestros datos parecen ser muy informativos y completos (¡los ingenieros pueden estar en desacuerdo!).

Quería mencionar que las nuevas características de ingeniería requerirán que repitamos algunos de los análisis previos que ya hemos realizado, ya que no lo hacemos. Quiero introducir nuevas características que agreguen ruido al conjunto de datos. Así que tenga en cuenta que siempre debemos volver a mirar nuestra lista de verificación y definir si necesitamos analizar las cosas una vez más.

3.1) Cree nuevas funciones útiles. Términos de interacción. Matemáticas y estadísticas básicas. Crear categorías con estructuras if-else

Como no soy experto en la materia en ingeniería, no crearé nuevas características a partir de términos de interacción. Me limitaré a verificar si alguna de las variables requiere algún tipo de transformación.

Lo primero que quiero hacer es verificar dos variables que parecen tener distribuciones inusuales. Es el caso de la edad y superplástico. Vamos a trazar su forma original y también registros a continuación (en rojo).

 par (mfrow = c (2,2))
 hist (hormigón $ edad) 
 hist (hormigón $ superplástico) 
 hist (log (concrete $ age), col = "red") 
 hist (log (concrete $ superplastic), col = "red")

Mientras me siento cómodo con la conversión de la edad a su forma logarítmica, en el caso de superplástico, con tantas observaciones siendo 0, tendré algunos problemas al tomar el registro de 0 así que tomaré el registro y luego los estableceré manualmente a 0.

A continuación, el código para convertir ambas características:

 concrete $ age <- log (concrete $ age)
 concrete $ superplastic <- log (concrete $ superplastic) 
 concrete $ superplastic <- ifelse (concrete $ superplastic == - Inf, 0, 
 concrete $ superplastic)
 head (concrete)

Nota: Pasé bastante tiempo en este punto tratando de crear una nueva característica superplástica al agrupar la superplastic featu original re en 3 categorías numéricas. Sin embargo, no tuve mucho éxito en términos de importancia para explicar la variable objetivo. No mostraré esos intentos, solo sé que de hecho traté de trabajar en eso por algún tiempo sin éxito. ¡Fallar es también parte del aprendizaje!

3.2) Crear muñecos para características categóricas. Preferiblemente utilizando One-Hot-Encoding

No estamos trabajando con características categóricas en este momento, por lo que esta sección se omitirá. Sin embargo, si se le presentaron algunas columnas de factores, deberá asegurarse de que su algoritmo pueda funcionar con ellas, o proceder con una codificación en caliente .

3.3) ¿Podemos extraer algunas texto importante de columnas de cadenas que utilizan expresiones regulares?

No estamos trabajando con datos de picadura en este momento, por lo que esta sección se omitirá. Esta debería ser una herramienta muy útil cuando tienes información relevante en el tipo de personaje desde el que puedes crear nuevas categorías útiles.

¡Afortunadamente no tuvimos que pasar por todo eso! Sin embargo, esas últimas 3 secciones son IMPRESCINDIBLES si trabajamos con ese tipo de información. Me aseguraré de analizar un conjunto de datos en el que pueda mostrar algo de eso.

Pasemos a la fase de Preparación de datos:

4) PREPARACIÓN DE LOS DATOS

4.1) Selección de funciones manuales. Elimine las funciones ruidosas, poco informativas, altamente correlacionadas o duplicadas.

Aquí es donde tomamos pasar el tiempo por segunda vez en este flujo de trabajo mirando nuestras características y detectando si alguna de ellas es lo suficientemente informativa o debería descartarse debido a la introducción de problemas. como podría ser la multicolinealidad.

Como ya había decidido, primero soltaré la función de cenizas (es posible que desee dejarla y eliminarla después de probar algunos modelos de línea de base, decidiendo descartarla si no ayuda con el rendimiento, Ya lo hice, así que lo dejaré ahora, como fue mi evaluación inicial)

 concrete $ ash <- NULL
 head (concrete)

Además de eso, no eliminaré nada más. [19659039] 4.2) Transformar datos si es necesario. Escale o normalice si es necesario.

La mayoría de los algoritmos que utilizaremos requieren que nuestras características numéricas se escalen o normalicen ( aquí explicamos por qué )

No haremos eso en esta sección precisa, pero déjelo para más adelante, ya que caret nos permite hacer algo de preprocesamiento dentro de su capacitación . Esto es especialmente útil porque el algoritmo se transformará automáticamente a su escala original al presentarnos predicciones y resultados. Si normalicé manualmente el conjunto de datos aquí, entonces tendría que volver a transformar manualmente las predicciones.

4.3) Extracción automática de características. Reducción de dimensionalidad (PCA, NMF, t-SNE)

Si eliminar funciones manualmente no es lo suficientemente sencillo, pero consideramos que nuestro conjunto de datos contiene demasiadas características o funciones demasiado correlacionadas, podemos aplicar reducción de dimensonality técnicas. El Análisis de Componentes Principales es una de esas técnicas, y una muy útil tanto para simplificar nuestro conjunto de datos como para eliminar el problema de la multicolinealidad para bien.

Factorización de matrices no negativas (NMF) y t-SNE son ​​otras dos técnicas de reducción de dimensionalidad útiles.

Con solo 8 características en nuestro conjunto de datos y 1 ya descartadas manualmente, no creo que necesitemos reducir la dimensionalidad.

4.4) ¿Se aleatoriza nuestro conjunto de datos?

No estamos seguros de que sea aleatorio, por lo que lo mezclaremos por las siguientes razones:

 set.seed (123) 
 concrete_rand <- concrete [sample(1:nrow(concrete)), ]
 dim (concrete_rand)

4.5) Definir un protocolo de evaluación: ¿cuántas muestras tenemos? Mantenga el método. ¿Se necesita validación cruzada?

Tenemos 1.030 muestras, por lo que este es definitivamente un pequeño conjunto de datos. Dividiremos el conjunto de datos en conjuntos de trenes y pruebas y nos aseguraremos de usar validación cruzada cuando entrenemos nuestro modelo. De esta forma, nos aseguramos de que estamos utilizando nuestras pocas observaciones lo mejor que podamos.

4.6) Dividir el conjunto de datos en conjuntos de prueba y tren (establecer la semilla para la replicabilidad)

Primero creamos un conjunto de predictores y un conjunto de variable objetivo

 X = concrete_rand [, -8]
 y = concrete_rand [, 8]

Comprobamos que todo está bien:

 str (X)
 str (y)

Luego procedemos a dividir nuestro nuevo X (predictores) y y (objetivo) se establece en conjuntos de entrenamiento y prueba.

Nota: no es necesario separar los conjuntos, y puede seguir adelante utilizando el método de la fórmula . Simplemente prefiero hacerlo de esta manera, porque de esta manera estoy seguro de que alinearé mi procedimiento con la forma en que trabajaría con el scikit-learn de Python.

Usaremos caret ‘s createDataPartition () función, que genera los índices de partición para nosotros, y los usaremos para realizar las divisiones:

 set.seed (123)
 part.index <- createDataPartition (concrete_rand $ strength, 
 p = 0.75, 
 list = FALSE)
 X_train <- X [part.index, ]
 X_test <- X [-part.index, ]
 y_train <- y [part.index]
 y_test <- y [-part.index]

Así que ahora tenemos 4 conjuntos. Dos predictores se dividen en tren y prueba y dos objetivo se dividen en tren y prueba también. Todos usan el mismo índice para particionar.

Una vez más, vamos a comprobar que todo funcionó bien:

 str (X_train) 
 str (X_test) 
 str (y_train) 
 str (y_test )

¡Bien! Estamos listos para irnos! ¡Ahora a la fase de modelado!

5) MODELADO

Como mencioné en la introducción, en esta fase cambiaré mi enfoque, y en lugar de revisar una lista de cosas que hacer, resumiré en conjunto cómo procederemos.

  • Usaremos el paquete caretEnsemble para entrenar una lista de modelos, todo al mismo tiempo
  • Esto nos permitirá usar los mismos 5 veces validación cruzada para cada modelo, gracias a la funcionalidad caret
  • Permitiremos procesamiento paralelo para aumentar la velocidad
  • No nos centraremos en la naturaleza de los algoritmos . Los usaremos y comentaremos los resultados
  • Utilizaremos un modelo lineal un máquinas de vectores de soporte con núcleo radial, un bosque aleatorio un modelo basado en árbol de aumento de gradiente y un modelo de aumento de gradiente modelo lineal
  • No haremos ajuste de hiperparámetro manual sino que permitiremos caret para realizar una sintonización predeterminada en cada modelo
  • Compararemos el rendimiento con el entrenamiento y los conjuntos de prueba, centrándonos en RMSE como nuestra métrica (error cuadrático medio)
  • usará una muy buena funcionalidad del paquete caretEnsemble y ensamblará la lista de modelos y luego los apilará para tratar de producir una combinación definitiva de modelos para mejorar aún más el rendimiento

Así que continuemos.

Primero configuramos el procesamiento paralelo y la validación cruzada en tra inControl ()

 registerDoParallel (4) 
 getDoParWorkers ()
 set.seed (123)
 my_control <- trainControl (method = "cv",  # para "validación cruzada" [19659200] number = 5,  # número de k-pliegues 
                            savePredictions = "final", 
 allowParallel = TRUE) 

Luego capacitamos a nuestra lista de modelos utilizando la función caretList () llamando a nuestro Conjuntos X_train y y_train. Especificamos trControl con nuestro objeto trainControl creado anteriormente y establecemos methodList en una lista de algoritmos (consulte la información del paquete caret para comprender qué modelos están disponibles y cómo usarlos).

 set.seed ( 222)
 model_list <- caretList (X_train, 
 y_train, 
 trControl = my_control, 
 methodList = c ("lm", "svmRadial", "rf", 
 "xgbTree", "XgbLinear"), 
 tuneList = NULL, 
 continue_on_fail = FALSE, 
 preProcess = c ("center", "scale"))
  • Uso X_train y y_train, pero puede usar perfectamente y ~ x1 + x2 + … + xn formula en su lugar
  • my_control especificó la validación cruzada de 5 veces y activó el procesamiento paralelo
  • tuneList es FALSO porque no estamos especificando la sintonización manual de hiperpares
  • continue_on_fail está establecida en FALSE, por lo que se detiene si algo sale mal
  • en preProcessing es donde escalamos el conjunto de datos. Elegimos “ centro ” y “ escala

Ahora que nuestra CaretList fue capacitada, podemos ver los resultados. Podemos acceder a cada modelo por separado. Aquí está el resultado de SVM:

 lista_modelo $ svmRadial

Observe que el cursor trata de ajustar automáticamente los parámetros disponibles para el modelo, y elige el mejor modelo usando RMSE como métrica de rendimiento.

Esto es lo mismo para cada uno de los otros modelos en nuestra lista de modelos. No revisaremos cada uno de ellos. ¡Eso es para que lo compruebes!

Vayamos directamente a nuestro objetivo, que es encontrar el modelo que tenga el RMSE más bajo. Primero lo evaluamos para los datos de entrenamiento.

 opciones (dígitos = 3)
 model_results <- data.frame (
 LM = min (lista_modelo $ lm $ resultados $ RMSE), 
 SVM = min (model_list $ svmRadial $ results $ RMSE), 
 RF = min (model_list $ rf $ results $ RMSE), 
 XGBT = min (model_list $ xgbTree $ results $ RMSE), 
 XGBL = min (model_list $ xgbLinear $ resultados $ RMSE) 
)
 print (model_results)

En términos de RMSE, el modelo xgbTree ofrece el mejor resultado, con una puntuación de 4.36 (recuerde que la potencia media fue de 35.8 ).

caretEnsemble offers a functionality to resample the performance of this model list and plot it:

resamples <- resamples(model_list)
dotplot(resamples, metric = “RMSE”)

We can also see that the xgbTree is also presenting a smaller variance compared to the other models.

Next, we will attempt to create a new model by ensembling our model_list in order to find the best possible model, hopefully a model that takes the best of the 5 we have trained and optimizes performance.

Ideally, we would ensemble models that are low correlated with each other. In this case we will see that some high correlation is present, but we will choose to move on regardless, just for the sake of showcasing this feature:

modelCor(resamples)

Firstly, we train an ensemble of our models using caretEnsemble(), which is going to perform a linear combination with all of them.

set.seed(222)
ensemble_1 <- caretEnsemble(model_list, 
                            metric = “RMSE”, 
                            trControl = my_control)
summary(ensemble_1)

As we can see, we managed to reduce RMSE for the training set to 4.156

Here’s a plot of our ensemble

plot(ensemble_1)

The red dashed line is the ensemble’s RMSE performance.

Next, we can be more specific and try to do an ensemble using other algorithms with caretStack().

Note: I tried some models but wasn’t able to improve performance. I’ll show just one of them which yielded the same performance over the training data. We will use both ensembles regardless, in order to check which one does better with unseen data.

set.seed(222)
ensemble_2 <- caretStack(model_list, 
                         method = “glmnet”, 
                         metric = “RMSE”, 
                         trControl = my_control)
print(ensemble_2)

Notice RMSE for the best model using glmnet was 4.15, the same as our first ensemble.

Finally, it’s time to evaluate the performance of our models over unseen data, which is in our test set.

We first predict the test set with each model and then compute RMSE:

# PREDICTIONS
pred_lm <- predict.train(model_list$lm, newdata = X_test)
pred_svm <- predict.train(model_list$svmRadial, newdata = X_test)
pred_rf <- predict.train(model_list$rf, newdata = X_test)
pred_xgbT <- predict.train(model_list$xgbTree, newdata = X_test)
pred_xg bL <- predict.train(model_list$xgbLinear, newdata = X_test)
predict_ens1 <- predict(ensemble_1, newdata = X_test)
predict_ens2 <- predict(ensemble_2, newdata = X_test)
# RMSE
pred_RMSE <- data.frame(ensemble_1 = RMSE(predict_ens1, y_test),
                        ensemble_2 = RMSE(predict_ens2, y_test),
                        LM = RMSE(pred_lm, y_test),
                        SVM = RMSE(pred_svm, y_test),
                        RF = RMSE(pred_rf, y_test),
                        XGBT = RMSE(pred_xgbT, y_test),
                        XGBL = RMSE(pred_xgbL, y_test))
print(pred_RMSE)

Surprisingly, the xgbLinear model out performs every other model on the test set, including our ensemble_1 and matching the performance of the ensemble_2

We also observe that in general , there is a difference in performance compared to the training set. This is to be expected. We could still try to manually tune hyper-parameters in order to reduce some overfitting, but at this point I believe we have achieved very strong performance over unseen data and I will leave further optimization for a future publication.

The last thing I wanted to show is variable importance. In order to do this, I will calculate our xgbLinear model separately, indicating I want to retain the variable importance and then plot it:

set.seed(123)
xgbTree_model <- train(X_train,
                       y_train,
                       trControl = my_control,
                       method = “xgbLinear”,
                       metric = “RMSE”,
                       preProcess = c(“center”,”scale”),
                       importance = TRUE)
plot(varImp(xgbTree_model))

Here we can see the high importance of variables age and cement to the prediction of concrete’s strength. This was to be expected since we had already observed a high correlation between them in our initial correlation plot.

Working with the log of age has also allowed us to improve predictability (as verified separately).

Notice some “unimportant” features present in the chart. Should we have dropped them? Can we simplify our model without impacting performance? We could certainly keep working on that if we needed a simpler model for any reason.

6) Conclusion

This has been quite the journey! We moved along most of the necessary steps in order to execute a complete and careful machine learning workflow. Even though we didn’t have to do a whole lot of changes and transformation to the original data as imported from the csvwe made sure we understood why and what we should have done otherwise.

Moreover, I was able to showcase the interesting work one can do with caret and caretEnsemble in terms of doing multiple modelling, quick and dirty, all at once and being able to quickly compare model performance. The more advanced data scientists and machine learning enthusiasts could possibly take this as a first draft before proceeding with the more advanced algorithms and fine hyper-parameter tuning that will get those extra bits of performance. For the sake of this work, it proved to be really strong even with the basic configuration.

In the book mentioned in the introduction, the author calculates correlation between predictions (using a neural network with 5 hidden layers) and the true values, obtaining a score of 0.924. It also mentions that compared to the original publication (the one his work was based on), this was a significant improvement (original publication achieved 0.885 using a similar neural network)

So how did we do computing the same correlation score as the book’s author?

pred_cor <- data.frame(ensemble_1 = cor(predict_ens1, y_test),
                       ensemble_2 = cor(predict_ens2, y_test),
                       LM = cor(pred_lm, y_test),
                       SVM = cor(pred_svm, y_test),
                       RF = cor(pred_rf, y_test),
                       XGBT = cor(pred_xgbT, y_test),
                       XGBL = cor(pred_xgbL, y_test))
print(pred_cor)

Pretty strong performance!


A comprehensive Machine Learning workflow with multiple modelling using caret and caretEnsemble in… was originally published in Towards Data Science on Medium, wher e people are continuing the conversation by highlighting and responding to this story.

Dejá un comentario