1. 为什么导出的 ONNX 总是被“焊死”成固定维度如果你用 PyTorch 训练完一个文本分类或序列标注模型兴冲冲地torch.onnx.export出来然后拿一条长度 128 的句子推理没问题换成长度 64 的句子直接报 shape mismatch——大概率不是模型写错了而是导出时输入输出维度被 dummy input 的形状“焊死”了。ONNX 的图结构里每个张量的 shape 默认是静态的。你喂进去的dummy_input是[1, 128]导出的模型就认为第一维永远是 1、第二维永远是 128。可变 batch、变长序列这些部署时最常见的需求全被这个默认行为挡在门外。这篇要解决的就是这件事怎么在torch.onnx.export阶段就用dynamic_axes把维度声明成动态的让一次导出适配多种 shape导出后怎么用 onnxruntime 实际跑不同尺寸的输入来验证以及当dynamic_axes不生效、中间节点仍然是死维度时怎么排查和补救。适合的人群做 NLP 序列模型部署、需要可变 batch 推理、或者被 ONNX 固定维度坑过的同学。下面所有配置和命令都可以直接复制改路径使用。2. 前置准备TaoToken 配置骨架与依赖环境在动手改导出脚本之前先把环境和访问凭证理顺。我习惯把模型导出、验证脚本放在同一个工程里用 TaoToken 统一管理模型调用和 API 访问的配置这样后面如果要接在线推理或做对比验证不用再单独折腾一套 key。TaoToken 的官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基址是 https://taotoken.net/api 这个不加 UTM。如果你只是本地导出 ONNX、用 onnxruntime 验证其实不依赖任何在线服务但如果你想把导出后的模型接到一个统一的推理服务里做端到端验证或者需要调用模型对话能力做结果比对那提前把 key 配好会省事很多。依赖安装这块核心就三个包pip install torch onnx onnxruntime numpy版本上不用太纠结PyTorch 1.10 以上、onnx 1.12 以上、onnxruntime 1.14 以上基本都支持本文的写法。如果你用的是 GPU 环境onnxruntime 换成onnxruntime-gpu即可验证脚本代码不用改。配置骨架我一般写成这样放在config.py里# config.py import os TAOTOKEN_API_BASE os.getenv(TAOTOKEN_API_BASE, https://taotoken.net/api) TAOTOKEN_API_KEY os.getenv(TAOTOKEN_API_KEY, ) ONNX_MODEL_PATH exports/model_dynamic.onnx DUMMY_BATCH 1 DUMMY_SEQ_LEN 128把 key 放在环境变量里不要硬编码进脚本。需要生成或管理 key 的话控制台在 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite API Keys 管理页在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 遇到参数不确定的时候翻一下比猜快。注意本文的 ONNX 导出和验证完全在本地完成TaoToken 的作用是给你一个统一的配置入口和后续在线验证的通道不是导出流程的必需依赖。别把它当成“导出工具”来理解。3. 可复制的 dynamic_axes 配置与 export 参数骨架这是全文最核心的一段。先看一个典型的、会导出成固定维度的错误写法import torch class TinySeqModel(torch.nn.Module): def __init__(self, vocab1000, hidden64, num_class2): super().__init__() self.emb torch.nn.Embedding(vocab, hidden) self.lstm torch.nn.LSTM(hidden, hidden, batch_firstTrue) self.fc torch.nn.Linear(hidden, num_class) def forward(self, input_ids): x self.emb(input_ids) x, _ self.lstm(x) x x[:, -1, :] return self.fc(x) model TinySeqModel().eval() dummy_input torch.randint(0, 1000, (1, 128)) torch.onnx.export( model, dummy_input, model_fixed.onnx, input_names[input_ids], output_names[logits], opset_version13, )这段跑完model_fixed.onnx的输入就是[1, 128]batch 和序列长度都动不了。正确做法是加dynamic_axesimport torch model TinySeqModel().eval() dummy_input torch.randint(0, 1000, (1, 128)) dynamic_axes { input_ids: {0: batch_size, 1: seq_len}, logits: {0: batch_size}, } torch.onnx.export( model, dummy_input, model_dynamic.onnx, input_names[input_ids], output_names[logits], dynamic_axesdynamic_axes, opset_version13, do_constant_foldingTrue, )dynamic_axes的语义是对名为input_ids的输入第 0 维命名为batch_size第 1 维命名为seq_len对名为logits的输出第 0 维命名为batch_size。名字是自定义的字符串只要不是纯数字ONNX 就把它当作符号维度symbolic dimension而不是固定值。几个容易踩的点我列成表格对照参数作用常见错误input_names给输入起名dynamic_axes 靠这个名字索引名字和 dynamic_axes 的 key 不一致静默失效output_names给输出起名输出维度没声明batch 仍然固定dynamic_axes声明哪些维度可变只声明输入不声明输出或维度索引写错opset_version算子集版本太低不支持某些动态算子建议 12 以上do_constant_folding常量折叠优化一般保持 True但某些动态 shape 场景要关掉如果你的模型有多个输入比如input_idsattention_mask每个都要单独声明dynamic_axes { input_ids: {0: batch_size, 1: seq_len}, attention_mask: {0: batch_size, 1: seq_len}, logits: {0: batch_size}, }输出如果有多个同理逐个写。维度索引从 0 开始{0: batch_size}表示第 0 维动态。命名建议统一用batch_size、seq_len这种语义化名字方便后面排查。4. 用 onnxruntime 加载并跑通不同 shape 的验证动作导出完不能只看文件生成了就完事必须实际用不同 shape 跑一遍。下面这个验证脚本可以直接用import numpy as np import onnxruntime as ort sess ort.InferenceSession(model_dynamic.onnx, providers[CPUExecutionProvider]) def run(shape): input_ids np.random.randint(0, 1000, sizeshape).astype(np.int64) outputs sess.run([logits], {input_ids: input_ids}) print(finput shape{shape} - output shape{outputs[0].shape}) run((1, 128)) run((4, 128)) run((1, 64)) run((8, 32))预期输出类似input shape(1, 128) - output shape(1, 2) input shape(4, 128) - output shape(4, 2) input shape(1, 64) - output shape(1, 2) input shape(8, 32) - output shape(8, 2)如果(4, 128)或(1, 64)报错说明动态维度没生效。这时候先检查dynamic_axes的 key 是否和input_names完全一致再检查维度索引有没有写反。想更直观地看 ONNX 图里每个节点的 shape 信息可以用import onnx model onnx.load(model_dynamic.onnx) for inp in model.graph.input: dims [d.dim_param or d.dim_value for d in inp.type.tensor_type.shape.dim] print(input:, inp.name, dims) for out in model.graph.output: dims [d.dim_param or d.dim_value for d in out.type.tensor_type.shape.dim] print(output:, out.name, dims)正常应该打印出[batch_size, seq_len]这样的符号名而不是[1, 128]这种数字。如果打印出来还是数字说明导出时 dynamic_axes 根本没被识别。5. 本篇常见错排查dynamic_axes 不生效与中间节点死维度错误一dynamic_axes 的 key 和 input_names 对不上。这是最高频的坑。比如input_names[input]但 dynamic_axes 写的是{input_ids: ...}ONNX 不会报错直接忽略导出结果还是固定维度。排查方法就是上面那段打印 input/output dims 的代码看符号名在不在。错误二只改了输入没改输出。输入动态了但输出logits的第 0 维还是固定 1。推理时 batch4 输入能进去输出却只有 1 行后面接的逻辑全乱。输出维度一定要一起声明。错误三中间节点仍然是死维度。这就是 excerpt 里提到的情况——你改了 graph 的 input/output 维度但网络内部某些节点的 shape 在导出时已经被常量折叠或算子推导固定住了。表现是输入输出看着是动态的但换个 shape 跑就报某个中间节点的维度不匹配。排查这种问题用 onnxruntime 的 verbose 日志import onnxruntime as ort so ort.SessionOptions() so.log_severity_level 1 sess ort.InferenceSession(model_dynamic.onnx, so, providers[CPUExecutionProvider])日志里会指出哪个节点在哪个维度上失败。常见原因是模型里有view、reshape、squeeze这类对 shape 敏感的算子写死了某个维度。解决办法是在 PyTorch 侧把这些操作改成动态友好的写法比如用x.reshape(x.size(0), -1)而不是x.view(1, -1)或者用torch.nn.functional.adaptive_avg_pool1d替代固定窗口的池化。错误四opset 版本太低。某些动态 shape 相关的算子需要 opset 12 以上才支持。如果你用的是很老的 PyTorch默认 opset 可能是 9 或 10动态维度会出问题。显式指定opset_version13或更高。错误五导出后手动改 dim_param 但没重跑验证。有人用onnx.load改dim_param再onnx.save这招对简单的输入输出节点有效但正如 excerpt 所说中间节点的问题它解决不了。改完必须用第 4 节的脚本重新跑不同 shape别只看文件保存成功。6. 语义一致的收尾把验证动作固化进你的导出流程导出 ONNX 这件事最怕的就是“导出了、没报错、上线才发现维度不对”。我的做法是把第 3 节的导出和第 4 节的验证合成一个脚本导出后立刻跑一组不同 shape全部通过才算成功。这样每次改模型结构或升级 PyTorch都能第一时间发现动态维度有没有被破坏。如果你后面要把这个模型接到统一的推理服务里做端到端测试或者需要调用模型对话能力做输出比对可以在验证脚本里通过 TaoToken 的 API 基址 https://taotoken.net/api 接入key 从 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 拿。模型对话入口在 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。如果你在做长期的编码或 Agent 类项目需要稳定的调用额度可以看 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。最后留一个我踩过的坑dynamic_axes里的维度名不要用?这种单字符虽然某些工具能识别但 onnxruntime 在部分版本下对?的处理不一致用batch_size、seq_len这种明确的名字最稳。导出脚本里加一行打印 input/output dims比事后 debug 省太多时间。
企业数字化 ERP 产品动态
相关推荐
跟 Trae 只说了一句话就仿了一个「兽音译者」小程序出来:TaoToken 统一 Key 接入与配置骨架 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 10:35:25
2026年4月OpenClaw腾讯云部署:5分钟安装与百炼APIKey配置避坑指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 10:35:25
open-code-review:用大语言模型实现自动化代码审查的工程实践 1. 从"PR 挂了三天没人理"到搭建 open-code-review我印象很深,上上个月周三下午,群里弹出一条消息:"各位,我的 PR 挂了两天半了,有没有人有空 review 一下?"三分钟后没人回,… · 2026/9/26 12:21:30
AI算力模组连接器选型:PogoPin多元化方案与可靠性验证实践 大家在AI服务器、液冷整机柜、GPU算力模组这些项目上也卷了蛮久了.真正干过硬件的小伙伴应该都有体会:算力芯片选型、散热方案、高速SerDes布线这几件事往往占据了80%以上的注意力,但最后整机在客户机房跑起来出问题,反而经常是“不起眼”的板… · 2026/9/26 12:21:17
Agent从脚本到产品:沙箱隔离与调度机制如何支撑百万级环境 这两年做Agent应用,我最深的体感是:写Agent逻辑不难,真正让人头疼的是怎么把Agent稳定、安全、规模化地跑起来。模型输出不可控、工具调用越权、环境互相污染、一重启状态全丢,这些问题在Demo阶段还能忍,一旦要上生产&… · 2026/9/26 12:21:17
一些大语言模型(LLM)相关的开源项目:用 TaoToken 统一 Key 跑通本地工具链 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 12:21:11
Tavo角色导入与聊天记录备份:新手到进阶完整指南 Tavo圈子里聊得最多的两个问题,一个是“怎么把别人调好的角色拿过来直接用”,另一个是“换手机、清缓存之后,我的聊天记录还在不在”。这两个问题看着简单,实际操作起来全是坑。我见过太多人因为不会导入角色,手动从零… · 2026/9/26 12:21:11
Windows上QEMU模拟ARM64安装银河麒麟V10信创适配指南 做信创适配的人,最头疼的一件事就是手里只有一台Windows笔记本,却要验证银河麒麟V10跑在鲲鹏ARM平台上到底行不行。买一台鲲鹏服务器当然最稳,可成本、机房、审批周期摆在那里,临时测试根本等不起;云上ARM实例方便&… · 2026/9/26 12:21:11
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46