Подтвердить что ты не робот

Поиск градиента конвективного фильтра Caffe в отношении ввода

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

Edit:

На основе ответа cesans я добавил код ниже. Размеры моего входного слоя [8, 8, 7, 96]. Мой первый conv-layer, 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(), равны размерам моих слоев в Caffe. После некоторого тестирования я обнаружил, что этот результат является градиентом потери по отношению к слою data и слою conv1.

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

4b9b3361

Ответ 1

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

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

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

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

Это вектор caffe, сохраняющий параметры разных слоев.
Глядя на первый слой ('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,)

Как вы можете видеть, этот слой выполняет 7x7 свертки на 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.


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

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

Ответ 2

Вы можете получить градиенты в терминах любого уровня при запуске прохода 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