← Назад к вопросам

Как интерпретировать модели 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:

  1. Берём интересующий нас пример
  2. Создаём "возмущённые" версии этого примера (слегка меняем признаки)
  3. Получаем предсказания модели для всех возмущённых примеров
  4. Весим возмущённые примеры по близости к исходному примеру (примеры ближе к исходному имеют больший вес)
  5. Обучаем линейную модель (легко интерпретируемую) на взвешенных примерах
  6. Коэффициенты линейной модели показывают важность каждого признака
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

ПараметрLIMESHAP
ТеорияЛокальное линейное приближениеЗначения Шапли (теория игр)
СтабильностьНестабильнаСтабильна
СкоростьБыстраяМедленная (особенно KernelExplainer)
Model-agnosticДаДа
ВизуализацияБазоваяОтличная
ЦелостностьНет гарантийАддитивна и непротиворечива

Практические рекомендации:

  1. Для быстрого объяснения: используй LIME
  2. Для production моделей: используй SHAP (стабильнее)
  3. Для деревьев: используй TreeExplainer (очень быстро)
  4. Для анализа всей модели: используй SHAP summary plots
  5. Для отладки ошибок: объясни ошибочные примеры обоими методами
  6. Для регулирования: 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 систем.

Как интерпретировать модели ML с помощью SHAP и LIME? | PrepBro