惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

爱范儿
爱范儿
P
Palo Alto Networks Blog
月光博客
月光博客
H
Hackread – Cybersecurity News, Data Breaches, AI and More
I
InfoQ
aimingoo的专栏
aimingoo的专栏
腾讯CDC
T
Threatpost
D
DataBreaches.Net
Vercel News
Vercel News
F
Fortinet All Blogs
Engineering at Meta
Engineering at Meta
C
Cybersecurity and Infrastructure Security Agency CISA
Forbes - Security
Forbes - Security
U
Unit 42
C
Check Point Blog
Blog — PlanetScale
Blog — PlanetScale
O
OpenAI News
量子位
TaoSecurity Blog
TaoSecurity Blog
Microsoft Azure Blog
Microsoft Azure Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
V
Visual Studio Blog
Recorded Future
Recorded Future
云风的 BLOG
云风的 BLOG
Security Archives - TechRepublic
Security Archives - TechRepublic
The Last Watchdog
The Last Watchdog
S
Security Affairs
Attack and Defense Labs
Attack and Defense Labs
罗磊的独立博客
Stack Overflow Blog
Stack Overflow Blog
Microsoft Security Blog
Microsoft Security Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
V
V2EX
小众软件
小众软件
S
SegmentFault 最新的问题
www.infosecurity-magazine.com
www.infosecurity-magazine.com
W
WeLiveSecurity
AI
AI
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
博客园 - 聂微东
I
Intezer
Know Your Adversary
Know Your Adversary
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
P
Proofpoint News Feed
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The Cloudflare Blog
博客园_首页
NISL@THU
NISL@THU
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO

Lei Mao's Log Book

2026 FIFA World Cup 备受嘲讽的会徽 Python Debugging Via VS Code In Docker Container Vargas Plateau Regional Park 徒步 Vargas Plateau Regional Park Apex 2026 FIFA World Cup 小组赛赛程 Retaining EXIF Metadata In GIMP San Francisquito Creek Joint Powers Authority 2025 Calendar Photo 麦当劳 The FIFA World Cup 套餐 Ardenwood Historic Farm 徒步 Ardenwood Historic Farm Synchronizations With TorchRec KeyedJaggedTensor Pacific Commons Linear Park 徒步 Pacific Commons Linear Park 2026 San Jose Half Marathon 竞赛 目标 Mountain View Shoreline Park 徒步 Mountain View Shoreline Park PyTorch AOTInductor Hybrid Lowering Carquinez Strait Regional Shoreline 徒步 Carquinez Strait Regional Shoreline PyTorch Triton Kernel Transparent Tracing and Compilation 脸庞 PyTorch Fake Export 2026 BRAIN Foundation 10K 竞赛 2026 Wild and Scenic Film Festival 参观 2026 Wild and Scenic Film Festival 系统工程程序员修 Bug FIFA 官方网站的语言 PyTorch Custom Operation 汉堡王 The Mandalorian and Grogu 套餐 Tilden Regional Parks Botanic Garden 参观 Tilden Regional Park Tilden Regional Parks Botanic Garden Tilden Regional Park 徒步 《寻秦记》电影版 ICML 2026 Area Chair Experience 2026 Foster City 5K Fun Run 竞赛 2026 年 3 月和 4 月该入手的模型手办 Docker Container GUI Display Using Wayland 马拉松破二 2026 Heart & Soles Run 5K 竞赛 How Is FARS, The Fully Automated Research System? 算计: 七天的死亡游戏 Lake Chabot Regional Park 徒步 Lake Chabot Regional Park 2023 年恐怖电影《感恩节》 2026 Airport Runway Run at San Carlos Airport 5K 竞赛 Page Table for Page-Locked Host Memory Don Edwards San Francisco Bay National Wildlife Refuge - Ravenswood 徒步 Don Edwards San Francisco Bay National Wildlife Refuge - Ravenswood 法外风云 Contra Costa Canal Regional Trail 徒步 Contra Costa Canal Regional Trail 娑婆诃 PyTorch Export 2026 Western Pacific 5K 竞赛 浮躁的科研和胡扯的自媒体 Connecting Logitech Devices On Linux 2026 Oakland Half Marathon 竞赛 Del Valle Regional Park 徒步 Del Valle Regional Park CUDA_LAUNCH_BLOCKING=1 踏切时间 Wildcat Canyon Regional Park 徒步 Wildcat Canyon Regional Park Credit Card Unauthorized Transaction While In Possession 莎拉的真伪人生 2026 Union City Superhero Fun Run 5K 竞赛 McLaughlin Eastshore State Park 徒步 McLaughlin Eastshore State Park Cloudflare Worker Proxy R2 Bucket Access Zorro 和 Batman Fix MacBook Pro Space Key Stuck Problem 2026 年 1 月和 2 月该入手的模型手办 2026 Brazen Victory 10K 竞赛 儿时的玩伴李峰 Marsh Creek Regional Trail 徒步 Marsh Creek Regional Trail Perfetto GPU Flow Artifacts 百万人推理 System Performance Optimizations QQ 幻想 2026 Brazen Bay Breeze 5K 竞赛 CUDA Shared Memory Bank Conflict-Free Vectorized Access Dota 闪电站出售 Mountain View Downtown 徒步 Mountain View Downtown C++ Latch and Barrier 2025 年跑步总结 2026 Rotary Mission Ten Half Marathon 竞赛 狗的素质等于人的素质 CUDA Rendezvous Stream Pleasanton Ridge Regional Park 徒步 Pleasanton Ridge Regional Park Xfinity Internet 多年来的使用感受 Randomized SVD Don Castro Regional Recreation Area 徒步 Don Castro Regional Recreation Area 拖车公司的大汉们
PyTorch Graph Symbolic Integer
Lei Mao · 2026-04-05 · via Lei Mao's Log Book

   blog 11 minutes read (About 1602 words)  visits

Introduction

When converting a PyTorch eager model to a graph, there are graph breaks if model tracing encounters control flow that depends on input data rather than meta-data or operations whose output meta-data depend on input data rather than meta-data. The meta-data includes things like tensor shapes and data types. The former graph break problem might be solved using conditional operations such as torch.cond provided that certain constraints are satisfied, such as the output meta-data of the two branches of the conditional operation must be the same. The latter graph break problem is sometimes more difficult to solve because it usually requires re-designing the neural network model.

Symbolic integers (SymInts) are used to represent variables that can span a range. The graph can allow dynamic shapes if the graph meta-data can be described using symbolic integers when the actual input meta-data is known.

In this blog post, I would like to quickly discuss what symbolic integers are and use a simple example to show how to specify symbolic integers in the graph when exporting a PyTorch model using torch.export.

The TopK Example

Suppose my model only consists of one torch.topk layer. The output shape of the torch.topk layer depends on the input tensor meta-data and the k value. To allow dynamic shapes of the model that depends on the k value, the k value must be represented as a symbolic integer in the graph.

To specify the k value as a symbolic integer via torch.export, there are a few ways, some of which do not work.

K As An Integer

If we specify the k value as a Python integer, it will not work because the graph will treat it as a constant instead of a symbolic integer.

k_int.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import torch

class TopKModelWithInt(torch.nn.Module):
def forward(self, x, k):

values, indices = torch.topk(x, k=k, dim=-1)
return values, indices

model = TopKModelWithInt()
x = torch.randn(10, 100)
k_int = 5


exported = torch.export.export(model, args=(x, k_int))

print("Exported program: ")
print(exported)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ python k_int.py
Exported program:
ExportedProgram:
class GraphModule(torch.nn.Module):
def forward(self, x: "f32[10, 100]", k):

topk = torch.ops.aten.topk.default(x, 5); x = None
getitem: "f32[10, 5]" = topk[0]
getitem_1: "i64[10, 5]" = topk[1]; topk = None
return (getitem, getitem_1)

Graph signature:

x: USER_INPUT
k: USER_INPUT


getitem: USER_OUTPUT
getitem_1: USER_OUTPUT

Range constraints: {}

K Via Proxy Tensor

If we specify the k value as a proxy tensor, i.e., its value is encoded in the proxy tensor meta-data, it will work because integers in meta-data will be represented as symbolic integers in the graph.

k_proxy_tensor.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import torch

class TopKModelWithProxyTensor(torch.nn.Module):
def forward(self, x, k_proxy):

k = k_proxy.size(0)
values, indices = torch.topk(x, k=k, dim=-1)
return values, indices

model = TopKModelWithProxyTensor()
x = torch.randn(10, 100)
k_proxy = torch.empty(1)
k_proxy.resize_(5)


k_dim = torch.export.Dim("k_value", min=1, max=100)


exported = torch.export.export(model, args=(x, k_proxy), dynamic_shapes={"x": None, "k_proxy": {0: k_dim}})

print("Exported program: ")
print(exported)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ python k_proxy_tensor.py
Exported program:
ExportedProgram:
class GraphModule(torch.nn.Module):
def forward(self, x: "f32[10, 100]", k_proxy: "f32[s99]"):

sym_size_int_1: "Sym(s99)" = torch.ops.aten.sym_size.int(k_proxy, 0); k_proxy = None


topk = torch.ops.aten.topk.default(x, sym_size_int_1); x = sym_size_int_1 = None
getitem: "f32[10, s99]" = topk[0]
getitem_1: "i64[10, s99]" = topk[1]; topk = None
return (getitem, getitem_1)

Graph signature:

x: USER_INPUT
k_proxy: USER_INPUT


getitem: USER_OUTPUT
getitem_1: USER_OUTPUT

Range constraints: {s99: VR[1, 100]}

K Via Tensor

The aforementioned approach of using a proxy tensor and dynamic shapes to specify the k value looks somewhat hacky. Consequently, PyTorch allows symbolic integers to be directly specified using tensors only if the tensor is the input of the graph.

k_tensor.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import torch

class TopKModelWithTensor(torch.nn.Module):
def forward(self, x, k_tensor):

k = k_tensor.item()
values, indices = torch.topk(x, k=k, dim=-1)
return values, indices

model = TopKModelWithTensor()
x = torch.randn(10, 100)
k_tensor = torch.tensor(5)


exported = torch.export.export(model, args=(x, k_tensor))

print("Exported program: ")
print(exported)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ python k_tensor.py
Exported program:
ExportedProgram:
class GraphModule(torch.nn.Module):
def forward(self, x: "f32[10, 100]", k_tensor: "i64[]"):

item: "Sym(u0)" = torch.ops.aten.item.default(k_tensor); k_tensor = None
ge: "Sym(u0 >= 0)" = item >= 0
_assert_scalar_default = torch.ops.aten._assert_scalar.default(ge, "Runtime assertion failed for expression u0 >= 0 on node 'ge'"); ge = _assert_scalar_default = None
le: "Sym(u0 <= 100)" = item <= 100
_assert_scalar_default_1 = torch.ops.aten._assert_scalar.default(le, "Runtime assertion failed for expression u0 <= 100 on node 'le'"); le = _assert_scalar_default_1 = None


topk = torch.ops.aten.topk.default(x, item); x = item = None
getitem: "f32[10, u0]" = topk[0]
getitem_1: "i64[10, u0]" = topk[1]; topk = None
return (getitem, getitem_1)

Graph signature:

x: USER_INPUT
k_tensor: USER_INPUT


getitem: USER_OUTPUT
getitem_1: USER_OUTPUT

Range constraints: {u0: VR[0, 100]}

Note that item() is used to extract the integer value from the tensor and typically will cause graph breaks. This is because in many cases, the output meta-data of some operations that depends on it will be data-dependent. The torch.topk operation is one of such operations because its output shape depends on the k_tensor value. PyTorch made an exception for this case to allow the k_tensor value to be specified as a tensor input of the graph, by evaluating the k_tensor value at runtime so that the k value is known before graph execution. The downside is that if k_tensor is a tensor on GPU, it will cause a GPU to CPU copy and also notice that we could not specify the range of k values via the standard dynamic shapes mechanism as used in the previous proxy tensor approach. Other graph frameworks and runtimes, such as ONNX, usually do not allow this kind of behaviors and might evaluate the k_tensor value at export time and treat it as a constant.

If the k_tensor is not the immediate input of the graph, rather than being an intermediate tensor in the graph, and somehow it is not bounded, then it is a graph break and PyTorch could not tolerate it.

Conclusions

There are pros and cons for the “K Via Proxy Tensor” approach and the “K Via Tensor” approach.

The “K Via Proxy Tensor” approach looks more grammatically correct to me and the graph can be wrong on pure device without GPU to CPU copy. However, if k is large, the user would have to construct a large proxy tensor whose only useful information is its meta-data and copy it to the device before graph execution, which can be inefficient. Of course, if the proxy tensor can just be constructed as a meta tensor without any actual data, then it will be very efficient.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import torch

class TopKModelWithProxyTensor(torch.nn.Module):
def forward(self, x, k_proxy):

k = k_proxy.size(0)
values, indices = torch.topk(x, k=k, dim=-1)
return values, indices

model = TopKModelWithProxyTensor()
x = torch.randn(10, 100)
k_proxy = torch.empty(1, device='meta')
k_proxy.resize_(5)


k_dim = torch.export.Dim("k_value", min=1, max=100)


exported = torch.export.export(model, args=(x, k_proxy), dynamic_shapes={"x": None, "k_proxy": {0: k_dim}})


package_path = torch._inductor.aoti_compile_and_package(
exported,
package_path="model.pt2"
)


lowered = torch._inductor.aoti_load_package(package_path)


selected_from_exported = exported.module()(x, k_proxy)
selected_from_lowered = lowered(x, k_proxy)

The “K Via Tensor” approach looks weird because it appears that PyTorch can allow graph breaks in the graph, which is against the general principle of graph representation. However, it is usually more efficient because there is no need to construct a proxy tensor and copy it to the device before graph execution sometimes will not introduce too much overhead.

References