258 lines
9.9 KiB
Markdown
258 lines
9.9 KiB
Markdown
# Polygraphy Python API
|
|
|
|
## Table of Contents
|
|
|
|
- [Introduction](#introduction)
|
|
- [Backends](#backends)
|
|
- [Loaders](#loaders)
|
|
- [Runners](#runners)
|
|
- [Writing A Custom Runner](#writing-a-custom-runner)
|
|
- [Comparator](#comparator)
|
|
- [Data Loaders](#data-loaders)
|
|
- [Logger](#logger)
|
|
- [Putting It All Together](#putting-it-all-together)
|
|
- [Enabling PyTorch](#enabling-pytorch)
|
|
- [Examples](#examples)
|
|
- [Python API Reference Documentation](#python-api-reference-documentation)
|
|
- [Building Python API Documentation Locally](#building-python-api-documentation-locally)
|
|
- [Deprecation Policy](#deprecation-policy)
|
|
|
|
## Introduction
|
|
|
|
The Polygraphy API consists broadly of two major components:
|
|
[`Backend`s](#backends) and the [`Comparator`](#comparator).
|
|
|
|
**NOTE:** To help you get started with the API, you can use the [`run`](./tools/run/) tool
|
|
with the `--gen-script` option to auto-generate template scripts that use the Polygraphy API.
|
|
|
|
> :warning: Any APIs not documented in the [API reference documentation](#python-api-reference-documentation)
|
|
should be considered internal only and do not adhere to the [deprecation policy](#deprecation-policy)
|
|
for public APIs. Thus, they may be modified or removed at any time without warning.
|
|
Avoid using undocumented APIs!
|
|
|
|
|
|
## Backends
|
|
|
|
A Polygraphy backend provides an interface for a deep learning framework.
|
|
Backends are comprised of two components: Loaders and Runners.
|
|
|
|
|
|
### Loaders
|
|
|
|
A `Loader` is a functor or callable that loads or operates on models in some way.
|
|
|
|
Existing `Loader`s can be composed for more advanced behaviors.
|
|
For example, we can implement a conversion like `ONNX -> TensorRT Network -> TensorRT Engine`:
|
|
|
|
```python
|
|
from polygraphy.backend.trt import EngineFromNetwork, NetworkFromOnnxPath
|
|
|
|
build_engine = EngineFromNetwork(NetworkFromOnnxPath("/path/to/model.onnx"))
|
|
```
|
|
|
|
`build_engine` is a callable that will build a TensorRT engine.
|
|
|
|
Polygraphy also provides immediately evaluated functional variants of each Loader.
|
|
These use the same names, except `snake_case` instead of `PascalCase`, and expose the same APIs.
|
|
Using the functional loaders, the conversion above would be:
|
|
|
|
```python
|
|
from polygraphy.backend.trt import engine_from_network, network_from_onnx_path
|
|
|
|
engine = engine_from_network(network_from_onnx_path("/path/to/model.onnx"))
|
|
```
|
|
|
|
`engine` is a TensorRT engine as opposed to a callable.
|
|
|
|
|
|
### Runners
|
|
|
|
A `Runner` uses a loader to load a model and can then run inference (see [`BaseRunner`](./backend/base/runner.py)).
|
|
|
|
**IMPORTANT:** Runners may reuse their output buffers. Thus, if you need to save outputs from multiple inferences, you should
|
|
make a copy of the outputs with `copy.deepcopy(outputs)`.
|
|
|
|
To use a runner, you just need to activate it, and then call `infer()`.
|
|
Note that activating a runner can be very expensive, so you should minimize the
|
|
number of times you activate a runner - ideally do not do this more than once.
|
|
|
|
It is recommended to use a context manager to activate and deactivate the
|
|
runner rather than calling the functions manually:
|
|
|
|
```python
|
|
from polygraphy.backend.trt import TrtRunner
|
|
|
|
with TrtRunner(build_engine) as runner:
|
|
outputs = runner.infer(feed_dict={"input0": input_data})
|
|
```
|
|
|
|
#### Writing A Custom Runner
|
|
|
|
Generally, you do not need to write custom runners unless you want to support a new backend.
|
|
|
|
In case you do, in the simplest case, you only need to implement two functions:
|
|
- `infer_impl`: Accepts a dictionary of numpy buffers, runs inference, and finally returns a dictionary containing the outputs.
|
|
- `get_input_metadata_impl`: Returns a [`TensorMetadata`](./common/struct.py) mapping input names to their shapes and data types.
|
|
You may use `None`, negative numbers, or strings to indicate dynamic dimensions.
|
|
|
|
For more advanced runners, where some setup is required, you may also need to implement the `activate_impl()` and `deactivate_impl()` functions.
|
|
|
|
For example, in the `TrtRunner`, engines are created in `activate_impl()` and destroyed in `deactivate_impl()`.
|
|
Importantly, the GPU is *not used at all* until these functions have been called (notice, for example,
|
|
that in the `TrtRunner`, the CUDA runtime library is only loaded in the `activate_impl()` function).
|
|
This allows the `Comparator` to optionally provide each runner with exclusive access to the GPU, to prevent any interference between runners.
|
|
|
|
|
|
## Comparator
|
|
|
|
The `Comparator` is used to run inference for runners, and then compare accuracy (see [Comparator.py](./comparator/comparator.py)).
|
|
This process is divided into two phases:
|
|
|
|
1. Running inference:
|
|
|
|
```python
|
|
run_results = Comparator.run(runners)
|
|
```
|
|
|
|
This function accepts a list of runners and returns a `RunResults` object (see [Comparator.py](./comparator/comparator.py))
|
|
containing the inference outputs of each run.
|
|
It also accepts an optional `data_loader` argument to control the input data. If not provided, it will use the
|
|
default data loader. `Comparator.run()` continues until inputs from the data loader are exhausted.
|
|
|
|
2. Comparing results:
|
|
|
|
```python
|
|
Comparator.compare_accuracy(run_results)
|
|
```
|
|
|
|
This function accepts the results returned by `Comparator.run` and compares them between runners.
|
|
|
|
**IMPORTANT:** The Comparator is designed for scenarios where you need to compare a small number
|
|
of inputs across multiple runners. It is **not** a good idea to use it
|
|
to validate a model with an entire dataset! Instead, runners should be used directly for such
|
|
cases (see the [example](../examples/api/02_validating_on_a_dataset)).
|
|
|
|
|
|
### Data Loaders
|
|
|
|
A data loader is used by the `Comparator` to load input data to feed to each runner
|
|
(for example, see Polygraphy's [default data loader](./comparator/data_loader.py)).
|
|
|
|
A data loader can be any generator or iterable that yields
|
|
a dictionary of input buffers. In the simplest case, this can just be a `list` of `dict`s.
|
|
|
|
In case you don't know details about the inputs ahead of time, you can access the `input_metadata`
|
|
property in your data loader, which will be set to an `TensorMetadata` instance by the Comparator.
|
|
|
|
**NOTE:** Polygraphy provides a default `DataLoader` class that uses numpy to generate random input buffers.
|
|
The input data can be bounded via parameters to the constructor.
|
|
|
|
|
|
## Logger
|
|
|
|
Polygraphy also includes a global logger which can control the verbosity not only of messages emitted by Polygraphy,
|
|
but also of those emitted by underlying frameworks, like TensorRT. For example, the `EXTRA_VERBOSE` verbosity corresponds
|
|
to TensorRT's `kVERBOSE` logging mode.
|
|
|
|
To set the verbosity of the logger, use:
|
|
```py
|
|
G_LOGGER.module_severity = severity
|
|
```
|
|
|
|
For example:
|
|
```py
|
|
G_LOGGER.module_severity = G_LOGGER.EXTRA_VERBOSE
|
|
```
|
|
|
|
By default logs are emitted to the stdout/stderr stream. If you would like to have them stored in a file, set the `log_file` property to your log file:
|
|
```py
|
|
G_LOGGER.log_file = "your_log_file.log"
|
|
```
|
|
|
|
If you would like to have logs being emitted to python `logging` module, set the following flag:
|
|
```py
|
|
G_LOGGER.use_python_logging_system = True
|
|
```
|
|
|
|
After that, you can define your logging configuration using the `logging` module.
|
|
|
|
## Putting It All Together
|
|
|
|
Now that you know the basic components of Polygraphy, let's take a look at how they fit together.
|
|
|
|
In this example, we will write a script that:
|
|
1. Builds a TensorRT engine from an ONNX model
|
|
2. Bounds input values in the range `[0, 2]`
|
|
3. Runs inference using ONNX-Runtime and TensorRT
|
|
4. Compares the results and checks that they match
|
|
|
|
```python
|
|
from polygraphy.backend.onnxrt import OnnxrtRunner, SessionFromOnnx
|
|
from polygraphy.backend.trt import TrtRunner, EngineFromNetwork, NetworkFromOnnxPath
|
|
from polygraphy.comparator import Comparator, DataLoader
|
|
|
|
model_path = "/path/to/model.onnx"
|
|
|
|
build_onnxrt_session = SessionFromOnnx(model_path)
|
|
build_engine = EngineFromNetwork(NetworkFromOnnxPath(model_path))
|
|
|
|
runners = [
|
|
OnnxrtRunner(build_onnxrt_session),
|
|
TrtRunner(build_engine),
|
|
]
|
|
|
|
data_loader = DataLoader(val_range=(0, 2))
|
|
|
|
run_results = Comparator.run(runners, data_loader=data_loader)
|
|
assert bool(Comparator.compare_accuracy(run_results))
|
|
```
|
|
|
|
## Enabling PyTorch
|
|
|
|
In order to enable PyTorch, you need to provide three things to the `PytRunner`:
|
|
1. A model loader: In the simplest case, this can be a callable that returns a `torch.nn.Module`.
|
|
|
|
2. `input_metadata`: A `TensorMetadata` describing the inputs of the model. This maps input names to their shapes and data types. As with other runners, `None` may be used to indicate dynamic dimensions.
|
|
|
|
**NOTE:** Other runners are able to automatically determine input metadata by inspecting the model definition, but because of the way PyTorch is implemented, it is difficult to write a generic function to determine model inputs from a `torch.nn.Module`.
|
|
|
|
3. `output_names`: A list of output names. This is used by the `Comparator` to match `PytRunner` outputs to those of other runners.
|
|
|
|
|
|
## Examples
|
|
|
|
You can find complete code examples that use the Polygraphy Python API [here](../examples/api).
|
|
|
|
|
|
## Python API Reference Documentation
|
|
|
|
For more details, see the [Polygraphy Python API reference documentation](https://docs.nvidia.com/deeplearning/tensorrt/latest/_static/polygraphy/index.html).
|
|
|
|
### Building Python API Documentation Locally
|
|
|
|
To build the API documentation, first install required packages:
|
|
|
|
```bash
|
|
python -m pip install -r docs/requirements.txt
|
|
```
|
|
|
|
and then use the `make` target to build docs:
|
|
|
|
```bash
|
|
make docs
|
|
```
|
|
|
|
The HTML documentation will be generated under `build/docs`
|
|
To view the docs, open `build/docs/index.html` in a browser or HTML viewer.
|
|
|
|
## Deprecation Policy
|
|
|
|
When removing or modifying public APIs in breaking ways, Polygraphy follows the following procedure:
|
|
|
|
1. The API is marked to be removed in some future version of Polygraphy (let's call it version `N`).
|
|
|
|
2. The API continues to work as normal until *at least* version `N`, but will issue deprecation warnings when used.
|
|
These warnings typically suggest which new APIs should be used instead.
|
|
|
|
3. The API may be removed in any Polygraphy version >= `N`.
|