chore: import upstream snapshot with attribution
Docker Image CI / build-ubuntu2004 (push) Has been cancelled
Docker Image CI / build-ubuntu2004 (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
# Comparing Frameworks
|
||||
|
||||
## Introduction
|
||||
|
||||
You can use the `run` subtool to compare a model between different frameworks.
|
||||
In the simplest case, you can supply a model, and one or more framework flags.
|
||||
By default, it will generate synthetic input data, run inference using the
|
||||
specified frameworks, then compare outputs of the specified frameworks.
|
||||
|
||||
## Running The Example
|
||||
|
||||
In this example, we'll outline various common use-cases for the `run` subtool:
|
||||
|
||||
- [Comparing TensorRT And ONNX-Runtime Outputs](#comparing-tensorrt-and-onnx-runtime-outputs)
|
||||
- [Comparing TensorRT Precisions](#comparing-tensorrt-precisions)
|
||||
- [Changing Tolerances](#changing-tolerances)
|
||||
- [Changing Comparison Metrics](#changing-comparison-metrics)
|
||||
- [Comparing Per-Layer Outputs Between ONNX-Runtime And TensorRT](#comparing-per-layer-outputs-between-onnx-runtime-and-tensorrt)
|
||||
|
||||
### Comparing TensorRT And ONNX-Runtime Outputs
|
||||
|
||||
To run the model in Polygraphy with both frameworks and perform an output
|
||||
comparison:
|
||||
|
||||
```bash
|
||||
polygraphy run dynamic_identity.onnx --trt --onnxrt
|
||||
```
|
||||
|
||||
The `dynamic_identity.onnx` model has dynamic input shapes. By default,
|
||||
Polygraphy will override any dynamic input dimensions in the model to
|
||||
`constants.DEFAULT_SHAPE_VALUE` (defined as `1`) and warn you:
|
||||
|
||||
<!-- Polygraphy Test: Ignore Start -->
|
||||
```
|
||||
[W] Input tensor: X (dtype=DataType.FLOAT, shape=(1, 2, -1, -1)) | No shapes provided; Will use shape: [1, 2, 1, 1] for min/opt/max in profile.
|
||||
[W] This will cause the tensor to have a static shape. If this is incorrect, please set the range of shapes for this input tensor.
|
||||
```
|
||||
<!-- Polygraphy Test: Ignore End -->
|
||||
|
||||
In order to suppress this message and explicitly provide input shapes to
|
||||
Polygraphy, use the `--input-shapes` option:
|
||||
|
||||
```
|
||||
polygraphy run dynamic_identity.onnx --trt --onnxrt \
|
||||
--input-shapes X:[1,2,4,4]
|
||||
```
|
||||
|
||||
### Comparing TensorRT Precisions
|
||||
|
||||
To build a TensorRT engine with reduced precision layers for comparison against
|
||||
ONNXRT, use one of the supported precision flags (e.g. `--tf32`, `--fp16`,`--int8`, etc.).
|
||||
For example:
|
||||
|
||||
```bash
|
||||
polygraphy run dynamic_identity.onnx --trt --fp16 --onnxrt \
|
||||
--input-shapes X:[1,2,4,4]
|
||||
```
|
||||
|
||||
> :warning: Getting acceptable accuracy with INT8 precision typically requires an additional calibration step:
|
||||
see the [developer guide](https://docs.nvidia.com/deeplearning/tensorrt/developer-guide/index.html#working-with-int8)
|
||||
and instructions on [how to do calibration](../../../../examples/cli/convert/01_int8_calibration_in_tensorrt)
|
||||
with Polygraphy on the command line.
|
||||
|
||||
### Changing Tolerances
|
||||
|
||||
The default tolerances used by `run` are usually appropriate for FP32 precision
|
||||
but may not be appropriate for reduced precisions. In order to relax tolerances,
|
||||
you can use the `--atol` and `--rtol` options to set absolute and relative
|
||||
tolerance respectively.
|
||||
|
||||
### Changing Comparison Metrics
|
||||
|
||||
You can use the `--check-error-stat` option to change the metric used for
|
||||
comparison. By default, Polygraphy uses an "elementwise" metric
|
||||
(`--check-error-stat elemwise`).
|
||||
|
||||
Other possible metrics for `--check-error-stat` are `mean`, `median`, and `max`, which
|
||||
compares the mean, median, and maximum absolute/relative error across the tensor, respectively.
|
||||
|
||||
To better understand this, suppose we are
|
||||
comparing two outputs `out0` and `out1`. Polygraphy takes
|
||||
the elementwise absolute and relative difference of these tensors:
|
||||
|
||||
<!-- Polygraphy Test: Ignore Start -->
|
||||
```
|
||||
absdiff = out0 - out1
|
||||
reldiff = absdiff / abs(out1)
|
||||
```
|
||||
<!-- Polygraphy Test: Ignore End -->
|
||||
|
||||
Then, for each index `i` in the output, Polygraphy checks whether
|
||||
`absdiff[i] > atol and reldiff[i] > rtol`. If any index satisfies this,
|
||||
then the comparison will fail. This is less stringent than comparing the maximum
|
||||
absolute and relative error across the entire tensor (`--check-error-stat max`) since if
|
||||
*different* indices `i` and `j` satisfy `absdiff[i] > atol` and `reldiff[j] > rtol`,
|
||||
then the `max` comparison will fail but the `elemwise` comparison may
|
||||
pass.
|
||||
|
||||
Putting it all together, the below example runs a `median` comparison between
|
||||
TensorRT using FP16 and ONNX-Runtime, using absolute and relative tolerances of `0.001`:
|
||||
|
||||
```bash
|
||||
polygraphy run dynamic_identity.onnx --trt --fp16 --onnxrt \
|
||||
--input-shapes X:[1,2,4,4] \
|
||||
--atol 0.001 --rtol 0.001 --check-error-stat median
|
||||
```
|
||||
|
||||
> You can also specify per-output values for `--atol`/`--rtol`/`--check-error-stat`.
|
||||
See the help output of the `run` subtool for more information.
|
||||
|
||||
### Comparing Per-Layer Outputs Between ONNX-Runtime And TensorRT
|
||||
|
||||
When network outputs do not match, it can be useful to compare per-layer outputs
|
||||
to see where the error is introduced. To do so, you can use the `--trt-outputs`
|
||||
and `--onnx-outputs` options respectively. These options accept one or more
|
||||
output names as their arguments. The special value `mark all` indicates that all
|
||||
tensors in the model should be compared:
|
||||
|
||||
```bash
|
||||
polygraphy run dynamic_identity.onnx --trt --onnxrt \
|
||||
--trt-outputs mark all \
|
||||
--onnx-outputs mark all
|
||||
```
|
||||
|
||||
To find the first mismatched output more easily, you can use the `--fail-fast`
|
||||
option which will cause the tool to exit after the first mismatch between
|
||||
outputs.
|
||||
|
||||
Note that use of `--trt-outputs mark all` can sometimes perturb the generated
|
||||
engine due to differences in timing, layer fusion choices, and format
|
||||
constraints, which can hide the failure. In that case, you may have to use a
|
||||
more sophisticated approach to bisect the failing model and generate a reduced
|
||||
test case that reproduces the error. See [Reducing Failing ONNX
|
||||
Models](../../../../examples/cli/debug/02_reducing_failing_onnx_models) for a tutorial on
|
||||
how to do this with Polygraphy.
|
||||
|
||||
## Further Reading
|
||||
|
||||
* In some cases you may need to do comparisons across multiple Polygraphy runs
|
||||
(for example, when comparing the output of a pre-built TensorRT engine or
|
||||
[Polygraphy network script](../../../../examples/cli/run/04_defining_a_tensorrt_network_or_config_manually)
|
||||
against ONNX-Runtime). See [Comparing Across Runs](../../../../examples/cli/run/02_comparing_across_runs) for a tutorial on how to
|
||||
accomplish this.
|
||||
|
||||
* For more details on working with dynamic shapes in TensorRT:
|
||||
* See [Dynamic Shapes in TensorRT](../../../../examples/cli/convert/03_dynamic_shapes_in_tensorrt/) for how to specify
|
||||
optimization profiles for use with the engine using the Polygraphy CLI
|
||||
* See [TensorRT and Dynamic Shapes](../../../../examples/api/07_tensorrt_and_dynamic_shapes/) for details on
|
||||
how to do this with the Polygraphy API
|
||||
|
||||
* For details on how to supply real input data, see [Comparing with Custom Input Data](../05_comparing_with_custom_input_data/).
|
||||
|
||||
* See [Debugging TensorRT Accuracy Issues](../../../../how-to/debug_accuracy.md) for a broader tutorial on how to debug accuracy failures using Polygraphy.
|
||||
@@ -0,0 +1,19 @@
|
||||
:·
|
||||
|
||||
Xintermediate"Identity
|
||||
|
||||
intermediateY"Identityonnx_dynamic_identityZ&
|
||||
X!
|
||||
|
||||
|
||||
|
||||
height
|
||||
widthb
|
||||
Y
|
||||
j1
|
||||
intermediate!
|
||||
|
||||
|
||||
|
||||
height
|
||||
widthB
|
||||
@@ -0,0 +1,72 @@
|
||||
# Comparing Across Runs
|
||||
|
||||
## Prerequisites
|
||||
For a general overview of how to use `polygraphy run` to compare the outputs of
|
||||
different frameworks, see the example on [Comparing Frameworks](../../../../examples/cli/run/01_comparing_frameworks).
|
||||
|
||||
## Introduction
|
||||
|
||||
There are situations where you may need to compare results across different invocations
|
||||
of the `polygraphy run` command. Some examples of this include:
|
||||
|
||||
* Comparing results across different platforms
|
||||
* Comparing results across different versions of TensorRT
|
||||
* Comparing different model types with compatible input(s)/output(s)
|
||||
|
||||
In this example, we'll demonstrate how to accomplish this with Polygraphy.
|
||||
|
||||
## Running The Example
|
||||
|
||||
### Comparing Across Runs
|
||||
|
||||
1. Save the input and output values from the first run:
|
||||
|
||||
```bash
|
||||
polygraphy run identity.onnx --onnxrt \
|
||||
--save-inputs inputs.json --save-outputs run_0_outputs.json
|
||||
```
|
||||
|
||||
2. Run the model again, this time loading the saved inputs and outputs from
|
||||
the first run. The saved inputs will be used as inputs for the current run, and
|
||||
the saved outputs will be used to compare against the first run.
|
||||
|
||||
```bash
|
||||
polygraphy run identity.onnx --onnxrt \
|
||||
--load-inputs inputs.json --load-outputs run_0_outputs.json
|
||||
```
|
||||
|
||||
The `--atol/--rtol/--check-error-stat` options all work the same as in the
|
||||
[Comparing Frameworks](../../../../examples/cli/run/01_comparing_frameworks) example:
|
||||
|
||||
```bash
|
||||
polygraphy run identity.onnx --onnxrt \
|
||||
--load-inputs inputs.json --load-outputs run_0_outputs.json \
|
||||
--atol 0.001 --rtol 0.001 --check-error-stat median
|
||||
```
|
||||
|
||||
### Comparing Different Models
|
||||
|
||||
We can also use this technique to compare different models, like TensorRT engines
|
||||
and ONNX modles (if they have matching outputs).
|
||||
|
||||
1. Convert the ONNX model to a TensorRT engine and save it to disk:
|
||||
|
||||
```bash
|
||||
polygraphy convert identity.onnx -o identity.engine
|
||||
```
|
||||
|
||||
2. Run the saved engine in Polygraphy, using the saved inputs from the ONNX-Runtime run as
|
||||
inputs to the engine, and compare the engine's outputs to the saved ONNX-Runtime outputs:
|
||||
|
||||
```bash
|
||||
polygraphy run --trt identity.engine --model-type=engine \
|
||||
--load-inputs inputs.json --load-outputs run_0_outputs.json
|
||||
```
|
||||
|
||||
|
||||
## Further Reading
|
||||
|
||||
For details on how to access and work with the saved outputs
|
||||
using the Python API, refer to [API example 08](../../../api/08_working_with_run_results_and_saved_inputs_manually/).
|
||||
|
||||
For information on comparing against custom outputs, refer to [`run` example 06](../06_comparing_with_custom_output_data/).
|
||||
@@ -0,0 +1,15 @@
|
||||
backend-test:[
|
||||
|
||||
xy"Identity
|
||||
test_identityZ
|
||||
x
|
||||
|
||||
|
||||
|
||||
|
||||
b
|
||||
y
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
|
||||
# Generating A Script For Advanced Comparisons
|
||||
|
||||
|
||||
## Introduction
|
||||
|
||||
For more advanced requirements, you may want to use the [API](../../../../polygraphy).
|
||||
Instead of writing a script from scratch, you can use `run`'s `--gen-script` option
|
||||
to create a Python script that you can use as a starting point.
|
||||
|
||||
|
||||
## Running The Example
|
||||
|
||||
1. Generate a comparison script:
|
||||
|
||||
```bash
|
||||
polygraphy run identity.onnx --trt --onnxrt \
|
||||
--gen-script=compare_trt_onnxrt.py
|
||||
```
|
||||
|
||||
The generated script will do exactly what the `run` command would otherwise do.
|
||||
|
||||
2. Run the comparison script, optionally after modifying it:
|
||||
|
||||
```bash
|
||||
python3 compare_trt_onnxrt.py
|
||||
```
|
||||
@@ -0,0 +1,15 @@
|
||||
backend-test:[
|
||||
|
||||
xy"Identity
|
||||
test_identityZ
|
||||
x
|
||||
|
||||
|
||||
|
||||
|
||||
b
|
||||
y
|
||||
|
||||
|
||||
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
# Defining A TensorRT Network Or Config Manually
|
||||
|
||||
|
||||
## Introduction
|
||||
|
||||
In some cases, it can be useful to define a TensorRT network from scratch using the Python API,
|
||||
or modify a network created by other means (e.g. a parser). Normally, this would restrict you
|
||||
from using CLI tools, at least until you build an engine, since the network cannot be serialized
|
||||
to disk and loaded on the command-line.
|
||||
|
||||
Polygraphy CLI tools provide a work-around for this - if your Python script defines a function
|
||||
named `load_network`, which takes no parameters and returns a TensorRT builder, network,
|
||||
and optionally parser, then you can provide your Python script in place of a model argument.
|
||||
|
||||
Similarly, we can create a custom TensorRT builder configuration using a script that defines
|
||||
a function called `load_config` which accepts a builder and network and returns a builder configuration.
|
||||
|
||||
In this example, the included `define_network.py` script parses an ONNX model and appends an identity
|
||||
layer to it. Since it returns the builder, network, and parser in a function called `load_network`,
|
||||
we can build and run a TensorRT engine from it using just a single command. The `create_config.py`
|
||||
script creates a new TensorRT builder configuration and enables FP16 mode.
|
||||
|
||||
|
||||
### TIP: Generating Script Templates Automatically
|
||||
|
||||
Instead of writing the network script from scratch, you can use
|
||||
`polygraphy template trt-network` to give you a starting point:
|
||||
|
||||
```bash
|
||||
polygraphy template trt-network -o my_define_network.py
|
||||
```
|
||||
|
||||
If you want to start from a model and modify the resulting TensorRT network instead
|
||||
of creating one from scratch, simply provide the model as an argument to `template trt-network`:
|
||||
|
||||
```bash
|
||||
polygraphy template trt-network identity.onnx -o my_define_network.py
|
||||
```
|
||||
|
||||
Similarly, you can generate a template script for the config using `polygraphy template trt-config`:
|
||||
|
||||
```bash
|
||||
polygraphy template trt-config -o my_create_config.py
|
||||
```
|
||||
|
||||
You can also specify builder configuration options to pre-populate the script.
|
||||
For example, to enable FP16 mode:
|
||||
|
||||
```bash
|
||||
polygraphy template trt-config --fp16 -o my_create_config.py
|
||||
```
|
||||
|
||||
|
||||
## Running The Example
|
||||
|
||||
1. Run the network defined in `define_network.py`:
|
||||
|
||||
```bash
|
||||
polygraphy run --trt define_network.py --model-type=trt-network-script
|
||||
```
|
||||
|
||||
2. Run the network from step (1) using the builder configuration defined in `create_config.py`:
|
||||
|
||||
```bash
|
||||
polygraphy run --trt define_network.py --model-type=trt-network-script --trt-config-script=create_config.py
|
||||
```
|
||||
|
||||
Note that we could have defined both `load_network` and `load_config` in the same script.
|
||||
In fact, we could have retrieved these functions from arbitrary scripts, or even modules.
|
||||
|
||||
*TIP: We can use the same approach with `polygraphy convert` to build, but not run, the engine.*
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
"""
|
||||
Creates a TensorRT builder configuration and enables FP16 tactics.
|
||||
"""
|
||||
import tensorrt as trt
|
||||
from polygraphy import func
|
||||
from polygraphy.backend.trt import CreateConfig
|
||||
|
||||
|
||||
# If we define a function called `load_config`, polygraphy can use it to
|
||||
# create the builder configuration.
|
||||
#
|
||||
# TIP: If our function isn't called `load_config`, we can explicitly specify
|
||||
# the name with the script argument, separated by a colon. For example: `create_config.py:my_func`.
|
||||
@func.extend(CreateConfig())
|
||||
def load_config(config):
|
||||
# NOTE: func.extend() causes the signature of this function to be `(builder, network) -> config`
|
||||
# For details on how this works, see examples/api/03_interoperating_with_tensorrt
|
||||
|
||||
config.set_flag(trt.BuilderFlag.FP16)
|
||||
|
||||
# Notice that we don't need to return anything - `extend()` takes care of that for us!
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
"""
|
||||
Parses an ONNX model, and then extends it with an Identity layer.
|
||||
"""
|
||||
from polygraphy import func
|
||||
from polygraphy.backend.trt import NetworkFromOnnxPath
|
||||
|
||||
parse_onnx = NetworkFromOnnxPath("identity.onnx")
|
||||
|
||||
|
||||
# If we define a function called `load_network`, polygraphy can
|
||||
# use it directly in place of using a model file.
|
||||
#
|
||||
# TIP: If our function isn't called `load_network`, we can explicitly specify
|
||||
# the name with the model argument, separated by a colon. For example, `define_network.py:my_func`.
|
||||
@func.extend(parse_onnx)
|
||||
def load_network(builder, network, parser):
|
||||
# NOTE: func.extend() causes the signature of this function to be `() -> (builder, network, parser)`
|
||||
# For details on how this works, see examples/api/03_interoperating_with_tensorrt
|
||||
|
||||
# Append an identity layer to the network
|
||||
prev_output = network.get_output(0)
|
||||
network.unmark_output(prev_output)
|
||||
|
||||
output = network.add_identity(prev_output).get_output(0)
|
||||
network.mark_output(output)
|
||||
|
||||
# Notice that we don't need to return anything - `extend()` takes care of that for us!
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
backend-test:[
|
||||
|
||||
xy"Identity
|
||||
test_identityZ
|
||||
x
|
||||
|
||||
|
||||
|
||||
|
||||
b
|
||||
y
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# Comparing With Custom Input Data
|
||||
|
||||
## Introduction
|
||||
|
||||
In some cases, we may want to run comparisons using custom input data.
|
||||
Polygraphy provides multiple ways to do so, which are detailed [here](../../../../how-to/use_custom_input_data.md).
|
||||
|
||||
In this example, we'll demonstrate 2 different approaches:
|
||||
|
||||
1. Using a data loader script by defining a `load_data()` function in a Python script (`data_loader.py`).
|
||||
Polygraphy will use `load_data()` to generate inputs at runtime.
|
||||
|
||||
2. Using a JSON file containing pre-generated inputs.
|
||||
For convenience, we'll use our script from above (`data_loader.py`) to save the inputs
|
||||
generated by `load_data()` to a file called `custom_inputs.json`.
|
||||
|
||||
*TIP: Generally, a data loader script is preferrable when working with large amounts of input data*
|
||||
*as it avoids the need to write to the disk.*
|
||||
*On the other hand, JSON files may be more portable and can help ensure reproducibility.*
|
||||
|
||||
Finally, we'll supply our custom input data to `polygraphy run` and compare outputs between
|
||||
ONNX-Runtime and TensorRT.
|
||||
|
||||
Since our model has dynamic shapes, we'll need to set up a TensorRT Optimization Profile.
|
||||
For details on how we can do this via the command-line,
|
||||
see [`convert` example 03](../../convert/03_dynamic_shapes_in_tensorrt).
|
||||
For simplicitly, we'll create a profile where `min` == `opt` == `max`.
|
||||
|
||||
*NOTE: It is important that our optimization profile works with the shapes provided by our*
|
||||
*custom data loader. In our very simple case, the data loader always generates inputs of*
|
||||
*shape (1, 2, 28, 28), so we just need to ensure this falls within [`min`, `max`].*
|
||||
|
||||
## Running The Example
|
||||
|
||||
1. Run the script to save input data to the disk.
|
||||
*NOTE: This is only necessary for option 2.*
|
||||
```bash
|
||||
python3 data_loader.py
|
||||
```
|
||||
|
||||
2. Run the model with TensorRT and ONNX-Runtime using custom input data:
|
||||
- Option 1: Using the data loader script:
|
||||
|
||||
```bash
|
||||
polygraphy run dynamic_identity.onnx --trt --onnxrt \
|
||||
--trt-min-shapes X:[1,2,28,28] --trt-opt-shapes X:[1,2,28,28] --trt-max-shapes X:[1,2,28,28] \
|
||||
--data-loader-script data_loader.py
|
||||
```
|
||||
|
||||
- Option 2: Using the JSON file containing the saved inputs:
|
||||
|
||||
```bash
|
||||
polygraphy run dynamic_identity.onnx --trt --onnxrt \
|
||||
--trt-min-shapes X:[1,2,28,28] --trt-opt-shapes X:[1,2,28,28] --trt-max-shapes X:[1,2,28,28] \
|
||||
--load-inputs custom_inputs.json
|
||||
```
|
||||
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
"""
|
||||
Demonstrates two methods of loading custom input data in Polygraphy:
|
||||
|
||||
Option 1: Defines a `load_data` function that returns a generator yielding
|
||||
feed_dicts so that this script can be used as the argument for
|
||||
the --data-loader-script command-line parameter.
|
||||
|
||||
Option 2: Writes input data to a JSON file that can be used as the argument for
|
||||
the --load-inputs command-line parameter.
|
||||
"""
|
||||
import numpy as np
|
||||
from polygraphy.json import save_json
|
||||
|
||||
INPUT_SHAPE = (1, 2, 28, 28)
|
||||
|
||||
|
||||
# Option 1: Define a function that will yield feed_dicts (i.e. Dict[str, np.ndarray])
|
||||
def load_data():
|
||||
for _ in range(5):
|
||||
yield {
|
||||
"x": np.ones(shape=INPUT_SHAPE, dtype=np.float32)
|
||||
} # Still totally real data
|
||||
|
||||
|
||||
# Option 2: Create a JSON file containing the input data using the `save_json()` helper.
|
||||
# The input to `save_json()` should have type: List[Dict[str, np.ndarray]].
|
||||
# For convenience, we'll reuse our `load_data()` implementation to generate the list.
|
||||
input_data = list(load_data())
|
||||
save_json(input_data, "custom_inputs.json", description="custom input data")
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
backend_test:y
|
||||
|
||||
XY"Identityonnx_dynamic_identityZ&
|
||||
X!
|
||||
|
||||
|
||||
|
||||
height
|
||||
widthb&
|
||||
Y!
|
||||
|
||||
|
||||
|
||||
height
|
||||
widthB
|
||||
@@ -0,0 +1,43 @@
|
||||
# Comparing With Custom Output Data
|
||||
|
||||
## Introduction
|
||||
|
||||
In some cases, it may be useful to compare against output values generated outside Polygraphy.
|
||||
The simplest way to do so is to create a `RunResults` object and save it to a file.
|
||||
|
||||
This example illustrates how you can generate custom input and output data outside of Polygraphy
|
||||
and seamlessly load it into Polygraphy for comparison.
|
||||
|
||||
## Running The Example
|
||||
|
||||
1. Generate the input and output data:
|
||||
|
||||
```bash
|
||||
python3 generate_data.py
|
||||
```
|
||||
|
||||
2. **[Optional]** Inspect the data.
|
||||
For inputs:
|
||||
|
||||
```bash
|
||||
polygraphy inspect data custom_inputs.json
|
||||
```
|
||||
|
||||
For outputs:
|
||||
|
||||
```bash
|
||||
polygraphy inspect data custom_outputs.json
|
||||
```
|
||||
|
||||
3. Run inference with the generated input data and then compare outputs against the custom outputs:
|
||||
|
||||
```bash
|
||||
polygraphy run identity.onnx --trt \
|
||||
--load-inputs custom_inputs.json \
|
||||
--load-outputs custom_outputs.json
|
||||
```
|
||||
|
||||
## Further Reading
|
||||
|
||||
For details on how to access and work with the outputs stored in `RunResults` objects
|
||||
using the Python API, refer to [API example 08](../../../api/08_working_with_run_results_and_saved_inputs_manually/).
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
"""
|
||||
Generates input and output data for an identity model and saves it to disk.
|
||||
"""
|
||||
import numpy as np
|
||||
from polygraphy.comparator import RunResults
|
||||
from polygraphy.json import save_json
|
||||
|
||||
INPUT_SHAPE = (1, 1, 2, 2)
|
||||
|
||||
|
||||
# We'll generate arbitrary input data and then "compute" the expected output data before saving both to disk.
|
||||
# In order for Polygraphy to load the input and output data, they must be in the following format:
|
||||
# - Input Data: List[Dict[str, np.ndarray]] (A list of feed_dicts)
|
||||
# - Output Data: RunResults
|
||||
|
||||
|
||||
# Generate arbitrary input data compatible with the model.
|
||||
#
|
||||
# TIP: We could have alternatively used a generator as in `run` example 05 (05_comparing_with_custom_input_data).
|
||||
# In that case, we would simply provide this script to `--data-loader-script` instead of saving the inputs here
|
||||
# and then using `--load-inputs`.
|
||||
input_data = {"x": np.ones(shape=INPUT_SHAPE, dtype=np.float32)}
|
||||
|
||||
# NOTE: Input data must be in a list (to support multiple sets of inputs), so we create one before saving it.
|
||||
# The `description` argument is optional:
|
||||
save_json([input_data], "custom_inputs.json", description="custom input data")
|
||||
|
||||
|
||||
# "Compute" the outputs based on the input data. Since this is an identity model, we can just copy the inputs.
|
||||
output_data = {"y": input_data["x"]}
|
||||
|
||||
# To save output data, we can create a RunResults object:
|
||||
custom_outputs = RunResults()
|
||||
|
||||
# The `add()` helper function allows us to easily add entries.
|
||||
#
|
||||
# NOTE: As with input data, output data must be in a list, so we create one before saving it.
|
||||
#
|
||||
# TIP: Alternatively, we can manually add entries using an approach like:
|
||||
# runner_name = "custom_runner"
|
||||
# custom_outputs[runner_name] = [IterationResult(output_data, runner_name=runner_name), ...]
|
||||
#
|
||||
# TIP: To store outputs from multiple different implementations, you can specify different `runner_name`s to `add()`.
|
||||
# If `runner_name` is omitted, a default is used.
|
||||
custom_outputs.add([output_data], runner_name="custom_runner")
|
||||
custom_outputs.save("custom_outputs.json")
|
||||
@@ -0,0 +1,15 @@
|
||||
backend-test:[
|
||||
|
||||
xy"Identity
|
||||
test_identityZ
|
||||
x
|
||||
|
||||
|
||||
|
||||
|
||||
b
|
||||
y
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# Checking for Intermediate NaN or Infinities
|
||||
|
||||
## Introduction
|
||||
|
||||
When debugging model accuracy issues in Polygraphy, it can be helpful to check layerwise outputs for potential problems. Polygraphy's `run` subtool provides a helpful flag `--validate` which can quickly diagnose problematic intermediate outputs.
|
||||
|
||||
This example demonstrates use of this flag with a model which intentionally generates an
|
||||
infinite output by adding infinity to the input tensor.
|
||||
|
||||
## Running The Example
|
||||
|
||||
<!-- Polygraphy Test: XFAIL Start -->
|
||||
```bash
|
||||
polygraphy run add_infinity.onnx --onnx-outputs mark all --onnxrt --validate
|
||||
```
|
||||
<!-- Polygraphy Test: XFAIL End -->
|
||||
|
||||
<!-- Polygraphy Test: Ignore Start -->
|
||||
You should see output like:
|
||||
```
|
||||
[I] onnxrt-runner-N0-05/13/22-22:35:48 | Completed 1 iteration(s) in 0.1326 ms | Average inference time: 0.1326 ms.
|
||||
[I] Output Validation | Runners: ['onnxrt-runner-N0-05/13/22-22:35:48']
|
||||
[I] onnxrt-runner-N0-05/13/22-22:35:48 | Validating output: B (check_inf=True, check_nan=True)
|
||||
[I] mean=inf, std-dev=nan, var=nan, median=inf, min=inf at (0,), max=inf at (0,), avg-magnitude=inf
|
||||
[E] Inf Detected | One or more non-finite values were encountered in this output
|
||||
[I] Note: Use -vv or set logging verbosity to EXTRA_VERBOSE to display non-finite values
|
||||
[E] FAILED | Errors detected in output: B
|
||||
[E] FAILED | Output Validation
|
||||
```
|
||||
<!-- Polygraphy Test: Ignore End -->
|
||||
|
||||
## See Also
|
||||
|
||||
* [Debugging TensorRT Accuracy Issues](../../../../how-to/debug_accuracy.md)
|
||||
Binary file not shown.
@@ -0,0 +1,138 @@
|
||||
# Adding Precision Constraints
|
||||
|
||||
## Introduction
|
||||
|
||||
When a model trained in FP32 is used to build a TensorRT engine that leverages
|
||||
[reduced precision optimizations](https://docs.nvidia.com/deeplearning/tensorrt/developer-guide/index.html#reduced-precision),
|
||||
certain layers in the model may need to be constrained to run in FP32 to
|
||||
preserve acceptable accuracy.
|
||||
|
||||
The following example demonstrates how to selectively constrain precisions of
|
||||
specified layers in a network. The provided ONNX model does the following:
|
||||
|
||||
1. Flips its input horizontally by right-multiplying by a 90 degree rotated identity matrix,
|
||||
2. Adds `FP16_MAX` to the flipped input, then subtracts `FP16_MAX` from the result,
|
||||
3. Flips the output of the subtraction horizontally by right-multiplying by the rotated identity.
|
||||
|
||||
If `x` is positive, step (2) in this procedure needs to be done in FP32 in
|
||||
order to achieve acceptable accuracy since values will exceed FP16 representable
|
||||
range (by design). However, when FP16 optimizations are enabled without
|
||||
constraints, TensorRT, having no knowledge of what range of values will be used
|
||||
for `x`, will usually choose to run all steps in this process in FP16:
|
||||
|
||||
* The GEMM operations in steps (1) and (3) will run faster in FP16 than in FP32
|
||||
(for large enough problem sizes)
|
||||
|
||||
* The pointwise operations in step (2) will run faster in FP16, and leaving the
|
||||
data in FP16 eliminates the need for additional reformats to/from FP32.
|
||||
|
||||
Hence, you need to constrain the allowed precisions in the TensorRT network in
|
||||
order for TensorRT to make appropriate choices when assigning layer precisions
|
||||
in the engine.
|
||||
|
||||
Polygraphy command-line tools provide multiple methods of constraining layer precisions:
|
||||
|
||||
1. The `--layer-precisions` option allows you to set precisions for individual layers.
|
||||
|
||||
2. A network post-processing script allows you to programmatically modify a TensorRT network
|
||||
parsed or otherwise generated by Polygraphy.
|
||||
|
||||
3. A network loader script allows you to construct the entire TensorRT network manually using the
|
||||
TensorRT Python API. During network construction, you can set layer precisions as desired.
|
||||
|
||||
|
||||
## Running The Example
|
||||
|
||||
**Warning:** _This example requires TensorRT 8.4 or later._
|
||||
|
||||
### Using The `--layer-precisions` Option
|
||||
|
||||
Run the following command to compare running the model with TensorRT using FP16
|
||||
optimizations against ONNX-Runtime in FP32:
|
||||
|
||||
<!-- Polygraphy Test: XFAIL Start -->
|
||||
```bash
|
||||
polygraphy run needs_constraints.onnx \
|
||||
--trt --fp16 --onnxrt --val-range x:[1,2] \
|
||||
--layer-precisions Add:float16 Sub:float32 --precision-constraints prefer \
|
||||
--check-error-stat median
|
||||
```
|
||||
<!-- Polygraphy Test: XFAIL End -->
|
||||
|
||||
To increase the chances that this command fails for the reasons outlined above,
|
||||
we'll force the `Add` to run in FP16 precision and the subsequent `Sub` to run in FP32.
|
||||
This will prevent them from being fused and cause the outputs of `Add` to overflow the FP16 range.
|
||||
|
||||
|
||||
### Using a Network Postprocessing Script to Constrain Precisions
|
||||
|
||||
Another option is to use a TensorRT network postprocessing script to apply precisions on the parsed network.
|
||||
|
||||
Use the provided network postprocessing script [add_constraints.py](./add_constraints.py) to constrain precisions in the model:
|
||||
|
||||
```
|
||||
polygraphy run needs_constraints.onnx --onnxrt --trt --fp16 --precision-constraints obey \
|
||||
--val-range x:[1,2] --check-error-stat median \
|
||||
--trt-network-postprocess-script ./add_constraints.py
|
||||
```
|
||||
|
||||
*TIP: You can use `--trt-npps` as shorthand for `--trt-network-postprocess-script`.*
|
||||
|
||||
By default Polygraphy looks for a function called `postprocess` in the script to execute. To specify
|
||||
a different function to use, suffix the script name with a colon followed by the function name, e.g.
|
||||
|
||||
<!-- Polygraphy Test: Ignore Start -->
|
||||
```
|
||||
polygraphy run ... --trt-npps my_script.py:custom_func
|
||||
```
|
||||
<!-- Polygraphy Test: Ignore End -->
|
||||
|
||||
|
||||
### Using A Network Loader Script To Constrain Precisions
|
||||
|
||||
Alternatively, you can use a network loader script to define the entire network manually,
|
||||
as a part of which you can set layer precisions.
|
||||
|
||||
The below section assumes you have read through the example on
|
||||
[Defining a TensorRT Network or Config Manually](../../../../examples/cli/run/04_defining_a_tensorrt_network_or_config_manually)
|
||||
and have a basic understanding of how to use the [TensorRT Python API](https://docs.nvidia.com/deeplearning/tensorrt/latest/_static/python-api/index.html).
|
||||
|
||||
First, run ONNX-Runtime on the model to generate reference inputs and golden outputs:
|
||||
|
||||
```bash
|
||||
polygraphy run needs_constraints.onnx --onnxrt --val-range x:[1,2] \
|
||||
--save-inputs inputs.json --save-outputs golden_outputs.json
|
||||
```
|
||||
|
||||
Next, run the provided network loader script
|
||||
[constrained_network.py](./constrained_network.py) which constrains precisions
|
||||
in the model, forcing TensorRT to obey constraints, using the saved input and comparing against the saved golden output:
|
||||
|
||||
```bash
|
||||
polygraphy run constrained_network.py --precision-constraints obey \
|
||||
--trt --fp16 --load-inputs inputs.json --load-outputs golden_outputs.json \
|
||||
--check-error-stat median
|
||||
```
|
||||
|
||||
Note that TensorRT may choose to run other layers in the network in FP32 besides
|
||||
the explicitly constrained layers if doing so would result in higher overall
|
||||
engine performance.
|
||||
|
||||
**[Optional]**: Run the network script but allow TensorRT to ignore precision
|
||||
constraints if necessary. This may be required to run the network if TensorRT
|
||||
has no layer implementation that satisfies the requested precision constraints:
|
||||
|
||||
```
|
||||
polygraphy run constrained_network.py --precision-constraints prefer \
|
||||
--trt --fp16 --load-inputs inputs.json --load-outputs golden_outputs.json \
|
||||
--check-error-stat median
|
||||
```
|
||||
|
||||
|
||||
## See Also
|
||||
|
||||
* [Working with Reduced Precision](../../../../how-to/work_with_reduced_precision.md) for a more general guide on how to debug
|
||||
reduced precision optimizations using Polygraphy.
|
||||
* [Defining a TensorRT Network or Config Manually](../../../../examples/cli/run/04_defining_a_tensorrt_network_or_config_manually) for
|
||||
instructions on how to create network script templates.
|
||||
* [TensorRT Python API Reference](https://docs.nvidia.com/deeplearning/tensorrt/latest/_static/python-api/index.html)
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
"""
|
||||
Postprocessing script to add precision constraints to a TensorRT network.
|
||||
"""
|
||||
|
||||
import tensorrt as trt
|
||||
|
||||
|
||||
def postprocess(network):
|
||||
"""
|
||||
Traverses the parsed network and constrains precisions
|
||||
for specific layers to FP32.
|
||||
|
||||
Args:
|
||||
network (trt.INetworkDefinition): The network to modify.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
for layer in network:
|
||||
# Set computation precision for Add and Sub layer to FP32
|
||||
if layer.name in ("Add", "Sub"):
|
||||
layer.precision = trt.float32
|
||||
|
||||
# Set the output precision for the Add layer to FP32. Without this,
|
||||
# the intermediate output data of the Add may be stored as FP16 even
|
||||
# though the computation itself is performed in FP32.
|
||||
if layer.name == "Add":
|
||||
layer.set_output_type(0, trt.float32)
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
"""
|
||||
Parses an ONNX model, then adds precision constraints so specific layers run in FP32.
|
||||
"""
|
||||
|
||||
from polygraphy import func
|
||||
from polygraphy.backend.trt import NetworkFromOnnxPath
|
||||
import tensorrt as trt
|
||||
|
||||
# Load the model, which implements the following network:
|
||||
#
|
||||
# x -> MatMul (I_rot90) -> Add (FP16_MAX) -> Sub (FP16_MAX) -> MatMul (I_rot90) -> out
|
||||
#
|
||||
# Without constraining the subgraph (Add -> Sub) to FP32, this model may
|
||||
# produce incorrect results when run with FP16 optimziations enabled.
|
||||
parse_network_from_onnx = NetworkFromOnnxPath("./needs_constraints.onnx")
|
||||
|
||||
|
||||
@func.extend(parse_network_from_onnx)
|
||||
def load_network(builder, network, parser):
|
||||
"""The below function traverses the parsed network and constrains precisions
|
||||
for specific layers to FP32.
|
||||
|
||||
See examples/cli/run/04_defining_a_tensorrt_network_or_config_manually
|
||||
for more examples using network scripts in Polygraphy.
|
||||
"""
|
||||
for layer in network:
|
||||
# Set computation precision for Add and Sub layer to FP32
|
||||
if layer.name in ("Add", "Sub"):
|
||||
layer.precision = trt.float32
|
||||
|
||||
# Set the output precision for the Add layer to FP32. Without this,
|
||||
# the intermediate output data of the Add may be stored as FP16 even
|
||||
# though the computation itself is performed in FP32.
|
||||
if layer.name == "Add":
|
||||
layer.set_output_type(0, trt.float32)
|
||||
BIN
Binary file not shown.
Reference in New Issue
Block a user