其他分享
首页 > 其他分享> > ONNX的模型优化与量化细节

ONNX的模型优化与量化细节

作者:互联网


layout: post
title: ONNX的模型优化与量化细节
date: 2021-09-21 18:18:48.000000000 +09:00
categories: [算法框架]
tags: [离线推理]


ONNX的模型优化与量化细节

ONNX基本介绍

ONNX自带了Runtime库,能够将ONNX Model部署到不同的硬件设备上进行推理,支持各种后端(如TensorRT/OpenVINO)。

基于ONNX Model的Runtime系统架构如下,可以看到Runtime实现功能是将ONNX Model转换为In-Memory Graph格式,之后通过将其转化为各个可执行的子图,最后通过GetCapability() API将子图分配到不同的后端(execution provider)执行。

ONNXRuntime high level system architecture

ONNX模型优化

onnx_simplifier的核心功能如下:

ONNX Simplifier is presented to simplify the ONNX model. It infers the whole computation graph and then replaces the redundant operators with their constant outputs.

simplify的基本流程如下:

  1. 利用onnxruntime推理计算图,得到各个节点的输入输出的infer shape
  2. 基于ONNX支持的优化方法进行ONNX模型的优化(如fuse_bn_into_conv
  3. 对ONNX模型的常量OP进行折叠:
    1. 基于get_constant_nodes获取常量OP
    2. 基于add_features_to_output将所有静态节点的输出扩展到ONNX图的输出节点列表中(主要为了后续步骤方便获取常量节点输出)
    3. 将1.中得到的常量OP从图中移除(断开连线),同时将其节点参数构建为其他节点的输入参数
    4. 清理图中的孤立节点(3.中断开连线的节点)

其optimize函数定义如下(可以简单看下目前支持的一些优化方法):

def optimize(model: onnx.ModelProto, skip_fuse_bn: bool, skipped_optimizers: Optional[Sequence[str]]) -> onnx.ModelProto:
    """
    :model参数: 待优化的ONXX模型.
    :return: 优化之后的ONNX模型.
    简化之前, 使用这个方法产生会在'forward_all'用到的ValueInfo
    简化之后,使用这个方法去折叠前一步产生的常量到initializer中并且消除没被使用的常量
    """
    onnx.checker.check_model(model)
    onnx.helper.strip_doc_string(model)
    optimizers_list = [
        'eliminate_deadend',
        'eliminate_nop_dropout',
        'eliminate_nop_cast',
        'eliminate_nop_monotone_argmax', 'eliminate_nop_pad',
        'extract_constant_to_initializer', 'eliminate_unused_initializer',
        'eliminate_nop_transpose',
        'eliminate_nop_flatten', 'eliminate_identity',
        'fuse_add_bias_into_conv',
        'fuse_consecutive_concats',
        'fuse_consecutive_log_softmax',
        'fuse_consecutive_reduce_unsqueeze', 'fuse_consecutive_squeezes',
        'fuse_consecutive_transposes', 'fuse_matmul_add_bias_into_gemm',
        'fuse_pad_into_conv', 'fuse_transpose_into_gemm', 'eliminate_duplicate_initializer'
    ]
    if not skip_fuse_bn:
        optimizers_list.append('fuse_bn_into_conv')
    if skipped_optimizers is not None:
        for opt in skipped_optimizers:
            try:
                optimizers_list.remove(opt)
            except ValueError:
                pass

    model = onnxoptimizer.optimize(model, optimizers_list, fixed_point=True)
    onnx.checker.check_model(model)
    return model

ONNX转FP16

ONNX支持FP32模型转换为FP16模型,接口如下:

import onnxmltools
from onnxmltools.utils.float16_converter import convert_float_to_float16

# Update the input name and path for your ONNX model
input_onnx_model = 'model.onnx'
# Change this path to the output name and path for your float16 ONNX model
output_onnx_model = 'model_f16.onnx'
# Load your model
onnx_model = onnxmltools.utils.load_model(input_onnx_model)
# Convert tensor float type from your input ONNX model to tensor float16
onnx_model = convert_float_to_float16(onnx_model)
# Save as protobuf
onnxmltools.utils.save_model(onnx_model, output_onnx_model)

具体实现在于float16.py,截断的逻辑:

核心代码:

def convert_np_to_float16(np_array, min_positive_val=1e-7, max_finite_val=1e4):
    def between(a, b, c):
        return np.logical_and(a < b, b < c)
    np_array = np.where(between(0, np_array, min_positive_val), min_positive_val, np_array)
    np_array = np.where(between(-min_positive_val, np_array, 0), -min_positive_val, np_array)
    np_array = np.where(between(max_finite_val, np_array, float('inf')), max_finite_val, np_array)
    np_array = np.where(between(float('-inf'), np_array, -max_finite_val), -max_finite_val, np_array)
    return np.float16(np_array)

参考资料

onnxruntime官方资料

onnx比较好的知乎介绍

onnx一些调研资料

onnx_simplifier常量折叠介绍

标签:ONNX,细节,fuse,onnx,np,量化,model,array
来源: https://blog.csdn.net/sunny0660/article/details/120405998