Tensorflow模型转caffe模型网上有很多教程,我参照这个https://blog.csdn.net/weixin_42699651/article/details/88932597,成功把例子vgg_16.ckpt转换为vgg16.caffemodel。
接下来我就要因地制宜了,尝试把MTCNN的tensorflow最终转换为caffe的模型。
以MTCNN的第一层网络P-Net为例:
tensorflow训练出的P-Net模型:
1、.meta文件保存了网络结构的定义
2、.data-00000-of-00001文件和.index文件合在一起组成了ckpt文件,保存了网络结构中所有的权重和偏执的数值。.data文件保存的是变量值,.index文件保存的是.data文件中的数据和.meta文件中结构图之间的映射关系
3、.checkpoint是一个文本文件,记录了训练过程中所有中间节点上保存的模型名称,首行记录的是最后一次保存的模型名称。
参考:https://blog.csdn.net/c20081052/article/details/82961988
P-Net的caffe网络文件:
新建一个文件夹,文件夹里分别存放以上五个文件,另加一个转换脚本tf2caffe.py,脚本代码如下:
import sys
import tensorflow as tf
import caffe
import numpy as np
import cv2
sess = tf.Session(config=tf.ConfigProto(allow_soft_placement=True))
from tensorflow.python import pywrap_tensorflow
checkpoint_path = "./pnet-3000000.index"
reader = pywrap_tensorflow.NewCheckpointReader(checkpoint_path)
var_to_shape_map = reader.get_variable_to_shape_map()
cf_prototxt = "./det1.prototxt"
cf_model = "./pnet.caffemodel"
def tensor4d_transform(tensor):
return tensor.transpose((3, 2, 0, 1))
def tensor2d_transform(tensor):
return tensor.transpose((1, 0))
def tf2caffe(checkpoint_path,cf_prototxt,cf_model):
net = caffe.Net(cf_prototxt, caffe.TRAIN)
for key_i in var_to_shape_map:
try:
if 'data' in key_i:
pass
elif 'weights' in key_i:
a = key_i.split('/')
if (len(a) == 3):
key_caffe = a[1]
else:
key_caffe = a[2]
if (reader.get_tensor(key_i).ndim == 4):
if (key_caffe == 'fc6'):
weights = tensor4d_transform(reader.get_tensor(key_i).reshape([7, 7, 512, 4096])).reshape(
[[7, 7, 512, 4096][3], -1])
elif key_caffe == 'fc7':
a = np.squeeze(reader.get_tensor(key_i)[0][0])
weights = tensor2d_transform(a) # 2dim
elif key_caffe == 'fc8':
a = np.squeeze(reader.get_tensor(key_i)[0][0])
weights = tensor2d_transform(a) # 2dim
else:
weights = tensor4d_transform(reader.get_tensor(key_i))
net.params[key_caffe][0].data.flat = weights.flat
elif 'biases' in key_i:
a = key_i.split('/')
if (len(a) == 3):
key_caffe = a[1]
else:
key_caffe = a[2]
net.params[key_caffe][1].data.flat = reader.get_tensor(key_i).flat
elif 'mean_rgb' in key_i:
pass
elif 'global' in key_i:
pass
else:
sys.exit("Warning! Unknown tf:{}".format(key_i))
except KeyError:
print("\nWarning! key error tf:{}".format(key_i))
net.save(cf_model)
print("\n- Finished.\n")
if __name__ == '__main__':
tf2caffe(checkpoint_path, cf_prototxt, cf_model)
重点:
checkpoint_path = "./vgg_16.ckpt"
然而我并没有.ckpt文件,所以我改为:
checkpoint_path = "./pnet-3000000"
因为不同的网络层名,维度都不一样,所以tf2caffe.py
需要根据自己要转换的网络进行修改,注意key_caffe和key_i
是否要修改,建议把tf2caffe.py
的脚本搞懂再进行修改,可以参考我特地为tf2caffe.py写的解析博文。
转换完成后,文件夹里多了一个p-net.caffemodel
文件,如下图:
新建一个文件夹,存放上一步转换完成的caffemodel和已知的.prototxt文件
依次执行命令:
1、~/caffe/build/tools/upgrade_net_proto_text det1.prototxt new_det1.prototxt
2、~/caffe/build/tools/upgrade_net_proto_binary det1.caffemodel new_det1.caffemodel
3、~/liq/ncnn/build/tools/caffe/caffe2ncnn new_det1.prototxt new_det1.caffemodel det1.param det1.bin
成功转换将得到
det1.param 和 det1.bin
再详细的步骤可以参考我的另一篇博文caffe转ncnn
正文就到这里结束了,下面是后续
把基于ncnn框架的mtcnn的工程,测试图片的模型加载更换成自己的,好消息是:模型可以被成功加载,并没有报错;坏消息是加载的模型效果很差,并没有检测出人脸。
模型更换前后对比:
找原因,发现
det1.param文件和原作者给的是一模一样的,更换后,可以成功加载人脸。所以问题出在det1.bin身上。博主想查看一下具体的bin文件到底哪里不一样,导致没有读出人脸
将det1.bin文件转化为.txt文件,代码:
import struct
import os
if __name__ == '__main__':
filepath='C:\\Users\\lhh\\Desktop\\det\\det1_2.bin'
f = open("C:\\Users\\lhh\\Desktop\\det\\det1_2.txt", "w")
binfile = open(filepath, 'rb') #打开二进制文件
size = os.path.getsize(filepath) #获得文件大小
for i in range(size):
data = binfile.read(10) #每次输出一个字节
f.write(str(data))
f.write('\n')
#print(data)
binfile.close()
发现果然二者不一样,但也看不出来是什么原因:
那转化为10进制再看看呢?代码如下:
import struct
import os
if __name__ == '__main__':
filepath='C:\\Users\\lhh\\Desktop\\det\\det1_1.bin'
f = open("C:\\Users\\lhh\\Desktop\\det\\det1_1.txt", "w")
binfile = open(filepath, 'rb') #打开二进制文件
size = os.path.getsize(filepath) #获得文件大小
for i in range(size):
data = binfile.read(1) #每次输出一个字节
num = struct.unpack('B', data)
f.write(str(num[0]))
f.write('\n')
#print(data)
binfile.close()
将ncnn原始工程给的caffemodel经过caffe转ncnn之后,得到的结果和原工程提供的.bin和.param是一模一样的,所以转换的第二步没有问题。
关键是第一步:TensorFlow转caffe:
情况1:转换步骤tf2caffe.py不对
情况2: 转换源文件pnet-3000000.index,pnet-300000.meta,pnet-3000000.data-00000-of-00001本身效果就不好
到底是情况1还是情况2,博主最终也不知道。