Python — Нахождение градиента конв-фильтра Caffe относительно ввода

Мне нужно найти градиент по отношению к входному слою для одного сверточного фильтра в сверточной нейронной сети (CNN) как способ визуализировать фильтры.
Учитывая обученную сеть в интерфейсе Python Caffe такой как тот в этот пример, Как я могу найти градиент конв фильтра по данным на входном слое?

Редактировать:

На основе ответ кесаном, Я добавил код ниже. Размеры моего входного слоя [8, 8, 7, 96], Мой первый кон-слой, conv1, имеет 11 фильтров с размером 1x5, в результате чего размеры [8, 11, 7, 92],

net = solver.net
diffs = net.backward(diffs=['data', 'conv1'])
print diffs.keys() # >> ['conv1', 'data']
print diffs['data'].shape # >> (8, 8, 7, 96)
print diffs['conv1'].shape # >> (8, 11, 7, 92)

Как видно из вывода, размеры массивов, возвращаемых net.backward() равны размерам моих слоев в Кафе. После некоторого тестирования я обнаружил, что этот результат представляет собой градиенты потерь относительно data слой и conv1 слой.

Тем не менее, мой вопрос заключался в том, как найти градиент одиночного фильтра фильтра по отношению к данным во входном слое, что является чем-то другим. Как мне этого добиться?

35

Решение

Сеть Caffe жонглирует двумя «потоками» чисел.
Первый — «поток» данных: изображения и метки проталкиваются через сеть. По мере прохождения этих входов через сеть они преобразуются в высокоуровневое представление и, в конечном итоге, в векторы вероятностей классов (в задачах классификации).
Второй «поток» содержит параметры различных слоев, веса сверток, смещения и т. Д. Эти числа / веса изменяются и запоминаются во время фазы поезда сети.

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

Вот пример, который я надеюсь прояснить:

import caffe
solver = caffe.SGDSolver( PATH_TO_SOLVER_PROTOTXT )
net = solver.net

Если вы сейчас посмотрите на

net.blobs

Вы увидите словарь, хранящий объект «caffe blob» для каждого слоя в сети. Каждый блоб имеет место для хранения данных и градиента

net.blobs['data'].data.shape    # >> (32, 3, 224, 224)
net.blobs['data'].diff.shape    # >> (32, 3, 224, 224)

И для сверточного слоя:

net.blobs['conv1/7x7_s2'].data.shape    # >> (32, 64, 112, 112)
net.blobs['conv1/7x7_s2'].diff.shape    # >> (32, 64, 112, 112)

net.blobs содержит первый поток данных, его форма соответствует форме входных изображений вплоть до результирующего вектора вероятности класса.

С другой стороны, вы можете увидеть другого члена net

net.layers

Это вектор-кафе, хранящий параметры разных слоев.
Глядя на первый слой ('data' слой):

len(net.layers[0].blobs)    # >> 0

Для входного слоя нет параметров для хранения.
С другой стороны, для первого сверточного слоя

len(net.layers[1].blobs)    # >> 2

В сети хранится одна капля для веса фильтра, а другая — для постоянного смещения. Вот они

net.layers[1].blobs[0].data.shape  # >> (64, 3, 7, 7)
net.layers[1].blobs[1].data.shape  # >> (64,)

Как видите, этот слой выполняет 7×7 свертки на 3-канальном входном изображении и имеет 64 таких фильтра.

Теперь, как получить градиенты? хорошо, как вы заметили

diffs = net.backward(diffs=['data','conv1/7x7_s2'])

Возвращает градиенты данные поток. Мы можем проверить это

np.all( diffs['data'] == net.blobs['data'].diff )  # >> True
np.all( diffs['conv1/7x7_s2'] == net.blobs['conv1/7x7_s2'].diff )  # >> True

(TL; DR) Вы хотите градиенты параметров, они хранятся в net.layers с параметрами:

net.layers[1].blobs[0].diff.shape # >> (64, 3, 7, 7)
net.layers[1].blobs[1].diff.shape # >> (64,)

Чтобы помочь вам сопоставить названия слоев и их индексы в net.layers вектор, вы можете использовать net._layer_names,


Обновить относительно использования градиентов для визуализации ответов фильтра:
Градиент обычно определяется для скаляр функция. Потеря — это скаляр, и поэтому вы можете говорить о градиенте веса пикселя / фильтра относительно скалярной потери. Этот градиент представляет собой одно число на пиксель / вес фильтра.
Если вы хотите получить вход, который приводит к максимальной активации конкретный Внутренний скрытый узел, вам нужна «вспомогательная» сеть, потеря которой в точности является мерой активации для конкретного скрытого узла, который вы хотите визуализировать. Получив эту вспомогательную сеть, вы можете начать с произвольного ввода и изменить его на основе градиентов вспомогательных потерь для входного слоя:

update = prev_in + lr * net.blobs['data'].diff
28

Другие решения

Вы можете получить градиенты с точки зрения любого слоя при запуске backward() проходить. Просто укажите список слоев при вызове функции. Чтобы показать градиенты с точки зрения слоя данных:

net.forward()
diffs = net.backward(diffs=['data', 'conv1'])`
data_point = 16
plt.imshow(diffs['data'][data_point].squeeze())

В некоторых случаях вы можете заставить все слои выполнять задом наперед, посмотрите на force_backward параметр модели.

https://github.com/BVLC/caffe/blob/master/src/caffe/proto/caffe.proto

10