Por Qué Importa la Explicabilidad
Cuando los sistemas de IA toman decisiones de alto riesgo—aprobaciones de préstamos, diagnósticos médicos, recomendaciones de contratación—entender por qué un modelo hizo una predicción específica se vuelve crítico. Marcos regulatorios como el “derecho a explicación” de GDPR y la EU AI (como se discute en AI-Assisted Bug Triaging: Intelligent Defect Prioritization at Scale) Act exigen transparencia.
Testear IA explicable (XAI) (como se discute en AI Code Smell Detection: Finding Problems in Test Automation with ML) valida que las explicaciones son precisas, consistentes y accionables—asegurando que los modelos no solo sean eficientes, sino comprensibles.
Técnicas XAI
1. LIME (Explicaciones Locales Interpretables Model-Agnósticas)
from lime.lime_tabular import LimeTabularExplainer
class (como se discute en [AI Copilot for Test Automation: GitHub Copilot, Amazon CodeWhisperer and the Future of QA](/blog/ai-copilot-testing)) ExplicadorLIME:
def __init__(self, modelo, datos_entrenamiento, nombres_caracteristicas, nombres_clases):
self.modelo = modelo
self.explicador = LimeTabularExplainer(
training_data=datos_entrenamiento,
feature_names=nombres_caracteristicas,
class_names=nombres_clases,
mode='classification'
)
def explicar_prediccion(self, instancia):
"""Generar explicación para instancia única"""
explicacion = self.explicador.explain_instance(
instancia,
self.modelo.predict_proba,
num_features=10
)
return {
'prediccion': self.modelo.predict([instancia])[0],
'principales_caracteristicas': explicacion.as_list()
}
# Uso
explicador = ExplicadorLIME(modelo_prestamo, X_train, nombres_caracteristicas, ['Denegado', 'Aprobado'])
instancia = X_test[0] # Préstamo denegado
explicacion = explicador.explicar_prediccion(instancia)
print("Predicción:", explicacion['prediccion'])
print("\nPrincipales factores:")
for caracteristica, peso in explicacion['principales_caracteristicas']:
print(f" {caracteristica}: {peso:.3f}")
# Salida:
# Predicción: Denegado
# Principales factores:
# puntuacion_credito <= 650: -0.45
# deuda_a_ingreso > 0.4: -0.32
2. SHAP (Explicaciones Aditivas SHapley)
import shap
class ExplicadorSHAP:
def __init__(self, modelo, datos_fondo):
self.modelo = modelo
self.explicador = shap.TreeExplainer(modelo, datos_fondo)
def explicar_instancia(self, instancia):
"""Obtener valores SHAP para predicción única"""
valores_shap = self.explicador.shap_values(instancia)
return {
'valor_base': self.explicador.expected_value,
'valores_shap': valores_shap,
'impacto_caracteristica': dict(zip(
nombres_caracteristicas,
valores_shap[0] if isinstance(valores_shap, list) else valores_shap
))
}
def obtener_importancia_global(self, X_test):
"""Importancia global de características"""
valores_shap = self.explicador.shap_values(X_test)
# Promedio de valores SHAP absolutos
shap_abs_promedio = np.abs(valores_shap).mean(axis=0)
return dict(sorted(
zip(nombres_caracteristicas, shap_abs_promedio),
key=lambda x: x[1],
reverse=True
))
# Uso
explicador_shap = ExplicadorSHAP(modelo_xgboost, X_train[:100])
explicacion = explicador_shap.explicar_instancia(X_test[[0]])
print("Impactos de características:")
for caracteristica, impacto in explicacion['impacto_caracteristica'].items():
print(f" {caracteristica}: {impacto:+.3f}")
Testeo de Explicabilidad
1. Testing de Consistencia
class TestadorConsistenciaExplicacion:
def __init__(self, explicador):
self.explicador = explicador
def testear_estabilidad(self, instancia, num_ejecuciones=10):
"""Testear si explicaciones son consistentes entre ejecuciones"""
explicaciones = []
for _ in range(num_ejecuciones):
exp = self.explicador.explicar_prediccion(instancia)
explicaciones.append(exp['principales_caracteristicas'])
# Calcular varianza en rankings de importancia
rangos_caracteristicas = {}
for exp in explicaciones:
for rango, (caracteristica, peso) in enumerate(exp):
if caracteristica not in rangos_caracteristicas:
rangos_caracteristicas[caracteristica] = []
rangos_caracteristicas[caracteristica].append(rango)
# Calcular puntaje de estabilidad
puntajes_estabilidad = {
caracteristica: 1 - (np.std(rangos) / len(exp))
for caracteristica, rangos in rangos_caracteristicas.items()
}
estabilidad_promedio = np.mean(list(puntajes_estabilidad.values()))
return {
'estabilidad_promedio': estabilidad_promedio,
'estabilidad_por_caracteristica': puntajes_estabilidad,
'es_estable': estabilidad_promedio > 0.8
}
# Uso
testador_consistencia = TestadorConsistenciaExplicacion(explicador_lime)
estabilidad = testador_consistencia.testear_estabilidad(instancia_test)
if not estabilidad['es_estable']:
print("⚠️ ADVERTENCIA: ¡Explicaciones son inestables!")
print(f"Estabilidad promedio: {estabilidad['estabilidad_promedio']:.2%}")
2. Testing de Fidelidad
class TestadorFidelidad:
def __init__(self, modelo, explicador):
self.modelo = modelo
self.explicador = explicador
def testear_ablacion_caracteristicas(self, instancia):
"""Remover características principales, verificar cambio de predicción"""
# Obtener predicción original
pred_original = self.modelo.predict_proba([instancia])[0]
# Obtener explicación
explicacion = self.explicador.explicar_prediccion(instancia)
principales_caracteristicas = explicacion['principales_caracteristicas'][:3]
# Ablacionar características principales
instancia_ablacionada = instancia.copy()
for nombre_caracteristica, peso in principales_caracteristicas:
indice_caracteristica = nombres_caracteristicas.index(nombre_caracteristica)
instancia_ablacionada[indice_caracteristica] = np.median(X_train[:, indice_caracteristica])
# Obtener nueva predicción
pred_ablacionada = self.modelo.predict_proba([instancia_ablacionada])[0]
# Calcular cambio de predicción
cambio_pred = abs(pred_original[1] - pred_ablacionada[1])
return {
'prediccion_original': pred_original[1],
'prediccion_ablacionada': pred_ablacionada[1],
'cambio_prediccion': cambio_pred,
'es_fiel': cambio_pred > 0.1
}
Testing de Cumplimiento Regulatorio
Derecho a Explicación GDPR
class TestadorCumplimientoGDPR:
def testear_adecuacion_explicacion(self, explicacion, prediccion):
"""Verificar que explicación cumple requisitos GDPR"""
verificaciones = {
'tiene_caracteristicas_legibles_humano': self.verificar_nombres_caracteristicas(explicacion),
'proporciona_valores_reales': self.verificar_valores_caracteristicas(explicacion),
'muestra_direccion_impacto': self.verificar_signos_impacto(explicacion),
'incluye_confianza': 'confianza' in prediccion,
'max_caracteristicas_razonable': len(explicacion['principales_caracteristicas']) <= 10
}
puntaje_cumplimiento = sum(verificaciones.values()) / len(verificaciones)
return {
'es_conforme': puntaje_cumplimiento >= 0.8,
'puntaje_cumplimiento': puntaje_cumplimiento,
'verificaciones_fallidas': [k for k, v in verificaciones.items() if not v]
}
Mejores Prácticas
Práctica | Descripción |
---|---|
Múltiples Métodos de Explicación | Usar LIME + SHAP para robustez |
Testear Estabilidad | Verificar que explicaciones no varíen salvajemente |
Validar Fidelidad | Asegurar que explicaciones reflejen modelo real |
Evaluación Humana | Expertos de dominio revisan explicaciones |
Ejemplos Contrastivos | Explicar diferencias entre instancias similares |
Global + Local | Proveer insights generales y específicos de instancia |
Conclusión
El testing de IA explicable asegura que los modelos no solo sean precisos, sino confiables y conformes. Al testear consistencia, fidelidad y adecuación regulatoria, los equipos construyen sistemas de IA que los humanos pueden entender, depurar y desplegar con confianza.