分组卷积的 Caffe 到 Keras 转换
Caffe to Keras conversion of grouped convolution
我正在尝试从一个非常简单的 Caffe 模型中获取权重并将其解释为功能齐全的 Keras 模型。
这是Caffe中model的原始定义,姑且称之为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)
问题:
虽然这两个模型在每一层似乎都有相似的参数,但问题是它们的权重形状不相等。我知道 Caffe 的形状顺序与 Keras 不同,但顺序不是这里的问题。
问题是 Keras 的最后一个卷积层与 Caffe 中的最后一个卷积层相比在第 3 维具有不同的值。见下文。
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 中 'conv2' 形状是 [(5, 5, 96, 256), (256,)]
,notice,即 48*2=96
.
此外,注意 conv2
层直接在最大池化层之后,因此 Keras 中的最大池化层可能有问题。
问题:
我是否正确地解释了从 Caffe 到 Keras 的模型定义?特别是最大池化层及其参数?
非常感谢!
注意 conv2
定义中的 group: 2
字段。这意味着你得到了 grouped convolution there ()。从技术上讲,这意味着您有 两个 个过滤器,每个过滤器的形状为 (128, 48, 5, 5)
。第一个将与前 48 个通道卷积并产生前 128 个输出,第二个用于剩余的输出。但是,Caffe 将两个权重存储在一个 blob 中,这就是为什么它的形状是 (128x2, 48, 5, 5)
Keras 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 可能很棘手。要得到绝对一样的结果,肯定会遇到很多像 in convolutions or . That is why if you import the weights from Caffe, you probably cannot replace the LRN layer with batchnorm. Fortunately, there are implementations of LRN in Keras, for instance here.
这样微妙的事情
我正在尝试从一个非常简单的 Caffe 模型中获取权重并将其解释为功能齐全的 Keras 模型。
这是Caffe中model的原始定义,姑且称之为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)
问题:
虽然这两个模型在每一层似乎都有相似的参数,但问题是它们的权重形状不相等。我知道 Caffe 的形状顺序与 Keras 不同,但顺序不是这里的问题。
问题是 Keras 的最后一个卷积层与 Caffe 中的最后一个卷积层相比在第 3 维具有不同的值。见下文。
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 中 'conv2' 形状是 [(5, 5, 96, 256), (256,)]
,notice,即 48*2=96
.
此外,注意 conv2
层直接在最大池化层之后,因此 Keras 中的最大池化层可能有问题。
问题:
我是否正确地解释了从 Caffe 到 Keras 的模型定义?特别是最大池化层及其参数?
非常感谢!
注意 conv2
定义中的 group: 2
字段。这意味着你得到了 grouped convolution there ((128, 48, 5, 5)
。第一个将与前 48 个通道卷积并产生前 128 个输出,第二个用于剩余的输出。但是,Caffe 将两个权重存储在一个 blob 中,这就是为什么它的形状是 (128x2, 48, 5, 5)
Keras 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 可能很棘手。要得到绝对一样的结果,肯定会遇到很多像