首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >分组卷积的Caffe变换

分组卷积的Caffe变换
EN

Stack Overflow用户
提问于 2019-02-09 20:55:43
回答 1查看 1.9K关注 0票数 4

我试图从一个非常简单的Caffe模型中获取权重,并将其解释为功能齐全的Keras模型。

这是Caffe中模型的最初定义,让我们称之为simple.prototxt

代码语言:javascript
复制
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 )将其传递到卷积层,然后max将其集合并传递给最终的卷积层。

下面是它在Keras中的实现,这要简单得多:

代码语言:javascript
复制
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)  

问题:

虽然这两种模型在每一层中似乎都有相似的参数,但问题是它们的重量形状并不相等。我知道Caffe和Keras有不同的形状顺序,但是顺序不是这里关注的问题。

问题是Keras的最后卷积层与Caffe中的最后卷积层在三维上具有不同的值。见下文。

Caffe的重量形状

代码语言:javascript
复制
>>> 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的重量形状:

代码语言:javascript
复制
>>> 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形状是相等的(忽略顺序)。但在咖啡豆中,conv2形状是[(256, 48, 5, 5), (256,)]),而在Keras中,conv2形状是[(5, 5, 96, 256), (256,)]注意到了48*2=96

另外,注意到,conv2层直接位于最大池层之后,所以在Keras中的最大池层可能有问题。

问题:

我是否正确地解释了从Caffe到Keras的模型定义?特别是最大池层及其参数?

非常感谢!

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-02-10 11:59:46

注意您的group: 2定义中的conv2字段。这意味着这里有一个成组卷积(卡菲:这群人是什么意思?)。从技术上讲,这意味着您有两个过滤器,每一个形状(128, 48, 5, 5)。第一个频道将与前48个频道合并,产生前128个输出,第二个通道用于其余的频道。然而,Caffe将两个重量存储在一个小块中,这就是为什么它的形状是(128x2, 48, 5, 5)的原因

在Keras Conv2D层中没有这类参数,但广泛采用的解决方法是将输入特征映射与Lambda层分开,用两个不同的卷积层处理它们,然后合并回一个特征映射。

代码语言:javascript
复制
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可能很棘手。要获得完全相同的结果,您必须遇到许多微妙的事情,如卷积中的不对称填充不同的最大池行为。这就是为什么如果您从Caffe导入权重,您可能无法用批规范替换LRN层。幸运的是,在Keras中有LRN的实现,例如这里

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54610665

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档