首页 > 其他分享 >polygraphy介绍

polygraphy介绍

时间:2023-11-10 20:55:42浏览次数:27  
标签:engine -- outputs 介绍 polygraphy onnx trt

Polygraphy

目录

工具说明

polygraphy 是一个深度学习模型调试工具,包含 python API和 命令行工具, 详细的示例与说明可参考github

依赖&安装

  • TensorRT-8.5.2.2
  • python -m pip install colored polygraphy --extra-index-url https://pypi.ngc.nvidia.com
  • 设置环境变量, 根据实际使用场景下载所需要的python依赖
export POLYGRAPHY_AUTOINSTALL_DEPS=1 ### k环境变量

polygraphy.config.AUTOINSTALL_DEPS = True ### python api
  • 根据不同backend, 下载依赖python -m pip install -r polygraphy/backend/<name>/requirements.txt(onnx、trt)

功能&Tools

run

该功能是最主要的一个功能, 实现了不同后端运行推理以及比较的功能。常用参数介绍如下:

logging
  • --verbose, 加上该参数则日志信息会更详细
  • --quiet, 精简日志信息
  • --verbosity, 上面两个参数详细或精简由polygraph logger定义。而该参数会更为灵活, 允许用户控制每个路径的详细程度。使用示例:--verbosity backend/trt:INFO backend/trt/loader.py:VERBOSE
  • --silent, 日志禁用所有输出
  • --log-file, 保存日志输出为指定文件
runner
  • --tf, --onnxrt, --pluginref, --trt, 分别表示使用TensorFlow、OnnxRuntime、CPU插件、TensorRT进行推理
model
  • model_file, 必选参数, 传入模型路径
  • --model-type, {frozen,keras,ckpt,onnx,engine,trt-network-script}, 其中frozen表示TensorFlow frozen graph。trt-network-script接受一个一个python脚本, 如传入my_custom_script.py:my_func, 默认为load_network 函数, 返回TensorRT Builder, Network或Parser
  • --input-shapes, 示例:--input-shapes image:[1,3,224,224]
onnx
  • --save-onnx, onnx保存路径
  • --save-external-data, 保存权重到外部文件
  • --onnx-outputs, 标记需要输出的层名(onnx), mark all即表示全部输出
  • --onnx-exclude-outputs, 需要排除输出的层名
  • --fp-to-fp16, 将onnx 模型转成fp16精度
tensorrt
  • --tf32、--fp16、--bf16、--fp8、--int8, 在tensorrt中启用不同的精度。
  • --sparse-weights, 启用稀疏化权重
  • --trt-outputs, 同--onnx-outputs
  • --trt-network-postprocess-script, 网络后处理
  • --save-engine, 保存engine的路径
data loader
  • --seed, 随机输入种子

  • --load-inputs, 输入数据文件路径

  • --data-loader-script, 自定义数据加载的脚本

  • --val-range, 数据生成的值范围

  • --calibration-cache, 保存校准cache, 如果路径中存在则会跳过校准过程

comparator
  • --save-inputs, --save-outputs, 保存输入输出, 并指定路径

  • --postprocess, 后处理脚本, 比如求topk

  • --validate, 检查NaNs和Infs

  • --fail-fast, 第一次比对失败即停止

  • --rtol, Relative tolerance, 输出比较的相对容差, 0.01表示两组输出误差在1%以内。该参数还可以这样使用:--rtol 1e-5 out0:1e-4 out1:1e-3

  • --atol, Absolute tolerance, 输出比较的绝对容差

  • --show-heatmaps, 是否展示相对误差和绝对误差的热图

其它tools参数类似,可通过polygraph debug -h查看, 这里不做介绍。

使用示例

convert

自定义data_loader以提供校准数据集, 并保存cache以供下次量化使用

polygraphy convert model.onnx --int8 \
    --data-loader-script ./data_loader.py \
    --calibration-cache identity_calib.cache \
    -o model.engine

data_loader如下:

import numpy as np

INPUT_SHAPE = (1, 1, 2, 2)

def load_data():
    for _ in range(5):
        yield {"x": np.ones(shape=INPUT_SHAPE, dtype=np.float32)}  # Still totally real data

debug

在生成tensorrt-engine的时候, 会结合当前机器状态(GPU, os/kernel,cpu, 系统负载,可用内存),所以优化策略会不一样

  1. 生成onnx结果
polygraphy run identity.onnx --onnxrt \
    --save-outputs golden.json
  1. 重复构建trt文件, 与onnx输出进行比对。执行下面命令会生成一个engine, 默认保存为./polygraphy_debug.engine。--check 运行run功能比较engine与onnx的结果。
polygraphy debug build identity.onnx --fp16 --save-tactics replay.json \
    --artifacts-dir replays --artifacts replay.json --until=10 \
    --check polygraphy run polygraphy_debug.engine --trt --load-outputs golden.json

inspect

加载并显示engine的信息

polygraphy inspect model engine/resnet/resnet18-3_224_224-ppcls-int8-dla_N-sparsity_N-bs1-3_224_224.trt \
    --model-type engine \
    --show layers

得到engine layers如下

surgeon

如上图所示的一个模型,用户可以通过surgeon得到其一个子图

在不知道输入shape的情况下, 可以传入auto。需要说明的是输入必须加上datatype, 所以可以输入--inputs x1:auto:auto

polygraphy surgeon extract model.onnx \
    --inputs x1:[1,3,224,224]:float32 \
    --outputs add_out:float32 \
    -o subgraph.onnx

得到子图如下

此外surgeon还可以折叠graph中的constant、删除未使用的节点、改变输入shape等

run

run接受一个模型输入, 一个或者多个推理框架的输入。

comparison metrics

用户可以通过--check-error-stat参数来指定对比的指标, 该参数默认是elementwise, 绝对误差和相对误差计算如下

absdiff = out0 - out1
reldiff = absdiff / abs(out1)

除了elementwise, 还可以选择mean、median和max

  • elementwise: 对于每一个输出索引i, 比对absdiff[i]是否大于atol, reldiff[i]> rtol
  • max:比较最大的 absdiff和reldiff

generating_comparison_script

可以通过python脚本进行更加灵活的比较, run提供了生成基础比较脚本的功能如下

polygraphy run $onnx_file \
    --trt --onnxrt \
    --trt-outputs mark all\
    --onnx-outputs mark all \
    --atol 1e-2 --rtol 1e-3 \
    --fail-fast \
    --save-engine ./engine/test.engine \
    --gen-script=compare_trt_onnxrt.py \
    --val-range [0,1]

得到的脚本如下

#!/usr/bin/env python3
# Template auto-generated by polygraphy [v0.49.0] on 11/06/23 at 19:59:18
# Generation Command: /home/vastai/miniconda3/envs/polygraph/bin/polygraphy run /home/vastai/benchmark/classification/modelzoo_int8/onnx/resnet/resnet18-3_224_224-ppcls_rebatch.onnx --trt --onnxrt --trt-outputs mark all --onnx-outputs mark all --atol 1e-2 --rtol 1e-3 --fail-fast --save-engine ./engine/test.engine --gen-script=compare_trt_onnxrt.py --val-range [0,1]
# This script compares /home/vastai/benchmark/classification/modelzoo_int8/onnx/resnet/resnet18-3_224_224-ppcls_rebatch.onnx between TensorRT and ONNX-Runtime.

from polygraphy.logger import G_LOGGER

from polygraphy import constants
from polygraphy.backend.common import SaveBytes
from polygraphy.backend.onnx import BytesFromOnnx, ModifyOutputs as ModifyOnnxOutputs, OnnxFromPath
from polygraphy.backend.onnxrt import OnnxrtRunner, SessionFromOnnx
from polygraphy.backend.trt import EngineBytesFromNetwork, EngineFromBytes, ModifyNetworkOutputs, NetworkFromOnnxPath, TrtRunner
from polygraphy.comparator import Comparator, CompareFunc, DataLoader
from polygraphy.exception import PolygraphyException

# Data Loader
data_loader = DataLoader(val_range={'': (0, 1)})

# Loaders
parse_network_from_onnx = NetworkFromOnnxPath('/home/vastai/benchmark/classification/modelzoo_int8/onnx/resnet/resnet18-3_224_224-ppcls_rebatch.onnx')
set_network_outputs = ModifyNetworkOutputs(parse_network_from_onnx, outputs=constants.MARK_ALL)
build_engine = EngineBytesFromNetwork(set_network_outputs)
save_engine_bytes = SaveBytes(build_engine, path='./engine/test.engine')
deserialize_engine = EngineFromBytes(save_engine_bytes)
load_onnx = OnnxFromPath('/home/vastai/benchmark/classification/modelzoo_int8/onnx/resnet/resnet18-3_224_224-ppcls_rebatch.onnx')
modify_outputs = ModifyOnnxOutputs(load_onnx, outputs=constants.MARK_ALL)
serialize_onnx = BytesFromOnnx(modify_outputs)
build_onnxrt_session = SessionFromOnnx(serialize_onnx)

# Runners
runners = [
    TrtRunner(deserialize_engine),
    OnnxrtRunner(build_onnxrt_session),
]

# Runner Execution
results = Comparator.run(runners, data_loader=data_loader)

success = True
# Accuracy Comparison
compare_func = CompareFunc.simple(rtol={'': 0.001}, atol={'': 0.01}, fail_fast=True)
success &= bool(Comparator.compare_accuracy(results, compare_func=compare_func, fail_fast=True))

# Report Results
if not success:
    raise PolygraphyException('FAILED')

comparing_onnx&engine1

polygraphy run onnx/resnet/resnet18-3_224_224-ppcls_rebatch.onnx \
    --trt --onnxrt \
    --trt-outputs mark all\
    --onnx-outputs mark all \
    --atol 1e-2 --rtol 1e-3 \
    --fail-fast \
    --val-range [0,1]
  • --trt --onnxrt表示分别调用tensorrt和onnxruntime的后端
  • --trt-outputs mark all表示保存各个层的输出, 可以通过指定层名称来保存对应的输出
  • 虽然传入的是onnx文件,但是polygraphy会生成序列化文件, 可以通过--save-engine保存该文件

输出如下, 运行过程中所依赖的包tensorrt、onnx、onnxruntime等包会自动下载。

comparing_onnx&engine2

上面是直接对比给定的onnx与其序列化的engine文件。一般较为常见的场景是, 直接转换成engine进行测试, 只有在精度异常时才会用到polygrahy工具。下面以resnet18模型为例, 介绍如何对比保存在disk的trt模型精度

  1. trt模型转换(optional, 可以直接拿已经转换好的模型)
## 默认fp32
polygraphy convert identity.onnx -o identity.engine
## int8校准
polygraphy convert resnet18.onnx --int8 \
    --data-loader-script ./data_loader.py \
    --calibration-cache imagenet_calib.cache \
    -o engine/resnet18_int8.engine

其中 data_loader.py如下所示

import os
import numpy as np
from PIL import Image

INPUT_SHAPE = (1, 3, 224, 224)
calib_data = "/home/vastai/nvbenchmark/data/ILSVRC2012_img_calib"

def get_image_data(image_file, input_shape = [1, 3, 224, 224]):
    """fix shape resize"""
    size = [input_shape[2],input_shape[3]]
    hints = [256, 256]
    mean = [
        0.485,
        0.456,
        0.406
    ]
    std = [
        0.229,
        0.224,
        0.225
    ]

    image = Image.open(image_file)
    if image.mode != "RGB":
        image = image.convert("RGB")
    if len(hints) != 0:
        y1 = max(0, int(round((hints[0] - size[0]) / 2.0)))
        x1 = max(0, int(round((hints[1] - size[1]) / 2.0)))
        y2 = min(hints[0], y1 + size[0])
        x2 = min(hints[1], x1 + size[1])
        image = image.resize(hints)
        image = image.crop((x1, y1, x2, y2))
    else:
        image = image.resize((size[1], size[0]))
    image = np.ascontiguousarray(image)
    if mean[0] < 1 and std[0] < 1:
        image = image.astype(np.float32)
        image /= 255.0
        image -= np.array(mean)
        image /= np.array(std)
    else:
        image = image - np.array(mean)  # mean
        image /= np.array(std)  # std
    image = image.transpose((2, 0, 1))
    image = image[np.newaxis, :]
    return image


def load_data():
    file_list = os.listdir(calib_data)
    for file in  file_list:
        data = get_image_data(os.path.join(calib_data, file))
        yield {"input": data}

  1. 测试int8模型精度,精度测试脚本参考vastml/benchmark
python infer_engine/test_topk.py \
    --trtmodel engine/resnet18_int8.engine \
    --image_path data/imagenet_val5k \
    --lable_file_path data/label.txt

得到int8精度如下

  1. 进入推理比对流程, 保存onnxruntime输入数据以及推理结果
polygraphy run $onnx_file --onnxrt \
    --onnx-outputs mark all \
    --save-inputs inputs.json --save-outputs run_0_outputs.json

其中, resnet onnx如下图所示

保存的inputs.json如下图所示

保存的run_0_outputs.json如下图所示

  1. 加载上一步输入数据, 作为trt模型的输入并对比推理结果

低精度模型与fp32模型层间输出略有差距, 需要提高容差

polygraphy run engine/resnet18_int8.engine --trt  --model-type=engine \
    --check-error-stat median \
    --trt-outputs mark all \
    --load-inputs inputs.json --load-outputs run_0_outputs.json \
    --rtol 0.06 \
    --fail-fast

两者对比结果如下

对比结果会发现, trt与onnx其实只对比了最后一层输出。参数--trt-outputs在这里是失效的(可以通过--gen-script=compar_all_layer.py 验证)

生成comparing_onnx&engine1的脚本, 如下

# Loaders
parse_network_from_onnx = NetworkFromOnnxPath('/home/vastai/nvbenchmark/polygraphy/resnet18.onnx')
set_network_outputs = ModifyNetworkOutputs(parse_network_from_onnx, outputs=constants.MARK_ALL)
build_engine = EngineBytesFromNetwork(set_network_outputs)
deserialize_engine = EngineFromBytes(build_engine)
load_onnx = OnnxFromPath('/home/vastai/nvbenchmark/polygraphy/resnet18.onnx')
modify_outputs = ModifyOnnxOutputs(load_onnx, outputs=constants.MARK_ALL)
serialize_onnx = BytesFromOnnx(modify_outputs)
build_onnxrt_session = SessionFromOnnx(serialize_onnx)

# Runners
runners = [
    TrtRunner(deserialize_engine),
    OnnxrtRunner(build_onnxrt_session),
]

# Runner Execution
results = Comparator.run(runners)

可以发现对于engine来说, 先是load_onnx, 在parse_network_from_onnx中标记每一层的输出,然后才是序列化和反序列化。而一般保存的engine是代码中的build_engine。所以说, 如果需要对比每一层的输出, 需要提前进行标记, 然后再保存优化的engine, 那么如果需要debug每一层的输出实际上步骤如下

  1. onnx转换成mark all对应的engine
polygraphy convert resnet18.onnx --int8 \
    --data-loader-script ./data_loader.py \
    --trt-outputs mark all \  ### 注意这里
    --calibration-cache imagenet_calib.cache \
    -o engine/resnet18_int8.engine \
  1. 此时的engine有N个输出, 无法直接测试精度, 但是用户可以通过inspect检查engine
polygraphy inspect model engine/resnet18_int8.engine \
    --model-type engine \
    --show layers > trt_mark_all.txt

可以看到engine如下, 详见trt_mark_all.txt

  1. 由于已经存在onnx运行的数据,所以直接加载进行对比
polygraphy run engine/resnet18_int8.engine --trt  --model-type=engine \
    --check-error-stat median \
    --trt-outputs mark all \
    --load-inputs inputs.json --load-outputs run_0_outputs.json \
    --rtol 0.1 > compare.txt

输出比对详见compare.txt

comparing_onnx&engine3

mobilenetv3等

appending

尝试onnx modify工具给onnx添加前三个输出, vacc编译正常通过

  • 是否影响图优化
  • 结果对比需要保持key一致

具体实现

API包含两大组件BackendComparator

Backend又包含Loader和和Runner, Loader可以实现推理框架想要的模型加载与转换的流程。

from polygraphy import constants
from polygraphy.backend.common import BytesFromPath
from polygraphy.backend.trt import EngineFromBytes, ModifyNetworkOutputs, EngineBytesFromNetwork

# Loaders
load_engine_bytes = BytesFromPath('/home/vastai/nvbenchmark/polygraphy/engine/resnet18_int8.engine')
set_engine_outputs = ModifyNetworkOutputs(load_engine_bytes, outputs=constants.MARK_ALL)
# deserialize_engine = EngineFromBytes(load_engine_bytes)
build_engine = EngineBytesFromNetwork(set_engine_outputs)
deserialize_engine = EngineFromBytes(build_engine)

Runner实现各推理框架的infer功能。需要注意的是, 一般runner最好只激活一次

Comparator对比流程如下所示

# Runners
runners = [
    TrtRunner(deserialize_engine),
]

# Runner Execution
results = Comparator.run(runners, data_loader=data_loader)

# Load results
for load_output in ['run_0_outputs.json']:
    results.extend(RunResults.load(load_output))

success = True
# Accuracy Comparison
compare_func = CompareFunc.simple(rtol={'': 0.06}, fail_fast=True, check_error_stat={'': 'median'})
success &= bool(Comparator.compare_accuracy(results, compare_func=compare_func, fail_fast=True))

# Report Results
if not success:
    raise PolygraphyException('FAILED')

结果比对代码source_code

标签:engine,--,outputs,介绍,polygraphy,onnx,trt
From: https://www.cnblogs.com/xle97/p/17825034.html

相关文章

  • 直播会议一体机主板|5G智能会议一体机安卓主板方案介绍
    5G直播会议一体机主板采用了强大的音视频输入输出,内置安卓13系统,适用于多种直播和会议软件,广泛应用于智能会议一体机、便携直播机、录播导播、无人直播、视频传输等多种领域。该主板采用国产6nm旗舰芯片紫光展锐T820处理器,提供了4+64G、6+128G、8+256G多种内存版本可选。在无线连接......
  • Redis Functions 介绍之二
    首先,让我们先回顾一下上一篇讲的在RedisFunctions中关于将key的名字作为参数和非key名字作为参数的区别,先看下面的例子。首先,我们先在一个Lua脚本文件mylib.lua中定义如下的库和函数。//--------------------mylib.lua文件开始-----------//#!luaname=myliblocalfunct......
  • Redis Functions 介绍之二
    首先,让我们先回顾一下上一篇讲的在RedisFunctions中关于将key的名字作为参数和非key名字作为参数的区别,先看下面的例子。首先,我们先在一个Lua脚本文件mylib.lua中定义如下的库和函数。//--------------------mylib.lua文件开始-----------//#!luaname=myliblocalfunctio......
  • asterisk、pbx、sip介绍
    最近在了解一个网络电话的项目,在网上搜索了一些关于这方便的资料,现在记录一下。(通讯行业是一个非常专业的领域,本人虽然做了很多年的开发,但有理解起来还是很吃力)1、viop:VoIP(VoiceoverInternetProtocol)简而言之就是将模拟信号(Voice)数字化,以数据封包(DataPacket)的形式在IP网络(IP......
  • Kubernetes常用命令及yml文件、集群网络 Kubernetes组件介绍及环境搭建
    Kubernetes常用命令及yml文件、集群网络Kubernetes组件介绍及环境搭建Kubernetes组件介绍及环境搭建一、kubernetes常用命令说明:因为k8s的命令都是通过kubectl组件接收的,这个组件只在master节点有,所以k8s的命令都是在master节点中执行kubectlgetnodes#查看当前集群中......
  • Net 高级调试之七:线程操作相关命令介绍
    一、简介今天是《Net高级调试》的第七篇文章。上一篇文章我们说了值类型,引用类型,数组等的内存表现形式。有了这个基础,我们可以更好的了解我们的程序在运行时的状态,内存里有什么东西,它们的结构组成是什么样子的,对我们调试程序是更有帮助的。今天,我们要说一些和线程有关的......
  • linux MAC/IPV4/IPV6/网际互连/网关/网桥全面介绍,TCP/IP协议族模型、TCP/IP传输层协
    鱼弦:内容合伙人、新星导师、全栈领域创作新星创作者、51CTO(Top红人+专家博主)、github开源爱好者(go-zero源码二次开发、游戏后端架构https://github.com/Peakchen) Linux是一种自由和开放源代码的类Unix操作系统。它支持多种网络协议,其中包括TCP/IP协议族。在本回答中,我将介绍L......
  • 一种在 ABAP 端扩展 SAP Fiori 应用的方法介绍
    有朋友在我的知识星球提问:HCMFAB_COMMON这个lib已经被很多app消费了,我想对HAMFAB_COMMON做一点扩展,希望原先消费它的app能使用最新的功能。有个群友给出了解答.StackOverflow的帖子:CansomeonepleaseadviseontheissueIamfacing.IamtryingtoextendaStand......
  • Python抽象类介绍
    抽象类什么是抽象类与java一样,python也有抽象类的概念。抽象类是一种特殊的类,它只能有抽象方法,不能被实例化,在子类继承抽象类时,不能通过实例化使用其抽象方法,必须实现该方法。抽象类的作用抽象类可以实现多个子类中共用的部分,而不需要重复写到实现类中。从设计角度去看,抽象类......
  • Go 接口:Go中最强大的魔法,接口应用模式或惯例介绍
    Go接口:Go中最强大的魔法,接口应用模式或惯例介绍目录Go接口:Go中最强大的魔法,接口应用模式或惯例介绍一、前置原则二、一切皆组合2.1一切皆组合2.2垂直组合2.2.1第一种:通过嵌入接口构建接口2.2.2第二种:通过嵌入接口构建结构体类型2.2.3第三种:通过嵌入结构体类型构建新结构......