tensorflow2.x中的h5转换为onnx以及onnx的x86端测评

tensorflow2.x中的h5转换为onnx

在日常使用中经常需要做模型转换,那tensorflow2.x的h5模型转onnx是必要的知识,如下代码实现了该转换,可以设置输入张量的形状,也可以选择opset。

# 读取h5模型转换为onnx模型,教程来自于https://github.com/onnx/tensorflow-onnx/blob/master/tutorials/keras-resnet50.ipynb
import tensorflow as tf
import numpy as np
import cv2
import tf2onnx
import onnxruntime as rt

######################################################################################################################
# 图片预处理,对输入图片进行等比例拉伸至指定尺寸,不足的地方填0,同时把像素值归一化到[-1, 1]
def image_preprocess(image, target_length, value=0.0, method=0):
    image = image.astype("float32")
    h, w, _ = image.shape                               # 获得原始尺寸
    ih, iw  = target_length, target_length              # 获得目标尺寸
    scale = min(iw/w, ih/h)                             # 实际拉伸比例
    nw, nh  = int(scale * w), int(scale * h)            # 实际拉伸后的尺寸
    image_resized = cv2.resize(image, (nw, nh))         # 实际拉伸图片
    image_paded = np.full(shape=[ih, iw, 3], fill_value=value)
    dw, dh = (iw - nw) // 2, (ih-nh) // 2
    image_paded[dh:nh+dh, dw:nw+dw, :] = image_resized  # 居中填充图片
    if method == 0:
        image_paded = image_paded / 255.                # 图片归一化
    elif method == 1:
        image_paded = image_paded / 127.5 - 1.0         # 图片标准化
    return image_paded

# 读取图片并预处理
image_path = "car.jpg"  # 图片路径
image = cv2.cvtColor(cv2.imread(image_path), cv2.COLOR_BGR2RGB).astype("float32")  # 读取图片
image = image_preprocess(image, 128, 0, 1)                                         # 图片预处理
image = np.expand_dims(image, axis=0).astype(np.float32)                           # 图片维度扩展

######################################################################################################################
# 读取h5模型
model = tf.keras.models.load_model("/xxx.h5")

# 推理h5模型
preds = model.predict(image)

# 保存h5模型为tf的save_model格式
# model.save("./" + model.name))


######################################################################################################################
# 定义模型转onnx的参数
spec = (tf.TensorSpec((None, 128, 128, 3), tf.float32, name="input"),)  # 输入签名参数,(None, 128, 128, 3)决定输入的size
output_path = model.name + "_13.onnx"                                   # 输出路径

# 转换并保存onnx模型,opset决定选用的算子集合
model_proto, _ = tf2onnx.convert.from_keras(model, input_signature=spec, opset=13, output_path=output_path)
output_names = [n.name for n in model_proto.graph.output]
print(output_names)  # 查看输出名称,后面推理用的到



######################################################################################################################
# 读取onnx模型,安装GPUonnx,并设置providers = ['GPUExecutionProvider'],可以实现GPU运行onnx
providers = ['CPUExecutionProvider']
m = rt.InferenceSession(output_path, providers=providers)

# 推理onnx模型
output_names = output_names
onnx_pred = m.run(output_names, {"input": image})

# 对比两种模型的推理结果
print('Keras Predicted:', preds)
print('ONNX Predicted:', onnx_pred[0])

# make sure ONNX and keras have the same results
np.testing.assert_allclose(preds, onnx_pred[0], rtol=1e-4)

onnx的x86端测评---单进程

针对onnx的x86端测评,该测评如果在GPU上进行,那单个进程就完全够用了,如果想要在GPU上运行需要:1. 安装GPU版本的onnxruntime;2. 代码选择GPU执行。如下代码是单进程版本的CPU上测评代码,如果要改为GPU版本只需要修改providers = ['CPUExecutionProvider']即可。

# 计算onnx模型的acc和混淆矩阵
import numpy as np
from tqdm import tqdm
import os
import cv2
import onnxruntime as rt

def image_preprocess(image, target_length, value=0.0, method=0):
    image = image.astype("float32")
    h, w, _ = image.shape                               # 获得原始尺寸
    ih, iw  = target_length, target_length              # 获得目标尺寸
    scale = min(iw/w, ih/h)                             # 实际拉伸比例
    nw, nh  = int(scale * w), int(scale * h)            # 实际拉伸后的尺寸
    image_resized = cv2.resize(image, (nw, nh))         # 实际拉伸图片
    image_paded = np.full(shape=[ih, iw, 3], fill_value=value)
    dw, dh = (iw - nw) // 2, (ih-nh) // 2
    image_paded[dh:nh+dh, dw:nw+dw, :] = image_resized  # 居中填充图片
    if method == 0:
        image_paded = image_paded / 255.                # 图片归一化
    elif method == 1:
        image_paded = image_paded / 127.5 - 1.0         # 图片标准化
    return image_paded

# 读取onnx模型
onnx_path = "./MobileNetV2.onnx"
output_names = ['Logits']   # 对应MobileNetV2.onnx进行修改,是生成模型时定义的名字
providers = ['CPUExecutionProvider']
model = rt.InferenceSession(onnx_path, providers=providers)

# 遍历识别每一幅图片并统计结果
path = "./test/"
label_name = ["label_1", "label_2", "label_3", "label_4", "label_5", "label_6", "label_7"]  # 必须跟tflite模型的标签顺序匹配
num_prs = [0.000000001 for _ in range(len(label_name))]
num_rec = [0.000000001 for _ in range(len(label_name))]
num_mat = [[0] * len(label_name) for _ in range(len(label_name))]
label_true, label_pred = [], []
for i in range(len(label_name)):                                      # 遍历每一个子文件夹
    label_path = path + label_name[i] + "/"
    image_list = os.listdir(label_path)
    for j in tqdm(range(len(image_list)), desc='Processing %d' % i):  # 遍历某文件夹下的所有图片
        image_path = label_path + image_list[j]
        image = cv2.cvtColor(cv2.imread(image_path), cv2.COLOR_BGR2RGB)
        image = image_preprocess(image, 128, 0, 1)
        image = np.expand_dims(image, axis=0).astype("float32")
        output = model.run(output_names, {"input": image})[0].tolist()
        label_id = np.argmax(output[0])
        pred_prob = output[0][label_id]
        num_prs[int(label_id)] += 1
        num_rec[i] += 1
        num_mat[i][int(label_id)] += 1
        label_true.append(i)
        label_pred.append(int(label_id))

# 计算总的准确率accuracy
num1, num2, num3 = 0, 0, 0
for i in range(len(label_name)):
    num1 += num_mat[i][i]
    num2 += num_prs[i]
    num3 += num_rec[i]
assert int(num2) == int(num3)
accuracy = (num1/num2*100*100//1)/100
print("total accuracy: "+ str(accuracy))

# 计算每个类别的precision
result_prs = {}
prs = [float(num_mat[i][i])/float(num_prs[i])*100 for i in range(len(label_name))]
for i in range(len(label_name)):
    result_prs[label_name[i]] = (prs[i]*100//1)/100
print("every precision: "+ str(result_prs))

# 计算每个类别的recall
result_rec = {}
rec = [float(num_mat[i][i])/float(num_rec[i])*100 for i in range(len(label_name))]
for i in range(len(label_name)):
    result_rec[label_name[i]] = (rec[i]*100//1)/100
print("every recall:   "+ str(result_rec))

# 打印混淆矩阵
print(label_name)
print(np.array(num_mat))

onnx的x86端测评---多进程

当然如果没有GPU,需要在CPU上测评,那还是需要多进程的测评

# 计算onnx模型的acc和混淆矩阵,多进程
import numpy as np
import os
import cv2
import onnxruntime as rt
from multiprocessing import Process, Queue

# 定义图片预处理函数
def image_preprocess(image, target_length, value=0.0, method=0):
    image = image.astype("float32")
    h, w, _ = image.shape                               # 获得原始尺寸
    ih, iw  = target_length, target_length              # 获得目标尺寸
    scale = min(iw/w, ih/h)                             # 实际拉伸比例
    nw, nh  = int(scale * w), int(scale * h)            # 实际拉伸后的尺寸
    image_resized = cv2.resize(image, (nw, nh))         # 实际拉伸图片
    image_paded = np.full(shape=[ih, iw, 3], fill_value=value)
    dw, dh = (iw - nw) // 2, (ih-nh) // 2
    image_paded[dh:nh+dh, dw:nw+dw, :] = image_resized  # 居中填充图片
    if method == 0:
        image_paded = image_paded / 255.                # 图片归一化
    elif method == 1:
        image_paded = image_paded / 127.5 - 1.0         # 图片标准化
    return image_paded

# 读取onnx模型
onnx_path = "./MobileNetV2.onnx"
output_names = ['Logits']       # 生成MobileNetV2.onnx模型时自定义的名字
providers = ['CPUExecutionProvider']
model = rt.InferenceSession(onnx_path, providers=providers)

# 定义进程函数
def fun(q, model, image_list, label_name, n):
    num_i = 0
    num_total = len(image_list)
    num_prs = np.array([0.000000001 for _ in range(len(label_name))])
    num_rec = np.array([0.000000001 for _ in range(len(label_name))])
    num_mat = np.array([[0] * len(label_name) for _ in range(len(label_name))])
    for image_path in image_list:
        i = label_name.index(image_path.split("/")[-2])
        image = cv2.cvtColor(cv2.imread(image_path), cv2.COLOR_BGR2RGB)
        image = image_preprocess(image, 128, 0, 1)
        image = np.expand_dims(image, axis=0).astype("float32")
        output = model.run(output_names, {"input": image})[0].tolist()
        label_id = np.argmax(output[0])
        num_prs[int(label_id)] += 1
        num_rec[i] += 1
        num_mat[i, int(label_id)] += 1
        num_i += 1
        if num_i%100 == 0:
            print("Process %d : finish %d | total %d | percentage %0.2f" % (n, num_i, num_total, num_i/num_total*100))
    q.put([num_prs, num_rec, num_mat])

# 遍历识别每一幅图片并统计结果
num_core = 20  # cpu核心数
q = Queue()
path = "./test/"
label_name = ["label_1", "label_2", "label_3", "label_4", "label_5", "label_6", "label_7"]  # 必须跟tflite模型的标签顺序匹配
num_prs = np.array([0.000000001 for _ in range(len(label_name))])
num_rec = np.array([0.000000001 for _ in range(len(label_name))])
num_mat = np.array([[0] * len(label_name) for _ in range(len(label_name))])

# 遍历每一个子文件夹,得到所有图片路径
image_list = []
for i in range(len(label_name)):
    label_path = path + label_name[i] + "/"
    for image_name in os.listdir(label_path):
        image_list.append(label_path+image_name)
print("finish read image %d" % len(image_list))

# 按照进程分配任务
delta = int(len(image_list)/num_core)
image_index = []
for i in range(num_core):
    image_index.append(i * delta)
image_index.append(len(image_list))

# 启动进程
process_list = []
for i in range(num_core):
    p = Process(target=fun, args=(q, model, image_list[image_index[i]:image_index[i+1]], label_name, i))
    p.start()
    process_list.append(p)
# 保持进程
for p in process_list:
    p.join()
# 整理结果
for i in range(num_core):
    result = q.get()
    num_prs += result[0]
    num_rec += result[1]
    num_mat += result[2]

# 计算总的准确率accuracy
num1, num2, num3 = 0, 0, 0
for i in range(len(label_name)):
    num1 += num_mat[i, i]
    num2 += num_prs[i]
    num3 += num_rec[i]
assert int(num2) == int(num3)
accuracy = (num1/num2*100*100//1)/100
print("total accuracy: "+ str(accuracy))

# 计算每个类别的precision
result_prs = {}
prs = [float(num_mat[i, i])/float(num_prs[i])*100 for i in range(len(label_name))]
for i in range(len(label_name)):
    result_prs[label_name[i]] = (prs[i]*100//1)/100
print("every precision: "+ str(result_prs))

# 计算每个类别的recall
result_rec = {}
rec = [float(num_mat[i, i])/float(num_rec[i])*100 for i in range(len(label_name))]
for i in range(len(label_name)):
    result_rec[label_name[i]] = (rec[i]*100//1)/100
print("every recall:   "+ str(result_rec))

# 打印混淆矩阵
print(label_name)
print(num_mat)

猜你喜欢

转载自blog.csdn.net/BIT_Legend/article/details/122268667