Траектория «Нейросети» · конспект 8 из 12

Интерпретируемость моделей

О чём эта тема
Нейросеть — «чёрный ящик»: она выдаёт ответ, но не объясняет его. Разбираются методы, которые позволяют понять, на какие признаки опирается модель: от коэффициентов линейной модели до окклюзии, LIME, вектора Шепли и Grad-CAM. По материалам лекции курса об объяснимости моделей.
Аннотация
Конспект начинается с причин, по которым предсказания модели нужно объяснять: обнаружение некорректных зависимостей, доверие в ответственных применениях, публикация результатов. Затем вводится различие между объяснимостью и интерпретируемостью, и разбирается простейший интерпретируемый случай — линейная модель и её стандартизованные коэффициенты. Далее рассматриваются методы, не зависящие от устройства модели: окклюзия с интерактивным тренажёром, LIME — локальная аппроксимация «чёрного ящика» интерпретируемой моделью, и вектор Шепли с его приближением SHAP. Заключительная часть посвящена градиентным методам для нейросетей: карте важности по градиенту, её проблемам (шум и насыщение), методам SmoothGrad и Integrated Gradients и, наконец, Grad-CAM — визуализации областей изображения, на которые смотрит свёрточная сеть.
Пререквизиты
Конспект 1 (линейная модель, сигмоида), конспект 2 (градиент, производная по входу), конспект 4 (свёртка, карта признаков), конспект 7 (предобученные сети, глобальный усредняющий пулинг).
Мотивация
В статье «Why Should I Trust You?» (Ribeiro et al., 2016) авторы обучили классификатор волков и хаски на изображениях, отобранных так, что на всех фотографиях волков на фоне был снег, а на фотографиях хаски — нет. Классификатор показал высокую точность, но выучил не животных, а снег на фоне. Без методов объяснения такую модель невозможно отличить от правильной — до тех пор, пока она не начнёт ошибаться на реальных данных.

1. Зачем объяснять предсказания

Модели машинного обучения представляют собой чёрный ящик, и иногда это становится препятствием для их внедрения. Есть как минимум три причины интересоваться объяснением предсказаний.

Обнаружение некорректных зависимостей. Модель может использовать совсем не те признаки, которые соответствуют реальным объектам: ориентироваться на фон или водяной знак, а не на свойства объекта, — как в примере с волками и снегом из мотивации.

Доверие к предсказаниям. Нельзя остановить ядерную электростанцию или назначить пациенту опасное лечение на основании предсказания «чёрного ящика»: даже маловероятная ошибка имеет тяжёлые последствия. Человек, принимающий решение, должен понимать, на основе каких признаков или симптомов оно сделано.

Публикации. Вероятность публикации научной статьи значительно повышается, если автор смог объяснить происхождение результатов своего исследования.

В англоязычной литературе встречаются два связанных термина. Объяснимость (англ. explainability) — методики, позволяющие объяснить механизм функционирования самой модели; для линейной регрессии это анализ коэффициентов. Интерпретируемость (англ. interpretability) — анализ того, как изменение входов модели влияет на её выходы; например, закрашивая часть пикселей изображения, можно выяснить, какие из них повлияли на предсказание. В конспекте рассматриваются методы обоих типов.

2. Простой случай: коэффициенты линейной модели

Линейная модель (конспект 1, формула (1.1)) интерпретируема сама по себе: если признаку присвоен большой по модулю вес, признак сильно влияет на предсказание. Однако сравнивать веса напрямую можно только тогда, когда признаки имеют одинаковый масштаб: вес при признаке «число комнат» (единицы) и вес при признаке «население района» (тысячи) несопоставимы. Поэтому перед сравнением признаки стандартизуют:

\[ z_j = \frac{x_j - \mu_j}{\sigma_j}, \tag{8.1}\]

где \(\mu_j\) и \(\sigma_j\) — среднее и стандартное отклонение признака \(j\) по обучающей выборке. После стандартизации все признаки безразмерны и имеют единичный масштаб, качество модели не меняется, а модули весов \(|w_j|\) становятся сопоставимой мерой важности признаков.

Типичная ошибка Сравнивают коэффициенты линейной модели, обученной на нестандартизованных признаках. Большой коэффициент при признаке с маленьким масштабом и маленький при признаке с большим масштабом могут давать одинаковый вклад в предсказание — выводы о «важности» по таким весам неверны.

3. Окклюзия: закрась и посмотри

Для модели-«чёрного ящика» весов не видно, но есть простейший способ спросить её саму: закрыть часть входа и посмотреть, как изменится предсказание. Если при закрашивании области вероятность класса резко падает — модель опиралась на эту область; если не меняется — область для предсказания не важна. Перебрав все положения закрашивающего квадрата, получают карту окклюзии — тепловую карту важности областей входа.

Тренажёр: карта окклюзии
вход (клик — передвинуть окклюдер 3×3)
карта окклюзии (темнее — важнее)

«Модель» тренажёра — один нейрон из конспекта 1: линейная комбинация пикселей и сигмоида; веса иллюстративные и сосредоточены на глазах и носе. Закройте глаз — вероятность «кот» падает сильно; закройте фон — не меняется. Карта окклюзии восстанавливает важные области, не заглядывая в веса.

Окклюзия проста и честна — она измеряет реальную реакцию модели, — но дорога: для карты разрешением \(k\times k\) модель нужно вызвать \(k^2\) раз, и результат зависит от размера и цвета закрашивающего квадрата.

4. LIME: локальная линейная аппроксимация

LIME (Local Interpretable Model-agnostic Explanations; Ribeiro et al., 2016) обобщает идею окклюзии. Мы хотим найти «стеклянный ящик» — интерпретируемую модель \(g\), которая локально, в окрестности объекта интереса \(x^*\), аппроксимирует «чёрный ящик» \(f\):

\[ \hat g = \underset{g \in G}{\operatorname{argmin}}\; L(f, g, \pi_{x}) + \Omega(g), \tag{8.2}\]

где \(G\) — семейство интерпретируемых моделей (линейные модели, неглубокие деревья), \(\pi_x\) — мера близости сгенерированных объектов к объекту интереса, функция ошибки \(L\) измеряет несоответствие предсказаний \(f\) и \(g\) на этих объектах, а \(\Omega(g)\) — штраф за сложность модели \(g\) (на практике вместо оптимизации штрафа просто ограничивают сложность: глубину дерева или число ненулевых коэффициентов).

Объекты в окрестности \(x^*\) генерируют по-разному в зависимости от типа данных: в текстах удаляют слова; изображения делят на области — суперпиксели — и поочерёдно закрашивают их средним цветом; для табличных данных бинарные признаки инвертируют, а к вещественным добавляют шум. Обучив на таких объектах и предсказаниях «чёрного ящика» линейную модель, получают веса суперпикселей — какие области говорят «за» класс, какие «против»:

Фотография собаки и кошки с выделенными LIME суперпикселями: для класса «немецкая овчарка» подсвечена морда собаки, для класса «полосатый кот» — кошка
Объяснения LIME для двух классов на одном изображении: зелёным выделены области, голосующие за класс. Из лекции курса об объяснимости
Типичная ошибка Распространяют объяснение LIME на всю модель. LIME аппроксимирует «чёрный ящик» только в окрестности одного конкретного объекта: для другого объекта та же модель может опираться на совсем другие признаки, и объяснение нужно строить заново.

5. Вектор Шепли и SHAP

Другой подход к вкладу признаков берёт начало в кооперативной теории игр. Пусть модель предсказывает доход человека по трём признакам: возраст, пол, профессия. Рассмотрим все возможные наборы (коалиции) признаков — от пустого до полного — и представим их в виде графа, где ребро — добавление одного признака:

Граф коалиций признаков: каждая вершина — модель, обученная на своём наборе признаков; переход вверх по ребру — добавление одного признака.

Обучив модель на каждом наборе, для объекта \(x_0\) получаем предсказание в каждой вершине. Маржинальный вклад признака — разница предсказаний двух моделей, соединённых ребром: например, если модель без признаков предсказывает 50 тыс., а модель на одном возрасте — 40 тыс., маржинальный вклад возраста в этой паре равен −10 тыс. Вклад признака в итоговое предсказание — значение Шепли — вычисляется как взвешенная сумма его маржинальных вкладов по всем рёбрам, где признак добавляется; вес ребра обратно пропорционален числу маржинальных вкладов на данном уровне графа (для трёх признаков веса получаются \(w_1 = w_4 = 1/3\), \(w_2 = w_3 = 1/6\)). В общем виде для признака \(i\):

\[ \phi_i = \sum_{S \subseteq N \setminus \{i\}} \frac{|S|!\,(|N|-|S|-1)!}{|N|!}\, \bigl(v(S \cup \{i\}) - v(S)\bigr), \tag{8.3}\]

где \(N\) — множество всех признаков, \(v(S)\) — предсказание модели, обученной на наборе \(S\). Ключевое свойство значений Шепли — аддитивность: сумма вкладов всех признаков вместе с базовым значением \(\phi_0\) (средним предсказанием) в точности даёт предсказание модели:

\[ f(x) = \phi_0 + \sum_{i=1}^{M} \phi_i \tag{8.4}\]

Прямой расчёт по (8.3) требует обучить модель на каждом подмножестве признаков — уже для 10 признаков это тысячи моделей. Библиотека SHAP использует приближённые алгоритмы (в частности, показано, что LIME с линейной регрессией и специальными весами даёт оценку значений Шепли) и строит наглядные диаграммы вкладов:

Диаграмма SHAP: вклады признаков в предсказание цены жилья, стрелки признаков RM и LSTAT сдвигают предсказание от базового значения
Вклады признаков в предсказание цены жилья по (8.4): от среднего значения E[f(X)] стрелки признаков сдвигают предсказание вверх (красные) и вниз (синие) до итогового f(x). Из лекции курса об объяснимости

6. Градиентные методы: карта важности

Для нейросети есть способ дешевле окклюзии: у сети можно взять производную. Обратное распространение ошибки (конспект 2) вычисляет градиент функции ошибки по весам, но ничто не мешает тем же механизмом вычислить градиент выхода по входу — насколько изменится логит класса при малом изменении каждого пикселя. Модуль этого градиента и есть простейшая карта важности (англ. saliency map; метод называют Vanilla Gradient):

\[ M_c(x) = \left|\frac{\partial\, y_c}{\partial\, x}\right|, \tag{8.5}\]

где \(y_c\) — логит интересующего класса \(c\). На Keras градиент по входу берётся через tf.GradientTape, которому нужно явно указать следить за входным тензором:

import tensorflow as tf
import numpy as np
from tensorflow.keras.applications import ResNet50

model = ResNet50(weights='imagenet')

img = tf.convert_to_tensor(x)             # батч из одного изображения (1, 224, 224, 3)
with tf.GradientTape() as tape:
    tape.watch(img)                       # следить за входом, а не только за весами
    preds = model(img)
    score = preds[:, tf.argmax(preds[0])] # логит наиболее вероятного класса

grads = tape.gradient(score, img)         # градиент выхода по входу
saliency = tf.reduce_sum(tf.abs(grads), axis=-1)[0]  # |градиент|, сумма по каналам
Фотография собаки и кошки и полученная по градиенту карта важности: очень зашумлённое серое изображение
Карта важности по методу Vanilla Gradient получается очень зашумлённой. Из лекции курса об объяснимости

У простого градиента две проблемы. Первая видна на рисунке — шум. Вторая — насыщение: если признак «идеально» характеризует объект, градиент по нему может быть нулевым. Пусть активация нейрона \(h = \max(0,\, 1 - i_1 - i_2)\) и \(i_1 = i_2 = 1\): выход \(h = 0\), и зануление любого одного из признаков его не меняет — по градиенту (и по одиночной окклюзии) оба признака выглядят неважными, хотя именно они определяют результат.

Обе проблемы смягчают усреднением. SmoothGrad усредняет карты важности по нескольким зашумлённым копиям изображения:

\[ M_c^{\text{smooth}}(x) = \frac{1}{n}\sum_{k=1}^{n} M_c\bigl(x + \varepsilon_k\bigr), \qquad \varepsilon_k \sim \mathcal{N}(0, \sigma^2), \tag{8.6}\]

Integrated Gradients вместо шума «восстанавливает» изображение от опорного \(x'\) (обычно чёрного) к исходному и интегрирует градиент вдоль этого пути, что частично решает и проблему насыщения:

\[ \mathrm{IG}(x) = (x - x') \cdot \int_{0}^{1} M_c\bigl(x' + \alpha\,(x - x')\bigr)\, d\alpha \;\approx\; (x - x') \cdot \sum_{k=1}^{m} M_c\Bigl(x' + \tfrac{k}{m}(x - x')\Bigr)\frac{1}{m}, \tag{8.7}\]

где число шагов \(m\) выбирают от 20 до 300.

Сравнение карт важности: исходное изображение, зашумлённый Vanilla Gradient, более сфокусированный SmoothGrad и Integrated Gradients
Сравнение методов: Vanilla Gradient, SmoothGrad и Integrated Gradients на одном изображении. Из лекции курса об объяснимости

Тот же приём — градиент по входу — используется и в противоположных целях: если не визуализировать градиент, а менять по нему изображение, усиливая ошибку, получаются состязательные атаки (англ. adversarial attacks) — незаметные глазу изменения пикселей, заставляющие сеть уверенно ошибаться.

Типичная ошибка Считают карту важности доказательством того, что модель работает правильно. Карты объяснений сами по себе хрупки: малое возмущение входа может заметно поменять карту при том же предсказании. Объяснение — инструмент диагностики и поиска проблем, а не сертификат корректности модели.

7. Grad-CAM: куда смотрит свёрточная сеть

Карта важности по входным пикселям — слишком мелкая: интереснее знать, какие области изображения активировали признаки класса. После каждого свёрточного слоя сеть хранит карты признаков (конспект 4), сохраняющие информацию о расположении объектов, — один канал соответствует одному признаку. Метод Grad-CAM строит карту класса как взвешенную сумму каналов последнего свёрточного слоя:

\[ \mathrm{CAM} = \mathrm{ReLU}\Bigl(\sum_{i=1}^{N_{ch}} w_i A_i\Bigr), \tag{8.8}\]

где \(A_i\) — каналы карты признаков, а веса \(w_i\) получаются усреднением градиента логита класса по соответствующему каналу (то же глобальное усреднение, что в конспекте 7). ReLU оставляет только области, влияющие на класс положительно. На Keras:

# модель, возвращающая и карту признаков последнего свёрточного слоя, и предсказание
grad_model = tf.keras.Model(model.inputs,
    [model.get_layer('conv5_block3_out').output, model.output])

def grad_cam(img, class_idx):
    with tf.GradientTape() as tape:
        fmaps, preds = grad_model(img)
        score = preds[:, class_idx]
    grads = tape.gradient(score, fmaps)          # градиент логита по картам признаков
    w = tf.reduce_mean(grads, axis=(0, 1, 2))     # усреднение по пространству — веса w_i
    cam = tf.nn.relu(tf.reduce_sum(fmaps[0] * w, axis=-1))  # формула (8.8)
    return cam / tf.reduce_max(cam)          # нормировка для отображения
Шесть тепловых карт Grad-CAM для разных классов: для класса «немецкая овчарка» подсвечена собака, для кошачьих классов — кошка, для класса «обогреватель» — предмет на заднем плане
Grad-CAM для шести наиболее вероятных классов одного изображения: для каждого класса подсвечена своя область. Из лекции курса об объяснимости

Разрешение карты Grad-CAM равно разрешению последней карты признаков (например, \(7\times7\) у ResNet50 для входа \(224\times224\)), поэтому при наложении на изображение её растягивают с интерполяцией: границы областей принципиально грубые. Существуют уточнённые варианты (Grad-CAM++ с градиентами второго порядка), лучше работающие при нескольких объектах одного класса на изображении.

Типичная ошибка Берут карту признаков не последнего свёрточного слоя, а раннего. Ранние слои реагируют на простые узоры (края, текстуры) и дают карту, не связанную с классом; смысл «куда смотрит сеть ради этого класса» имеет именно последний свёрточный слой, ближайший к классификатору.

Контрольные вопросы