#推理 #onnx #形状 #运行时 #web-gpu #wgpu #gpu加速

wonnx

Wonnx是基于wgpu的ONNX运行时,旨在成为一个通用的GPU运行时,使用Rust编写

9个不稳定版本 (4个破坏性更新)

0.5.1 2023年9月30日
0.5.0 2023年4月20日
0.4.0 2023年3月5日
0.3.0 2022年7月31日
0.2.1 2021年11月10日

#39 in WebAssembly

每月26次下载
用于 3 crate

MIT/Apache

550KB
11K SLoC

WONNX

GitHub Workflow Status docs.rs Crates.io (latest) Crates.io

Wonnx是一个100%使用Rust编写的GPU加速ONNX推理运行时,适用于Web。

支持的平台(由wgpu启用)

API Windows Linux & Android macOS & iOS
Vulkan
Metal
DX12 ✅ (仅限Windows 10)
DX11 🚧
GLES3 🆗

✅ = 首选支持 — 🆗 = 尽力支持 — 🚧 = 不支持,但正在开发支持

入门

从命令行

确保您的系统支持Vulkan、Metal或DX12以访问GPU。然后,可以下载二进制发布版本,或者安装Rust并运行cargo install --git https://github.com/webonnx/wonnx.git wonnx-cli来安装CLI。

CLI工具(nnx)提供了一个方便的界面来实验模型(有关更多信息,请参阅README

nnx info ./data/models/opt-squeeze.onnx
nnx infer ./data/models/opt-squeeze.onnx -i data=./data/images/pelican.jpeg --labels ./data/models/squeeze-labels.txt --top 3

从Rust

wonnx crate作为依赖项添加(如果您有cargo-add,则运行cargo add wonnx)。然后,查看示例以获取使用示例,或浏览API文档

从Python

pip install wonnx

然后,使用

from wonnx import Session
session = Session.from_path(
    "../data/models/single_relu.onnx"
)
inputs = {"x": [-1.0, 2.0]}
assert session.run(inputs) == {"y": [0.0, 2.0]}

然后运行python3并使用上面的Python代码!

有关Python包的详细信息,包括构建说明,请参阅wonnx-py

在浏览器中,使用WebGPU + WebAssembly

npm install @webonnx/wonnx-wasm

然后,在客户端

import init, { Session, Input } from "@webonnx/wonnx-wasm";

// Check for WebGPU availability first: if(navigator.gpu) { .. }
await init();
const session = await Session.fromBytes(modelBytes /* Uint8Array containing the ONNX file */);
const input = new Input();
input.insert("x", [13.0, -37.0]);
const result = await session.run(input); // This will be an object where the keys are the names of the model outputs and the values are arrays of numbers.
session.free();
input.free();

@webonnx/wonnx-wasm提供了访问WONNX的接口,该接口作为WebAssembly模块包含,并将使用浏览器的WebGPU实现。有关涉及打包器的更完整的使用示例,请参阅wonnx-wasm-example

有关JS/WASM包的详细信息,包括构建说明,请参阅wonnx-wasm

开发

要修改wonnx本身,请按照以下步骤操作

  • 安装Rust
  • 为GPU API安装Vulkan、Metal或DX12。
  • git克隆此仓库。
git clone https://github.com/webonnx/wonnx.git

然后,您就准备好了!您可以通过cargo运行其中一个示例

cargo run --example squeeze --release

运行其他模型

  • 要运行onnx模型,首先使用nnx prepare进行简化(在仓库内部替换为cargo run -- prepare
nnx prepare -i ./some-model.onnx ./some-model-prepared.onnx

要指定动态维度参数,例如添加--set batch_size=1

您还可以使用外部工具,例如onnx-simplifier,使用以下命令

# pip install -U pip && pip install onnx-simplifier
python -m onnxsim mnist-8.onnx opt-mnist.onnx
cargo run --example mnist --release

测试过的模型

  • Squeezenet
  • MNIST
  • BERT

GPU选择

除非在WebAssembly中运行,否则您可以通过以下环境变量设置来影响WGPU的GPU选择

  • WGPU_ADAPTER_NAME使用您想要使用的适配器名称的子串(例如1080将匹配NVIDIA GeForce 1080ti)。
  • WGPU_BACKEND使用您想要使用的后端列表,以逗号分隔(vulkanmetaldx12dx11gl)。
  • WGPU_POWER_PREFERENCE使用当未指定特定适配器名称时的电源偏好(highlow

贡献:实现新的算子

即使没有在深度学习、WGSL或Rust方面有丰富的经验,也欢迎贡献。我希望这个项目能成为我们学习这些技术的一个沙箱,而不仅仅是项目的初始范围。

要实现算子,您只需做以下事情

  1. compiler.rs中添加一个新匹配模式
  2. 使用get_attribute函数检索其属性值
    let alpha = get_attribute("alpha", Some(1.0), node);
    // or without default value
    let alpha = get_attribute::<f32>("alpha", None, node);
  1. 使用context添加您想要在WGSL着色器中使用的任何变量。
  2. templates文件夹中编写一个新的WGSL模板。

structs.wgsl中有可用类型,但您也可以在模板中生成新的类型。

  1. 请尊重每个条目的绑定布局,从0开始递增1,输入在前,输出在后。如果绑定的数量超过4,则增加绑定组。您可以在sequencer.rs中更改输入。
  2. 编写逻辑。

上下文中有默认变量。

  • {{ i_lens[0] }}:输入0的长度。这也适用于输出:{{ o_lens[0] }}以及其他输入{{ i_lens[1] }}
  • {{ i_shape[0] }}:输入0的维度数组。要获取数组的第一个维度,只需使用:{{ i_shape[0][0] }}
  • {{ i_chunks[0] }}:输入0每个维度的块大小。默认情况下,每个变量都表示为一个长值数组,要获取特定值,必须按块移动。这些块在这个变量中表示。要获取第一个维度的块大小,使用:{{ i_chunks[0][0] }}
  • {{ op_type }}操作类型,如激活,使用相同的模板。
  1. 使用utils函数进行测试,并将其放置在测试文件夹中。测试可以如下所示
#[test]
fn test_matmul_square_matrix() {
    // USER INPUT

    let n = 16;
    let mut input_data = HashMap::new();

    let data_a = ndarray::Array2::eye(n);
    let mut data_b = ndarray::Array2::<f32>::zeros((n, n));
    data_b[[0, 0]] = 0.2;
    data_b[[0, 1]] = 0.5;

    let sum = data_a.dot(&data_b);

    input_data.insert("A".to_string(), data_a.as_slice().unwrap());
    input_data.insert("B".to_string(), data_b.as_slice().unwrap());

    let n = n as i64;
    let model = model(graph(
        vec![tensor("A", &[n, n]), tensor("B", &[n, n])],
        vec![tensor("C", &[n, n])],
        vec![],
        vec![],
        vec![node(vec!["A", "B"], vec!["C"], "MatMul", "MatMul", vec![])],
    ));

    let session =
        pollster::block_on(wonnx::Session::from_model(model)).expect("Session did not create");

    let result = pollster::block_on(session.run(input_data)).unwrap();

    // Note: it is better to use a method that compares floats with a tolerance to account for differences
    // between implementations; see `wonnx/tests/common/mod.rs` for an example.
    assert_eq!((&result["C"]).try_into().unwrap(),sum.as_slice().unwrap());
}

查看tera文档以获取其他模板操作:[https://tera.netlify.app/docs/](https://tera.netlify.app/docs/)

  1. 如果您在任何时候想优化多个节点,可以在sequencer.rs中进行。

支持的运算符(参考ONNX IR

运算符 自版本 实现 支持形状推断
Abs 13, 6, 1
Acos 7
Acosh 9
Add 14, 13, 7, 6, 1
And 7, 1
ArgMax 13, 12, 11, 1
ArgMin 13, 12, 11, 1
Asin 7
Asinh 9
Atan 7
Atanh 9
AveragePool 11, 10, 7, 1
BatchNormalization 15, 14, 9, 7, 6, 1
BitShift 11
Cast 13, 9, 6, 1
Ceil 13, 6, 1
Clip 13, 12, 11, 6, 1
Compress 11, 9
Concat 13, 11, 4, 1
ConcatFromSequence 11
Constant 13, 12, 11, 9, 1
ConstantOfShape 9
Conv 11, 1
ConvInteger 10
ConvTranspose 11, 1
Cos 7
Cosh 9
CumSum 14, 11
DepthToSpace 13, 11, 1
DequantizeLinear 13, 10
Det 11
Div 14, 13, 7, 6, 1
Dropout 13, 12, 10, 7, 6, 1
Einsum 12
Elu 6, 1
Equal 13, 11, 7, 1
Erf 13, 9
Exp 13, 6, 1
Expand 13, 8
EyeLike 9
Flatten 13, 11, 9, 1
Floor 13, 6, 1
GRU 14, 7, 3, 1
Gather 13, 11, 1 ✅ (axis=0)
GatherElements 13, 11
GatherND 13, 12, 11
Gemm 13, 11, 9, 7, 6, 1 ✅*
GlobalAveragePool 1
GlobalLpPool 2, 1
GlobalMaxPool 1
Greater 13, 9, 7, 1
GridSample 16
HardSigmoid 6, 1
Hardmax 13, 11, 1
Identity 16, 14, 13, 1
如果 16, 13, 11, 1
实例归一化 6, 1
IsInf 10
IsNaN 13, 9
LRN 13, 1
LSTM 14, 7, 1
LeakyRelu 6, 1
Less 13, 9, 7, 1
Log 13, 6, 1
Loop 16, 13, 11, 1
LpNormalization 1
LpPool 11, 2, 1
MatMul 13, 9, 1
MatMulInteger 10
Max 13, 12, 8, 6, 1
MaxPool 12, 11, 10, 8, 1
MaxRoiPool 1
MaxUnpool 11, 9
Mean 13, 8, 6, 1
Min 13, 12, 8, 6, 1
Mod 13, 10
Mul 14, 13, 7, 6, 1
Multinomial 7
Neg 13, 6, 1
非最大值抑制 11, 10
NonZero 13, 9
Not 1
OneHot 11, 9 ✅ (axis=-1)
可选 15
OptionalGetElement 15
OptionalHasElement 15
Or 7, 1
PRelu 9, 7, 6, 1
Pad 13, 11, 2, 1 ✅ (mode=constant, pads>=0)
Pow 15, 13, 12, 7, 1 ✅ (broadcast=0 and data type is f32)
QLinearConv 10
QLinearMatMul 10
QuantizeLinear 13, 10
RNN 14, 7, 1
RandomNormal 1
RandomNormalLike 1
RandomUniform 1
RandomUniformLike 1
倒数 13, 6, 1
ReduceL1 13, 11, 1
ReduceL2 13, 11, 1
ReduceLogSum 13, 11, 1
ReduceLogSumExp 13, 11, 1
ReduceMax 13, 12, 11, 1
ReduceMean 13, 11, 1
ReduceMin 13, 12, 11, 1
ReduceProd 13, 11, 1
ReduceSum 13, 11, 1
ReduceSumSquare 13, 11, 1
Relu 14, 13, 6, 1
Reshape 14, 13, 5, 1
Resize 13, 11, 10
ReverseSequence 10
RoiAlign 16, 10
Round 11
Scan 11, 9, 8
Scatter (已弃用) 11, 9
ScatterElements 16, 13, 11
ScatterND 16, 13, 11
Selu 6, 1
SequenceAt 11
SequenceConstruct 11
SequenceEmpty 11
SequenceErase 11
SequenceInsert 11
SequenceLength 11
Shape 15, 13, 1
Shrink 9
Sigmoid 13, 6, 1
Sign 13, 9
Sin 7
Sinh 9
Size 13, 1
Slice 13, 11, 10, 1
Softplus 1
Softsign 1
SpaceToDepth 13, 1
Split 13, 11, 2, 1
SplitToSequence 11
Sqrt 13, 6, 1
Squeeze 13, 11, 1
StringNormalizer 10
Sub 14, 13, 7, 6, 1
Sum 13, 8, 6, 1
Tan 7
Tanh 13, 6, 1
TfIdfVectorizer 9
ThresholdedRelu 10
Tile 13, 6, 1
TopK 11, 10, 1
Transpose 13, 1
Trilu 14
Unique 11
Unsqueeze 13, 11, 1
Upsample (已弃用) 10, 9, 7
Where 16, 9
Xor 7, 1
函数 自版本
伯努利 15
CastLike 15
Celu 12
DynamicQuantizeLinear 11
GreaterOrEqual 12
HardSwish 14
LessOrEqual 12
LogSoftmax 13, 11, 1
MeanVarianceNormalization 13, 9
负对数似然损失 13, 12
Range 11
Softmax 13, 11, 1
SoftmaxCrossEntropyLoss 13, 12

已知限制

  • 操作 ClipResizeReshapeSplitPadReduceSum 接受(通常是可选的)次要输入来设置各种参数(例如,axis)。这些输入仅支持作为初始化器张量提供时(即不依赖于输入并且不是其他操作的输出),因为WONNX预先将所有操作编译为着色器,并且必须提前知道这些参数。

  • 内部不支持64位整数(原因是它们不在当前的WGSL版本中得到支持);具有64位标量输入和初始化器的输入转换为32位值(可能溢出)。

  • 对于 MatMulGemm,矩阵维度必须是2的倍数,或者输出矩阵的大小必须是(1, N)。矩阵乘法仅支持浮点数,不支持整数(这是WebGPU/WGSL的限制)。

形状推断

WONNX需要知道每个操作的输入和输出张量的形状,以便生成执行它的着色器代码。然而,ONNX模型并不总是包含中间值的信息。形状推断是从输入和输出的形状以及每个操作的特征推断中间值形状的过程。

WONNX支持有限形式的形状推断(确定模型图中各个节点形状的过程)。形状推断可以通过程序和命令行界面进行。在执行形状推断之前,所有动态维度参数都需要替换为静态值。形状推断仅从输入形状推断特定支持操作的输出形状(见上表)。如果节点的任何输入的形状未知,则推断无法成功。对于已经为输出形状完全定义的节点,将保持不变(并使用这些输出作为输入的节点的形状推断)。

要使用命令行界面执行形状推断,请运行类似以下命令的命令(此处 batch_sizesequence_length 是动态维度参数;- 标志启用形状推断)

nnx prepare model.onnx model-prepared.onnx --set batch_size=1 --set sequence_length=255 -i

要程序化执行形状推断,请使用来自 wonnx_preprocessing::shape_inference 模块的 apply_dynamic_dimensionsinfer_shapes

常量折叠

一些模型包含可以静态确定的子图输出,因为它们不依赖于推理期间提供的特定输入。WONNX可以将这样的常量中间值替换为静态值(“常量折叠”)。以下情况支持此操作

  • 类型为 Constant 的节点的输出(这些被替换为初始化器)
  • 已知输入形状的类型为 Shape 的节点的输出(在推理前或推理期间)
  • 所有输入都是常量(可能在折叠后),并且操作符由WONNX支持的节点的输出

常量折叠作为形状推断的一部分执行,除非禁用(从CLI传递 --no-fold-constants 以禁用)。这样做是为了支持使用例如 Shape/Squeeze/Unsqueeze 等运算符动态计算形状的模型,这些运算符依赖于动态设置的维度参数(例如批大小)。

许可证

根据您选择的以下许可证之一授权

以下文件除外

  • 数据/模型:

    • mobilenetv2-7.onnx:[来源](https://github.com/onnx/models/blob/main/vision/classification/mobilenet/model/mobilenetv2-7.onnx),仅Apache-2.0许可证。
    • squeezenet-labels.txt:[来源](https://github.com/onnx/models/blob/main/vision/classification/synset.txt),仅Apache-2.0许可证。
  • 数据/图片:

    • pelican.jpeg:[来源](https://en.wikipedia.org/wiki/Pelican#/media/File:Pelikan_Walvis_Bay.jpg),(C)Rui Ornelas,[CC-BY 2.0](https://creativecommons.org/licenses/by/2.0/)。
    • bald_eagle.jpeg:[来源](https://en.wikipedia.org/wiki/Bald_eagle#/media/File:Bald-Eagle-9114-cropped.jpg),(C)David R. Tribble,[CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)。

贡献

除非您明确声明,否则您有意提交以包含在作品中的任何贡献都应如上所述双重许可,不附加任何额外条款或条件。

依赖项

~11–44MB
~697K SLoC