chore: import upstream snapshot with attribution
Docker Image CI / build-ubuntu2004 (push) Waiting to run

This commit is contained in:
wehub-resource-sync
2026-07-13 13:36:55 +08:00
commit c8a779b1bb
1887 changed files with 3245738 additions and 0 deletions
@@ -0,0 +1,48 @@
# Converting To TensorRT And Running Inference
## Introduction
Polygraphy includes a high-level Python API that can convert models
and run inference with various backends. For an overview of the Polygraphy
Python API, see [here](../../../polygraphy/).
In this example, we'll look at how you can leverage the API to easily convert an ONNX
model to TensorRT and run inference with FP16 precision enabled. We'll then save the
engine to a file and see how you can load it again and run inference.
## Running The Example
1. Install prerequisites
* Ensure that TensorRT is installed
* Install other dependencies with `python3 -m pip install -r requirements.txt`
2. **[Optional]** Inspect the model before running the example:
```bash
polygraphy inspect model identity.onnx
```
3. Run the script that builds and runs the engine:
```bash
python3 build_and_run.py
```
4. **[Optional]** Inspect the TensorRT engine built by the example:
```bash
polygraphy inspect model identity.engine
```
5. Run the script that loads the previously built engine, then runs it:
```bash
python3 load_and_run.py
```
## Further Reading
For more details on the Polygraphy Python API, see the
[Polygraphy API reference](https://docs.nvidia.com/deeplearning/tensorrt/latest/_static/polygraphy/index.html).
@@ -0,0 +1,67 @@
#!/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.
#
"""
This script builds and runs a TensorRT engine with FP16 precision enabled
starting from an ONNX identity model.
"""
import numpy as np
from polygraphy.backend.trt import (
CreateConfig,
EngineFromNetwork,
NetworkFromOnnxPath,
SaveEngine,
TrtRunner,
)
def main():
# We can compose multiple lazy loaders together to get the desired conversion.
# In this case, we want ONNX -> TensorRT Network -> TensorRT engine (w/ fp16).
#
# NOTE: `build_engine` is a *callable* that returns an engine, not the engine itself.
# To get the engine directly, you can use the immediately evaluated functional API.
# See examples/api/06_immediate_eval_api for details.
build_engine = EngineFromNetwork(
NetworkFromOnnxPath("identity.onnx"), config=CreateConfig(fp16=True)
) # Note that config is an optional argument.
# To reuse the engine elsewhere, we can serialize and save it to a file.
# The `SaveEngine` lazy loader will return the TensorRT engine when called,
# which allows us to chain it together with other loaders.
build_engine = SaveEngine(build_engine, path="identity.engine")
# Once our loader is ready, inference is simply a matter of constructing a runner,
# activating it with a context manager (i.e. `with TrtRunner(...)`) and calling `infer()`.
#
# NOTE: You can use the activate() function instead of a context manager, but you will need to make sure to
# deactivate() to avoid a memory leak. For that reason, a context manager is the safer option.
with TrtRunner(build_engine) as runner:
inp_data = np.ones(shape=(1, 1, 2, 2), dtype=np.float32)
# NOTE: The runner owns the output buffers and is free to reuse them between `infer()` calls.
# Thus, if you want to store results from multiple inferences, you should use `copy.deepcopy()`.
outputs = runner.infer(feed_dict={"x": inp_data})
assert np.array_equal(outputs["y"], inp_data) # It's an identity model!
print("Inference succeeded!")
if __name__ == "__main__":
main()
@@ -0,0 +1,15 @@
 backend-test:[

xy"Identity
test_identityZ
x




b
y




@@ -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.
#
"""
This script loads the TensorRT engine built by `build_and_run.py` and runs inference.
"""
import numpy as np
from polygraphy.backend.common import BytesFromPath
from polygraphy.backend.trt import EngineFromBytes, TrtRunner
def main():
# Just as we did when building, we can compose multiple loaders together
# to achieve the behavior we want. Specifically, we want to load a serialized
# engine from a file, then deserialize it into a TensorRT engine.
load_engine = EngineFromBytes(BytesFromPath("identity.engine"))
# Inference remains virtually exactly the same as before:
with TrtRunner(load_engine) as runner:
inp_data = np.ones(shape=(1, 1, 2, 2), dtype=np.float32)
# NOTE: The runner owns the output buffers and is free to reuse them between `infer()` calls.
# Thus, if you want to store results from multiple inferences, you should use `copy.deepcopy()`.
outputs = runner.infer(feed_dict={"x": inp_data})
assert np.array_equal(outputs["y"], inp_data) # It's an identity model!
print("Inference succeeded!")
if __name__ == "__main__":
main()
@@ -0,0 +1,45 @@
# Comparing Frameworks
## Introduction
One of the core features of Polygraphy is comparison of model outputs across multiple
different backends. This makes it possible to check the accuracy of one backend with
respect to another.
In this example, we'll look at how you can use the Polygraphy API to run inference
with synthetic input data using ONNX-Runtime and TensorRT, and then compare the results
using two different comparison methods:
1. A simple comparison using absolute tolerance
2. A more comprehensive comparison using distance metrics (L2 distance, cosine similarity, and PSNR)
## Running The Example
1. Install prerequisites
* Ensure that TensorRT is installed
* Install other dependencies with `python3 -m pip install -r requirements.txt`
2. Run the example
```bash
python3 example.py
```
3. **[Optional]** Inspect the inference outputs from the example:
```bash
polygraphy inspect data inference_results.json
```
## Comparison Methods
The example demonstrates two approaches for comparing outputs:
- **Simple Comparison**: Uses absolute tolerance to determine if outputs match within a specified threshold.
- **Distance Metrics**: Performs a more comprehensive comparison using multiple metrics including:
- L2 distance (Euclidean distance)
- Cosine similarity (measures the angle between vectors)
- PSNR (Peak Signal-to-Noise Ratio, useful for comparing image-like data)
These comparison methods help validate that frameworks produce equivalent results within acceptable margins.
@@ -0,0 +1,90 @@
#!/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.
#
"""
This script runs an identity model with ONNX-Runtime and TensorRT,
then compares outputs.
"""
from polygraphy.backend.onnxrt import OnnxrtRunner, SessionFromOnnx
from polygraphy.backend.trt import EngineFromNetwork, NetworkFromOnnxPath, TrtRunner
from polygraphy.comparator import Comparator, CompareFunc
def main():
# The OnnxrtRunner requires an ONNX-RT session.
# We can use the SessionFromOnnx lazy loader to construct one easily:
build_onnxrt_session = SessionFromOnnx("identity.onnx")
# The TrtRunner requires a TensorRT engine.
# To create one from the ONNX model, we can chain a couple lazy loaders together:
build_engine = EngineFromNetwork(NetworkFromOnnxPath("identity.onnx"))
runners = [
TrtRunner(build_engine),
OnnxrtRunner(build_onnxrt_session),
]
# `Comparator.run()` will run each runner separately using synthetic input data and
# return a `RunResults` instance. See `polygraphy/comparator/struct.py` for details.
#
# TIP: To use custom input data, you can set the `data_loader` parameter in `Comparator.run()``
# to a generator or iterable that yields `Dict[str, np.ndarray]`.
run_results = Comparator.run(runners)
# `Comparator.compare_accuracy()` checks that outputs match between runners.
#
# TIP: The `compare_func` parameter can be used to control how outputs are compared (see API reference for details).
# The default comparison function is created by `CompareFunc.simple()`, but we can construct it
# explicitly if we want to change the default parameters, such as tolerance.
assert bool(
Comparator.compare_accuracy(
run_results, compare_func=CompareFunc.simple(atol=1e-8)
)
)
# Use distance metrics comparison for more comprehensive evaluation
assert bool(
Comparator.compare_accuracy(
run_results,
compare_func=CompareFunc.distance_metrics(
l2_tolerance=1e-5, # Maximum allowed L2 norm (Euclidean distance)
cosine_similarity_threshold=0.99, # Minimum cosine similarity (angular similarity)
)
)
)
print("All outputs matched using distance metrics (L2 norm, Cosine Similarity)")
# Use quality metrics for signal quality evaluation
assert bool(
Comparator.compare_accuracy(
run_results,
compare_func=CompareFunc.quality_metrics(
psnr_tolerance=50.0, # Minimum Peak Signal-to-Noise Ratio in dB
snr_tolerance=25.0 # Minimum Signal-to-Noise Ratio in dB
)
)
)
print("All outputs matched using quality metrics (PSNR, SNR)")
# We can use `RunResults.save()` method to save the inference results to a JSON file.
# This can be useful if you want to generate and compare results separately.
run_results.save("inference_results.json")
if __name__ == "__main__":
main()
@@ -0,0 +1,15 @@
 backend-test:[

xy"Identity
test_identityZ
x




b
y




@@ -0,0 +1,2 @@
onnx
onnxruntime
@@ -0,0 +1,34 @@
# Validating Accuracy On A Real Dataset
## Introduction
The `Comparator` provided by Polygraphy can be useful for comparing a small number of
results across multiple runners, but is not well suited for validating a single runner
with a real dataset that includes labels or golden values - especially if the dataset is large.
In such cases, it is recommended to use a runner directly instead.
*NOTE: It is possible to provide custom input data to `Comparator.run()` using the `data_loader`*
*parameter. This may be a viable option when using a smaller dataset.*
In this example, we use a `TrtRunner` directly to validate an identity model on
a trivial dataset. Unlike using the `Comparator`, using a runner gives you complete
freedom as to how you load your input data, as well as how you validate the results.
Since all runners provide the same interface, you can freely drop-in other runners
without touching the rest of your validation code. For example, in this case, validating
the model using ONNX-Runtime would require changing just 2 lines; this is left as an
exercise for the reader.
## Running The Example
1. Install prerequisites
* Ensure that TensorRT is installed
* Install other dependencies with `python3 -m pip install -r requirements.txt`
2. Run the example
```bash
python3 example.py
```
@@ -0,0 +1,54 @@
#!/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.
#
"""
This script uses the Polygraphy Runner API to validate the outputs
of an identity model using a trivial dataset.
"""
import numpy as np
from polygraphy.backend.trt import EngineFromNetwork, NetworkFromOnnxPath, TrtRunner
# Pretend that this is a very large dataset.
REAL_DATASET = [
np.ones((1, 1, 2, 2), dtype=np.float32),
np.zeros((1, 1, 2, 2), dtype=np.float32),
np.ones((1, 1, 2, 2), dtype=np.float32),
np.zeros((1, 1, 2, 2), dtype=np.float32),
] # Definitely real data
# For an identity network, the golden output values are the same as the input values.
# Though such a network appears useless at first glance, it can be very useful in some cases (like here!).
EXPECTED_OUTPUTS = REAL_DATASET
def main():
build_engine = EngineFromNetwork(NetworkFromOnnxPath("identity.onnx"))
with TrtRunner(build_engine) as runner:
for data, golden in zip(REAL_DATASET, EXPECTED_OUTPUTS):
# NOTE: The runner owns the output buffers and is free to reuse them between `infer()` calls.
# Thus, if you want to store results from multiple inferences, you should use `copy.deepcopy()`.
outputs = runner.infer(feed_dict={"x": data})
assert np.array_equal(outputs["y"], golden)
print("Validation succeeded!")
if __name__ == "__main__":
main()
@@ -0,0 +1,15 @@
 backend-test:[

xy"Identity
test_identityZ
x




b
y




@@ -0,0 +1,41 @@
# Interoperating With TensorRT
## Introduction
A key feature of Polygraphy is complete interoperability with TensorRT, as well as
with other backends. Since Polygraphy does not hide the underlying backend APIs,
it is possible to freely switch between using the Polygraphy API and a backend API,
such as TensorRT.
In this example, we'll look at how you can retain access to the advanced functionality
provided by a backend without giving up the conveniences provided by Polygraphy - the
best of both worlds.
Polygraphy provides an `extend` decorator which can be used to easily extend existing
Polygraphy loaders. This can be useful in many scenarios, but for this example,
we will focus on cases where you may want to:
- Modify the TensorRT network prior to building the engine
- Use a TensorRT builder flag not currently supported by Polygraphy
## Running The Example
1. Install prerequisites
* Ensure that TensorRT is installed
* Install other dependencies with `python3 -m pip install -r requirements.txt`
2. **[Optional]** Inspect the TensorRT network generated by `load_network()`.
This will invoke `load_network()` from within the script and display the
generated TensorRT network, which should be named `"MyIdentity"`:
```bash
polygraphy inspect model example.py --trt-network-func load_network --show layers attrs weights
```
3. Run the example:
```bash
python3 example.py
```
@@ -0,0 +1,80 @@
#!/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.
#
"""
This script demonstrates how to use Polygraphy in conjunction with APIs
provided by a backend. Specifically, in this case, we use TensorRT APIs
to print the network name and enable FP16 mode.
"""
import numpy as np
import tensorrt as trt
from polygraphy import func
from polygraphy.backend.trt import (
CreateConfig,
EngineFromNetwork,
NetworkFromOnnxPath,
TrtRunner,
)
# TIP: The immediately evaluated functional API makes it very easy to interoperate
# with backends like TensorRT. For details, see example 06 (`examples/api/06_immediate_eval_api`).
# We can use the `extend` decorator to easily extend lazy loaders provided by Polygraphy
# The parameters our decorated function takes should match the return values of the loader we are extending.
# For `NetworkFromOnnxPath`, we can see from the API documentation that it returns a TensorRT
# builder, network and parser. That is what our function will receive.
@func.extend(NetworkFromOnnxPath("identity.onnx"))
def load_network(builder, network, parser):
# Here we can modify the network. For this example, we'll just set the network name.
network.name = "MyIdentity"
print(f"Network name: {network.name}")
# Notice that we don't need to return anything - `extend()` takes care of that for us!
# In case a builder configuration option is missing from Polygraphy, we can easily set it using TensorRT APIs.
# Our function will receive a TensorRT IBuilderConfig since that's what `CreateConfig` returns.
@func.extend(CreateConfig())
def load_config(config):
# Polygraphy supports the fp16 flag, but in case it didn't, we could do this:
config.set_flag(trt.BuilderFlag.FP16)
def main():
# Since we have no further need of TensorRT APIs, we can come back to regular Polygraphy.
#
# NOTE: Since we're using lazy loaders, we provide the functions as arguments - we do *not* call them ourselves.
build_engine = EngineFromNetwork(load_network, config=load_config)
with TrtRunner(build_engine) as runner:
inp_data = np.ones(shape=(1, 1, 2, 2), dtype=np.float32)
# NOTE: The runner owns the output buffers and is free to reuse them between `infer()` calls.
# Thus, if you want to store results from multiple inferences, you should use `copy.deepcopy()`.
outputs = runner.infer({"x": inp_data})
assert np.array_equal(outputs["y"], inp_data) # It's an identity model!
print("Inference succeeded!")
if __name__ == "__main__":
main()
@@ -0,0 +1,15 @@
 backend-test:[

xy"Identity
test_identityZ
x




b
y




@@ -0,0 +1,46 @@
# Int8 Calibration In TensorRT
## Introduction
Int8 calibration in TensorRT involves providing a representative set of input data
to TensorRT as part of the engine building process. The
[calibration API](https://docs.nvidia.com/deeplearning/tensorrt/latest/_static/python-api/infer/Int8/Calibrator.html)
included in TensorRT requires the user to handle copying input data to the GPU and
manage the calibration cache generated by TensorRT.
While the TensorRT API provides a higher degree of control, we can greatly simplify the
process for many common use-cases. For that purpose, Polygraphy provides a calibrator, which
can be used either with Polygraphy or directly with TensorRT. In the latter
case, the Polygraphy calibrator behaves exactly like a normal TensorRT int8 calibrator.
In this example, we'll look at how you can use Polygraphy's calibrator to calibrate a network
with (fake) calibration data, and how you can manage the calibration cache with just a single
parameter.
## Running The Example
1. Install prerequisites
* Ensure that TensorRT is installed
* Install other dependencies with `python3 -m pip install -r requirements.txt`
2. Run the example:
```bash
python3 example.py
```
3. The first time you run the example, it will create a calibration cache
called `identity-calib.cache`. If you run the example again, you should see that
it now uses the cache instead of running calibration again:
```bash
python3 example.py
```
## Further Reading
For more information on how int8 calibration works in TensorRT, see the
[developer guide](https://docs.nvidia.com/deeplearning/tensorrt/developer-guide/index.html#optimizing_int8_c)
@@ -0,0 +1,73 @@
#!/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.
#
"""
This script demonstrates how to use the Calibrator API provided by Polygraphy
to calibrate a TensorRT engine to run in INT8 precision.
"""
import numpy as np
from polygraphy.backend.trt import (
Calibrator,
CreateConfig,
EngineFromNetwork,
NetworkFromOnnxPath,
TrtRunner,
)
from polygraphy.logger import G_LOGGER
# The data loader argument to `Calibrator` can be any iterable or generator that yields `feed_dict`s.
# A `feed_dict` is just a mapping of input names to corresponding inputs.
def calib_data():
for _ in range(4):
# TIP: If your calibration data is already on the GPU, you can instead provide GPU pointers
# (as `int`s), Polygraphy `DeviceView`s, or PyTorch tensors instead of NumPy arrays.
#
# For details on `DeviceView`, see `polygraphy/cuda/cuda.py`.
yield {"x": np.ones(shape=(1, 1, 2, 2), dtype=np.float32)} # Totally real data
def main():
# We can provide a path or file-like object if we want to cache calibration data.
# This lets us avoid running calibration the next time we build the engine.
#
# TIP: You can use this calibrator with TensorRT APIs directly (e.g. config.int8_calibrator).
# You don't have to use it with Polygraphy loaders if you don't want to.
calibrator = Calibrator(data_loader=calib_data(), cache="identity-calib.cache")
# We must enable int8 mode in addition to providing the calibrator.
build_engine = EngineFromNetwork(
NetworkFromOnnxPath("identity.onnx"),
config=CreateConfig(int8=True, calibrator=calibrator),
)
# When we activate our runner, it will calibrate and build the engine. If we want to
# see the logging output from TensorRT, we can temporarily increase logging verbosity:
with G_LOGGER.verbosity(G_LOGGER.VERBOSE), TrtRunner(build_engine) as runner:
# Finally, we can test out our int8 TensorRT engine with some dummy input data:
inp_data = np.ones(shape=(1, 1, 2, 2), dtype=np.float32)
# NOTE: The runner owns the output buffers and is free to reuse them between `infer()` calls.
# Thus, if you want to store results from multiple inferences, you should use `copy.deepcopy()`.
outputs = runner.infer({"x": inp_data})
assert np.array_equal(outputs["y"], inp_data) # It's an identity model!
if __name__ == "__main__":
main()
@@ -0,0 +1,15 @@
 backend-test:[

xy"Identity
test_identityZ
x




b
y




@@ -0,0 +1,36 @@
# Using The TensorRT Network API
## Introduction
In addition to loading existing models, TensorRT allows you to define networks by hand
using the network API.
In this example, we'll look at how you can use Polygraphy's `extend` decorator, covered in
[example 03](../03_interoperating_with_tensorrt), in conjunction with the `CreateNetwork`
loader to seamlessly integrate a network defined using TensorRT APIs with Polygraphy.
## Running The Example
1. Install prerequisites
* Ensure that TensorRT is installed
* Install other dependencies with `python3 -m pip install -r requirements.txt`
2. **[Optional]** Inspect the TensorRT network generated by `create_network()`.
This will invoke `create_network()` from within the script and display the generated TensorRT network:
```bash
polygraphy inspect model example.py --trt-network-func create_network --show layers attrs weights
```
3. Run the example:
```bash
python3 example.py
```
## Further Reading
For more information on the TensorRT Network API, see the
[TensorRT API documentation](https://docs.nvidia.com/deeplearning/tensorrt/latest/_static/python-api/infer/Graph/Network.html)
@@ -0,0 +1,74 @@
#!/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.
#
"""
This script demonstrates how to use the extend() API covered in example 03
to construct a TensorRT network using the TensorRT Network API.
"""
import numpy as np
import tensorrt as trt
from polygraphy import func
from polygraphy.backend.trt import CreateNetwork, EngineFromNetwork, TrtRunner
INPUT_NAME = "input"
INPUT_SHAPE = (64, 64)
OUTPUT_NAME = "output"
# Just like in example 03, we can use `extend` to add our own functionality to existing lazy loaders.
# `CreateNetwork` will create an empty network, which we can then populate ourselves.
@func.extend(CreateNetwork())
def create_network(builder, network):
# This network will add 1 to the input tensor.
inp = network.add_input(name=INPUT_NAME, shape=INPUT_SHAPE, dtype=trt.float32)
ones = network.add_constant(
shape=INPUT_SHAPE, weights=np.ones(shape=INPUT_SHAPE, dtype=np.float32)
).get_output(0)
add = network.add_elementwise(
inp, ones, op=trt.ElementWiseOperation.SUM
).get_output(0)
add.name = OUTPUT_NAME
network.mark_output(add)
# Notice that we don't need to return anything - `extend()` takes care of that for us!
def main():
# After we've constructed the network, we can go back to using regular Polygraphy APIs.
#
# NOTE: Since we're using lazy loaders, we provide the `create_network` function as
# an argument - we do *not* call it ourselves.
build_engine = EngineFromNetwork(create_network)
with TrtRunner(build_engine) as runner:
feed_dict = {
INPUT_NAME: np.random.random_sample(INPUT_SHAPE).astype(np.float32)
}
# NOTE: The runner owns the output buffers and is free to reuse them between `infer()` calls.
# Thus, if you want to store results from multiple inferences, you should use `copy.deepcopy()`.
outputs = runner.infer(feed_dict)
assert np.array_equal(outputs[OUTPUT_NAME], (feed_dict[INPUT_NAME] + 1))
print("Inference succeeded!")
if __name__ == "__main__":
main()
@@ -0,0 +1,92 @@
# Immediately Evaluated Functional API
## Introduction
<!-- Polygraphy Test: Ignore Start -->
Most of the time, the lazy loaders included with Polygraphy have several advantages:
- They allow us to defer the work until we actually need to do it, which can potentially save
time.
- Since constructed loaders are extremely light-weight, runners using lazily evaluated loaders can be
easily copied into other processes or threads, where they can then be launched.
If runners instead referenced entire models/inference sessions, it would be non-trivial to copy them in this way.
- They allow us to define a sequence of operations in advance by chaining loaders together, which
provides an easy way to build reusable functions.
For example, we could create a loader that imports a model from ONNX and generates a serialized TensorRT Engine:
```python
build_engine = EngineBytesFromNetwork(NetworkFromOnnxPath("/path/to/model.onnx"))
```
- They allow for special semantics where if a callable is provided to a loader, it takes ownership
of the return value, whereas otherwise it does not. These special semantics are useful for
sharing objects between multiple loaders.
However, this can sometimes lead to code that is less readable, or even downright confusing.
For example, consider the following:
```python
# Each line in this example looks almost the same, but has significantly
# different behavior. Some of these lines even cause memory leaks!
EngineBytesFromNetwork(NetworkFromOnnxPath("/path/to/model.onnx")) # This is a loader instance, not an engine!
EngineBytesFromNetwork(NetworkFromOnnxPath("/path/to/model.onnx"))() # This is an engine.
EngineBytesFromNetwork(NetworkFromOnnxPath("/path/to/model.onnx")()) # And it's a loader instance again...
EngineBytesFromNetwork(NetworkFromOnnxPath("/path/to/model.onnx")())() # Back to an engine!
EngineBytesFromNetwork(NetworkFromOnnxPath("/path/to/model.onnx"))()() # This throws - can you see why?
```
For that reason, Polygraphy provides immediately-evaluated functional
equivalents of each loader. Each functional variant uses the same name as the loader, but
`snake_case` instead of `PascalCase`. Using the functional variants, loader code like:
```python
parse_network = NetworkFromOnnxPath("/path/to/model.onnx")
create_config = CreateConfig(fp16=True, tf32=True)
build_engine = EngineFromNetwork(parse_network, create_config)
engine = build_engine()
```
becomes:
```python
builder, network, parser = network_from_onnx_path("/path/to/model.onnx")
config = create_config(builder, network, fp16=True, tf32=True)
engine = engine_from_network((builder, network, parser), config)
```
<!-- Polygraphy Test: Ignore End -->
In this example, we'll look at how you can leverage the functional API to convert an ONNX
model to a TensorRT network, modify the network, build a TensorRT engine with FP16 precision
enabled, and run inference.
We'll also save the engine to a file to see how you can load it again and run inference.
## Running The Example
1. Install prerequisites
* Ensure that TensorRT is installed
* Install other dependencies with `python3 -m pip install -r requirements.txt`
2. **[Optional]** Inspect the model before running the example:
```bash
polygraphy inspect model identity.onnx
```
3. Run the script that builds and runs the engine:
```bash
python3 build_and_run.py
```
4. **[Optional]** Inspect the TensorRT engine built by the example:
```bash
polygraphy inspect model identity.engine
```
5. Run the script that loads the previously built engine, then runs it:
```bash
python3 load_and_run.py
```
@@ -0,0 +1,74 @@
#!/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.
#
"""
This script uses Polygraphy's immediately evaluated functional APIs
to load an ONNX model, convert it into a TensorRT network, add an identity
layer to the end of it, build an engine with FP16 mode enabled,
save the engine, and finally run inference.
"""
import numpy as np
from polygraphy.backend.trt import (
TrtRunner,
create_config,
engine_from_network,
network_from_onnx_path,
save_engine,
)
def main():
# In Polygraphy, loaders and runners take ownership of objects if they are provided
# via the return values of callables. For example, we don't need to worry about object
# lifetimes when we use lazy loaders.
#
# Since we are immediately evaluating, we take ownership of objects, and are responsible for freeing them.
builder, network, parser = network_from_onnx_path("identity.onnx")
# Extend the network with an identity layer (purely for the sake of example).
# Note that unlike with lazy loaders, we don't need to do anything special to modify the network.
# If we were using lazy loaders, we would need to use `func.extend()` as described
# in example 03 and example 05.
prev_output = network.get_output(0)
network.unmark_output(prev_output)
output = network.add_identity(prev_output).get_output(0)
output.name = "output"
network.mark_output(output)
# Create a TensorRT IBuilderConfig so that we can build the engine with FP16 enabled.
config = create_config(builder, network, fp16=True)
engine = engine_from_network((builder, network), config)
# To reuse the engine elsewhere, we can serialize it and save it to a file.
save_engine(engine, path="identity.engine")
with TrtRunner(engine) as runner:
inp_data = np.ones((1, 1, 2, 2), dtype=np.float32)
# NOTE: The runner owns the output buffers and is free to reuse them between `infer()` calls.
# Thus, if you want to store results from multiple inferences, you should use `copy.deepcopy()`.
outputs = runner.infer(feed_dict={"x": inp_data})
assert np.array_equal(outputs["output"], inp_data) # It's an identity model!
print("Inference succeeded!")
if __name__ == "__main__":
main()
@@ -0,0 +1,15 @@
 backend-test:[

xy"Identity
test_identityZ
x




b
y




@@ -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.
#
"""
This script uses Polygraphy's immediately evaluated functional APIs
to load the TensorRT engine built by `build_and_run.py` and run inference.
"""
import numpy as np
from polygraphy.backend.common import bytes_from_path
from polygraphy.backend.trt import TrtRunner, engine_from_bytes
def main():
engine = engine_from_bytes(bytes_from_path("identity.engine"))
with TrtRunner(engine) as runner:
inp_data = np.ones((1, 1, 2, 2), dtype=np.float32)
# NOTE: The runner owns the output buffers and is free to reuse them between `infer()` calls.
# Thus, if you want to store results from multiple inferences, you should use `copy.deepcopy()`.
outputs = runner.infer(feed_dict={"x": inp_data})
assert np.array_equal(outputs["output"], inp_data) # It's an identity model!
print("Inference succeeded!")
if __name__ == "__main__":
main()
@@ -0,0 +1 @@
numpy
@@ -0,0 +1,121 @@
# Using Dynamic Shapes With TensorRT
## Introduction
*NOTE: This example is intended for use with TensorRT 8.0 or newer.*
*Older versions may require slight modifications to the example code.*
In order to use dynamic input shapes with TensorRT, we have to specify a range
(or multiple ranges) of possible shapes when we build the engine.
TensorRT optimization profiles provide the means of doing so.
Using the TensorRT API, the process involves two steps:
1. During engine building, specify one or more optimization profiles.
An optimization profile includes 3 shapes for each input:
- `min`: The minimum shape for which the profile should work.
- `opt`: The shape which TensorRT should optimize for.
Generally, you'd want this to correspond to the most commonly used shape.
- `max`: The maximum shape for which the profile should work.
2. During inference, set the input shape(s) in the execution context, then
use the `IOutputAllocator` API to provide a callback to allocate enough
device memory for the outputs.
Polygraphy can simplify both steps and help you avoid common pitfalls:
1. It provides a `Profile` abstraction, which is an `OrderedDict` that
can be converted to a TensorRT `IOptimizationProfile` and includes some utility functions:
- `fill_defaults`: Fills the profile with default shapes based on the network.
- `to_trt`: Creates a TensorRT `IOptimizationProfile` using the shapes in this `Profile`.
What's more, `Profile` will automatically handle complexities like the
distinction between shape-tensor vs. non-shape-tensor inputs - you do not
need to worry about this distinction yourself.
2. The `TrtRunner` will automatically handle dynamic shapes in the model.
As in `Profile`, distinctions between shape-tensor and non-shape-tensor inputs
are handled automatically.
Additionally, the runner will only update the context binding shapes when required,
as changing the shapes has a small overhead. The output device buffers will only
be resized if their current size is smaller that the context outputs, thus avoiding
unnecessary reallocation.
### Setting The Stage
For the sake of this example, we'll imagine a hypothetical scenario:
We're running an inference workload using an image classification model.
Normally, we use this model in an online scenario - i.e. we want the lowest possible
latency, so we'll process one image at a time.
For this case, assume `batch_size` is `[1]`.
However, if we have too many users, then we need to employ dynamic batching so that
our throughput doesn't suffer. Our range of batch sizes is still small to
keep the latency acceptable. Our most frequently used batch size is 4.
For this case, assume `batch_size` is in the range `[1, 32]`.
In even rarer cases, we need to process large amounts of data offline. In this case,
we use a very large batch size to improve our throughput.
For this case, assume `batch_size` is `[128]`.
### Performance Considerations
In implementing our inference pipeline, we need to consider a few tradeoffs:
- A profile with a large range will not perform as well as for the entire range as
multiple profiles each with smaller ranges.
- Switching shapes within a profile has a small but non-zero cost.
- Switching profiles within a context has a larger cost than switching shapes within a profile.
- We can avoid the cost of switching profiles by creating a separate execution context
for each profile and selecting the appropriate context at runtime.
However, keep in mind that each context will require some additional memory.
### A Possible Solution
Assuming the image size is `(3, 28, 28)`, we'll create three separate
optimization profiles, and a separate context for each:
1. For the low latency case:
`min=(1, 3, 28, 28), opt=(1, 3, 28, 28), max=(1, 3, 28, 28)`
2. For the dynamic batching case:
`min=(1, 3, 28, 28), opt=(4, 3, 28, 28), max=(32, 3, 28, 28)`
Note that we use a batch size of `4` for `opt` since that's the most common case.
3. For the offline case:
`min=(128, 3, 28, 28), opt=(128, 3, 28, 28), max=(128, 3, 28, 28)`
For each context, we'll create a corresponding `TrtRunner`. If we make sure that
we own the engine and the context (by not providing them via lazy loaders), then
the cost of activating a runner should be small - it just needs to allocate
input and output buffers. Hence, we'll be able to activate runners on-demand quickly.
## Running The Example
1. Install prerequisites
* Ensure that TensorRT is installed
* Install other dependencies with `python3 -m pip install -r requirements.txt`
2. Run the example:
```bash
python3 example.py
```
3. **[Optional]** Inspect the generated engine:
```bash
polygraphy inspect model dynamic_identity.engine
```
## Further Reading
For more information on using dynamic shapes with TensorRT, see the
[developer guide](https://docs.nvidia.com/deeplearning/tensorrt/developer-guide/index.html#work_dynamic_shapes)
@@ -0,0 +1,12 @@
:[

XY"Identityonnx_dynamic_identityZ%
X


batch_size


b
Y
B
@@ -0,0 +1,154 @@
#!/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.
#
"""
This script builds an engine with 3 separate optimization profiles, each
built for a specific use-case. It then creates 3 separate execution contexts
and corresponding `TrtRunner`s for inference.
"""
import numpy as np
from polygraphy.backend.trt import (
CreateConfig,
Profile,
TrtRunner,
engine_from_network,
network_from_onnx_path,
save_engine,
)
from polygraphy.logger import G_LOGGER
def main():
# A Profile maps each input tensor to a range of shapes.
# The `add()` method can be used to add shapes for a single input.
#
# TIP: To save lines, calls to `add` can be chained:
# profile.add("input0", ...).add("input1", ...)
#
# Of course, you may alternatively write this as:
# profile.add("input0", ...)
# profile.add("input1", ...)
#
profiles = [
# The low-latency case. For best performance, min == opt == max.
Profile().add("X", min=(1, 3, 28, 28), opt=(1, 3, 28, 28), max=(1, 3, 28, 28)),
# The dynamic batching case. We use `4` for the opt batch size since that's our most common case.
Profile().add("X", min=(1, 3, 28, 28), opt=(4, 3, 28, 28), max=(32, 3, 28, 28)),
# The offline case. For best performance, min == opt == max.
Profile().add(
"X", min=(128, 3, 28, 28), opt=(128, 3, 28, 28), max=(128, 3, 28, 28)
),
]
# See examples/api/06_immediate_eval_api for details on immediately evaluated functional loaders like `engine_from_network`.
# Note that we can freely mix lazy and immediately-evaluated loaders.
engine = engine_from_network(
network_from_onnx_path("dynamic_identity.onnx"),
config=CreateConfig(profiles=profiles),
)
# We'll save the engine so that we can inspect it with `inspect model`.
# This should make it easy to see how the engine bindings are laid out.
save_engine(engine, "dynamic_identity.engine")
# We'll create, but not activate, three separate runners, each with a separate context.
#
# TIP: By providing a context directly, as opposed to via a lazy loader,
# we can ensure that the runner will *not* take ownership of it.
#
low_latency = TrtRunner(engine.create_execution_context())
# NOTE: The following two lines may cause TensorRT to display errors since profile 0
# is already in use by the first execution context. We'll suppress them using G_LOGGER.verbosity().
#
with G_LOGGER.verbosity(G_LOGGER.CRITICAL):
# We can use the `optimization_profile` parameter of the runner to ensure that the correct optimization profile is used.
# This eliminates the need to call `set_profile()` later.
dynamic_batching = TrtRunner(
engine.create_execution_context(), optimization_profile=1
) # Use the second profile, which is intended for dynamic batching.
# For the sake of example, we *won't* use `optimization_profile` here.
# Instead, we'll use `set_profile()` after activating the runner.
offline = TrtRunner(engine.create_execution_context())
# Finally, we can activate the runners as we need them.
#
# NOTE: Since the context and engine are already created, the runner will only need to
# allocate input and output buffers during activation.
input_img = np.ones((1, 3, 28, 28), dtype=np.float32) # An input "image"
with low_latency:
outputs = low_latency.infer({"X": input_img})
assert np.array_equal(outputs["Y"], input_img) # It's an identity model!
print("Low latency runner succeeded!")
# While we're serving requests online, we might decide that we need dynamic batching
# for a moment.
#
# NOTE: We're assuming that activating runners will be cheap here, so we can bring up
# the dynamic batching runner just-in-time.
#
# TIP: If activating the runner is not cheap (e.g. input/output buffers are large),
# it might be better to keep the runner active the whole time.
#
with dynamic_batching:
# We'll create fake batches by repeating our fake input image.
small_input_batch = np.repeat(input_img, 4, axis=0) # Shape: (4, 3, 28, 28)
outputs = dynamic_batching.infer({"X": small_input_batch})
assert np.array_equal(outputs["Y"], small_input_batch)
# If we need dynamic batching again later, we can activate the runner once more.
#
# NOTE: This time, we do *not* need to set the profile.
#
with dynamic_batching:
# NOTE: We can use any shape that's in the range of the profile without
# additional setup - Polygraphy handles the details behind the scenes!
#
large_input_batch = np.repeat(input_img, 16, axis=0) # Shape: (16, 3, 28, 28)
outputs = dynamic_batching.infer({"X": large_input_batch})
assert np.array_equal(outputs["Y"], large_input_batch)
print("Dynamic batching runner succeeded!")
with offline:
# NOTE: When we first activate this runner, we need to set the profile index (it's 0 by default).
# Since we provided our own execution context when we created the runner, we need to do this *only once*.
# Our settings persist since the context will remain alive even after the runner is deactivated.
# If we had instead allowed the runner to own the context, we'd need to repeat this step each time we activated the runner.
#
# Alternatively, we could have used the `optimization_profile` parameter (see above).
#
offline.set_profile(
2
) # Use the third profile, which is intended for the offline case.
large_offline_batch = np.repeat(
input_img, 128, axis=0
) # Shape: (128, 3, 28, 28)
outputs = offline.infer({"X": large_offline_batch})
assert np.array_equal(outputs["Y"], large_offline_batch)
print("Offline runner succeeded!")
if __name__ == "__main__":
main()
@@ -0,0 +1,47 @@
# Working With Run Results And Saved Inputs Manually
## Introduction
Inference inputs and outputs from `Comparator.run` can be serialized and saved to JSON
files so they can be reused. Inputs are stored as `List[Dict[str, np.ndarray]]` while outputs
are stored in a `RunResults` object, which can keep track of the outputs of multiple runners
from multiple inference iterations.
Command-line tools providing `--save-inputs` and `--save-outputs` options generally use these formats.
Usually, you'll only use saved inputs or `RunResults` with other Polygraphy APIs or
tools (as in [this example](../../cli//run/06_comparing_with_custom_output_data/)
or [this one](../../cli/inspect/05_inspecting_inference_outputs/)), but sometimes,
you may want to work with the underlying NumPy arrays manually.
Polygraphy includes convenience APIs that make it easy to load and manipulate these objects.
This example illustrates how you can load saved inputs and/or `RunResults` from a file
using the Python API and then access the NumPy arrays stored within.
## Running The Example
1. Generate some inference inputs and outputs:
```bash
polygraphy run identity.onnx --trt --onnxrt \
--save-inputs inputs.json --save-outputs outputs.json
```
2. **[Optional]** Use `inspect data` to view the inputs on the command-line:
```bash
polygraphy inspect data inputs.json --show-values
```
3. **[Optional]** Use `inspect data` to view the outputs on the command-line:
```bash
polygraphy inspect data outputs.json --show-values
```
4. Run the example:
```bash
python3 example.py
```
@@ -0,0 +1,75 @@
#!/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.
#
"""
This script demonstrates how to use the `load_json` and `RunResults` APIs to load
and manipulate inference inputs and outputs respectively.
"""
from polygraphy.comparator import RunResults
from polygraphy.json import load_json
def main():
# Use the `load_json` API to load inputs from file.
#
# NOTE: The `save_json` and `load_json` standalone helpers should be used only with non-Polygraphy objects.
# Polygraphy objects that support serialization include `save` and `load` methods.
inputs = load_json("inputs.json")
# Inputs are stored as a `List[Dict[str, np.ndarray]]`, i.e. a list of feed_dicts,
# where each feed_dict maps input names to NumPy arrays.
#
# TIP: In the typical case, we'll only have one iteration, so we'll only look at the first item.
# If you need to access inputs from multiple iterations, you can do something like this instead:
#
# for feed_dict in inputs:
# for name, array in feed_dict.items():
# ... # Do something with the inputs here
#
[feed_dict] = inputs
for name, array in feed_dict.items():
print(f"Input: '{name}' | Values:\n{array}")
# Use the `RunResults.load` API to load results from file.
#
# TIP: You can provide either a file path or a file-like object here.
results = RunResults.load("outputs.json")
# The `RunResults` object is structured like a `Dict[str, List[IterationResult]]``,
# mapping runner names to inference outputs from one or more iterations.
# An `IterationResult` behaves just like a `Dict[str, np.ndarray]` mapping output names
# to NumPy arrays.
#
# TIP: In the typical case, we'll only have one iteration, so we can unpack it
# directly in the loop. If you need to access outputs from multiple iterations,
# you can do something like this instead:
#
# for runner_name, iters in results.items():
# for outputs in iters:
# ... # Do something with the outputs here
#
for runner_name, [outputs] in results.items():
print(f"\nProcessing outputs for runner: {runner_name}")
# Now you can read or modify the outputs for each runner.
# For the sake of this example, we'll just print them:
for name, array in outputs.items():
print(f"Output: '{name}' | Values:\n{array}")
if __name__ == "__main__":
main()
@@ -0,0 +1,15 @@
 backend-test:[

xy"Identity
test_identityZ
x




b
y




@@ -0,0 +1,33 @@
# Working With PyTorch Tensors
## Introduction
Some runners like `OnnxrtRunner` and `TrtRunner` can accept and return PyTorch tensors
in addition to NumPy arrays. When PyTorch tensors are provided in the inputs, the runner
will return the outputs as PyTorch tensors as well. This can be especially useful in
cases where PyTorch supports a data type that is not supported by NumPy, such as BFloat16.
Polygraphy's included TensorRT `Calibrator` can also accept PyTorch tensors directly.
This example uses PyTorch tensors on the GPU where possible (i.e. if a GPU-enabled version
of PyTorch is installed). When the tensors already reside on GPU memory, no additional copies
are required in the runner/calibrator.
## Running The Example
1. Install prerequisites
* Ensure that TensorRT is installed
* Install other dependencies with `python3 -m pip install -r requirements.txt`
2. Run the example:
```bash
python3 example.py
```
## See Also
* [Inference With TensorRT](../00_inference_with_tensorrt/)
* [INT8 Calibration In TensorRT](../04_int8_calibration_in_tensorrt/)
@@ -0,0 +1,76 @@
#!/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.
#
"""
This script demonstrates how to use PyTorch tensors with the TensorRT runner and calibrator.
"""
import torch
from polygraphy.backend.trt import (
Calibrator,
CreateConfig,
TrtRunner,
engine_from_network,
network_from_onnx_path,
)
# If your PyTorch installation has GPU support, then we'll allocate the tensors
# directly in GPU memory. This will mean that the calibrator and runner can skip the
# host-to-device copy we would otherwise incur with NumPy arrays.
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
def calib_data():
for _ in range(4):
yield {"x": torch.ones((1, 1, 2, 2), dtype=torch.float32, device=DEVICE)}
def main():
calibrator = Calibrator(data_loader=calib_data())
engine = engine_from_network(
network_from_onnx_path("identity.onnx"),
config=CreateConfig(int8=True, calibrator=calibrator),
)
with TrtRunner(engine) as runner:
inp_data = torch.ones((1, 1, 2, 2), dtype=torch.float32, device=DEVICE)
# NOTE: The runner owns the output buffers and is free to reuse them between `infer()` calls.
# Thus, if you want to store results from multiple inferences, you should use `copy.deepcopy()`.
#
# When you provide PyTorch tensors in the feed_dict, the runner will try to use
# PyTorch tensors for the outputs. Specifically:
# - If the `copy_outputs_to_host` argument to `infer()` is set to `True` (the default),
# it will return PyTorch tensors in CPU memory.
# - If `copy_outputs_to_host` is `False`, it will return:
# - PyTorch tensors in GPU memory if you have a GPU-enabled PyTorch installation.
# - Polygraphy `DeviceView`s otherwise.
#
outputs = runner.infer({"x": inp_data})
# `copy_outputs_to_host` defaults to True, so the outputs should be PyTorch
# tensors in CPU memory.
assert isinstance(outputs["y"], torch.Tensor)
assert outputs["y"].device.type == "cpu"
assert torch.equal(outputs["y"], inp_data.to("cpu")) # It's an identity model!
if __name__ == "__main__":
main()
@@ -0,0 +1,15 @@
 backend-test:[

xy"Identity
test_identityZ
x




b
y




@@ -0,0 +1,2 @@
tensorrt>=8.5
torch>=1.13.0
+64
View File
@@ -0,0 +1,64 @@
# Polygraphy Python API Examples
This directory includes examples that use the Polygraphy Python API.
For examples of the command-line tools, see the [cli](../cli/) directory instead.
You may find it useful to read the [Python API Overview](../../polygraphy/) prior
to looking at the API examples.
## Generating Your Own Examples
In the event that the examples here do not cover a particular use-case, you can typically
use `polygraphy run` to fill the gap; `polygraphy run` is capable of dynamically generating
Python scripts that use the Polygraphy API that do exactly what the tool would otherwise do.
Thus, if `polygraphy run` includes functionality you need, but you cannot find a
corresponding API example, try running:
```bash
polygraphy run --gen - <options...>
```
The argument to `--gen` should be the name of the file in which to write the generated script.
The special value `-` corresponds to `stdout`.
For example, running:
```bash
polygraphy run --gen - model.onnx --trt --onnxrt
```
will display something like this on `stdout`:
```py
#!/usr/bin/env python3
# Template auto-generated by polygraphy [v0.31.0] on 01/01/20 at 10:10:10
# Generation Command: polygraphy run --gen - model.onnx --trt --onnxrt
# This script compares model.onnx between TensorRT and ONNX-Runtime
from polygraphy.logger import G_LOGGER
from polygraphy.backend.onnxrt import OnnxrtRunner, SessionFromOnnx
from polygraphy.backend.trt import EngineFromNetwork, NetworkFromOnnxPath, TrtRunner
from polygraphy.comparator import Comparator
import sys
# Loaders
parse_network_from_onnx = NetworkFromOnnxPath('model.onnx')
build_engine = EngineFromNetwork(parse_network_from_onnx)
build_onnxrt_session = SessionFromOnnx('model.onnx')
# Runners
runners = [
TrtRunner(build_engine),
OnnxrtRunner(build_onnxrt_session),
]
# Runner Execution
results = Comparator.run(runners)
success = True
# Accuracy Comparison
success &= bool(Comparator.compare_accuracy(results))
# Report Results
cmd_run = ' '.join(sys.argv)
if not success:
G_LOGGER.critical(f"FAILED | Command: {cmd_run}"))
G_LOGGER.finish(f"PASSED | Command: {cmd_run}"))
```