keras保存的为.h5模型,而tensorflow中模型压缩需要的是.pb的二进制文件。因此,首先需要将h5文件转换为pb文件。
from keras.models import load_model
import tensorflow as tf
import os
import os.path as osp
from keras import backend as K
#路径参数
input_path = '/home/jtl/keras-yolo3-master/h5_to_pb/'
weight_file = 'transfer_multi_4cl_1207.h5'
weight_file_path = osp.join(input_path,weight_file)
output_graph_name = weight_file[:-3] + '.pb'
#转换函数
def h5_to_pb(h5_model,output_dir,model_name,out_prefix = "output_",log_tensorboard = False):
if osp.exists(output_dir) == False:
os.mkdir(output_dir)
out_nodes = []
for i in range(len(h5_model.outputs)):
out_nodes.append(out_prefix + str(i + 1))
tf.identity(h5_model.output[i],out_prefix + str(i + 1))
sess = K.get_session()
from tensorflow.python.framework import graph_util,graph_io
init_graph = sess.graph.as_graph_def()
main_graph = graph_util.convert_variables_to_constants(sess,init_graph,out_nodes)
graph_io.write_graph(main_graph,output_dir,name = model_name,as_text = False)
if log_tensorboard:
from tensorflow.python.tools import import_pb_to_tensorboard
import_pb_to_tensorboard.import_to_tensorboard(osp.join(output_dir,model_name),output_dir)
#输出路径
output_dir = osp.join(os.getcwd(),"trans_model")
#加载模型
h5_model = load_model(weight_file_path)
h5_to_pb(h5_model,output_dir = output_dir,model_name = output_graph_name)
print('model saved')
得到列.pb模型后,想知道.pb模型是否能正常工作
# -*- coding:utf-8 -*-
import argparse
import tensorflow as tf
import cv2
import numpy as np
def load_graph(frozen_graph_filename):
# We parse the graph_def file
with tf.gfile.GFile(frozen_graph_filename, "rb") as f:
graph_def = tf.GraphDef()
graph_def.ParseFromString(f.read())
# We load the graph_def in the default graph
with tf.Graph().as_default() as graph:
tf.import_graph_def(
graph_def,
input_map=None,
return_elements=None,
name="prefix",
op_dict=None,
producer_op_list=None
)
return graph
def preprocess_input(image, net_h, net_w):
new_h, new_w, _ = image.shape
# determine the new size of the image
if (float(net_w)/new_w) < (float(net_h)/new_h):
new_h = (new_h * net_w)//new_w
new_w = net_w
else:
new_w = (new_w * net_h)//new_h
new_h = net_h
# resize the image to the new size
resized = cv2.resize(image[:,:,::-1]/255., (new_w, new_h))
# embed the image into the standard letter box
new_image = np.ones((net_h, net_w, 3)) * 0.5
new_image[(net_h-new_h)//2:(net_h+new_h)//2, (net_w-new_w)//2:(net_w+new_w)//2, :] = resized
new_image = np.expand_dims(new_image, 0)
return new_image
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("--frozen_model_filename", default="trans_model/transfer_multi_4cl_1207.pb", type=str,
help="Frozen model file to import")
args = parser.parse_args()
# 加载已经将参数固化后的图
graph = load_graph(args.frozen_model_filename)
# We can list operations
# op.values() gives you a list of tensors it produces
# op.name gives you the name
# 输入,输出结点也是operation,所以,我们可以得到operation的名字
for op in graph.get_operations():
print(op.name, op.values())
#yolo3的模型
# prefix/input_1 (,)
#prefix/conv_0/kernel (,)
# ...
# prefix/output_1 (,)
# prefix/output_2 (,)
# prefix/output_3 (,)
# 操作有:prefix/input_1
# 操作有:prefix/output_1
# 为了预测,我们需要找到我们需要feed的tensor,那么就需要该tensor的名字
# 注意prefix/input_1仅仅是操作的名字,prefix/input_1:0才是tensor的名字
x = graph.get_tensor_by_name('prefix/input_1:0')
# yolo3实际上有三个输出,我们这边只测试列一个输出
y = graph.get_tensor_by_name('prefix/output_1:0')
#载入图片和调整
image = cv2.imread('157.jpg')
batch_input = np.zeros((1, 416 , 416, 3))
batch_input[0] = preprocess_input(image, 416, 416)
with tf.Session(graph=graph) as sess:
y_out = sess.run(y, feed_dict={
x: batch_input
})
print(y_out) # [[ 0.]] Yay!
print("finish")
可以看到输出为一系列的anchor。
-----------------------------------------------------------------------------------------------------------------------------------
安装完bazel后,进入tensorflow目录下:
bazel build tensorflow/tools/graph_transforms:transform_graph
本来还要运行下面这行,但是总告诉我找不到目录,sudo bazel 也不行,暂时把这步跳过。
bazel build tensorflow/tools/quantization:quantize_graph
若不知道输入输出,可以通过 bazel build tensorflow / tools / graph_transforms:summarize_graph产生summarize_graph,然后再通过bazel-bin来运行。具体例子如下:
bazel-bin/tensorflow/tools/graph_transforms/summarize_graph --in_graph='/home/jtl/kerasyolo3/m/transfer_multi_4cl_1207.pb'
可以看到输入为input_1,输出为output_1,output_2,output_3.
根据教程https://github.com/tensorflow/tensorflow/tree/master/tensorflow/tools/graph_transforms,例如调用图形转换工具命令:
bazel-bin/tensorflow/tools/graph_transforms/transform_graph --in_graph='/home/jtl/kerasyolo3/m/transfer_multi_4cl_1207.pb' --out_graph='/home/jtl/kerasyolo3/m/transfer_multi_4cl_1207-1.pb' --inputs='input_1:0' --outputs='output_1:0','output_2:0','output_3:0' --transforms='
strip_unused_nodes(type=float, shape="1,416,416,3")
remove_nodes(op=Identity, op=CheckNumerics)
fold_old_batch_norms
'
按照tensorflow中graph transforms说明中运行官方的模型没有问题 ,但是运行我的yolo3模型有问题。1.remove node貌似会把残差层都跳过,最后保存一个2k的文件。2.fold_old_batch_norms对BN层优化会报错。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
只有使用quantize,将浮点数转变为8位bit后保存的pb文件才能被运行。其它的优化就算保存了pb文件运行起来也会出现错误。
bazel-bin/tensorflow/tools/graph_transforms/transform_graph --in_graph='/home/jtl/kerasyolo3/m/transfer_multi_4cl_1207.pb' --out_graph='/home/jtl/kerasyolo3/m/transfer_multi_4cl_1207-quantize.pb' --inputs='input_1:0' --outputs='output_1:0','output_2:0','output_3:0' --transforms='
quantize_weights'
32位变成8位后,文件大小变成原来的1/4。
下一步在tensorflow下运行pb文件,进行目标检测,发现权重削减后的模型居然比不削减的pb文件运行起来慢25%左右,貌似意思是tensorflow在运行是会把8bit 转为浮点数。