【问题标题】:What's the difference between convolution in Keras vs Caffe?Keras 和 Caffe 中的卷积有什么区别?
【发布时间】:2019-02-16 20:50:04
【问题描述】:

我正在尝试将大型 Caffe 网络复制到 Keras(基于 tensorflow 后端)。但是即使在单个卷积层上我也遇到了很大的麻烦。

一般的简单卷积

假设我们有一个形状为 (1, 500, 500, 3) 的 4D 输入,我们必须使用 96 过滤器对这个输入执行单个卷积,内核大小为 114x4 步幅。

让我们设置权重和输入变量:

w = np.random.rand(11, 11, 3, 96)  # weights 1
b = np.random.rand(96)  # weights 2 (bias)

x = np.random.rand(500, 500, 3)

Keras 中的简单卷积

这就是它在 Keras 中的定义方式:

from keras.layers import Input
from keras.layers import Conv2D
import numpy as np

inp = Input(shape=(500, 500, 3))
conv1 = Conv2D(filters=96, kernel_size=11, strides=(4, 4), activation=keras.activations.relu, padding='valid')(inp)                                                            


model = keras.Model(inputs=[inp], outputs=conv1)
model.layers[1].set_weights([w, b])  # set weights for convolutional layer


predicted = model.predict([x.reshape(1, 500, 500, 3)])
print(predicted.reshape(1, 96, 123, 123))  # reshape keras output in the form of Caffe

Caffe 中的简单卷积

simple.prototxt:

name: "simple"
input: "inp"
input_shape {
  dim: 1
  dim: 3
  dim: 500
  dim: 500
}
layer {
  name: "conv1"
  type: "Convolution"
  bottom: "inp"
  top: "conv1"
  param {
    lr_mult: 1
    decay_mult: 1
  }
  param {
    lr_mult: 2
    decay_mult: 0
  }
  convolution_param {
    num_output: 96
    kernel_size: 11
    pad: 0
    stride: 4
  }
}
layer {
  name: "relu1"
  type: "ReLU"
  bottom: "conv1"
  top: "conv1"
}

Python 中的 Caffe:

import caffe

net = caffe.Net('simple.prototxt', caffe.TEST)
net.params['conv1'][0].data[...] = w.reshape(96, 3, 11, 11)  # set weights 1
net.params['conv1'][1].data[...] = b  # set weights 2 (bias)
net.blobs['inp'].reshape(1, 3, 500, 500) # reshape input layer to fit our input array x
print(net.forward(inp=x.reshape(1, 3, 500, 500)).get('conv1'))

问题

如果我们执行两个 sn-ps 代码,我们会注意到输出彼此不同。我知道Caffe的对称填充等差异很少,但我什至没有在这里使用填充。然而 Caffe 的输出与 Keras 的输出不同......

为什么会这样?我知道 Theano 后端没有像 Caffe 那样利用相关性,因此它需要内核旋转 180 度,但对于 tensorflow 是否相同?据我所知,Tensorflow 和 Caffe 都使用互相关而不是卷积。

如何在 Keras 和 Caffe 中制作两个使用卷积的相同模型?

任何帮助将不胜感激,谢谢!

【问题讨论】:

    标签: python keras conv-neural-network caffe convolution


    【解决方案1】:

    我发现了问题,但我不知道如何解决它......

    这两个卷积层之间的区别在于它们项目的对齐方式。此对齐问题仅在过滤器数量等于N 时才会出现,这样N > 1 && N > S 其中S 是过滤器的维度。换句话说,只有当我们从卷积中得到一个行数和列数都大于1的多维数组时,才会出现这样的问题。

    证据:

    为了看到这一点,我简化了输入和输出数据,以便我们可以更好地分析两个层的机制。

    simple.prototxt:

    input: "input"
    input_shape {
      dim: 1
      dim: 1
      dim: 2
      dim: 2
    }
    layer {
      name: "conv1"
      type: "Convolution"
      bottom: "input"
      top: "conv1"
      convolution_param {
        num_output: 2
        kernel_size: 1
        pad: 0
        stride: 1
      }
    }
    layer {
      name: "relu1"
      type: "ReLU"
      bottom: "conv1"
      top: "conv1"
    }
    

    simple.py:

    import keras
    import caffe
    import numpy as np
    from keras.layers import Input, Conv2D
    from keras.activations import relu
    from keras import Model
    
    filters = 2  # greater than 1 and ker_size
    ker_size = 1 
    
    _input = np.arange(2 * 2).reshape(2, 2)
    _weights = [np.reshape([[2 for _ in range(filters)] for _ in range(ker_size*ker_size)], (ker_size, ker_size, 1, filters)), np.reshape([0 for _ in range(filters)], (filters,))]  # weights for Keras, main weight is array of 2`s while bias weight is array of 0's
    _weights_caffe = [_weights[0].T, _weights[1].T]  # just transpose them for Caffe
    
    # Keras Setup
    
    keras_input = Input(shape=(2, 2, 1), dtype='float32')
    keras_conv = Conv2D(filters=filters, kernel_size=ker_size, strides=(1, 1), activation=relu, padding='valid')(keras_input)
    model = Model(inputs=[keras_input], outputs=keras_conv)
    model.layers[1].set_weights([_weights[0], _weights[1]])
    
    # Caffe Setup
    
    net = caffe.Net("simpler.prototxt", caffe.TEST)
    net.params['conv1'][0].data[...] = _weights_caffe[0]
    net.params['conv1'][1].data[...] = _weights_caffe[1]
    net.blobs['input'].data[...] = _input.reshape(1, 1, 2, 2)
    
    
    # Predictions
    
    
    print("Input:\n---")
    print(_input)
    print(_input.shape)
    print("\n")
    
    print("Caffe:\n---")
    print(net.forward()['conv1'])
    print(net.forward()['conv1'].shape)
    print("\n")
    
    print("Keras:\n---")
    print(model.predict([_input.reshape(1, 2, 2, 1)]))
    print(model.predict([_input.reshape(1, 2, 2, 1)]).shape)
    print("\n")
    

    输出

    Input:
    ---
    [[0 1]
     [2 3]]
    (2, 2)
    
    
    Caffe:
    ---
    [[[[0. 2.]
       [4. 6.]]
    
      [[0. 2.]
       [4. 6.]]]]
    (1, 2, 2, 2)
    
    
    Keras:
    ---
    [[[[0. 0.]
       [2. 2.]]
    
      [[4. 4.]
       [6. 6.]]]]
    (1, 2, 2, 2)
    

    分析

    如果您查看 Caffe 模型的输出,您会注意到我们的 2x2 数组首先加倍(因此我们有一个包含 2 个 2x2 数组的数组),然后对每个数组执行矩阵乘法两个带有我们权重矩阵的数组。像这样的:

    原创

    [[[[0. 2.]
       [4. 6.]]
    
      [[0. 2.]
       [4. 6.]]]]
    

    变身

    [[[[(0 * 2) (2 * 2)]
       [(4 * 2) (6 * 2)]]
    
      [[(0 * 2) (2 * 2)]
       [(4 * 2) (6 * 2)]]]]
    

    Tensorflow 做了一些不同的事情,它似乎首先 按升序对齐输出的 2D 向量,然后做了与 Caffe 相同的事情。这似乎是一种奇怪的行为,我无法理解他们为什么会这样做。

    解决方案:

    我已经回答了我自己关于问题的原因的问题,但我还不知道有什么干净的解决方案。我仍然觉得我的答案不够令人满意,因此我将接受具有实际解决方案的问题。

    我知道的唯一解决方案是创建自定义层,这对我来说不是一个很好的解决方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-16
      • 1970-01-01
      相关资源
      最近更新 更多