代码之家  ›  专栏  ›  技术社区  ›  ShellRox

分组卷积的Caffe到Keras转换

  •  2
  • ShellRox  · 技术社区  · 6 年前

    我试图从一个非常简单的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 将其传递到卷积层,然后max将其聚集并传递到最终的卷积层。

    这是它在 克拉斯 更简单的是:

    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不同,但这里不关心订购问题。

    问题是,与CAFFE中的最后一个卷积层相比,KERS中的最后一个卷积层在三维上具有不同的值。见下文。


    重量形状 卡费 :

    >>> 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,)])
    

    重量形状 克拉斯 :

    >>> 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,)])
    

    这似乎是一种奇怪的行为。如你所见, conv1 caffe和keras的形状相等(忽略顺序)。但在Caffe conv2 形状是 [(256, 48, 5, 5), (256,)]) 而在角膜“conv2”形状是 [(5, 5, 96, 256), (256,)] , 通知 ,那 48*2=96 .

    也, 通知 那个 二维卷积 层直接位于最大池层之后,因此KERS中的最大池层可能有问题。


    问题:

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

    非常感谢你!

    1 回复  |  直到 6 年前
        1
  •  1
  •   Dmytro Prylipko    6 年前

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

    喀拉斯没有这样的参数 Conv2D 层,但广泛采用的解决方法是将输入特征图 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 .

    推荐文章