查找关于输入的Caffe转换滤波器的梯度
c++
caffe
deep-learning
neural-network
5
0

我需要找到卷积神经网络(CNN)中单个卷积过滤器相对于输入层的梯度,以可视化过滤器
给定Caffe的Python界面中经过训练的网络(例如本示例中的网络) ,那么我如何才能找到conv过滤器相对于输入层中数据的梯度?

编辑:

根据cesans回答 ,我添加了以下代码。我的输入层的尺寸为[8, 8, 7, 96] 。我的第一CONV层, 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层的损耗的梯度。

但是,我的问题是如何针对输入层中的数据找到单个转换滤波器的梯度,这是另外一回事。我该如何实现?

参考资料:
Stack Overflow
收藏
评论
共 2 个回答
高赞 时间 活跃

Caffe网玩弄两个数字“流”。
首先是数据“流”:通过网络推送的图像和标签。当这些输入通过网络进行时,它们将转换为高级表示,并最终转换为类概率向量(在分类任务中)。
第二个“流”保存不同层的参数,卷积的权重,偏差等。在网络的训练阶段更改并获知这些数字/权重。

尽管这两个“流”扮演着根本不同的角色,但是caffe使用相同的数据结构blob来存储和管理它们。
然而,对于每一层,有两个不同的斑点矢量,每个流一个。

我希望可以阐明以下示例:

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

如果你现在看

net.blobs

您将看到一个字典,该字典为网络中的每一层存储一个“ caffe blob”对象。每个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

网络存储一个Blob用于过滤器权重,另一个Blob用于恒定偏置。他们来了

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

如您所见,该层在3通道输入图像上执行7x7卷积,并具有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

收藏
评论
新手导航
  • 社区规范
  • 提出问题
  • 进行投票
  • 个人资料
  • 优化问题
  • 回答问题

关于我们

常见问题

内容许可

联系我们

@2020 AskGo
京ICP备20001863号