Градиент слоев загруженной нейросети в Chainer

Я загружаю предварительно обученную модель в Chainer:

net=chainer.links.VGG16Layers(pretrained_model='auto')

Затем я делаю прямой проход с некоторыми данными и добавляю слой потерь:

acts = net.predict([image]).array loss=chainer.Variable(np.array(np.sum(np.square(acts-one_hot))))

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

Типичный обратный метод не работает.


person saman jahangiri    schedule 01.11.2018    source источник


Ответы (2)


Если вы хотите получить .grad входного изображения, вы должны обернуть ввод chainer.Variable.
Однако VGGLayers.extract() не поддерживает ввод Variable, поэтому в этом случае вам следует вызвать .forward() или его функцию переноса __call__().

import chainer
from chainer import Variable
from chainer import functions as F
from cv2 import imread
from chainer.links.model.vision import vgg

net = vgg.VGG16Layers(pretrained_model='auto')

# convert raw image (np.ndarray, dtype=uint32) to a batch of Variable(dtype=float32)
img = imread("path/to/image")
img = Variable(vgg.prepare(img))
img = img.reshape((1,) + img.shape)  # (channel, width, height) -> (batch, channel, width, height)

# just call VGG16Layers.forward, which is wrapped by __call__()
prob = net(img)['prob']
intermediate = F.square(prob)
loss = F.sum(intermediate)

# calculate grad
img_grad = chainer.grad([loss], [img])  # returns Variable
print(img_grad.array) # some ndarray
person Yuki Hashimoto    schedule 06.11.2018
comment
Я не уверен, должен ли я спрашивать об этом здесь; но зачем вам нужен vgg.prepare img, чтобы передать его в net(), но вы можете передать изображение как есть в net.predict()? Разве это не один и тот же метод? И почему vgg.prepare() создает таблицу 3x3 (9 копий) данного изображения? - person saman jahangiri; 11.11.2018
comment
1. vgg.prepare просто включается в net.predict(). 2. img.shape == (1440, 1920, 3), vgg.prepare(img).shape == (3, 224, 224). img.dtype == dtype('uint8'), vgg.prepare(img).dtype == dtype('float32'). Это причина. - person Yuki Hashimoto; 12.11.2018
comment
Я до сих пор не понимаю, почему vgg.prepare делает 9 уменьшенных копий данного изображения в изображении 224x224: если вы дадите ему изображение, подобное X, оно даст вам xxx (x - уменьшенная версия X) xxx xxx - person saman jahangiri; 12.11.2018
comment
Нет, как показано в моем последнем комментарии, он не дает 9 уменьшенных копий. Если вы получаете копии, что-то идет не так. Он только изменяет размер, масштабирует и транспонирует (H, W, C) -> (C, H, W). - person Yuki Hashimoto; 12.11.2018
comment
Я задал вопрос более точно и прикрепил ввод и вывод здесь: stackoverflow.com/questions/53257608/ - person saman jahangiri; 12.11.2018

Пункт 1.
НЕ вызывайте VGGLayers.predict(), который не предназначен для вычисления обратного распространения.
Вместо этого используйте VGGLayers.extract().

Пункт 2.
НЕ применяйте np.square() и np.sum() непосредственно к chainer.Variable.
НЕОБХОДИМО использовать F.square() и F.sum() вместо chainer.Variable.

Пункт 3.
Используйте loss.backward(), чтобы получить .grad для обучаемых параметров. (шаблон 1)
Используйте loss.backward(retain_grad=True), чтобы получить .grad для всех переменных. (шаблон 2)
Используйте chainer.grad(), чтобы получить .grad для определенной переменной. (шаблон 3)

Код:

import chainer
from chainer import functions as F, links as L
from cv2 import imread

net = L.VGG16Layers(pretrained_model='auto')
img = imread("/path/to/img")
prob = net.extract([img], layers=['prob'])['prob']  # NOT predict, which overrides chainer.config['enable_backprop'] as False
intermediate = F.square(prob)
loss = F.sum(intermediate)

# pattern 1:
loss.backward()
print(net.fc8.W.grad)  # some ndarray
print(intermediate.grad)  # None
###########################################
net.cleargrads()
intermediate.grad = None
prob.grad = None
###########################################

# pattern 2:
loss.backward(retain_grad=True)
print(net.fc8.W.grad)  # some ndarray
print(intermediate.grad)  # some ndarray

###########################################
net.cleargrads()
intermediate.grad = None
prob.grad = None
###########################################

# pattern 3:
print(chainer.grad([loss], [net.fc8.W]))  # some ndarray
print(intermediate.grad)  # None
person Yuki Hashimoto    schedule 04.11.2018
comment
Большое спасибо за ответ, Юки, это работает! - person saman jahangiri; 04.11.2018
comment
Я пометил решенным, но из-за того, что у меня меньше 15 репутаций, пока не могу проголосовать. - person saman jahangiri; 05.11.2018
comment
Как я могу получить градиент входного слоя (изображения)? Потому что у VGG нет такого слоя. - person saman jahangiri; 05.11.2018
comment
Спасибо за пометку решена! Я добавил еще один ответ, чтобы получить входное изображение. Это немного сложно, поэтому, если у вас есть вопрос, не стесняйтесь спрашивать! - person Yuki Hashimoto; 06.11.2018
comment
Я не уверен, что понимаю разницу между извлечением и прогнозированием. Какое отношение имеет предсказание к фону? Потому что интуитивно он должен просто сделать пас вперед. - person saman jahangiri; 15.11.2018
comment
Не могли бы вы объяснить разницу между тремя шаблонами? - person saman jahangiri; 15.11.2018
comment
predict отключает обратное распространение. В этой функции обратное распространение не включено. В extract включено обратное распространение. Однако он содержит vgg.prepare и не принимает ввод Variable.__call__ аналогичен forward, он включает обратное распространение и принимает Variable, но не принимает ввод uint8. Он принимает только предварительно обработанные входные данные, соответствующие входным данным ImageNet. - person Yuki Hashimoto; 16.11.2018