WebNNUtils/OnnxConverter
WebNNUtils/OnnxConverter 是一个 WebNN 编译器,可将 .onnx 模型编译为纯 JavaScript(JS)模型,使用 WebNN API 在网页浏览器中进行机器学习推理,且没有框架开销。
概述
虽然 JS 机器学习框架可以在浏览器中评估模型,但它们需要分发框架本身,并在加载时进行昂贵的预处理,从而影响延迟。在预处理期间,框架会确定算子的输入形状,在 CPU 与加速执行之间划分算子,并优化模型图。
使用 OnnxConverter 则没有这样的开销。在编译时,OnnxConverter 接收固定形状的 .onnx 文件,并生成用于构建等效 WebNN 图的 JS 代码。生成的 JS 代码可以直接在浏览器中使用,无需任何额外的框架依赖项。
隐私和安全
🔒 仅客户端处理
所有模型转换和 JavaScript 生成操作都只在你的本地环境中运行。任何模型数据、权重或知识产权都不会传输到任何外部服务器,也不会存储在外部服务器上。这使得该工具适用于:
- 专有模型
- 敏感的知识产权
- 具有严格数据治理要求的企业环境
- 任何需要完全数据隐私的场景
生成的输出
OnnxConverter 会生成多个文件:
- 一个 JavaScript 文件(
.js),包含 WebNN 图构建函数 - 一个或多个二进制文件(
.bin),包含模型权重 - 可选的 JSON 元数据文件,用于调试
核心输出是一个具有以下签名的 JavaScript 函数:
function loadModelGraph(operand_input, weights_buffers, builder) {
// 生成的 WebNN 图构建代码
return output_operand; // or {output1: operand1, output2: operand2}
}其中:
operand_input- 模型的输入操作数weights_buffers- 包含模型权重的 ArrayBuffer 数组builder- MLGraphBuilder WebNN 对象
安装
安装所需的 Python 依赖项:
pip install -U onnx protobuf numpy命令行接口
语法
python OnnxConverter.py MODEL_FILE [OPTIONS]必需参数
| Argument | Description |
|---|---|
model_file | 输入 ONNX 模型文件的路径(.onnx) |
可选参数
| Argument | Description |
|---|---|
--weights_file | 输出权重文件的路径(默认值:weights.bin) |
--output_file | 输出 JavaScript 文件的路径(默认值:model.js) |
--max_weights_size | 单个权重文件的最大大小,单位为 MB(默认值:1024) |
基本使用方法
步骤 1:转换模型
python OnnxConverter.py mobilenetv2-12.onnx --output_file mobilenet/mobilenet.js --weights_file mobilenet/weights.bin此命令会生成:
mobilenet/mobilenet.js- WebNN 图构建函数mobilenet/weights0.bin- 模型权重(可能会拆分为多个文件)
步骤 2:后处理(必需)
生成的 JavaScript 需要经过后处理,以处理代码顺序和 CPU/GPU 划分:
# 重新排序操作,以确保正确解析依赖关系
python ReorderModel.py mobilenet/mobilenet.js
# 在 CPU 与 GPU 执行之间划分操作
python CPUGraphPartitioner.py mobilenet/mobilenet.js步骤 3:在浏览器中加载权重
加载权重文件并将其传递给你的 WebNN 模型:
const weights_file = 'weights0.bin';
let cache = await caches.open("weights");
let weights_response = await cache.match(weights_file);
if (!weights_response) {
await cache.add(weights_file);
weights_response = await cache.match(weights_file);
}
const weights_buffer = await weights_response.arrayBuffer();
// 创建 WebNN 上下文和构建器
const context = await navigator.ml.createContext({ powerPreference: 'high-performance' });
const builder = new MLGraphBuilder(context);
// 安装 CPU 操作 polyfill
InstallCpuOps(builder);
// 加载模型图
const model_output = loadModelGraph(input_operand, [weights_buffer], builder);
deviceType选项已从 WebNN 规范的MLContextOptions中移除,因此当前的浏览器会忽略它。请改用powerPreference和accelerated提示,参见 Migrating from deviceType。
架构和处理流水线
形状推断处理
OnnxConverter 不执行传统的形状推断。相反,它会生成在运行时处理动态形状的 WebNN 图节点。WebNN 图构建器通过 polyfill 方法进行了扩展,这些方法可以处理 JavaScript 数字(而不仅仅是张量)。
例如,包含形状操作的 ONNX 图:
// 伪代码
a = tensor.shape();
b = a * 2;会生成调用 shape() 和 mul() 操作的 WebNN 节点,其中 mul 通过 polyfill 实现,以处理 shape() 返回的 JavaScript 数字。
CPU 操作 polyfill
在调用 loadModelGraph 之前,请安装 CPU 操作 polyfill:
const context = await navigator.ml.createContext({ powerPreference: 'high-performance' });
const builder = new MLGraphBuilder(context);
InstallCpuOps(builder);该 polyfill(定义于 CpuOps.js)为 MLGraphBuilder 扩展了可处理 JavaScript 数字的算子,从而支持形状操作和常量折叠等操作。
处理流水线
完整的转换过程包括三个阶段:
- OnnxConverter.py - 主编译器,生成 JS 图构建代码
- ReorderModel.py - 修复代码生成中输入操作数在被需要之后才计算的问题
- CPUGraphPartitioner.py - 为需要在 CPU 上执行的操作添加
cpu_前缀标注
多文件权重支持
对于大型模型,权重可以自动拆分为多个文件:
python OnnxConverter.py large_model.onnx --max_weights_size 512这会创建多个权重文件(weights0.bin、weights1.bin 等),它们必须全部加载:
const weight_files = ['weights0.bin', 'weights1.bin'];
const weights_buffers = [];
for (const file of weight_files) {
const response = await fetch(file);
weights_buffers.push(await response.arrayBuffer());
}
const model_output = loadModelGraph(input_operand, weights_buffers, builder);支持的 ONNX 操作
OnnxConverter 支持一整套 ONNX 操作,包括:
核心操作
- 卷积:Conv(支持 groups、padding、strides、dilations)
- 激活:Relu、Sigmoid、Softmax、LeakyRelu
- 池化:GlobalAveragePool
- 算术:Add、Sub、Mul、Div、Pow、Sqrt
张量操作
- 形状操作:Reshape、Transpose、Unsqueeze、Squeeze
- 数据移动:Concat、Gather、Slice、Expand
- 比较:Equal、Less、Where
高级操作
- 量化:DequantizeLinear、DynamicQuantizeLinear、MatMulInteger
- 矩阵操作:MatMul
- 专用操作:DepthToSpace、ConstantOfShape、ReduceMean
软件实现的操作
有些操作是用 JavaScript 实现的,而不是原生 WebNN:
- Slice:仅对常量切片提供有限的实现
- Squeeze:动态轴压缩
- Range:序列生成
调试和分析
代码生成问题
如果生成的代码存在依赖问题,请使用处理流水线:
python OnnxConverter.py model.onnx --output_file model.js
python ReorderModel.py model.js
python CPUGraphPartitioner.py model.js理解生成的代码
生成的 JavaScript 包含:
- 模型权重的常量定义
- 构建计算图的 WebNN 操作调用
- 模型的输入/输出处理
最佳实践
- 始终进行后处理:对生成的代码运行 ReorderModel.py 和 CPUGraphPartitioner.py
- 安装 Polyfill:在加载模型之前使用 InstallCpuOps()
- 处理多个权重文件:为大型模型中拆分的权重文件做好处理
- 缓存权重:对权重文件使用浏览器缓存,以提升加载性能
- 本地测试:通过 HTTP 服务器提供文件,以获得正确的加载行为
限制
- 不支持动态形状推断(形状在运行时解析)
- 复杂模型需要后处理流水线
- 对部分 ONNX 操作的支持有限
- 某些操作类型需要 CPU 回退