来自Caffe到分组卷积Keras转换

问题描述 投票:2回答:1

我试图从一个非常简单的模型,来自Caffe采取的权重,并把它解释功能齐全Keras模型。

这是来自Caffe模型的原始定义,姑且称之为simple.prototxt

input: "im_data"
input_shape {
  dim: 1
  dim: 3
  dim: 1280
  dim: 1280
}
layer {
  name: "conv1"
  type: "Convolution"
  bottom: "im_data"
  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: 5
    stride: 4
  }
}
layer {
  name: "relu1"
  type: "ReLU"
  bottom: "conv1"
  top: "conv1"
}
layer {
  name: "pool1"
  type: "Pooling"
  bottom: "conv1"
  top: "pool1"
  pooling_param {
    pool: MAX
    kernel_size: 3
    pad: 0
    stride: 2
  }
}
layer {
  name: "norm1"
  type: "LRN"
  bottom: "pool1"
  top: "norm1"
  lrn_param {
    local_size: 5
    alpha: 0.0001
    beta: 0.75
  }
}
layer {
  name: "conv2"
  type: "Convolution"
  bottom: "norm1"
  top: "conv2"
  param {
    lr_mult: 1
    decay_mult: 1
  }
  param {
    lr_mult: 2
    decay_mult: 0
  }
  convolution_param {
    num_output: 256
    kernel_size: 5
    pad: 2
    group: 2
  }
}
layer {
  name: "relu2"
  type: "ReLU"
  bottom: "conv2"
  top: "conv2"
}

在来自Caffe该层定义可能看起来复杂,但它只是需要1280x1280x3把它传递给卷积层,那么最大池,它并把它传递到最终卷积层尺寸的图像。

下面是它在Keras实现这是更简单:

from keras.models import Model
from keras.layers import Input, BatchNormalization, 
from keras.activations import relu, softmax

im_data = Input(shape=(1280, 1280, 3),
                   dtype='float32',
                   name='im_data')
conv1 = Conv2D(filters=96,
               kernel_size=11,
               strides=(4, 4),
               activation=relu,
               padding='same',
               name='conv1')(im_data)

pooling1 = MaxPooling2D(pool_size=(3, 3),
                        strides=(2, 2),
                        padding='same',
                        name='pooling1')(conv1)
normalized1 = BatchNormalization()(pooling1)  # https://stats.stackexchange.com/questions/145768/importance-of-local-response-normalization-in-cnn

conv2 = Conv2D(filters=256,
               kernel_size=5,
               activation=relu,
               padding='same',
               name='conv2')(normalized1)
model = Model(inputs=[im_data], outputs=conv2)  

Problem:

虽然这两种模式似乎在每个层参数类似,但问题是,他们的体重形状是不相等的。据我所知,来自Caffe具有Keras形状不同的顺序,但排序是不是在这里的关注。

问题是,Keras的最后卷积层具有相比于来自Caffe最后卷积层在第三维度不同的值。见下文。


重量型材来自Caffe:

>>> net = caffe.net('simple.prototxt', 'premade_weights.caffemodel', caffe.TEST)
>>> for i in range(len(net.layers)):
...     if len(net.layers[i].blobs) != 0:  # if layer has no weights
...         print(("name", net._layer_names[i]))
...         print("weight_shapes", [v.data.shape for v in net.layers[i].blobs])
('name', 'conv1')
('weight_shapes', [(96, 3, 11, 11), (96,)])
('name', 'conv2')
('weight_shapes', [(256, 48, 5, 5), (256,)])

重量型材Keras:

>>> for layer in model.layers:
...     if len(layer.get_weights()) != 0:
...         print(("name", layer.name))
...         print(("weight_shapes", [w.shape for w in layer.get_weights()]))  
('name', 'conv1')
('weight_shapes', [(11, 11, 3, 96), (96,)])
('name', 'conv2')
('weight_shapes', [(5, 5, 96, 256), (256,)])

这似乎是不可思议的行为。正如你看到的,在来自Caffe和Keras conv1形状是相等的(忽略顺序)。但在来自Caffe conv2形状[(256, 48, 5, 5), (256,)]),而在Keras“CONV 2”造型[(5, 5, 96, 256), (256,)],通知,即48*2=96

此外,请注意conv2层是最大池层后直接,所以可能会有一些错误的Keras最大汇聚层。


Question:

我有没有正确地从来自Caffe解释模型定义Keras?尤其是最大池层和它的参数?

非常感谢你!

python keras deep-learning caffe
1个回答
2
投票

group: 2定义注意conv2领域。这意味着你得到了一个grouped卷积那里(Caffe: What does the group param mean?)。从技术上讲,这意味着你有两个过滤器,每个形状(128, 48, 5, 5)的。第一个将与第一通道48和卷积产生第一128个输出,第二个是用于其余的。然而,来自Caffe存储在单个斑点的两个砝码,这就是为什么它的形状是(128x2, 48, 5, 5)

有在Keras Conv2D层没有这样PARAM,但广泛采用的解决方法是将输入的特征地图Lambda层,其中所述两个不同的卷积处理的层分裂他们然后合并回单特征地图。

from keras.layers import Concatenate

normalized1_1 = Lambda(lambda x: x[:, :, :, :48])(normalized1)
normalized1_2 = Lambda(lambda x: x[:, :, :, 48:])(normalized1)

conv2_1 = Conv2D(filters=128,
                 kernel_size=5,
                 activation=relu,
                 padding='same',
                 name='conv2_1')(normalized1_1)

conv2_2 = Conv2D(filters=128,
                 kernel_size=5,
                 activation=relu,
                 padding='same',
                 name='conv2_2')(normalized1_2)

conv2 = Concatenate(name='conv_2_merge')([conv2_1, conv2_2])

我没有检查是否正确的代码,但这个想法应该是这样的。

关于你的任务:从来自Caffe转换网络Keras可能会非常棘手。要获得绝对相同的结果,你必须遇到很多像盘旋或asymmetric padding different max-pooling behavior微妙的东西。这就是为什么,如果你从来自Caffe导入的权重,你可能无法替换batchnorm的LRN层。幸运的是,在Keras LRN的实现,例如here

© www.soinside.com 2019 - 2024. All rights reserved.