Распаковка черного ящика с помощью ценности Шепли и теории игр

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

Важность функции

  • Прежде всего, давайте разберемся, как работает важность функции. Важность признаков измеряет увеличение ошибки предсказания модели после перестановки значений признаков. Это означает, что «важность» функции определяется зависимостью модели от этой функции. Если ошибка модели увеличивается после перетасовки значений функции, это означает, что функция сыграла большую роль в прогнозе. И наоборот, если ошибка модели останется неизменной после перетасовки, эта функция, вероятно, не сильно повлияет на решение модели.
  • Однако важность признаков корректно работает только на линейных моделях. Это связано с характером важности функции, которая суммирует как эффект основной функции, так и эффекты взаимодействия с другими функциями. Таким образом, любой признак с отрицательным результатом суммируется с положительным результатом. Это как раздавить результат в один большой кусок. В результате он на самом деле не отражает индивидуальные отрицательные и положительные эффекты этой функции.
  • Кроме того, добавление коррелированных функций усложняет интерпретацию выходных данных. Допустим, есть одна важная функция «беговая дорожка» для похудения. Теперь я добавил «лестничную дорожку», которая также эффективна для похудения. Давайте также предположим, что лестничная дорожка и беговая дорожка сильно коррелированы. Теперь все эти две функции опускаются до посредственного уровня на графике важности функций, вместо того, чтобы оставаться на верхней позиции. Такой результат требует дополнительного времени и усилий для интерпретации результата.

Значение Shaley является отличной альтернативой для преодоления таких недостатков. Так что же такое значение Шепли?

Значение Шепли

Значение Шепли вытекает из теории игр. Теория игр — это исследование интерактивного принятия решений между рациональными агентами. В случае игры логические игроки, принимающие решения, будут принимать стратегические решения в зависимости от действий других игроков, чтобы выиграть игру или достичь желаемых целей. Значение Шепли становится удобным при оценке вклада каждого участника в результат игры. Это значение рассчитывается путем усреднения веса предельного вклада всех агентов во всех возможных коалициях. В машинном обучении игрок или агент соответствует функции, и важность этой функции вычисляется с помощью значения Шепли. Ниже приведено уравнение для получения значения Шепли. Глядя на описания, написанные на человеческом языке под формулой, было бы полезно интерпретировать формулу.

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

В отличие от важности функции, которая добавила бы отрицательный эффект к положительным эффектам взаимодействия и эффектам основной функции одновременно, значения Шепли учитывают снижение производительности. Кроме того, он более надежен в получении результата. На этом преимущества ценности Шепли не заканчиваются. Работает не только с линейными моделями, но и с нейронными сетями! С этим значением вы можете интерпретировать любую модель машинного обучения.

Вы можете легко реализовать это значение, используя библиотеку SHAP (Shapley Additive exPlanations) в python. Недостатком SHAP является то, что он требует больших вычислительных ресурсов и медленный. Кроме того, необходимо знать, что значение Шепли никогда не следует интерпретировать как причинно-следственную связь. Тот факт, что определенная функция была полезна для предсказания, не всегда подразумевает наличие причинно-следственной связи.

Вы можете столкнуться с некоторыми ошибками из-за проблем несовместимости версий с Tensorflow. Я объясню, как их исправить, и проведу простой эксперимент. Содержание эксперимента охватывает классификацию изображений макияжа в американском стиле и макияжа в корейском стиле, а также особенности, которые повлияли на прогноз модели.

Это не обширный эксперимент, а быстрая проверка того, как SHAP можно применять в нейронных сетях. В этом эксперименте я использовал модель CNN, обученную на небольшом наборе данных. Таким образом, результат может значительно улучшиться за счет увеличения размера набора данных. Вы можете получить доступ к подробному коду и набору данных по этой ссылке.

Вот очень полезные советы по устранению ошибок при реализации SHAP.

Ресурс исчерпан: OOM при распределении тензора с формой [ , , ,]

Когда вы столкнетесь с этой проблемой, попробуйте выполнить следующие два шага:

  1. Уменьшен размер пакета до 16 (может быть больше или меньше, но до такой степени, что он не будет показывать соответствующую ошибку)
  2. Уменьшите размер ввода до 100 (может быть больше или меньше, но до такой степени, что не будет отображаться соответствующая ошибка)

Если вы используете версию Tensorflow › 2.4.0

Перезапустите среду выполнения и включите следующий код в самый верх своего кода.

import tensorflow as tf
import tensorflow.compat.v1.keras.backend as K
tf.compat.v1.disable_eager_execution()

Эксперимент

У меня есть две папки, состоящие из обучающих наборов и тестовых наборов. Каждое изображение в папке отсортировано по двум классам: «американский_макияж» и «корейский_макияж».

base_dir = '/content/gdrive/MyDrive/research/images'
train_dir = os.path.join(base_dir, 'train')
test_dir = os.path.join(base_dir, 'test')
class_name = os.listdir(train_dir)
class_name_test = os.listdir(test_dir)
print(class_name)
#['american_makeup', 'korean_makeup'] 
print(class_name_test)
#['american_makeup', 'korean_makeup']

На данный момент каждый класс записывается с категориальной переменной. Мне нужно закодировать их в целочисленные переменные. Здесь я использовал LabelEncoder, чтобы изменить имена классов на целое число. Как только вы закончите с целочисленным кодированием, следующим шагом будет выполнение одноразового кодирования. Имя класса «американский_макияж» теперь помечено как [1,0], а «корейский_макияж» помечено как [0,1].

integer_encoded = LabelEncoder().fit_transform(class_name)
integer_encoded = integer_encoded.reshape(len(integer_encoded), 1)
onehot_encoded = OneHotEncoder(sparse=False).fit_transform(integer_encoded)
print(onehot_encoded)
#[[1. 0.]  [0. 1.]]
integer_encoded_test = LabelEncoder().fit_transform(class_name_test)
integer_encoded_test = integer_encoded_test.reshape(len(integer_encoded_test), 1)
onehot_encoded_test = OneHotEncoder(sparse=False).fit_transform(integer_encoded_test)
print(onehot_encoded_test)
#[[1. 0.]  [0. 1.]]

Набор обучающих данных и набор тестовых данных изменяются в размере и форме и сохраняются в новых списках. Каждая из соответствующих меток хранится в списках меток поезда и теста.

train_image = []
train_label = []
test_image = []
test_label = []
# for train dataset
for i in range(len(class_name)):
    path = os.path.join(train_dir, class_name[i])
    img_list = os.listdir(path)
    for j in img_list:
        img = os.path.join(path, j)
        img = cv2.imread(img, cv2.IMREAD_COLOR)
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        img = cv2.resize(img, (100, 100), interpolation =
              cv2.INTER_CUBIC)
        img = img.reshape((100, 100, 3))
        train_image.append(img)
        train_label.append(onehot_encoded[i])
# for test dataset
for i in range(len(class_name_test)):
    path = os.path.join(train_dir, class_name_test[i])
    img_list = os.listdir(path)
    for j in img_list:
        img = os.path.join(path, j)
        img = cv2.imread(img, cv2.IMREAD_COLOR)
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        img = cv2.resize(img, (100, 100), interpolation =
              cv2.INTER_CUBIC)
        img = img.reshape((100, 100, 3))
        test_image.append(img)
        test_label.append(onehot_encoded[i])

Есть еще несколько шагов, которые необходимо выполнить перед применением SHAP. Во-первых, нам нужно нормализовать данные. Еще одна важная задача — знать, как хранятся ваши данные. Сначала были заказаны изображения макияжа в американском стиле, а затем изображения макияжа в корейском стиле в моем наборе данных. Чтобы избежать нежелательной утечки информации или обучения, я снова перетасовал все данные.

# Normalize image data
X_train = shuffle(train_image.reshape(283, 100, 100, 3).astype("float32") / 255, random_state = seed)
X_test = shuffle(test_image.reshape(20, 100, 100, 3).astype("float32") / 255, random_state = seed)

# Shuffle data or use library to randomly split your data
train_label = shuffle(train_label, random_state = seed)
test_label = shuffle(test_label, random_state = seed)

Теперь вы, наконец, можете создать свою модель! Архитектура вашей модели полностью зависит от того, как вы хотите построить свою модель, поскольку SHAP не зависит от модели!

def CNN():
input_layer = keras.Input(shape=(100,100,3))
x = keras.layers.Conv2D(150, (3,3), padding='same', activation = 'relu', kernel_initializer = keras.initializers.HeUniform(seed=seed))(input_layer)
x = keras.layers.BatchNormalization()(x)
x = keras.layers.MaxPooling2D((2, 2))(x)
x = keras.layers.Conv2D(150, (3,3), padding='same', activation = 'relu', kernel_initializer = keras.initializers.HeUniform(seed=seed))(x)
x = keras.layers.BatchNormalization()(x)
x = keras.layers.MaxPooling2D((2, 2))(x)
x = keras.layers.Conv2D(150, (3,3), padding='same', activation = 'relu', kernel_initializer = keras.initializers.HeUniform(seed=seed))(x)
x = keras.layers.BatchNormalization()(x)
x = keras.layers.MaxPooling2D((2, 2))(x)
x = keras.layers.Conv2D(150, (3,3), padding='same', activation = 'relu', kernel_initializer = keras.initializers.HeUniform(seed=seed))(x)
x = keras.layers.BatchNormalization()(x)
x = keras.layers.MaxPooling2D((2, 2))(x)
x = keras.layers.Flatten()(x)
x = keras.layers.Dense(128, activation='relu', kernel_initializer = keras.initializers.HeUniform(seed=seed))(x)
x = keras.layers.Dropout(0.5)(x)
output_layer = keras.layers.Dense(2, activation='sigmoid')(x)
model = keras.Model(inputs=input_layer, outputs=output_layer, name = 'CNN')
model.compile(loss='binary_crossentropy', optimizer= keras.optimizers.SGD(learning_rate=0.001),  metrics=['acc', 'AUC'])
return model

После обучения модель была протестирована на десяти изображениях американских и десяти корейских знаменитостей. Давайте посмотрим на один из примеров из результата.

idx = 6
input_val = X_test[idx:idx+1]
output_val = model.predict(input_val)
real = test_label[idx:idx+1]
print("Prediction : ", np.argmax(output_val))
print("Ground Truth : ", np.argmax(real))
plt.imshow(input_val.reshape(100, 100, 3),interpolation='nearest')
plt.show()

Модель точно классифицировала образ! Как насчет остальных? Из двадцати изображений три были неправильными.

Интересно, что повлияло на классификацию модели. Вообще говоря, американский макияж и корейский макияж имеют два очень разных стиля. Американский макияж имеет тенденцию подчеркивать форму изогнутых бровей, длинные накладные ресницы и очень дымчатый макияж глаз. С другой стороны, корейский макияж требует очень естественных бровей, светлых теней, четкого макияжа и красно-оранжевых губ. Я собираюсь посмотреть, действительно ли моя догадка способствовала предсказанию модели.

Это образы американского стиля макияжа. Отрицательные значения SHAP относятся к отрицательному влиянию, а положительное значение SHAP относится к положительному влиянию на решения моделей. Картинка слева — это изображение земной правды. Тот, что посередине, это метка 0, это американский стиль макияжа, а тот, что справа, это метка 1, это корейский макияж.

Удивительно, но красные точки сосредоточены вокруг глаз и бровей для среднего изображения, которое имеет метку 0 (американский макияж). Синие точки вокруг глаз и бровей на изображениях в корейском стиле предполагают, что эти части говорят о том, что это не изображения корейского макияжа. Теперь посмотрим на обратный результат.

Макияж в корейском стиле намного менее выражен по сравнению с американским макияжем. Тем не менее, мы можем предположить, что когда модель классифицировала изображения как корейский макияж, это во многом было связано с общей кожей или структурой лица, поскольку красные точки разбросаны по всем лицам.

Как насчет неправильно классифицированных случаев?

Здесь мы видим, что обе метки 0 и 1 имеют красные и синие точки повсюду на изображении. Интересным моментом является то, что все случаи с неправильной маркировкой не белые. Такие результаты могут дать очень важные подсказки, где исправить и поработать над вашей моделью. В реальных условиях такие предвзятые результаты могут привести к катастрофическим последствиям и дискриминации определенных групп людей.

Насколько полезен SHAP? Какое время жить :-)