Skip to content

feat(conversion): convert small antialiased bilinear downscales by default - #5024

Open
shoumikhin wants to merge 2 commits into
pytorch:mainfrom
shoumikhin:antialias-bilinear-default
Open

shoumikhin wants to merge 2 commits into
pytorch:mainfrom
shoumikhin:antialias-bilinear-default

Conversation

@shoumikhin

Copy link
Copy Markdown
Contributor

This sits on top of #5023, so the first commit here is #5023. Only the last commit is new.

What is wrong today

Antialiased resize (F.interpolate with antialias=True) runs in TensorRT only with convert_antialiased_resize=True. Otherwise it splits the graph. This is common at the start of image models, which resize camera frames. Exporting frameworks often cannot pass extra settings.

What this change does

convert_antialiased_resize becomes Optional[bool], with None as the new default:

  • None converts float32 bilinear downscales with at most 6 input pixels per output pixel along each axis (up to about 3x). On A100, H100 and Jetson Orin GPUs with TensorRT 11.4 and a PyTorch 2.16 build for CUDA 13.2, its output matched PyTorch's bit for bit, also at 6 nonzero taps. Elsewhere it can differ by a few float32 roundings; set False for PyTorch's exact output.
  • True converts every antialiased resize, as before.
  • False keeps them all in PyTorch, as before. A falsy value such as 0 means False. Settings pickled before this setting existed load as False.

The limit and the per-axis check are DEFAULT_MAX_ANTIALIAS_TAPS and antialias_converts_by_default in impl/upsample.py. Without settings, the validator uses the defaults. The docs name the overload that torch_executed_ops matches, torch.ops.aten._upsample_bilinear2d_aa.default.

Upscales, larger downscales, half-precision inputs and bicubic resize stay in PyTorch. They were not always bit for bit: a 4x downscale was within 5e-5 on 0 to 255, a 1.25x upscale one float32 rounding off.

Speed, for a resize followed by a 3x3 convolution: 0.18 ms in one engine against 0.35 ms split for 2x3x288x512 to 234x416, and 0.80 ms against 1.31 ms for 8x3x1080x1920 to 360x640. A resize alone is slower in TensorRT; the gain is the avoided split. Memory: for the second case the engine needs about 21 MiB of activation memory, about what PyTorch allocates for the resize with False.

Tests

  • test_antialias2d_matches_pytorch runs the default at 3 and 6 nonzero taps (within 1e-4).
  • test_antialias2d_validator_default: by default 6 taps convert, also with align_corners or a 0.5 scale; 7 taps (either axis, align_corners or a scale), an upscale, float16 and float64 do not. True converts 7 taps, 0 nothing.
  • test_antialias2d_setting_is_bool, test_antialias2d_old_pickle_keeps_resize_in_pytorch: the two False cases above. test_antialias2d_default_float64_log: no wrong truncate_double hint.
  • test_antialias2d_engines: by default 1 engine, with False the split; bicubic stays in PyTorch.
  • All of test_upsample_aten.py passes on A100 and H100.

…DA kernel

The antialiased resize converter computed its filter weights in float64.
PyTorch's CUDA kernel computes them in float32, and its compiler fuses
some multiply and add steps. So for float32 inputs the two results
differed by up to about 4e-3 on pixel values from 0 to 255.

That is normally harmless, but images are often resized and then rounded
to uint8. Many exact resize results end in .5, so a small difference
moves a pixel by one level. At a 288x512 to 234x416 downscale, 0.2% to
0.7% of the pixels changed. In Praxis-1, whose encoder rounds its resized
camera frames like this, the resize alone moved the encoded output by
up to 2.2% of its largest value against eager PyTorch.

For float32 inputs, compute the weights the way the kernel does: float32
at every step, the same fused multiply-adds, and the weights summed in
order. The fused multiply-adds use a float64 product on every NumPy
version. float64 inputs keep float64 weights, as in PyTorch. A window
with no input pixels, which explicit scales can cause, keeps zero
weights like the kernel instead of dividing 0 by 0.

Also mask the pixels past a window's end before the multiply instead of
after it, so TensorRT can fuse the multiply and the sum like the kernel.

The new weights equal PyTorch's CUDA weights exactly, which a new test
reads out by resizing an identity matrix. On A100 and H100 GPUs with
TensorRT 11.4, the TensorRT output was bit for bit the same as PyTorch's
for bilinear downscales by up to about 3 (at most 6 input pixels per
output pixel per axis) and for some bicubic sizes. Other cases, such as
bilinear upscales and larger downscales, stayed within a few float32
roundings.
…fault

Antialiased resize (F.interpolate with antialias=True) only ran in
TensorRT with convert_antialiased_resize=True. Otherwise it stayed in
PyTorch and split the graph, often right at the start of an image model.

convert_antialiased_resize is now Optional[bool], and None is the new
default:
- None converts float32 bilinear downscales with at most 6 input pixels
  per output pixel along each axis, up to about 3x. In our measurements
  on A100, H100 and Jetson Orin GPUs with TensorRT 11.4 and a PyTorch
  2.16 build for CUDA 13.2, TensorRT summed the taps in the same order as
  PyTorch's CUDA kernel there, and the output was bit for bit the same.
  Elsewhere it can differ by a few float32 roundings. Upscales,
  half-precision inputs, larger downscales and bicubic resize stay in
  PyTorch.
- True converts every antialiased bilinear and bicubic resize, as before.
  The output can then differ from PyTorch by a few roundings.
- False keeps them all in PyTorch, as before. A falsy value such as 0
  means False, and settings pickled before this setting existed load as
  False, so refit partitions them as before.

The limit and the per-axis check live in impl/upsample.py as
DEFAULT_MAX_ANTIALIAS_TAPS and antialias_converts_by_default. A validator
called without settings now uses the defaults, and the documented opt-out
names the operator overload that torch_executed_ops matches.
@meta-cla meta-cla Bot added the cla signed label Nov 12, 2026
@github-actions github-actions Bot added documentation Improvements or additions to documentation component: tests Issues re: Tests component: conversion Issues re: Conversion stage component: core Issues re: The core compiler component: converters Issues re: Specific op converters component: api [Python] Issues re: Python API component: dynamo Issues relating to the `torch.compile` or `torch._dynamo.export` paths labels Nov 12, 2026
@github-actions
github-actions Bot requested a review from zewenli98 November 12, 2026 15:20
@shoumikhin
shoumikhin force-pushed the antialias-bilinear-default branch from 6af773c to 35f05f9 Compare November 14, 2026 17:20

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla signed component: api [Python] Issues re: Python API component: conversion Issues re: Conversion stage component: converters Issues re: Specific op converters component: core Issues re: The core compiler component: dynamo Issues relating to the `torch.compile` or `torch._dynamo.export` paths component: tests Issues re: Tests documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants