| name | trt-onnx-quickstart |
| description | Build and verify a TensorRT engine from a Hugging Face model ID or ONNX file, with numerical parity checked against ONNX Runtime. Use when the user imports a non-LLM model to TensorRT, needs a verified engine from ONNX, hits trtexec "unsupported operator", must verify the engine matches ONNX numerically, debugs a polygraphy parity failure (large max abs diff at FP16), or configures multi-input dynamic shapes. Triggers: convert ONNX to TensorRT, Hugging Face to TensorRT, trtexec onnx, trtexec unsupported operator, optimum-cli export, polygraphy parity check, polygraphy run --trt --onnxrt, parity check failed, max abs diff, verify engine matches ONNX, --minShapes, dynamic shapes trtexec, multi-input shape profile, FP16 engine, INT64 warning. Adjacent skills: `trt-torch-quickstart` (PyTorch frontend), `trt-cpp-runtime-quickstart` (C++ engine load). LLM token generation belongs in TensorRT-LLM, not here.
|
| license | Apache-2.0 |
| metadata | {"author":"NVIDIA Corporation","version":"1.0","tags":["onnx","import","huggingface","quickstart","fp16"]} |
TensorRT ONNX Quickstart
Take a developer from "I have a Hugging Face model ID" or "an ONNX file" to "a TensorRT engine whose outputs match the source model within tolerance." Follows Path 1 (ONNX → TensorRT) of the Import Workflows Guide, specialized for the most common starting point: a Hugging Face Hub model.
When to Use
| Scenario | Use this skill? |
|---|
Has a Hugging Face model ID (google-bert/bert-base-uncased) and wants TRT-accelerated inference | Yes |
Has an .onnx file and wants a .plan engine with verified parity | Yes |
Ran trtexec --onnx=... and hit a warning/error they don't recognize | Yes |
| Wants LLM token generation (Llama, Mistral, Qwen text generation) | No — route to TensorRT-LLM |
| Has a PyTorch model and wants to stay in PyTorch | No — use trt-torch-quickstart |
Already has a .plan engine and wants to run inference from C++ | No — use trt-cpp-runtime-quickstart |
| Is migrating a weakly-typed network to strongly-typed | No — use trt-strong-typing-migration |
| Has a full diffusion pipeline (SD, FLUX) | Partial — must be component-split first; this skill imports one component at a time |
Prerequisites
Verify each before continuing. Failures here surface as confusing errors later, so fix them before running the import.
-
NVIDIA GPU + driver matching TensorRT 11.x. TRT 11 requires CUDA 13.x. Confirm:
nvidia-smi
-
Python ≥ 3.10. TRT 11 dropped 3.9 and earlier.
-
TensorRT 11 installed — including the trtexec CLI, this skill's primary build tool. The pip wheel does not ship trtexec. Pick one:
- NGC container (recommended for first-time users):
nvcr.io/nvidia/tensorrt:<tag> — bundles trtexec, Python bindings, libraries, and most common dependencies. Run with --gpus all.
.tar.gz or .deb from TensorRT downloads: installs trtexec to a system path.
The pip path (pip install --extra-index-url https://pypi.nvidia.com tensorrt-cu13) provides Python bindings but not trtexec, so it is insufficient on its own. For pip-only, route the developer to the alternative Python-builder flow in import_workflows.md Path 1 Option B; this skill assumes trtexec is available.
Verification:
python3 -c "import tensorrt; print(tensorrt.__version__)"
trtexec --help | head -5
-
Polygraphy and optimum-onnx installed.
pip install polygraphy onnx onnxruntime onnx_graphsurgeon onnxsim 'optimum-onnx'
pip install sentence-transformers
onnx_graphsurgeon is required by polygraphy surgeon sanitize (Step 3) — Polygraphy does not bundle it. onnxruntime is required by optimum-cli export (Step 2, to fix dynamic axes) and Polygraphy's parity check (Step 5); stock NGC containers do not always ship it.
Note: is the active maintained package — the older no longer includes ONNX integration. Do use (last release 2025-01, unmaintained).
Step 1 — Choose a model and confirm it's supported
Consult the Supported Models matrix. If listed, the dtype column gives a known-good starting precision. If not, it is still expected to work — file an issue if it doesn't.
If the model is a diffusion pipeline, stop here — TRT cannot ingest the whole pipeline object. Split it into components (text encoder, UNet/DiT, VAE) and import each separately. See supported_models.md for validated splits, and consider whether trt-export-rewrite is a better fit.
This skill's worked example uses google-bert/bert-base-uncased from the encoder-NLP section.
Step 2 — Export to ONNX
The optimum-cli export onnx command's --task flag selects which head of the model to export. The wrong task yields a valid graph but the wrong output — and Step 5 parity validation won't catch it, because reference and engine are both wrong.
Pick the task from this table based on what the developer wants to do with the model:
| Developer's intent | --task | Typical model families |
|---|
| Get hidden-state embeddings or pooled output | feature-extraction | BERT, RoBERTa, sentence-transformers, CLIP |
| Classify images into N classes | image-classification | ResNet, ViT, MobileNet, EfficientNet |
| Classify text into N labels | text-classification | BERT for sentiment, DistilBERT, etc. |
| Detect / segment objects | object-detection, image-segmentation | DETR, Mask R-CNN |
| Transcribe speech | automatic-speech-recognition | Whisper, Wav2Vec2 |
| Generate captions / VQA | image-to-text, visual-question-answering | BLIP, LLaVA |
| Translation / summarization | text2text-generation | T5, BART |
For an unknown model, run optimum-cli export onnx --help or check the Optimum supported tasks table. If unsure, surface the choice to the developer rather than guessing.
Sentence-transformers models need an extra install
If the model ID starts with sentence-transformers/ (e.g. all-MiniLM-L6-v2, bge-base-en-v1.5), install sentence-transformers before exporting:
pip install sentence-transformers
Without it, optimum-cli falls back to plain transformers and emits raw token-level last_hidden_state instead of the pooled sentence embedding the model is designed to produce. The exporter prints library name was inferred as sentence_transformers, which is not installed. Falling back to transformers. — treat that warning as a hard error and install before retrying.
optimum-cli export onnx \
--model google-bert/bert-base-uncased \
--task feature-extraction \
bert_onnx/
Expected warnings (not errors)
The exporter often prints a "max diff between reference and ONNX exported model is not within the set tolerance 1e-05" line (with max diff around 1e-5 to 1e-4). This is its own self-check at a strict tolerance — the export still succeeded. Step 5's Polygraphy parity check uses --atol 1e-2 --rtol 1e-2 --check-error-stat mean, the correct tolerance for FP16 inference. Don't surface this warning as a failure.
Authentication for gated models
LLaMA, FLUX, and some Mistral and Qwen variants are gated behind HF agreements and require huggingface-cli login before download. If optimum-cli exits with a 401, run huggingface-cli login with a token from huggingface.co/settings/tokens and retry.
If the export fails
The most common cause is a HF model using patterns torch.export / torch.onnx.export cannot trace — complex-number arithmetic, data-dependent control flow, non-tensor forward arguments, output dataclasses. Do not patch the upstream library on disk. The correct workflow is agentic monkey-patching at runtime: route the developer to the trt-export-rewrite skill, which drives it with five concrete patterns from the Qwen-Image case study.
Step 3 — Sanitize the ONNX
ONNX exporters often produce graphs with constant-folding opportunities, spurious dynamic axes, or shape-inference gaps. Sanitizing cleans these up, but it is a quality pass, not a correctness gate — TRT parses many un-sanitized exports fine. Both stages below are therefore optional and allowed to fail: each falls through to its input, and Step 3 always leaves a model.clean.onnx for the rest of the workflow (worst case, a copy of the exported model). Run:
cp bert_onnx/model.onnx bert_onnx/model.clean.onnx
python3 -m onnxsim bert_onnx/model.clean.onnx bert_onnx/model.simplified.onnx \
&& cp bert_onnx/model.simplified.onnx bert_onnx/model.clean.onnx
polygraphy surgeon sanitize bert_onnx/model.clean.onnx \
-o bert_onnx/model.surgeon.onnx --fold-constants \
&& cp bert_onnx/model.surgeon.onnx bert_onnx/model.clean.onnx
Step 3.5 — Inspect the ONNX inputs (parameterize the rest of the workflow)
Every step from here uses input names and ranks that depend on the model. Read them out of the sanitized ONNX before building. Do not assume input_ids / attention_mask — those are BERT-family-specific.
polygraphy inspect model bert_onnx/model.clean.onnx
Example output for bert-base-uncased (verified against optimum-onnx 0.1.0, opset 18):
---- 3 Graph Input(s) ----
{input_ids [dtype=int64, shape=('batch_size', 'sequence_length')],
attention_mask [dtype=int64, shape=('batch_size', 'sequence_length')],
token_type_ids [dtype=int64, shape=('batch_size', 'sequence_length')]}
---- 1 Graph Output(s) ----
{last_hidden_state [dtype=float32, shape=('batch_size', 'sequence_length', 768)]}
Inputs and outputs vary per model and per --task. Inspect, don't assume.
Build the shape strings from this output. Every input with a dynamic (named) axis needs a --minShapes / --optShapes / --maxShapes entry; fixed axes are reproduced literally. Use representative values — --opt is what the engine optimizes for; --min and --max define the supported range.
If any input reports an unknown-rank axis (printed as ?), shape inference failed; reroute through trt-export-rewrite to fix the export.
Step 4 — Bake FP16 into the model (TRT 11) or set the build flag (TRT 10)
Start with FP16 — the safe default for encoder and vision models on modern GPUs. (Use BF16 on Blackwell/Hopper if the developer reports persistent FP16 accuracy issues. Sentence-embedding models sometimes prefer FP32 over lossy FP16 — surface the option.)
TRT 11 removed the weak-typing builder flags (--fp16, --int8, --bf16, BuilderFlag::kFP16, etc. — see the migration guide and the sibling trt-strong-typing-migration SKILL). In TRT 11 you bake precision into the model before building, and trtexec honors the dtypes it finds via ModelOpt AutoCast.
Detect the version first:
TRT_MAJOR=$(python3 -c "import tensorrt as t; print(t.__version__.split('.')[0])")
echo "TensorRT major version: $TRT_MAJOR"
TRT 11: AutoCast the model, then build
pip install nvidia-modelopt
python3 -m modelopt.onnx.autocast \
--onnx_path=<MODEL>.clean.onnx \
--output_path=<MODEL>.fp16.onnx
trtexec \
--onnx=<MODEL>.fp16.onnx \
--saveEngine=<MODEL>.fp16.plan \
--memPoolSize=workspace:4096 \
--minShapes=<INPUT1>:<MINSHAPE>,... \
--optShapes=<INPUT1>:<OPTSHAPE>,... \
--maxShapes=<INPUT1>:<MAXSHAPE>,...
TRT 10: pass the legacy --fp16 flag
trtexec \
--onnx=<MODEL>.clean.onnx \
--saveEngine=<MODEL>.fp16.plan \
--memPoolSize=workspace:4096 \
--fp16 \
--minShapes=<INPUT1>:<MINSHAPE>,... \
--optShapes=<INPUT1>:<OPTSHAPE>,... \
--maxShapes=<INPUT1>:<MAXSHAPE>,...
Worked example for BERT (three inputs, all dynamic on batch and sequence axes), TRT 11 path:
python3 -m modelopt.onnx.autocast \
--onnx_path=bert_onnx/model.clean.onnx \
--output_path=bert_onnx/model.fp16.onnx
trtexec \
--onnx=bert_onnx/model.fp16.onnx \
--saveEngine=bert.fp16.plan \
--memPoolSize=workspace:4096 \
--minShapes=input_ids:1x32,attention_mask:1x32,token_type_ids:1x32 \
--optShapes=input_ids:8x128,attention_mask:8x128,token_type_ids:8x128 \
--maxShapes=input_ids:16x512,attention_mask:16x512,token_type_ids:16x512
Worked example for ResNet-50 (one input, dynamic on batch axis only), TRT 11 path:
python3 -m modelopt.onnx.autocast \
--onnx_path=resnet50_onnx/model.clean.onnx \
--output_path=resnet50_onnx/model.fp16.onnx
trtexec \
--onnx=resnet50_onnx/model.fp16.onnx \
--saveEngine=resnet50.fp16.plan \
--memPoolSize=workspace:4096 \
--minShapes=pixel_values:1x3x224x224 \
--optShapes=pixel_values:16x3x224x224 \
--maxShapes=pixel_values:32x3x224x224
For purely static-shape models, omit all three shape flags.
Common errors at this step
- "Engine plan file is generated on an incompatible device" — plans are not portable across compute capabilities. Rebuild on the deployment GPU.
- OOM during build — lower
--memPoolSize=workspace:N. If still failing, try --tacticSources=-CUBLAS_LT to disable expensive tactic sources.
- Unsupported operator —
trtexec names the op. Route to trt-unsupported-op for triage (decompose / plugin / switch frontend / file bug).
- INT64 warnings — TRT casts to INT32. Usually safe; if values exceed INT32 range, rerun Step 3's
polygraphy surgeon sanitize --fold-constants.
- "Input has missing shape information" — you missed an input in the
--minShapes / --optShapes / --maxShapes flags. Compare against Step 3.5's output and add the missing entry.
Step 5 — Verify numerical parity
This is the step most developers skip — and the one that most often surfaces a silent correctness bug.
Construct the parity command from the same input names used in Step 4. Beyond input shapes, three knobs matter; get them right or the result is misleading:
--val-range for any input with constrained semantics. Polygraphy fills with [0, 1) floats by default. For BERT-family input_ids you must constrain to vocabulary range, set attention_mask to all-ones, and token_type_ids to all-zeros — otherwise the model sees garbage and FP16 noise amplifies through the attention softmax.
--check-error-stat mean for transformer outputs. FP16 commonly has a few outlier positions with large abs diff; the mean across all positions is the meaningful metric for downstream use (embedding similarity, classification). Default max is too pessimistic for FP16 transformers and produces false failures.
--seed for repeatability across runs and CI environments.
Worked example for BERT — run polygraphy against the same ONNX file you fed to trtexec (the AutoCast-converted .fp16.onnx on TRT 11; the .clean.onnx on TRT 10):
polygraphy run bert_onnx/model.fp16.onnx \
--trt --onnxrt \
--atol 1e-2 --rtol 1e-2 \
--check-error-stat mean \
--input-shapes input_ids:[8,128] attention_mask:[8,128] token_type_ids:[8,128] \
--val-range input_ids:[0,30000] attention_mask:[1,1] token_type_ids:[0,0] \
--seed 42
polygraphy run bert_onnx/model.clean.onnx \
--trt --onnxrt \
--atol 1e-2 --rtol 1e-2 \
--check-error-stat mean \
--input-shapes input_ids:[8,128] attention_mask:[8,128] token_type_ids:[8,128] \
--val-range input_ids:[0,30000] attention_mask:[1,1] token_type_ids:[0,0] \
--seed 42 \
--fp16
Expected output: Pass Rate: 100.0% with mean_absdiff ≤ 1e-3 per output. Pass Rate: 0.0% with max_absdiff near 1e-2 but mean_absdiff well below tolerance is the outlier pattern above — use --check-error-stat mean.
Worked example for ResNet-50 (same FP16 recipe; TRT-11 path shown — for TRT 10 add --fp16 and read from model.clean.onnx):
polygraphy run resnet50_onnx/model.fp16.onnx \
--trt --onnxrt \
--atol 1e-2 --rtol 1e-2 \
--check-error-stat mean \
--input-shapes pixel_values:[16,3,224,224] \
--val-range pixel_values:[0,1] \
--seed 42
Polygraphy runs the model through ONNX Runtime (reference) and TensorRT (engine) on the seeded inputs and compares outputs per the chosen error stat.
If parity fails
- Diverges only at FP16, not FP32: a few layers are losing precision. Try
--strongly-typed with an explicit FP32 cast on the offending subgraph (Polygraphy names the layer). For LLM-style models, prefer BF16 over FP16 on Hopper/Blackwell.
- Diverges at all precisions: the export is wrong. Re-run Steps 2–3 fresh and check
polygraphy inspect model for shape-inference issues. If the model uses patterns covered by trt-export-rewrite, reroute.
- Diverges only for specific input shapes: dynamic-shape profile is too narrow. Widen
--minShapes / --maxShapes and rebuild.
Step 6 — Run the engine
trtexec --loadEngine=bert.fp16.plan \
--shapes=input_ids:8x128,attention_mask:8x128,token_type_ids:8x128 \
--verbose
This confirms the saved .plan deserializes and runs (Step 5's parity used its own engine, not this file) and reports throughput and per-iteration latency. For production, see the developer guide on IExecutionContext and stream-aware execution; the .plan is portable to any compatible GPU + TRT runtime.
Success criteria
The skill is complete when all of the following are true:
bert.fp16.plan exists and trtexec --loadEngine=... runs without error.
polygraphy run --trt --onnxrt --atol 1e-2 --rtol 1e-2 --check-error-stat mean reports PASSED across all configured input shapes.
- The developer can articulate which precision was used and why.
If any are not true, hand control back with the specific failing diagnostic — do not declare success.
References