Instructions to use edelfrade/BiRefNet_lite-webgpu with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers.js
How to use edelfrade/BiRefNet_lite-webgpu with Transformers.js:
// npm i @huggingface/transformers import { pipeline } from '@huggingface/transformers'; // Allocate pipeline const pipe = await pipeline('image-segmentation', 'edelfrade/BiRefNet_lite-webgpu');
Replace Sum with Add chains (no WebGPU Sum kernel: std::bad_alloc in wasm)
Browse files- README.md +5 -1
- onnx/model.onnx +2 -2
- onnx/model_fp16.onnx +2 -2
- rebuild.py +27 -2
README.md
CHANGED
|
@@ -22,7 +22,11 @@ The ONNX export in [onnx-community/BiRefNet_lite-ONNX](https://huggingface.co/on
|
|
| 22 |
Too many storage buffers in shader. Current: 17, Max is 16
|
| 23 |
```
|
| 24 |
|
| 25 |
-
This repo replaces those 4 Concat and 59 Split nodes with trees of smaller ones (at most 6 inputs per Concat, 5 outputs per Split, so every node uses ≤ 7 storage buffers).
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
|
| 27 |
| File | Precision | Size | Needs |
|
| 28 |
|---|---|---|---|
|
|
|
|
| 22 |
Too many storage buffers in shader. Current: 17, Max is 16
|
| 23 |
```
|
| 24 |
|
| 25 |
+
This repo replaces those 4 Concat and 59 Split nodes with trees of smaller ones (at most 6 inputs per Concat, 5 outputs per Split, so every node uses ≤ 7 storage buffers).
|
| 26 |
+
|
| 27 |
+
It also replaces the 20 `Sum` nodes with chains of `Add` (same order). ONNX Runtime's WebGPU backend has no Sum kernel, so Sum ran on the CPU, inside WebAssembly's 4 GB heap; in the deformable convolutions it adds four 392 MiB tensors (784 MiB each after the casts to fp32 the CPU needs), which fails with `std::bad_alloc`.
|
| 28 |
+
|
| 29 |
+
Nothing else changes: the outputs are **bit-for-bit identical** to the original on ONNX Runtime CPU, for both files.
|
| 30 |
|
| 31 |
| File | Precision | Size | Needs |
|
| 32 |
|---|---|---|---|
|
onnx/model.onnx
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:fda4d8be0d03f3e4ee2f61aa4a4f2b72e0175b237839e0afc37db0e4d9420a11
|
| 3 |
+
size 224191490
|
onnx/model_fp16.onnx
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:fb9bdae2f16fefd542929fe6efa52a0f8333bab5d92f35fdfcfb57ad0bc95e0d
|
| 3 |
+
size 114724609
|
rebuild.py
CHANGED
|
@@ -13,6 +13,11 @@ values (concatenation and splitting are associative along one axis):
|
|
| 13 |
- Split: at most SPLIT_FAN_OUT outputs per node (+ data + sizes inputs)
|
| 14 |
Every node then uses at most MAX_BUFFERS storage buffers.
|
| 15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
Usage: python rebuild.py <in.onnx> <out.onnx>
|
| 17 |
"""
|
| 18 |
|
|
@@ -128,11 +133,31 @@ class Rewriter:
|
|
| 128 |
split(node.input[0], list(zip(sizes, outputs)), node.name)
|
| 129 |
return made
|
| 130 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
def run(self) -> dict[str, int]:
|
| 132 |
-
stats = {"Concat": 0, "Split": 0}
|
| 133 |
new_nodes: list[onnx.NodeProto] = []
|
| 134 |
for node in self.graph.node:
|
| 135 |
-
if
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
new_nodes.extend(self.concat_tree(node))
|
| 137 |
stats["Concat"] += 1
|
| 138 |
elif buffers(node) > MAX_BUFFERS and node.op_type == "Split":
|
|
|
|
| 13 |
- Split: at most SPLIT_FAN_OUT outputs per node (+ data + sizes inputs)
|
| 14 |
Every node then uses at most MAX_BUFFERS storage buffers.
|
| 15 |
|
| 16 |
+
It also replaces Sum with a chain of Add nodes: ONNX Runtime's WebGPU backend
|
| 17 |
+
has no Sum kernel, so Sum runs on the CPU in WebAssembly memory, and the
|
| 18 |
+
deformable convolutions' Sums of four 392 MiB tensors exhaust its 4 GB heap
|
| 19 |
+
("std::bad_alloc").
|
| 20 |
+
|
| 21 |
Usage: python rebuild.py <in.onnx> <out.onnx>
|
| 22 |
"""
|
| 23 |
|
|
|
|
| 133 |
split(node.input[0], list(zip(sizes, outputs)), node.name)
|
| 134 |
return made
|
| 135 |
|
| 136 |
+
def add_chain(self, node: onnx.NodeProto) -> list[onnx.NodeProto]:
|
| 137 |
+
# ((a + b) + c) + d: the same order Sum adds in, but Add has a WebGPU kernel.
|
| 138 |
+
inputs = [i for i in node.input if i]
|
| 139 |
+
if len(inputs) == 1:
|
| 140 |
+
return [helper.make_node("Identity", inputs, list(node.output), name=node.name)]
|
| 141 |
+
made: list[onnx.NodeProto] = []
|
| 142 |
+
acc = inputs[0]
|
| 143 |
+
for index, other in enumerate(inputs[1:], start=2):
|
| 144 |
+
out = node.output[0] if index == len(inputs) else self.fresh(node.name)
|
| 145 |
+
made.append(helper.make_node("Add", [acc, other], [out], name=self.fresh(node.name)))
|
| 146 |
+
acc = out
|
| 147 |
+
return made
|
| 148 |
+
|
| 149 |
def run(self) -> dict[str, int]:
|
| 150 |
+
stats = {"Concat": 0, "Split": 0, "Sum": 0}
|
| 151 |
new_nodes: list[onnx.NodeProto] = []
|
| 152 |
for node in self.graph.node:
|
| 153 |
+
if node.op_type == "Sum":
|
| 154 |
+
# ONNX Runtime's WebGPU backend has no Sum kernel, so Sum runs on the
|
| 155 |
+
# CPU, inside WebAssembly's 4 GB heap. In the deformable convolutions it
|
| 156 |
+
# adds four 392 MiB tensors (784 MiB each after the fp16 → fp32 casts
|
| 157 |
+
# the CPU needs): "std::bad_alloc".
|
| 158 |
+
new_nodes.extend(self.add_chain(node))
|
| 159 |
+
stats["Sum"] += 1
|
| 160 |
+
elif buffers(node) > MAX_BUFFERS and node.op_type == "Concat":
|
| 161 |
new_nodes.extend(self.concat_tree(node))
|
| 162 |
stats["Concat"] += 1
|
| 163 |
elif buffers(node) > MAX_BUFFERS and node.op_type == "Split":
|