Мне нужно найти градиент по отношению к входному слою для одного сверточного фильтра в сверточной нейронной сети (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
слой.
Тем не менее, мой вопрос заключался в том, как найти градиент одиночного фильтра фильтра по отношению к данным во входном слое, что является чем-то другим. Как мне этого добиться?
Сеть 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
Вы можете получить градиенты с точки зрения любого слоя при запуске 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