Un Proyecto completo de Machine Learning en Python: Tercera Parte

Cesar Vega
27 septiembre, 2019

. . .

Interpretar un modelo de aprendizaje automático y presentar resultados


Este artículo se basa en el video publicado originalmente en inglés por Will Koehrsen en Medium. Por favor, visita el siguiente enlace y recomienda el artículo original si te gusta el contenido:



Los modelos de Machine learning son a menudo criticados como cajas negras: ponemos los datos en un lado y obtenemos respuestas -a menudo muy precisas- sin explicaciones por el otro. En la tercera parte de esta serie, que muestra una solución completa para el machine learning, examinaremos el modelo que hemos desarrollado para intentar comprender cómo hace las predicciones y lo que nos puede enseñar sobre el problema. Terminaremos discutiendo quizás la parte más importante de un proyecto de machine learning: la documentación de nuestro trabajo y la presentación de los resultados.

La primera parte de la serie cubrió la limpieza de datos, el análisis exploratorio de datos, la ingeniería de características y la selección de características. La segunda parte cubrió la imputación de valores perdidos, la implementación y comparación de modelos de machine learning, la afinación de hiperparámetros mediante búsquedas aleatorias con validación cruzada y la evaluación de un modelo.

Todo el código de este proyecto está en GitHub. El tercer Notebook Jupyter, correspondiente a este post, está aquí. ¡Animo a todos a compartir, usar y construir sobre este código!

. . .

Como recordatorio, estamos trabajando en un problema de machine learning de regresión supervisada. Usando los datos de energía de los edificios de la ciudad de Nueva York, hemos desarrollado un modelo que puede predecir el Energy Star Score de un edificio. El modelo final que construimos es un Gradient Boosted Regressor (Regresor con aumento de gradiente) que es capaz de predecir el Energy Star Score en los datos de la prueba con un margen de 9,1 puntos (en una escala de 1 a 100).


Interpretación de Modelos

El regresor con aumento de gradiente se sitúa en algún lugar en el centro de la escala de interpretación del modelo: todo el modelo es complejo, pero está formado por cientos de árboles de decisión, que por sí mismos son bastante comprensibles. Examinaremos tres maneras de entender cómo nuestro modelo hace predicciones:

Los dos primeros métodos son específicos para los conjuntos de árboles, mientras que el tercero – como se podría haber adivinado por el nombre – se puede aplicar a cualquier modelo de machine learning. LIME es un paquete relativamente nuevo y representa un paso emocionante en el esfuerzo continuo para explicar las predicciones de machine learning.


Importancias de las características

La importancia de las características intenta mostrar la relevancia de cada característica para la tarea de predecir el objetivo. Los detalles técnicos de las importaciones de las características son complejos (miden la disminución media de impurezas o la reducción de errores por la inclusión de la característica), pero podemos usar los valores relativos para comparar qué características son las más relevantes. En Scikit-Learn, podemos extraer la importancia de las características de cualquier conjunto de aprendices basados en árboles.

Con el model entrenado como nuestro modelo, podemos encontrar la importancia de las características usando el model.feature_importances_. Entonces podemos ponerlos en un DataFrame de pandas y mostrar o trazar el top ten más importante:


import pandas as pd

# model es el modelo entrenado
importances = model.feature_importances_

# train_features es el dataframe de las características de entrenamiento
feature_list = list(train_features.columns)

# Extraiga las características importantes en un dataframe
feature_results = pd.DataFrame({'feature': feature_list, 
                                'importance': importances})

# Mostrar los 10 principales más importantes
feature_results = feature_results.sort_values('importance', 
                                              ascending = False).reset_index(drop=True)

feature_results.head(10)



El Site EUI (Intensidad de Uso de Energía) y el Weather Normalized Site Electricity Intensity son, con mucho, las características más importantes, representando más del 66% de la importancia total. Después de las dos características principales, la importancia disminuye significativamente, lo que indica que tal vez no necesitemos retener las 64 características de los datos para lograr un alto rendimiento. (En el notebook Jupyter, echo un vistazo al uso de las 10 funciones principales y descubro que el modelo no es tan preciso.)

Basándonos en estos resultados, finalmente podemos responder a una de nuestras preguntas iniciales: los indicadores más importantes del Energy Star Score de un edificio son el Site EUI y la Weather Normalized Site Electricity Intensity. Aunque queremos tener cuidado de no leer demasiado en las características importantes, son una forma útil de empezar a entender cómo el modelo hace sus predicciones.


Visualización de un árbol de decisión único

Mientras que todo el gradient boosting regressor puede ser difícil de entender, cualquier árbol de decisión individual es bastante intuitivo. Podemos visualizar cualquier árbol del bosque usando la función Scikit-Learn export_graphviz, primero extraemos un árbol del conjunto y luego lo guardamos como un archivo dot:


from sklearn import tree

# Extraer un solo árbol (número 105)
single_tree = model.estimators_[105][0]

# Guarde el árbol en un archivo dot
tree.export_graphviz(single_tree, out_file = 'images/tree.dot', 
                     feature_names = feature_list)


Usando el software de visualización Graphviz podemos convertir el archivo dot a un png desde la línea de comandos:


dot -Tpng images/tree.dot -o images/tree.png


El resultado es un árbol de decisión completo:


Árbol de decisión completo en el modelo


Esto es un poco abrumador! Aunque este árbol sólo tiene una profundidad de 6 (el número de capas), es difícil de seguir. Podemos modificar la llamada a export_graphviz y limitar nuestro árbol a una profundidad más razonable de 2:



Cada nodo (caja) en el árbol tiene cuatro piezas de información:

  1. La pregunta que se hace sobre el valor de una característica del punto de datos: esto determina si salimos a la derecha o a la izquierda del nodo.
  2. El mse que es una medida del error del nodo
  3. El samples, que es el número de ejemplos (puntos de datos) en el nodo
  4. El value es la estimación del objetivo para todos los muestreos en el nodo


Nodo individual en el árbol de decisión


(Los nodos de hoja sólo tienen 2.-4. porque representan la estimación final y no tienen hijos).

Un árbol de decisión hace una predicción para un punto de datos comenzando en el nodo superior, llamado root, y trabajando hacia abajo a través del árbol. En cada nodo, se hace una pregunta de sí o no al punto de datos. Por ejemplo, la pregunta para el nodo anterior es: ¿Tiene el edificio un Site EUI menor o igual a 68,95? Si la respuesta es sí, el edificio se coloca en el nodo hijo derecho, y si la respuesta es no, el edificio va al nodo hijo izquierdo.

Este proceso se repite en cada capa del árbol hasta que el punto de datos se coloca en un nodo hoja, en la parte inferior del árbol (los nodos de hoja se recortan de la imagen del árbol pequeño). La predicción para todos los puntos de datos en un nodo hoja es el value. Si hay múltiples puntos de datos (samples) en un nodo hoja, todos obtienen la misma predicción. A medida que aumenta la profundidad del árbol, el error en el conjunto de entrenamiento disminuirá porque hay más nodos hoja y los ejemplos se pueden dividir más finamente. Sin embargo, un árbol demasiado profundo se ajustará demasiado a los datos de entrenamiento y no podrá generalizarse a nuevos datos de pruebas.

En el segundo artículo, afinamos algunos de los hiperparámetros del modelo, que controlan aspectos de cada árbol como la profundidad máxima del árbol y el número mínimo de muestras requeridas en un nodo de hoja. Ambos tienen un impacto significativo en el equilibrio entre el ajuste insuficiente y el ajuste excesivo, y la visualización de un único árbol de decisión nos permite ver cómo funcionan estos ajustes.

Aunque no podemos examinar todos los árboles del modelo, al mirar uno de ellos podemos entender cómo cada aprendiz hace una predicción. Este método basado en diagramas de flujo se parece mucho a la forma en que un ser humano toma decisiones, respondiendo a una pregunta sobre un único valor a la vez. Los conjuntos basados en árboles de decisión combinan las predicciones de muchos árboles de decisión individuales para crear un modelo más preciso con menos varianza. Los conjuntos de árboles tienden a ser muy precisos, y también son intuitivos de explicar.


Explicaciones locales interpretables de modelos agnósticos (LIME)

La última herramienta que exploraremos para intentar entender cómo nuestro modelo «piensa» es una nueva entrada en el campo de las explicaciones del modelo. El objetivo de LIME es explicar una predicción única a partir de cualquier modelo de machine learning mediante la creación de una aproximación del modelo localmente cerca del punto de datos utilizando un modelo simple como la regresión lineal (los detalles completos se pueden encontrar en el paper).

Aquí usaremos LIME para examinar una predicción en la que el modelo se equivoca por completo para ver qué nos puede decir acerca de por qué el modelo comete errores.

Primero tenemos que encontrar la observación en la que nuestro modelo se equivoca más. Hacemos esto entrenando y prediciendo con el modelo y extrayendo el ejemplo en el que el modelo tiene el mayor error:


from sklearn.ensemble import GradientBoostingRegressor

# Crea el modelo con los mejores hyperparamters
model = GradientBoostingRegressor(loss='lad', max_depth=5, max_features=None,
                                  min_samples_leaf=6, min_samples_split=6, 
                                  n_estimators=800, random_state=42)

# Ajuste y pruebe las características
model.fit(X, y)
model_pred = model.predict(X_test)

# Encuentra los residuales
residuals = abs(model_pred - y_test)
    
# Extrae la predicción más incorrecta
wrong = X_test[np.argmax(residuals), :]

print('Prediction: %0.4f' % np.argmax(residuals))
print('Actual Value: %0.4f' % y_test[np.argmax(residuals)])


Prediction: 12.8615
Actual Value: 100.0000


A continuación, creamos el objeto explicativo LIME pasando por nuestros datos de entrenamiento, el modo, las etiquetas de entrenamiento y los nombres de las características de nuestros datos. Finalmente, le pedimos al objeto explicativo que explique la predicción errónea, pasándole la función de observación y la de predicción.


import lime 

# Crear a objeto lime explicativo
explainer = lime.lime_tabular.LimeTabularExplainer(training_data = X, 
                                                   mode = 'regression',
                                                   training_labels = y,
                                                   feature_names = feature_list)


# Explicación de la predicción incorrecta
exp = explainer.explain_instance(data_row = wrong, 
                                 predict_fn = model.predict)

# Dibujar la explicación de predicción
exp.as_pyplot_figure();


La gráfica que explica esta predicción está a continuación:



Aquí está cómo interpretar la gráfica: Cada entrada en el eje Y indica un valor de una variable y las barras rojas y verdes muestran el efecto que este valor tiene en la predicción. Por ejemplo, la entrada superior dice que el Site EUI es superior a 95,90, lo que resta unos 40 puntos a la predicción. La segunda entrada dice que la Weather Normalized Site Electricity Intensity (Intensidad Eléctrica del Sitio Normalizado del Clima) es menor de 3.80, lo que añade unos 10 puntos a la predicción. La predicción final es un término de intercepción más la suma de cada una de estas contribuciones individuales.

Podemos echar otro vistazo a la misma información llamando al método explicador .show_in_notebook().


# Show the explanation in the Jupyter Notebook
exp.show_in_notebook()



Esto muestra el proceso de razonamiento del modelo de la izquierda mostrando las contribuciones de cada variable a la predicción. La tabla de la derecha muestra los valores reales de las variables para el punto de datos.

¡Para este ejemplo, la predicción del modelo era de alrededor de 12 y el valor real era de 100! Aunque inicialmente esta predicción puede ser desconcertante, mirando la explicación, podemos ver que no fue una suposición extrema, sino una estimación razonable dados los valores para el punto de datos. El Site EUI fue relativamente alto y esperaríamos que el Energy Star Score fuera bajo (porque el índice EUI está fuertemente correlacionado negativamente con el puntaje), una conclusión compartida por nuestro modelo. En este caso, la lógica era defectuosa porque el edificio tenía una puntuación perfecta de 100.

Puede ser frustrante cuando un modelo se equivoca, pero explicaciones como estas nos ayudan a entender por qué el modelo es incorrecto. Además, basándonos en la explicación, puede que queramos investigar por qué el edificio tiene una puntuación perfecta a pesar de tener un Site EUI tan alto. Tal vez podamos aprender algo nuevo sobre el problema que se nos habría escapado sin investigar el modelo. Herramientas como ésta no son perfectas, pero nos ayudan mucho a entender el modelo, lo que a su vez nos permite tomar mejores decisiones.


Documentar el trabajo e informar los resultados

Una parte que a menudo se pasa por alto en cualquier proyecto técnico es la documentación y la presentación de informes. Podemos hacer el mejor análisis del mundo, pero si no comunicamos claramente los resultados, entonces no tendrán ningún impacto.

Cuando documentamos un proyecto de ciencia de datos, tomamos todas las versiones de los datos y el código y lo empaquetamos para que nuestro proyecto pueda ser reproducido o construido por otros científicos de datos. Es importante recordar que el código se lee más a menudo de lo que está escrito, y queremos asegurarnos de que nuestro trabajo es comprensible tanto para los demás como para nosotros mismos si volvemos a él unos meses después. Esto significa poner comentarios útiles en el código y explicar su razonamiento. Creo que los notebooks Jupyter son una gran herramienta para la documentación porque permiten explicaciones y códigos uno tras otro.

Los notebooks Jupyter también pueden ser una buena plataforma para comunicar los hallazgos a otros. Usando extensiones de notebooks, podemos ocultar el código de nuestro informe final, porque aunque es difícil de creer, no todo el mundo quiere ver un montón de código Python en un documento!

Personalmente, me cuesta resumir sucintamente mi trabajo porque me gusta repasar todos los detalles. Sin embargo, es importante entender a su audiencia cuando usted está presentando y adaptar el mensaje en consecuencia. Con eso en mente, aquí está “mi comida para llevar de 30 segundos” del proyecto:

  • 1. Usando los datos de energía de la ciudad de Nueva York, es posible construir un modelo que pueda predecir el Energy Star Score de los edificios con un margen de 9,1 puntos.
  • 2. El EUI del Sitio y la Intensidad Eléctrica Normalizada del Clima son los factores más relevantes para predecir el Energy Star Score.

Originalmente, este proyecto me fue dado como una «tarea» de selección de empleo por una start-up. Para el informe final, querían ver tanto mi trabajo como mis conclusiones, así que desarrollé un Notebook Jupyter para entregarlo. Sin embargo, en lugar de convertirlo directamente a PDF en Jupyter, lo convertí a un archivo tex de Latex que luego edité en texStudio antes de renderizarlo a un PDF para la versión final. La salida PDF por defecto de Jupyter tiene una apariencia decente, pero se puede mejorar significativamente con unos pocos minutos de edición. Además, el látex es un poderoso sistema de preparación de documentos y es bueno conocer lo básico.

Al fin y al cabo, nuestro trabajo es tan valioso como las decisiones que permite, y ser capaz de presentar resultados es una habilidad crucial. Además, al documentar adecuadamente el trabajo, permitimos que otros reproduzcan nuestros resultados, nos dan retroalimentación para que podamos convertirnos en mejores científicos de datos, y construir sobre nuestro trabajo para el futuro.


Conclusiones

A lo largo de esta serie de publicaciones, hemos realizado un proyecto completo de machine learning de extremo a extremo. Comenzamos limpiando los datos, nos trasladamos a la construcción de modelos y, finalmente, analizamos cómo interpretar un modelo de machine learning. Como recordatorio, la estructura general de un proyecto de machine learning está a continuación:

  • 1. Limpieza y formateo de datos
  • 2. Análisis exploratorio de datos
  • 3. Ingeniería y selección de características
  • 4. Comparar varios modelos de machine learning en una métrica de rendimiento
  • 5. Realizar el ajuste de hiperparámetros en el mejor modelo
  • 6. Evaluar el mejor modelo en el conjunto de pruebas
  • 7. Interpretar los resultados del modelo en la medida de lo posible
  • 8. Sacar conclusiones y redactar un informe bien documentado

Aunque los pasos exactos varían según el proyecto, y el machine learning es a menudo un proceso iterativo en lugar de lineal, esta guía le será de gran utilidad a la hora de abordar futuros proyectos de machine learning. Espero que esta serie le haya dado confianza para poder implementar tus propias soluciones de machine learning, pero recuerda, ¡ninguno de nosotros lo hace por sí solo! Si quieres ayuda, hay muchas comunidades increíblemente solidarias donde puedes buscar consejo.

Algunos recursos que he encontrado útiles a lo largo de mi proceso de aprendizaje:

Como siempre, agradezco los comentarios y la discusión y se puede contactar en Twitter @koehrsen_will.

7

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Comunidades en Español