← Назад к вопросам
Как интерпретировать модели ML с помощью SHAP и LIME?
3.0 Senior🔥 122 комментариев
#Машинное обучение#Метрики и оценка моделей
Комментарии (2)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Интерпретация моделей ML с помощью SHAP и LIME
Проблема интерпретируемости
Чёрный ящик (Black Box) — проблема, когда модель делает правильные предсказания, но непонятно почему. Это критично для:
- Регулирования (GDPR, требования объяснимости решений)
- Отладки модели (какие ошибки и почему)
- Доверия пользователей
- Обнаружения bias и fairness проблем
Шапли и LIME — это методы объяснения предсказаний конкретных примеров.
LIME (Local Interpretable Model-Agnostic Explanations)
LIME — это локальный метод, который приближает сложную модель линейной моделью в окрестности конкретного примера.
Как работает LIME:
- Берём интересующий нас пример
- Создаём "возмущённые" версии этого примера (слегка меняем признаки)
- Получаем предсказания модели для всех возмущённых примеров
- Весим возмущённые примеры по близости к исходному примеру (примеры ближе к исходному имеют больший вес)
- Обучаем линейную модель (легко интерпретируемую) на взвешенных примерах
- Коэффициенты линейной модели показывают важность каждого признака
import lime
import lime.lime_tabular
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
# Загружаем данные
iris = load_iris()
X = iris.data
y = iris.target
# Обучаем модель
model = RandomForestClassifier()
model.fit(X, y)
# Инициализируем LIME
explainer = lime.lime_tabular.LimeTabularExplainer(
X,
feature_names=iris.feature_names,
class_names=iris.target_names,
mode="classification"
)
# Объясняем конкретный пример
example = X[0:1] # берём первый пример
exp = explainer.explain_instance(
X[0],
model.predict_proba,
num_features=4 # топ-4 признака
)
# Показываем результат
exp.show_in_notebook()
# Программно получаем объяснение
for feature, weight in exp.as_list():
print(f"{feature}: {weight:.3f}")
Пример объяснения:
Setosa probability: 0.95
- sepal length (cm) <= 5.1: 0.35 (положительно влияет на Setosa)
- sepal width (cm) <= 3.5: 0.30
- petal length (cm) <= 1.9: 0.25
- petal width (cm) <= 0.3: 0.05
Преимущества LIME:
- Работает с любой моделью (model-agnostic)
- Интуитивна и легко объясняется
- Быстрая вычислений
Недостатки LIME:
- Нестабильна (малые изменения в данных дают разные объяснения)
- Локальная интерпретация (может быть не репрезентативна)
- Может дать неправильное объяснение, если линейное приближение плохое
SHAP (SHapley Additive exPlanations)
SHAP — это фундаментально другой подход, основанный на концепции значений Шапли из теории игр.
Концепция значений Шапли:
Идея: вклад признака можно рассчитать, если считать задачу в виде кооперативной игры.
- Каждый признак — это "игрок"
- Предсказание модели — это "выигрыш" коалиции игроков
- Значение Шапли каждого признака — справедливая доля вклада этого признака
Формула значения Шапли для признака j:
# Упрощённое объяснение (полный расчёт более сложный)
# Для каждого подмножества S из остальных признаков:
SHAP[j] = (1/N!) * sum(
f(S + {j}) - f(S) # вклад признака j
)
Пример с SHAP:
import shap
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
# Загружаем и обучаем модель
iris = load_iris()
X = iris.data
y = iris.target
model = RandomForestClassifier()
model.fit(X, y)
# Создаём объяснитель SHAP
explainer = shap.TreeExplainer(model) # оптимизирован для деревьев
shap_values = explainer.shap_values(X)
# explainer.expected_value содержит базовое предсказание (mean)
print(f"Base value: {explainer.expected_value}")
# shap_values[class_idx] содержит значения Шапли для каждого класса
# Для класса 0:
shap_class_0 = shap_values[0]
print(f"SHAP values shape: {shap_class_0.shape}")
# Объясняем конкретный пример
example_idx = 0
for feature_idx, feature_name in enumerate(iris.feature_names):
contribution = shap_class_0[example_idx, feature_idx]
print(f"{feature_name}: {contribution:.4f}")
# Визуализация
shap.force_plot(explainer.expected_value[0], shap_class_0[0], X[0],
feature_names=iris.feature_names)
# Summary plot (важность всех признаков по всем примерам)
shap.summary_plot(shap_class_0, X, feature_names=iris.feature_names)
# Зависимость одного признака от предсказания
shap.dependence_plot(0, shap_class_0, X, feature_names=iris.feature_names)
Типы объяснителей SHAP:
# Для деревьев: самый быстрый
explainer = shap.TreeExplainer(model)
# Для любых моделей: используем выборку
explainer = shap.KernelExplainer(
model.predict,
shap.sample(X, 100) # background data
)
# Для нейросетей
explainer = shap.DeepExplainer(model, X_background)
Преимущества SHAP:
- Теоретически обоснован (значения Шапли из теории игр)
- Стабилен: для одного примера даёт один результат
- Аддитивен: сумма вкладов признаков = предсказание - базовое значение
- Универсален: работает с любыми моделями
- Красивые визуализации: force plot, summary plot, зависимости
Недостатки SHAP:
- Вычислительно дорогой (особенно для больших моделей и данных)
- Требует выбора "background data" для KernelExplainer
- Более сложен в понимании (теория игр)
Сравнение LIME и SHAP
| Параметр | LIME | SHAP |
|---|---|---|
| Теория | Локальное линейное приближение | Значения Шапли (теория игр) |
| Стабильность | Нестабильна | Стабильна |
| Скорость | Быстрая | Медленная (особенно KernelExplainer) |
| Model-agnostic | Да | Да |
| Визуализация | Базовая | Отличная |
| Целостность | Нет гарантий | Аддитивна и непротиворечива |
Практические рекомендации:
- Для быстрого объяснения: используй LIME
- Для production моделей: используй SHAP (стабильнее)
- Для деревьев: используй TreeExplainer (очень быстро)
- Для анализа всей модели: используй SHAP summary plots
- Для отладки ошибок: объясни ошибочные примеры обоими методами
- Для регулирования: SHAP обеспечивает лучшее обоснование решений
Пример интеграции в ML pipeline:
def explain_prediction(model, X, example_idx, method="shap"):
"""
Объясняет предсказание модели.
"""
if method == "shap":
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(X)
return explainer, shap_values[example_idx]
elif method == "lime":
explainer = lime.lime_tabular.LimeTabularExplainer(X)
exp = explainer.explain_instance(
X[example_idx],
model.predict_proba
)
return explainer, exp
# Использование
explainer, explanation = explain_prediction(model, X, 0, method="shap")
Оба метода критичны для создания доверчивых и объяснимых AI систем.