目标检测yolo3模型压缩

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 转为浮点数。

你可能感兴趣的:(人工智障)