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, 系统负载,可用内存),所以优化策略会不一样
- 生成onnx结果
polygraphy run identity.onnx --onnxrt \
--save-outputs golden.json
- 重复构建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模型精度
- 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}
- 测试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精度如下
- 进入推理比对流程, 保存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如下图所示
- 加载上一步输入数据, 作为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每一层的输出实际上步骤如下
- 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 \
- 此时的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
- 由于已经存在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包含两大组件Backend
和Comparator
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