Modelos interpretables con Python SHAPEnrique Blanco 27 abril, 2021 Recientemente se publicó en este blog un interesante artículo sobre la interpretabilidad de modelos de Machine Learning. Los modelos de inteligencia artificial se han vuelto complejos, en especial con el auge del Deep Learning. Este es un problema grave, especialmente cuando los humanos son responsables de las decisiones basadas en soluciones digitales. El objetivo de hacer que los algoritmos sean accesibles, entendible e interpretables es fundamental para lo que se conoce como «Inteligencia Artificial Explicable (XAI)«, y en los últimos tres años se ha convertido en un área de investigación muy activa. En este post vamos a realizar un ejemplo simple de cómo dotar de explicabilidad e interpretabilidad a un modelo de análisis de sentimiento de regresión logística lineal usando la librería de Python Shap. Tomando como ejemplo un análisis típico de sentimiento sobre un dataset de críticas de películas, veremos cómo los valores SHAP de cada palabra nos permitirán entender cuáles son las más importantes a la hora de tomar la decisión de si esa crítica es buena o mala.Con un modelo lineal el valor SHAP para la característica para la predicción (asumiendo que las características son independientes) es simplemente: Dado que estamos explicando un modelo de regresión logística, las unidades de los valores SHAP estarán en el espacio log-odds.No nos vamos a complicar para realizar esta prueba de usabilidad; el conjunto de datos que utilizamos es el conjunto de datos clásico de IMDB de este enlace. Para empezar a usar la librería, dentro de nuestro entorno virtual de Python, simplemente hay que ejecutar el siguiente comando en un terminal: pip install shap Importando librerías Para esta rápida prueba, vamos a usar scikit-learn como librería desde la que haremos uso de las funciones necesarias. import sklearn from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report import numpy as np import shap from gensim.parsing.preprocessing import remove_stopwords shap.initjs() Cargando y procesando en IMDB dataset corpus, y = shap.datasets.imdb() Como ejemplo, tenemos la siguiente reseña: corpus[10] This film is one giant pant load. Paul Schrader is utterly lost in his own bad screenplay. And his directing is about as comatose as it can be without his actually having been sleepwalking during the process. <br /><br />The worst though is Woody Harrelson, whom I ordinarily like when he's properly cast... Estamos manejando un dataset perfectamente balanceado, con 12500 reseñas positivas y el mismo número de reseñas negativas. print('Posibles etiquetas para el dataset: ', np.unique(y, return_counts=True)) Posibles etiquetas para el dataset: (array([False, True]), array([12500, 12500])) Antes de empezar con la tokenización de las palabras que componen las reseñas de las películas, debemos eliminar las stopwords de nuestro dataset, sobre lo que ya hablamos con anterioridad en este blog. Estas palabras no añaden información válida a la reseña y son muy frecuentes (conjunciones, preposiciones, pronombres, verbos auxiliares, etc.) Este paso se puede abordar con un simple bucle para todas las reseñas: corpus_cleaned = list() for review in corpus: corpus_cleaned.append(remove_stopwords(review).replace('</br>', '')) Por ejemplo, la reseña corpus[10] queda de la siguiente manera: corpus_cleaned[10] This film giant pant load. Paul Schrader utterly lost bad screenplay. And directing comatose actually having sleepwalking process. </></>The worst Woody Harrelson, I ordinarily like he\'s properly cast... Partición train-test del dataset Una vez nuestro dataset está limpio de stopwords, se debe realizar la partición entrenamiento-testeo (vamos a elegir una razón 80%-20%) y se procede a su transformación TF-IDF con la TfidfVectorizer de sklearn. corpus_train, corpus_test, y_train, y_test = train_test_split(corpus_cleaned, y, test_size=0.2, random_state=42) vectorizer = TfidfVectorizer(min_df=10) X_train = vectorizer.fit_transform(corpus_train).toarray() # sparse also works but Explanation slicing is not yet supported X_test = vectorizer.transform(corpus_test).toarray() Con este paso ya tenemos la partición train-test realizada con 20,000 muestras de entrenamiento y 5,000 muestras de testeo. Cada una de esas muestras o arrays tiene 16,364 elementos. print(X_train.shape) print(X_test.shape) (20000, 16364) (5000, 16364) Entrenando un algoritmo de Regresión Logística model = sklearn.linear_model.LogisticRegression(penalty="l2", C=0.1) model.fit(X_train, y_train) LogisticRegression(C=0.1, class_weight=None, dual=False, fit_intercept=True, intercept_scaling=1, l1_ratio=None, max_iter=100, multi_class='auto', n_jobs=None, penalty='l2', random_state=None, solver='lbfgs', tol=0.0001, verbose=0, warm_start=False) Tras este rápido entrenamiento, el comportamiento de nuestro modelo es aceptable. Se consiguen unas precisiones y recalls en el intervalo 0.82-0.88 para los datos de validación. Para esta partición, el comportamiento es muy similar al obtenido en el train dataset. print(classification_report(y_test, model.predict(X_test))) precision recall f1-score support False 0.88 0.82 0.85 2515 True 0.83 0.88 0.85 2485 accuracy 0.85 5000 macro avg 0.85 0.85 0.85 5000 weighted avg 0.85 0.85 0.85 5000 Ahora bien, ¿en qué palabras se está fijando nuestro sencillo modelo de regresión logística para determinar si una secuencia de palabras pertenece a una reseña buena o mala? Con SHAP, visualizar esta información es muy sencillo. Explicación de nuestro modelo lineal explainer = shap.Explainer(model, X_train, feature_names=vectorizer.get_feature_names()) shap_values = explainer(X_test) Resumen de la contribución de todas las características Para obtener una descripción general de qué características son más importantes para un modelo, se pueden trazar los valores SHAP de cada característica para cada muestra. El gráfico siguiente clasifica las características por la suma de las magnitudes de los valores SHAP en todas las muestras y usa los valores SHAP para mostrar la distribución de los impactos que cada característica tiene en la salida del modelo. El color representa el valor de la característica (rojo alto, azul bajo). shap.plots.beeswarm(shap_values) Figura 1. Beeswarm summary plot de la contribución de las características más relevantes para el modelo. Podemos observar cómo altos valores de frecuencias en palabras como bad o worst tienen un impacto negativo importante en la toma de decisiones del modelo. Si esas palabras aparecen en una reseña, es bastante probable que la misma sea mala. Por el contrario, palabras como great, best o love son indicativos de que la reseña tiene altas probabilidades de ser positivas. Al final, el criterio de nuestro modelo de clasificación termina siendo bastante simple y fácilmente interpretable. Explicación de la predicción del sentimiento de la segunda reseña ind = 1 print("Reseña Positiva" if y_test[ind] else "Reseña Negativa") print(corpus_test[ind]) Reseña Positiva The idea ia short film lot information. Interesting, entertaining leaves viewer wanting more. The producer produced short film excellent quality compared short film I seen. I rated film highest possible rating. I recommend shown office managers ... Otra forma de visualizar la contribución de cada componente a la decisión final del modelo sobre una secuencia de muestra es el force plot (descrito en el paper Nature BME paper): shap.plots.force(shap_values[ind]) Figura 2. Force plot de la contribución de las palabras más relevantes a la toma de decisión. Otra librería similar es LIME, la cual es capaz de explicar cualquier clasificador, con dos o más clases. Todo lo que necesitamos es que el clasificador implemente una función que tome texto sin formato o una matriz numérica y genere una probabilidad para cada clase, también integrado para clasificadores de scikit-learn. Jugaremos con esta librería en próximos posts. Para mantenerte al día con el área de Internet of Things de Telefónica visita nuestra página web o síguenos en Twitter, LinkedIn y YouTube Deep Learning para predecir la calidad del aireVideo Post #26: Python para todos, Diferencia entre Método y Función
AI of Things Iníciate en Inteligencia Artificial generativa (y responsable) con estos cursos gratuitos de Google Aprende sobre los conceptos y principios de la IA generativa o cómo crear y desplegar soluciones de IA: modelos de lenguaje, generación de imágenes... y también sobre IA responsable.
Nacho Palou Typosquatting: cómo detectarlo y protegerse No siempre es fácil detectar y protegerse del typosquatting. Estas recomendaciones de nuestros expertos te ayudan a reducir el riesgo.
Nacho Palou Qué es el invierno-IA y cómo evitarlo ¿Estamos a las puertas de un nuevo invierno-IA? ¿Qué factores podrían propiciarlo? El miedo, una legislación restrictiva o la falta de avances pueden impactar el futuro de la IA.
Nacho Palou El poder de la digitalización sostenible en la lucha contra el cambio climático El cambio climático es considerado el mayor desafío de nuestro tiempo. Sus efectos abarcan desde la desertización y sequías hasta inundaciones y aumento del nivel del mar. Algunas de...
Telefónica Tech Boletín semanal de Ciberseguridad, 27 de mayo – 2 de junio Descubierta puerta trasera en cientos de placas base Gigabyte Investigadores de ciberseguridad de Eclypsium descubrieron una puerta trasera secreta en el firmware de cientos de modelos de placas base Gigabyte,...
Nacho Palou Cómo el lenguaje pone en riesgo la Ciberseguridad de las empresas La Ciberseguridad es un asunto fundamental para las empresas y organizaciones, de cualquier tamaño y sector. Los ciberataques pueden tener consecuencias graves o muy graves —incluso fatales— para los...