Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

6. Interpretabilidad con LIME

Este capítulo corresponde a la sección 9.10.4.7 del proyecto. LIME (Local Interpretable Model-agnostic Explanations; Ribeiro, Singh y Guestrin, 2016) explica una predicción individual en tres pasos:

  1. Genera perturbaciones alrededor de la instancia.

  2. Obtiene la predicción del modelo para cada perturbación.

  3. Ajusta a esas predicciones un modelo lineal ponderado por cercanía, y sus coeficientes indican qué variables empujaron localmente la probabilidad de default hacia arriba o hacia abajo.

Se explica el modelo con mayor AUC de cada entorno entre los que entregan probabilidades. LinearSVC queda excluido, porque solo produce valores de decisión.

Modelo explicado en cada entorno: {'scikit-learn': 'GradBoost', 'PySpark': 'GradBoost'}

6.1 Espacio de explicación

El modelo recibe 91 columnas: numéricas estandarizadas y dummies. Explicarlo directamente en ese espacio tendría dos problemas:

  • las reglas serían ilegibles (por ejemplo, “int_rate > 0,63 desviaciones estándar”);

  • LIME perturbaría por separado cada dummy de una misma variable y crearía préstamos imposibles, como uno a 36 y a 60 meses a la vez.

Por eso LIME trabaja sobre las 26 variables originales:

  • 16 numéricas en su escala real, con los faltantes imputados por la mediana de train;

  • 3 indicadores binarios;

  • 7 categóricas codificadas como enteros.

La función de predicción que recibe LIME aplica el preprocesamiento ajustado en el capítulo 2 y después el modelo. Las estadísticas que LIME usa para perturbar (cuartiles de las numéricas y frecuencias de las categóricas) se calculan con el conjunto de entrenamiento completo.

Espacio de explicación: (1076248, 26)

6.2 Funciones de predicción de cada entorno

  • scikit-learn: se carga el mejor estimador guardado en el capítulo 3 (joblib) y se aplica SkPreprocessor.transform seguido de predict_proba. Todo ocurre en memoria local.

  • PySpark: los modelos de Spark no pueden guardarse en este equipo, porque escribir con Spark en Windows requiere winutils.exe de Hadoop. Por eso se reentrena el mejor modelo de Spark con sus mejores hiperparámetros (sección 4.3) sobre el mismo conjunto de entrenamiento cacheado, sin repetir la validación cruzada. Con la semilla fija, el modelo es el mismo que eligió el CrossValidator, y así se comprueba reproduciendo el AUC de prueba. Para cada lote de perturbaciones, la función de predicción de LIME:

    1. crea un DataFrame de Spark (createDataFrame) con las 5000 filas;

    2. aplica el Pipeline de preprocesamiento y el modelo;

    3. trae las probabilidades al driver con toPandas().

    Lo que se transfiere son las perturbaciones de LIME, no el dataset.

GradBoost reentrenado con {'stepSize': 0.1, 'maxBins': 32, 'seed': 42, 'cacheNodeIds': True, 'maxIter': 100, 'maxDepth': 5} en 483 s
AUC test reentrenado = 0.719463 | AUC capítulo 4 = 0.719463 | máx |Δp| = 0.0e+00

6.3 Selección de instancias mal clasificadas

Se eligen dos préstamos del conjunto de prueba que ambos modelos clasifican mal, usando el umbral de cada modelo fijado en entrenamiento:

  • Falso negativo: el préstamo terminó en default, pero los dos modelos le asignaron baja probabilidad. Se toma el caso con la menor probabilidad promedio.

  • Falso positivo: el préstamo se pagó por completo, pero los dos modelos le asignaron alta probabilidad de default. Se toma el caso con la mayor probabilidad promedio.

Explicar las mismas instancias en ambos entornos permite comparar si los dos modelos se equivocan por las mismas razones.

Préstamos de prueba mal clasificados por ambos modelos: 58,983 de 269,062
Umbrales: scikit-learn 0.2959 | PySpark 0.2941
Loading...
Loading...

6.4 Explicaciones

Para cada instancia y entorno, LIME genera 5000 perturbaciones y muestra las 10 variables con mayor peso local. Las barras naranjas aumentan la probabilidad de default y las azules la reducen.

36099806 | scikit-learn 0.75 36099806 | PySpark 397.14 55961482 | scikit-learn 0.10 55961482 | PySpark 375.33 Name: tiempo LIME (s), dtype: float64
<Figure size 1700x500 with 2 Axes>
<Figure size 1700x500 with 2 Axes>
Loading...
Loading...

Estabilidad de las explicaciones

LIME es estocástico: las perturbaciones cambian con cada ejecución. Para medir la estabilidad se repite la explicación del falso negativo con el modelo de scikit-learn con 5 semillas distintas y se registra con qué frecuencia cada variable aparece entre las 5 más influyentes. Se usa scikit-learn porque cada llamada en PySpark cuesta varios segundos.

emp_length_missing 5 term 5 int_rate 5 dti 5 fico_range_high 5 Name: veces en el top-5 (de 5 repeticiones), dtype: int64

6.5 Discusión y limitaciones de LIME en entornos distribuidos

El modelo explicado es gradient boosting en ambos entornos. El modelo de Spark reentrenado reproduce exactamente las probabilidades del capítulo 4, con una diferencia máxima nula, lo que confirma que GBTClassifier es determinista con semilla y particionado fijos. El resultado central del análisis es que los dos modelos se equivocan por las mismas razones: en ambos préstamos, las cinco condiciones más influyentes coinciden en scikit-learn y en PySpark, en un orden casi idéntico (int_rate, term, emp_length_missing, fico_range_high y dti). Son precisamente los predictores que el EDA identificó como los más asociados al default, lo que otorga validez sustantiva a las explicaciones.

El falso negativo (préstamo 36099806) recibe en ambos entornos una probabilidad de default de aproximadamente 0,02. Su solicitud describe un perfil de muy bajo riesgo: tasa de 6,03 %, puntaje FICO de 824, plazo de 36 meses, razón deuda/ingreso de 9,4, ingresos de 108 mil dólares, hipoteca y 33 años de historial crediticio. Aun así, el préstamo terminó en default. Las variables disponibles al momento de la originación no pueden anticipar choques posteriores, como la pérdida del empleo, una enfermedad o una separación, de modo que este tipo de error es irreducible con la información disponible y no constituye un defecto del modelo. El falso positivo (préstamo 55961482), con probabilidades de 0,79 a 0,87, presenta el perfil opuesto: tasa de 28,99 %, entre las más altas del conjunto (máximo de 30,99 %), plazo de 60 meses, monto de 35 mil dólares, vivienda en arriendo, ninguna hipoteca y apenas 3,2 años de historial. Sin embargo, fue pagado por completo. En este caso el error se explica sobre todo por int_rate, que es la propia evaluación de riesgo de Lending Club: los modelos aprenden en gran medida a reproducir esa evaluación y, con ello, heredan sus errores. La presencia sistemática de emp_length_missing = 0 entre los factores es coherente con el EDA, donde la ausencia de la antigüedad laboral elevaba el default del 19,5 % al 26,9 %. Por último, al repetir la explicación del falso negativo con cinco semillas, el conjunto de las cinco variables más influyentes no varía, de modo que las explicaciones son estables con 5000 perturbaciones.

Las limitaciones de LIME se agravan en un entorno distribuido. El método opera en memoria local: el explicador necesita las estadísticas del conjunto de entrenamiento (cuartiles y frecuencias), que aquí se calcularon en pandas sobre 1,08 millones de filas y 26 variables, y que con datos más voluminosos exigirían un cálculo distribuido o un muestreo. Además, cada explicación requiere un viaje de ida y vuelta a Spark: crear un DataFrame con las perturbaciones, aplicar el Pipeline y el modelo, y traer las probabilidades al driver. Mientras en scikit-learn una explicación tomó entre 0,1 y 0,75 segundos, en PySpark tomó entre 375 y 397 segundos, unas 500 veces más. El volumen de cómputo, de apenas 5000 filas, no explica esa diferencia; la explica el costo fijo de cada trabajo de Spark (planificación, serialización entre Python y la JVM y el reparto del DataFrame en las 400 particiones de spark.default.parallelism). Explicar miles de predicciones de este modo sería inviable sin agrupar las perturbaciones de muchas instancias en un único trabajo o recurrir a métodos diseñados para Spark. A ello se suma que, en este equipo, el modelo de Spark no pudo persistirse en disco sin winutils.exe y tuvo que reentrenarse (8 minutos), mientras que el de scikit-learn se recuperó directamente con joblib.

Por último, LIME tiene limitaciones generales que conviene no perder de vista. Las perturbaciones se generan de forma independiente por variable e ignoran su correlación, de modo que producen préstamos poco plausibles, por ejemplo con una tasa baja y un FICO bajo, cuando la correlación entre ambas variables es de −0,41; la explicación describe entonces el comportamiento del modelo en regiones sin datos. Los resultados dependen, además, de decisiones arbitrarias como la discretización por cuartiles, el ancho del kernel y el número de perturbaciones. Y la explicación es local: describe una predicción concreta, no el comportamiento global del modelo ni el mecanismo causal del default.