tensorflow的模型到底怎么转成caffemodel?

1. 简介

一般情况下,LZ是使用tensorflow的,好像之前在学校只要python跑通,benchmark再刷高一点就行了。现在在公司,要把自己的模型进行移植,之前是后端的移植,最近前端也提了需求,前端一般都是用海思芯片,只支持caffe,所以为了先测试时间得把tf的模型转成caffemodel。

程序猿总有不会的,总有要学习的不是嘛!

然后在网上搜索了很久,发现有些教程实在有些复杂,今天就以一个简单的网络结构为例,进行记录。说明这个网络中只包含卷积层和全连接层,后续碰到其他层会继续补充。

2. tensorflow模型准备

转换模型的第一步是不是得先准备个tensorflow的模型,没模型可以先随便训练个,有点参数和输出结果就行。经过训练后得到了对应的ckpt模型,因为后续要使用pycaffe搭建对应的网络,所以我们还得先查看下模型的名称

import numpy as np
import tensorflow as tf
#输出变量名和对应的参数
with tf.Session() as sess:
    saver = tf.compat.v1.train.import_meta_graph('../models/model.ckpt.meta')
    saver.restore(sess, tf.train.latest_checkpoint('../models/'))
    for var in tf.compat.v1.trainable_variables():
        print("*************************************")
        print(var.name)
#         print(np.array(sess.run(var)))

这段代码的目的就是为了看下保存的模型名称,当然如果把最后一行反注释,可以看到对应训练得到的weights和biases,所以对于一个公司来说,模型是非常重要的,在后端实际场景使用中还会转成pb模型并进行加密,这样即使对方得到了你的pb文件,也没法反解出对应的参数,但是如果是ckpt,可以看到查出网络结构和参数非常容易。

3. 生成对应的prototxt文件

之前因为不熟悉,都是单纯手动写prototxt,实在是太傻了,pycaffe可以轻松解决这个问题。

先写出主要的框架

import caffe

# 定义你的caffe网络结构
def deploy():
	# 定义你的输入数据
    data_img = caffe.layers.Input(shape=dict(dim=[1, 3, 102, 102]))
    
    # 定义你的网络结构
    # 网络结构要和你的tf模型完全一致,为了后续load参数方便,最好每层名字也对应一致
	.
	.
	.
   
    return caffe.to_proto(out)

# 定义一个函数,将网络结构写入到对应的prototxt中
def write_deploy():
    with open('your_model.prototxt', 'w') as f:
        f.write('name:"model_name"\n')
        f.write(str(deploy()))

if __name__ == '__main__':
    write_deploy()

4. 读取对应参数值,并生成对应的caffemodel

这个时候既需要tensorflow的接口,又需要caffe的接口了,生成prototxt的时候,我们已经获得了对应的网络结构了,下面只需要将对应的参数保存到caffe的params中就可以了

# load tf model_parameters
import tensorflow as tf
import numpy as np
import caffe

def generate_caffe_model():
    cf_prototxt = "your_model.prototxt"
    cf_model = "your_model.caffemodel"
    # 定义自己的net
    net = caffe.Net(cf_prototxt, caffe.TEST) 
    with tf.Session() as sess:
    # 导入tf的网络结构和变量名称
        saver = tf.train.import_meta_graph("../models/model.ckpt.meta", 
                                           clear_devices=True)
        saver.restore(sess, tf.train.latest_checkpoint("../models/"))
        all_vars = tf.trainable_variables()
        # 针对不同变量,存储对应的参数
        for var in all_vars:
            name = var.name
            v_param = np.array(sess.run(var))
            print("*********************************")
#             print(name, v_param.ndim, v_param.shape)
            if 'weights' in name:
                layer_name = name[:-10]#.strip("/weights:0")
                print(layer_name)
                if v_param.ndim == 4:
                    v_4d = np.transpose(v_param, [3, 2, 0, 1])
                    net.params[layer_name][0].data[...] = v_4d
                    print("conv weights {} succefully".format(layer_name))
                else:
                    v_2d = np.transpose(v_param, [1, 0])
                    net.params[layer_name][0].data[...] = v_2d
                    print("fc weights {} succefully".format(layer_name))
                    
            elif 'biases' in name:
                layer_name = name[:-9] #.strip("/biases:0")
                net.params[layer_name][1].data[...] = v_param
                print("{} biases succefully".format(layer_name))
            else:
                print("please check if something wrong !")
                continue
        # 保存caffemodel        
        net.save(cf_model)
        print("\n -- Finished. -- \n")

转换的时候其实有好几个注意点。

a. 卷积

  • tensorflow卷积参数的存储方式是:
    (kernel_h, kernel_w, input_channel, output_channel)

  • caffe卷积参数的存储方式是:
    (output_channel, input_channel, kernel_h, kernel_w)

  • 卷积的存储权重和偏差的位置:
    权重存储的位置为:conv_params[conv][0]
    偏差存储的位置为:conv_params[conv][1]

b. 全连接

  • tensorflow全连接参数的存储方式是:
    (input_dim, output_dim)
  • caffe全连接参数存储方式是:
    (output_dim, input_dim)
  • 全连接的存储权重和偏差的位置:
    权重存储的位置为:conv_params[fc][0]
    偏差存储的位置为:conv_params[fc][1]

5. 验证模型转换是否成功

这时就需要使用tensorflow模型和caffe模型inference后的结果对比,caffe模型做deploy的代码框架:

# inference part
import caffe
import cv2

# some preprocess operation
def preprocess(img):
   #定义一些预处理操作,尽量和tensorflow的预处理方式保持一致
    return img
    
def deploy_part():
    net_file = "your_model.prototxt"
    caffe_model = "your_model.caffemodel"
    test_dir = "test.bmp"
    # 定义对应的网络
    net = caffe.Net(net_file, caffe_model, caffe.TEST)
    img = cv2.imread(test_dir)
    img = preprocess(img)
    img = img.transpose((2, 0, 1))
    
    net.blobs['Input1'].data[...] = img
    out = net.forward()
    print(out)

if __name__ == '__main__':
    deploy_part()

这里还有一个问题需要注意的是tensorflow的数据格式一般都是(N, H,W, C)的方式,而在caffe中是(N, C, H, W),opencv读入图片的格式为(H, W, C),所以在传入数据时需要使用transpose进行转换。

这个tensorflow inference后得到的结果:
在这里插入图片描述
这个是caffe deploy后得到的结果:
在这里插入图片描述
从上面两个图中可以看到两者结果的差异体现在小数点后第8位,并且这是最后数据的结果,基本上结果差异非常的小。

对于tensorflow转caffemodel,不知道小伙伴们有没有清楚一些呢?

Logo

海思 HiSpark 开发者社区是上海海思为开发者搭建的内容和交流平台,依托完备芯片解决方案、专业开发环境、开源/开放的软件资源与社区技术内容,构建完整服务生态开发者的闭环体系。

更多推荐