keras版本(.h5文件)转化为caffe版本(caffemodel和prototxt文件)的总结和思考

本文详细介绍了如何将使用Keras训练的深度学习模型转换为Caffe模型,涉及关键步骤包括:保证层名称唯一性、输入输出名称连贯、数据格式匹配。通过遍历Keras模型的每一层,提取参数和名称,并创建对应的Caffe网络框架。对于不同的层如InputLayer和Conv2D,分别给出了转换策略。最后,生成prototxt文件和参数文件,实现模型的完整转换。此外,还讨论了Keras和Caffe在算子上的异同以及如何处理网络子模型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

版本转化核心

keras训练的模型转化为caffe模型的核心思想是拆分keras的每层,之后赋值给caffe版本对应层,这其中保证以下:

  • 每层的名称的唯一性;
  • 每层输入名称和输出名称上下连贯性;
  • 每层数据的格式符合caffe模型;

keras版本加载

import keras
file = '../xxx.h5'
keras_model = keras.models.load_model(file, custom_objects=backbone.custom_objects)

keras模型每层的参数和名称的提取

for layer in keras_model.layers:
	# 获取该层的算子类型
	layer_type = type(layer)
	# 该层的名称
	name = layer.name
	# 该层的参数
	config = layer.get_config()
	# 获取该层的权重和偏置
	blobs = layer.get_weights()
	blobs_num = len(blobs)
	# 获取该层的输入名称
	bottom = layer.input.name
	# 获取该层的输出名称
	top = layer.output.name

caffe网络框架的创建

import caffe
# NetSpec 是包含Tops(可以直接赋值作为属性)的集合。
# 调用 NetSpec.to_proto 创建包含所有层(layers)的网络参数,这些层(layers)需要被赋值,
# 并使用被赋值的名字。
# 就是利用NetSpec可以构建caffe模型的网络框架,再利用.to_proto()就可以生成网络框架文件.prototxt。
# 之后在(netscope)(http://ethereon.github.io/netscope/#/editor)在线网站就可以可视化网络了。
caffe_net = caffe.NetSpec()

caffe每层的参数和名称的赋予

算子的参数可参考网址:官方文档,可以查询不同算子的参数。
这里列举常用的算子:

outputs = dict()
if layer_type = keras.layer.InputLayer:
	# 获取输入的形状
	input_shape = config['batch_input_shape']
	# 将(N,h,w,c)更改成(N,c,h,w)
    input_shape = [1, input_shape[3], input_shape[1], input_shape[2]]
    # 将输入算子添加到caffe网络中,其方式类似于字典,其中name为该层的名称,作为keys,算子作为values。
    caffe_net[name] = L.Input(shape=[dict(dim=input_shape)])
    outputs[top] = name 

对于Conv2D这个算子,需要考虑padding的方式。因为keras和caffe表示方法不同。
keras有两种’same’和’valid’:
“same”:
对于常用的参数如下

  • kernel_size = 3, strides = 2, 对于这样的参数,则如果输入为奇数,会出现pad只能进行单边补齐,在tesnorflow中,一般是右下,但是在caffe中,没有对应的pad单边参数,所以设计参数时尽量避免这种设计。否者转换比较困难。可以将kernel_size=2, strides=2,这样可以满足条件,但是感受野小了些。
  • kernel_size = 3/2, strides = 1, 则pad=1。
    ‘valid’:
    直接pad=0, 即可。
    caffe中表示padding的方式是直接赋值,参考上述网址,可知Conv2D层如下:
  layer {
    name: "conv1"
    type: "Convolution"
    bottom: "data"
    top: "conv1"
    # learning rate and decay multipliers for the filters
    param { lr_mult: 1 decay_mult: 1 }
    # learning rate and decay multipliers for the biases
    param { lr_mult: 2 decay_mult: 0 }
    convolution_param {
      num_output: 96     # learn 96 filters
      kernel_size: 11    # each filter is 11x11
      stride: 4          # step 4 pixels between each filter application
      weight_filler {
        type: "gaussian" # initialize the filters from a Gaussian
        std: 0.01        # distribution with stdev 0.01 (default mean: 0)
      }
      bias_filler {
        type: "constant" # initialize the biases to zero (0)
        value: 0
      }
    }
  }
Parameters (ConvolutionParameter convolution_param)
Required
num_output (c_o): the number of filters
kernel_size (or kernel_h and kernel_w): specifies height and width of each filter
Strongly Recommended
weight_filler [default type: 'constant' value: 0]
Optional
bias_term [default true]: specifies whether to learn and apply a set of additive biases to the filter outputs
pad (or pad_h and pad_w) [default 0]: specifies the number of pixels to (implicitly) add to each side of the input
stride (or stride_h and stride_w) [default 1]: specifies the intervals at which to apply the filters to the input
group (g) [default 1]: If g > 1, we restrict the connectivity of each filter to a subset of the input. Specifically, the input and output channels are separated into g groups, and the ith output group channels will be only connected to the ith input group channels.

我们重点关注参数

if layer_type = keras.layers.Conv2D:
	
	

prototxt文件的生成

with open(caffe_net_file, 'w') as fpb:
    print(caffe_net.to_proto(), file=fpb)

caffe参数的生成

# 构建字典,保存参数
blobs[0] = np.array(blobs[0]).transpose(3, 2, 0, 1)
net_params[name] = blobs
# 生成参数文件
caffe_model = caffe.Net(caffe_net_file, caffe.TEST)
for layer in caffe_model.params.keys():
    for n in range(0, len(caffe_model.params[layer])):
        caffe_model.params[layer][n].data[...] = net_params[layer][n]
caffe_model.save(caffe_params_file)

caffe可转换算子

- InputLayer
- Slice
- Dense
- Dropout
- ZeroPadding2D
- Multiply、Concatenate、Maximum、Add
- Conv2D、Conv2DTranspose
- BatchNormalization
- MaxPooling2D、AveragePooling2D、GlobalAveragePooling2D
- relu、prelu、elu、softmax、sigmoid、tanh

keras和caffe在算子方面的异同

对于keras.layer.Lambda来拆分输入,可以使用caffe的slice替代。

网络重用,如何定义caffe自网络

如果网络添加了子网络,单独提取子网络。这里利用到keras中layer的属性。记住关键就是识别子网络的名称构成,子网络名称/layer名称/算子。之后再将每层名称和参数添加到对应的字典里。就可以了。

submodel_names = ['a'] + [submodel_name] + [submodel_name + '_'+ str(i) for i in range(1,5)]
            for i, node in enumerate(submodel._inbound_nodes):  # get inbound nodes to current layer
                input_layers = node.inbound_layers  # get layers pointing to this node
                if input_layers:
                    if type(layer._inbound_nodes[0].inbound_layers[0]).__name__ == "InputLayer":
                        bottom = input_layers[0].output.name
                        top_cp = submodel_names[i] + '/' + top
                        name_cp = submodel_names[i] + '/' + name
                        caffe_net[name_cp] = L.Convolution(caffe_net[outputs[bottom]], **kwargs)

                    else:
                        bottom_cp = submodel_names[i] + '/' + bottom
                        name_cp = submodel_names[i] + '/' + name
                        top_cp = submodel_names[i] + '/' + top
                        caffe_net[name_cp] = L.Convolution(caffe_net[outputs[bottom_cp]], **kwargs)

                    blobs[0] = np.array(blobs[0]).transpose(3, 2, 0, 1)
                    net_params[name_cp] = blobs
                    outputs[top_cp] = name_cp
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值