
ONNX Runtime Web 中的 ONNX Protobuf 生成代码onnx.js / onnx.d.ts 的生成、依赖修复与工程实践【免费下载链接】onnxruntimeONNX Runtime: cross-platform, high performance ML inferencing and training accelerator项目地址: https://gitcode.com/GitHub_Trending/on/onnxruntime导读在 ONNX Runtime 的前端 JavaScript 生态中模型文件.onnx本质上是遵循 ONNX 规范定义的 protobuf 序列化数据。js/web/lib/onnxjs/ort-schema/protobuf/目录存放了 ONNX 协议定义的生成代码——onnx.js与onnx.d.ts它们是 onnxjsONNX Runtime 的纯 JS 推理后端解析标准 ONNX 模型的核心依赖。本文围绕该目录的 README 展开说明这些生成文件的来源、所依赖的 protobufjs / long 版本以及针对两个已知 bug 的修复方案并结合 onnxjs 的模型加载实现 剖析生成代码在真实推理链路中的用法帮助读者理解生成代码的来龙去脉并掌握在自身 TypeScript 项目中规避同类坑位的具体做法。一、目录里有什么一份生成物清单js/web/lib/onnxjs/ort-schema/protobuf/目录内容非常精简共 3 个文件文件说明README.md本文所依据的说明文档交代生成来源与已知问题onnx.jsprotobufjs 生成的 ONNX 协议序列化/反序列化运行时代码onnx.d.ts与onnx.js配套的 TypeScript 类型声明文件按 README 的说法这两个文件是从 onnx-proto 的一个 forkupdate-v9 分支生成的即 ONNX 协议定义的 protobuf 描述.proto经 protobufjs 工具链编译后的产物并非手写代码。因此在日常开发中不应直接修改这两个文件而应回到 proto 源定义重新生成。从生成的onnx.d.ts内容看这份生成物覆盖了 ONNX 完整的数据结构体系AttributeProto、TensorProto、GraphProto、TypeProto、SparseTensorProto、ModelProto等接口与枚举一应俱全并包含Version枚举_START_VERSION 0到IR_VERSION 9。onnx.js则使用protobufjs/minimal实现了完整的 encode / decode / verify / fromObject / toObject 静态方法。也就是说这一对文件足够支撑对任意标准 ONNX 模型的二进制解析与序列化。二、生成代码的版本依赖protobufjs7.2.4 与 long5.2.3README 明确指出生成代码基于以下两个运行时依赖protobufjs7.2.4protobuf 编解码的运行时核心生成的onnx.js顶部即require(protobufjs/minimal)long5.2.3用于表示 64 位整数的库ONNX 的int64字段在 JS 中会映射为Long类型。这两个版本号在仓库的包配置中可以得到印证js/web/package.json 的dependencies中声明了protobufjs: ^7.2.4与long: ^5.2.3js/node/package.json 中同样声明了protobufjs: ^7.2.4。注意^前缀意味着安装时会解析到 7.x / 5.x 的最新兼容版本因此这里记录的 7.2.4 / 5.2.3 是生成时的基准版本也是 README 描述两个 bug 时的上下文版本。为什么 64 位整数会成为问题焦点在生成的onnx.d.ts中凡是 ONNX 规范里定义为int64的字段如AttributeProto.i、AttributeProto.ints、ModelProto.irVersion等其类型都被声明为number | Long/** AttributeProto i */ i?: (number|Long|null);在 onnxjs 的运行时逻辑中这些Long值会被统一转换为普通 JS number 再参与计算。例如 js/web/lib/onnxjs/util.ts 中的LongUtil.longToNumber()就专门处理这一转换static longToNumber(n: Long | bigint | number) { if (Long.isLong(n)) { return n.toNumber(); } else if (typeof n bigint) { return Number(n); } return n; }该工具方法被用于转换irVersion、opset 版本、张量维度dims、节点属性等所有可能出现Long的场景参见 js/web/lib/onnxjs/model.ts 对irVersion与opsetImport的读取。可见long库在生成代码与运行时中都是关键依赖其类型声明一旦出错整个编译链路都会受影响——这正是 README 记录第二个 bug 的背景。三、bug 1long5.2.3 的 CommonJS 类型导出问题与 postinstall 修复问题现象README 记载 long5.2.3 存在两个 bug第一个是type export does not work with commonjs即 long 包的类型导出在 CommonJS 模块体系下不生效会导致 TypeScript 编译期报出类型缺失或解析失败的错误。这是 long.js 的已知问题对应 dcodeIO/long.js 的 PR #124 所讨论的内容。修复方案README 给出的做法是为 long 添加一个 postinstall 脚本在依赖安装完成后自动执行修补。在 ONNX Runtime 的包配置中可以看到同源思路的具体实现例如 js/web/package.json 中的preprepare: node -e \require(node:fs).copyFileSync(./node_modules/long/index.d.ts, ./node_modules/long/umd/index.d.ts)\该命令在 prepare 阶段把 long 包根目录的index.d.ts复制到umd/子目录确保 CommonJS/UMD 入口能够解析到类型声明。虽然仓库内这条命令挂在preprepare上其本质与 README 所述的 postinstall 修补策略一致在安装/构建阶段以脚本方式对 node_modules 中的 long 进行类型文件修补从而绕开上游类型导出缺陷。给开发者的启示在自己的项目中若遇到long类型解析失败可参考此模式在package.json中挂一个postinstall或preprepare脚本将正确的.d.ts拷贝到模块解析预期的位置若不想维护脚本也可在tsconfig.json的paths中为long显式指定类型声明路径。四、bug 2onnx.d.ts 中的 import 语句需要改写问题现象第二个 bug 出现在生成的 TypeScript 声明文件onnx.d.ts内部。protobufjs 生成器产出的声明文件包含如下语句import Long require(long);这种import ... require(...)写法在部分 TypeScript 编译配置尤其是使用 ESM /esModuleInterop语义的项目下会引发类型导入错误。修复方案README 明确记载该行已被替换为 ES 模块风格的默认导入import Long from long;这一替换已经完成并已对onnx.d.ts应用了代码格式化。验证仓库中的 js/web/lib/onnxjs/ort-schema/protobuf/onnx.d.ts文件开头正是import Long from long; import * as $protobuf from protobufjs;说明修复已实际落地。这也是为什么该文件虽然是生成代码却在版本库中被保留为已修补状态的原因——直接重新生成会覆盖这一手写修复因此在升级 protobufjs 或重新生成时需要再次应用同样的替换。深层原因import Long require(long)是 TypeScript 针对 CommonJS 模块的专用导入语法仅允许在 CommonJS 模块目标下使用当项目以 ESM 方式编译或启用严格模块检查时会报错。改为import Long from long后借助esModuleInterop的默认导入语义即可兼容两种模块体系。五、生成代码在 onnxjs 中的实际调用链理解生成代码的用途最直观的方式是看它如何被 onnxjs 消费。onnxjs 的标准 ONNX 模型加载入口在 js/web/lib/onnxjs/model.tsimport { onnx } from ./ort-schema/protobuf/onnx; // ... private loadFromOnnxFormat(buf: Uint8Array, graphInitializer?: Graph.Initializer): void { const modelProto onnx.ModelProto.decode(buf); const irVersion LongUtil.longToNumber(modelProto.irVersion); if (irVersion 3) { throw new Error(only support ONNX model with IR_VERSION3); } this._opsets modelProto.opsetImport.map((i) ({ domain: i.domain as string, version: LongUtil.longToNumber(i.version!), })); this._graph Graph.from(modelProto.graph!, graphInitializer); }调用链如下onnx.ModelProto.decode(buf)将模型二进制Uint8Array反序列化为ModelProto对象——这一步由生成的onnx.js完成通过LongUtil.longToNumber把irVersion与 opset 版本从Long转为 number并校验 IR_VERSION ≥ 3取出modelProto.graph交给Graph.from()构建内部图结构js/web/lib/onnxjs/graph.ts 同目录依赖该类型。onnx.d.ts中的onnx命名空间类型onnx.ModelProto、onnx.GraphProto、onnx.AttributeProto等为整个 onnxjs 的图构建、属性解析、张量处理模块提供了类型约束例如 js/web/lib/onnxjs/attribute.ts、js/web/lib/onnxjs/tensor.ts 均导入该命名空间类型。可以说onnx.jsonnx.d.ts是 onnxjs 理解标准 ONNX 模型二进制格式的翻译层。六、工程实践要点总结综合 README 与仓库源码围绕这份生成代码的工程实践可以归纳为以下几点区分生成物与手写代码onnx.js/onnx.d.ts由 onnx-proto forkupdate-v9 分支经 protobufjs 生成改 proto 定义后需重新生成而不是手改生成物锁定依赖版本上下文生成代码依赖protobufjs7.2.4与long5.2.3升级这些库后应回归验证生成代码与新版本运行时是否兼容记住两处手写修补long 的 CommonJS 类型导出问题通过安装后脚本拷贝index.d.ts修复onnx.d.ts中的import Long require(long)已被替换为import Long from long重新生成后需再次应用验证手段修改或升级后可通过 onnxjs 的模型加载路径Model.load→onnx.ModelProto.decode跑通一个标准.onnx模型的加载来确认序列化层工作正常。这些经验不仅适用于 ONNX Runtime Web 仓库本身对于任何依赖 protobufjs 生成代码 long 64 位整数的 TypeScript 项目都有直接参考价值。【免费下载链接】onnxruntimeONNX Runtime: cross-platform, high performance ML inferencing and training accelerator项目地址: https://gitcode.com/GitHub_Trending/on/onnxruntime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考