Download make_model.py from Reigningwet/wet-stems-hd: direct link, hf CLI and curl.
- Browser
- Download file 2.5 kB
-
https://huggingface.co/Reigningwet/wet-stems-hd/resolve/main/make_model.py
- Command line
-
hf download hf://Reigningwet/wet-stems-hd/make_model.py
-
curl -L -o make_model.py https://huggingface.co/Reigningwet/wet-stems-hd/resolve/main/make_model.py
2.5 kB
| """Rebuilds htdemucs-wet.onnx (the Wet Studio HD stem split model). | |
| Input: htdemucs_fp16weights.onnx from huggingface.co/StemSplitio/htdemucs-onnx | |
| (revision d54ed9eb60e258ea82131c6ee14578628816456a), an MIT export of Meta's | |
| HT-Demucs v4. Why: that export doesn't build on WebGPU in onnxruntime-web | |
| (a double-typed ConstantOfShape in its iSTFT has no kernel there), and its | |
| 24,917 nodes take ~37 s to load. With the input shape fixed, everything that | |
| only depends on shapes folds to constants: 1,427 standard-ONNX nodes, a 3 s | |
| load on WebGPU. Big float tensors are then stored as float16 (+ a Cast), so | |
| the file is 174 MB. Output matches the original to ~90 dB SDR. | |
| pip install onnx onnxruntime numpy | |
| python make_model.py htdemucs_fp16weights.onnx htdemucs-wet.onnx | |
| """ | |
| import sys, onnx, numpy as np, onnxruntime as ort | |
| from onnx import numpy_helper, helper, TensorProto | |
| src, dst = sys.argv[1], sys.argv[2] | |
| so = ort.SessionOptions() | |
| so.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_BASIC # constant folding only: stays standard ONNX | |
| so.optimized_model_filepath = dst + '.folded' | |
| ort.InferenceSession(src, so, providers=['CPUExecutionProvider']) | |
| m = onnx.load(dst + '.folded'); g = m.graph | |
| users = {} | |
| for n in g.node: | |
| for i in n.input: users.setdefault(i, []).append(n.op_type) | |
| def half(keep): # store the float tensors `keep` picks as float16, cast back to float at load | |
| inits, casts = [], [] | |
| for t in g.initializer: | |
| if t.data_type == TensorProto.FLOAT and keep(t): | |
| inits.append(numpy_helper.from_array(numpy_helper.to_array(t).astype(np.float16), t.name + '__fp16')) | |
| casts.append(helper.make_node('Cast', [t.name + '__fp16'], [t.name], to=TensorProto.FLOAT, name=t.name + '__cast')) | |
| else: | |
| inits.append(t) | |
| del g.initializer[:]; g.initializer.extend(inits) | |
| nodes = list(g.node); del g.node[:]; g.node.extend(casts + nodes) | |
| # 1) the network's weights (conv/matmul, not the STFT kernels) | |
| half(lambda t: int(np.prod(t.dims)) >= 1024 and users.get(t.name) and all(u in ('Conv', 'ConvTranspose', 'MatMul') for u in users[t.name]) and 'stft' not in t.name.lower()) | |
| # 2) the remaining big tensors: STFT kernels and folded embeddings; the iSTFT window envelope stays float32 | |
| half(lambda t: int(np.prod(t.dims)) >= 100000 and 'Cast_output' not in t.name) | |
| ops = [o for o in m.opset_import if o.domain == '']; del m.opset_import[:]; m.opset_import.extend(ops) | |
| onnx.checker.check_model(m) | |
| onnx.save(m, dst) | |