首页 文章

找到关于输入的Caffe conv滤波器的梯度

提问于
浏览
35

我需要在卷积神经网络(CNN)中找到关于单个卷积滤波器的输入层的梯度,作为visualize the filters的一种方法 .
给定Caffe的Python接口中训练有素的网络,例如this example中的网络,如何根据输入层中的数据找到转换滤波器的梯度?

Edit:

基于answer by cesans,我添加了以下代码 . 我的输入图层的尺寸是 [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() 返回的数组的尺寸等于Caffe中我的图层的尺寸 . 经过一些测试后,我发现这个输出分别是 data 层和 conv1 层的损失梯度 .

但是,我的问题是如何根据输入层中的数据找到单个转换滤波器的梯度,这是另外的 . 我怎样才能做到这一点?

2 回答

  • 28

    Caffe net玩弄了两个"streams"的数字 .
    第一个是数据"stream":通过网络推送的图像和标签 . 当这些输入通过网络进行时,它们被转换为高级表示并最终转换为类概率向量(在分类任务中) .
    第二个"stream"保存不同层的参数,卷积的权重,偏差等 . 这些数字/权重在网络的训练阶段期间被改变和学习 .

    尽管这两个角色发挥着根本不同的作用,但是caffe仍然使用相同的数据结构来存储和管理它们 .
    但是,对于每个层,每个流有两个 different 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用于滤波器权重,另一个用于恒定偏置 . 他们来了

    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 关于使用渐变来显示过滤器响应:
    通常为 scalar 函数定义渐变 . 损耗是标量,因此您可以说像素/滤波器权重相对于标量损失的梯度 . 该梯度是每个像素/滤波器权重的单个数字 .
    如果您希望得到最大程度激活 specific 内部隐藏节点的结果,则需要一个"auxiliary" net,该损失只是对要显示的特定隐藏节点的激活的度量 . 一旦有了这个辅助网络,就可以从任意输入开始,根据输入层的辅助损耗梯度更改此输入:

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

    运行 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

相关问题