Ответ 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