Saltar al contenido

Explicando la explicabilidad

Un aspecto importante en cualquier aplicación de Machine Learning que suele pasar desapercibida para mucha gente es la habilidad de los modelos predictivos para explicar porqué devuelven ciertas predicciones en lugar de otras. Llamamos explicabilidad o interpretabilidad a dicha habilidad.

Por ejemplo, supongamos que trabajamos como consultores externos para una empresa elaborando modelos. Seguramente, a dicha empresa no le va a gustar que le digamos que el modelo que forma parte vital de su negocio es una “caja negra” que funciona mágicamente (de forma inexplicable) y que seguramente no podrán mantener una vez hayamos pasado a otro proyecto.

En otros casos, la explicabilidad de un modelo no solo es importante, sino obligatoria. En industrias altamente reguladas (por ejemplo, en el sector financiero) el explicar porqué un modelo funciona de la manera que funciona es obligatorio. En la Unión Europea, la reciente implementación de la nueva directiva de datos GDPR, en concreto su artículo 22 indica que:

El sujeto de los datos tendrá el derecho a no ser sujeto a una decisión basada únicamente en un proceso automático…

según la interpretación (dado que la ley es muy, muy nueva), esto puede llegar a obligar a que los usuarios tengan derecho a saber porqué un sistema automático ha tomado una decisión que les afecta (y digo puede porque la regulación no es muy concreta al respecto, pero hay debate sobre cómo esto puede cargarse modelos de caja negra tipo deep learning).

Vamos a comentar aquí algunas medidas que nos permiten proporcionar cierta explicabilidad a nuestros modelos:

En primer lugar, pongo las características de mi ordenador y de los paquetes que vamos a usar. En concreto, vamos a usar lo siguiente:

Imports y eso

Cargamos los datos

Para empezar con algo sencillito, vamos a usar el dataset de casas de Boston, Boston Housing Dataset que contiene información sobre los precios y características de bloques de casas en la ciudad de Boston. La variable objetivo de este dataset PREDV es el precio medio de las casas en un bloque en función de las características de dicha área residencial.

Está disponible en scikit-learn por lo tanto no tenemos que descargarnos nada:

CRIMZNINDUSCHASNOXRMAGEDISRADTAXPTRATIOBLSTAT
00.0063218.02.310.00.5386.57565.24.09001.0296.015.3396.904.98
10.027310.07.070.00.4696.42178.94.96712.0242.017.8396.909.14
20.027290.07.070.00.4697.18561.14.96712.0242.017.8392.834.03
30.032370.02.180.00.4586.99845.86.06223.0222.018.7394.632.94
40.069050.02.180.00.4587.14754.26.06223.0222.018.7396.905.33

Nota: Los precios están en miles de dólares, y el dataset es de 1978. Hoy en dia no hay casas que valgan 24,000$ desgraciadamente 😉

El diccionario de datos lo podéis consultar aquí. De todas formas todos los datasets de scikit-learn tienen el atributo DESCR que nos proporciona su descripción:

Ahora generamos el dataset de entrenamiento:

Ya tenemos un dataset de entrenamiento y otro de validación, tenemos varias opciones a la hora de proporcionar cierta explicabilidad a nuestras predicciones:

Opción 1. Usar modelos explicables.

Hay ciertos modelos que por su definición son intrínsecamente explicables.

Por ejemplo, para modelos de regresión lineal, podemos crear una fórmula y explicar que el modelo simplemente es un producto lineal de coeficientes y las variables independientes.

Por ejemplo, podemos ver los coeficientes que se multiplican a cada variable de forma sencilla mediante el atributo _coef del modelo.

Para modelos basados en árboles, podemos usar el atributo feature_importances para conocer la importancia de cada variable a la hora de hacer una partición (partir un nodo en subnodos).

Ahora que tenemos el modelo ajustado podemos usar feature_importances_

Así que en el caso concreto de este modelo, la variable que más importancia tiene a la hora de realizar una predicción es la variable RM (lo cual tiene sentido, el precio de una casa está muy relacionado con su número de habitaciones) y la variable LSTAT (barrios con mayor gente de clase alta tendrán casas más caras y al revés).

Si el modelo que elegimos no es uno de los de arriba, tenemos que usar otras formas de proporcionar explicabilidad al modelo. Vamos a ver un par de métodos.

Permutation importances

Permutation importances (importancia de permutaciones) es un método para proporcionar explicabilidad a un estimador de caja negra.

En este método, la importancia de cada variable independiente a la hora de predecir se estima mirando cómo varía la puntuación o el error (precisión, F1, \(R^{2}\), etc.) del modelo cuando dicha variable no está disponible

Podemos usar el paquete eli5 para importancia de cada variable mediante Permutation Importances.

Por ejemplo, vamos a usar ahora un modelo SVR (Máquina de Vectores Soporte para problemas de regresión) para entrenar en el mismo dataset y usaremos eli5 para calcular la importancia de cada variable independiente mediante Permutation Importance.

Ajustamos el modelo a los datos

Ahora entrenamos el the PermutationImportance explainer en el dataset de test. Debemos pasarle el argumento scoring que indica la función de evaluación que vamos a usar. Podemos usar cualquiera de las que proporciona scikit-learn por defecto o crear la nuestra propia.

Por ejemplo, nosotros seleccionamos el error absoluto medio (neg_mean_absolute_error en scikit-learn). Es importante mencionar que en sklearn los errores se proporcionan en negativo.

Ahora podemos usar la función explain_weights para que eli5 nos indique la importancia de cada variable independiente a la hora de tomar una decisión.

WeightFeature
0.0927 ± 0.0260 TAX
0.0750 ± 0.0193 INDUS
0.0649 ± 0.0398 AGE
0.0638 ± 0.0419 LSTAT
0.0546 ± 0.0160 RAD
0.0414 ± 0.0384 ZN
0.0382 ± 0.0219 CRIM
0.0379 ± 0.0165 PTRATIO
0.0352 ± 0.0183 DIS
0.0030 ± 0.0040 RM
0.0007 ± 0.0016 CHAS
0.0003 ± 0.0001 NOX
-0.0032 ± 0.0357 B

Vemos, que para el modelo SVM, la variable más importante a la hora de predecir el precio de una casa es la cantidad de impuestos que se pagan en la zona y la edad de las casas.

Hay que tener en cuenta que diferentes algoritmos darán diferentes importancias a cada variable! Por ejemplo, si ahora usamos un Perceptrón Multicapa (MLP):

WeightFeature
1.0067 ± 0.1530 TAX
0.8525 ± 0.3151 B
0.8334 ± 0.3482 ZN
0.6072 ± 0.1576 LSTAT
0.1445 ± 0.0718 CRIM
0.0991 ± 0.0849 PTRATIO
0.0216 ± 0.0207 DIS
0.0003 ± 0.0049 CHAS
-0.0000 ± 0.0008 NOX
-0.0034 ± 0.0320 RAD
-0.0333 ± 0.0204 RM
-0.0471 ± 0.2234 AGE
-0.1557 ± 0.1739 INDUS

Vemos que dicho algoritmo predice que a la hora de predecir el precio de las viviendas las variables más importantes son el nivel fiscal y el porcentaje de la población de raza negra (algoritmos racistas, anyone?).

LIME

LIME (Local Interpretable Model-Agnostic Explanations) es un método que se puede usar para explicar el output de cualquier clasificador. Es una técnica bastante reciente (aquí está el paper, y la explicación del método por parte del autor)

A diferencia de Permutacion Importances, LIME se aplica a una observación cada vez, y funciona haciendo perturbaciones aleatorias de dicha observación (es decir, cambiando un poquito la observación cada vez) y viendo cómo varían las prediciones del modelo (es decir, la clase predicha). Una vez hecho esto se entrena un modelo de regresión lineal usando como variables independientes las permutaciones y como variable dependiente (u objetivo) la predicción del modelo para cada perturbación.

Traducido del artículo del autor:

La función de decisión original del modelo se representa mediante el fondo de color azul/rosa y es claramente no lineal. La equis de color rojo brillante representa la observación que está siendo explicada (llamémosla X). Tomamos un muestreo de observaciones alrededor de X, y les asignamos pesos según su proximidad a X (aquí representamos el peso mediante el tamaño). Calculamos la predicción del modelo original en cada una de estas observaciones perturbadas y entonces aprendemos un modelo lineal (línea intermitente) que aproxima el modelo bien en la vecindad de X. Nótese que la explicación de dicho modelo no es fiable a nivel global, pero es fiable a nivel local alrededor de X.

Podemos usar el paquete lime para usar LIME en python.

Para este ejemplo vamos a usar un dataset clásico de clasificación, el dataset de Cáncer de Pecho (Breast Cancer Dataset), que contiene mediciones sobre observaciones de células potencialmente cancerosas, y donde la variable objetivo es si la paciente tiene cáncer maligno o benigno.

Generamos dataset de entrenamiento y test de nuevo.

Ahora utilizamos un modelo SVM para clasificación para entrenar un modelo que clasifique correctamente los cánceres malignos

Como este dataset es un dataset tabular estructurado (o sea, una tabla/dataframe), podemos usar LimeTabularExplainer para usar LIME. Simplemente tenemos que usar como argumentos los datos de entrenamiento (X_train, o sea las variables independientes), los nombres de las variables independientes, los nombres de las clases de la variable objetivo. También es conveniente usar discretize_continuous que agrupa las variables continuas y hace que sea más fácil interpretar los resultados.

Ahora podemos tomar una observación (una medición de una paciente) y explicar porqué nuestro modelo SVM tomó la decisión de si es cáncer maligno o no. Dado que este artículo está escrito en un jupyter notebook podemos usar la función show_in_notebook para que nos muestre una bonita tabla.

En esta predicción en particular, el modelo predijo que era benigno con un 59% de probabilidades, y esto fué debido sobretodo a las variables texture error, mean smoothness, y mean radius. Básicamente, el valor de las variables en color naranja apoyan el caso benigno y las variables en azul soportan el caso de cáncer maligno.

Partial Dependence Plots

Partial Dependence Plots (Gráficas de Dependencia Parcial) son similares a la técnica de Permutation importances, pero en vez de observar el impacto de la existencia de una variable en las predicciones, se concentra en analizar el impacto del valor de una variable respecto a las predicciones.

Scikit-learn’s tiene la función plot_partial_dependence que crea este tipo de gráficos. Por algún motivo solo está implementado para algoritmos Gradient Boosting Trees. Eventualmente estará disponible para otros estimadores, pero mientras tanto podemos usar el paquete pdpbox para dibujar un gráfico de dependencias (pdp por sus siglas en inglés).

Usamos el dataset del cáncer de mama de nuevo.

Para usar PDP, tenemos que seleccionar una variable independiente a la vez, por ejemplo podemos seleccionar la variable mean radius, que mide el radio medio de todas las células cancerosas en la imagen.

Podemos usar el mismo algoritmo SVM para este ejemplo.

Ahora simplemente tenemos que usar la función pdp_isolate para calcular el impacto de la variable mean texture en las predicciones del algoritmo.

Básicamente esta función computa el valor de la variable objetivo en un rango de 20 valores distintos de la variable mean texture.

Ahora podemos usar la función pdp_plot para ver el gráfico de dependencias en sí.

Vemos que hasta un valor de 20mm, conforme aumenta el radio medio de las células las predicciones van bajando, acercándose a la clase 0 que en este dataset codifica el cáncer maligno. Por lo tanto vemos que conforme aumenta el valor del radio medio de las células hay más probabilidades de que el cáncer sea maligno.

Bueno eso es todo y ¡hasta la próxima!

Deja una respuesta

Tu dirección de correo electrónico no será publicada.

61 + = sixty three

Pybonacci