Move verified ONNX export to its dedicated repository
Browse files- README.md +8 -5
- docs/EXPORTS.md +5 -3
- onnx/README.md +7 -117
- onnx/SOURCE.json +0 -16
- onnx/checksums.sha256 +0 -8
- onnx/decode.onnx +0 -3
- onnx/duration.onnx +0 -3
- onnx/export_onnx.py +0 -261
- onnx/inference_onnx.py +0 -229
- onnx/parity_report.json +0 -31
- onnx/requirements.txt +0 -7
- release_manifest.json +7 -47
README.md
CHANGED
|
@@ -262,11 +262,14 @@ The result is a 24 kHz mono `float32` waveform. Long input is split at punctuati
|
|
| 262 |
|
| 263 |
### ONNX Runtime
|
| 264 |
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
|
|
|
| 268 |
|
| 269 |
```bash
|
|
|
|
|
|
|
| 270 |
python -m pip install -r onnx/requirements.txt
|
| 271 |
python onnx/inference_onnx.py \
|
| 272 |
--text "The complete model now runs through ONNX Runtime." \
|
|
@@ -278,7 +281,7 @@ python onnx/inference_onnx.py \
|
|
| 278 |
The neural model is split into `duration.onnx` and `decode.onnx`; together they
|
| 279 |
contain the complete learned text-to-waveform path. The English eSpeak-ng
|
| 280 |
frontend remains CPU-side code. See the
|
| 281 |
-
[`ONNX
|
| 282 |
for graph contracts, provenance, parity measurements, browser deployment, and
|
| 283 |
re-export instructions.
|
| 284 |
|
|
@@ -342,7 +345,7 @@ This release is inference-first. New-voice and new-language adaptation are **not
|
|
| 342 |
| `inference.py` | Public Python API and CLI |
|
| 343 |
| `inflect_vits_frontend.py` | English normalization, phonemization, and punctuation frontend |
|
| 344 |
| `runtime/` | Self-contained model implementation |
|
| 345 |
-
| `
|
| 346 |
| `samples/` | Held-out example generations |
|
| 347 |
| `evaluation/final/` | Frozen benchmark prompts, reports, and protocol artifacts |
|
| 348 |
| `docs/` | API, deployment, evaluation, adaptation, and export documentation |
|
|
|
|
| 262 |
|
| 263 |
### ONNX Runtime
|
| 264 |
|
| 265 |
+
The official verified FP32 export is published separately as
|
| 266 |
+
[`Inflect-Micro-v2-ONNX`](https://huggingface.co/owensong/Inflect-Micro-v2-ONNX).
|
| 267 |
+
It supports dynamic lengths, CPU/CUDA/DirectML provider selection,
|
| 268 |
+
deterministic seeds, and the same long-text wrapper without importing PyTorch:
|
| 269 |
|
| 270 |
```bash
|
| 271 |
+
git clone https://huggingface.co/owensong/Inflect-Micro-v2-ONNX
|
| 272 |
+
cd Inflect-Micro-v2-ONNX
|
| 273 |
python -m pip install -r onnx/requirements.txt
|
| 274 |
python onnx/inference_onnx.py \
|
| 275 |
--text "The complete model now runs through ONNX Runtime." \
|
|
|
|
| 281 |
The neural model is split into `duration.onnx` and `decode.onnx`; together they
|
| 282 |
contain the complete learned text-to-waveform path. The English eSpeak-ng
|
| 283 |
frontend remains CPU-side code. See the
|
| 284 |
+
[`ONNX repository`](https://huggingface.co/owensong/Inflect-Micro-v2-ONNX)
|
| 285 |
for graph contracts, provenance, parity measurements, browser deployment, and
|
| 286 |
re-export instructions.
|
| 287 |
|
|
|
|
| 345 |
| `inference.py` | Public Python API and CLI |
|
| 346 |
| `inflect_vits_frontend.py` | English normalization, phonemization, and punctuation frontend |
|
| 347 |
| `runtime/` | Self-contained model implementation |
|
| 348 |
+
| [`Inflect-Micro-v2-ONNX`](https://huggingface.co/owensong/Inflect-Micro-v2-ONNX) | Separate official FP32 ONNX graphs, torch-free runner, parity report, checksums, and exporter |
|
| 349 |
| `samples/` | Held-out example generations |
|
| 350 |
| `evaluation/final/` | Frozen benchmark prompts, reports, and protocol artifacts |
|
| 351 |
| `docs/` | API, deployment, evaluation, adaptation, and export documentation |
|
docs/EXPORTS.md
CHANGED
|
@@ -6,14 +6,16 @@
|
|
| 6 |
| --- | --- | --- |
|
| 7 |
| PyTorch FP32 | Supported | Canonical weights and the fully tested runtime. |
|
| 8 |
| PyTorch FP16/BF16 | Not released | Smaller in memory, but not validated for waveform quality or CPU compatibility. |
|
| 9 |
-
| ONNX FP32 | Supported |
|
| 10 |
| INT8 / INT4 | Not released | Naive weight quantization can audibly damage the integrated waveform decoder. |
|
| 11 |
| GGUF | Not applicable today | GGUF targets transformer/LLM runtimes and is not a drop-in container for this VITS-family convolutional waveform model. |
|
| 12 |
| Core ML / TFLite | Not released | Requires separate conversion and target-device validation. |
|
| 13 |
|
| 14 |
## Supported ONNX package
|
| 15 |
|
| 16 |
-
The official
|
|
|
|
|
|
|
| 17 |
|
| 18 |
- `onnx/duration.onnx`: token sequence to aligned acoustic distribution;
|
| 19 |
- `onnx/decode.onnx`: aligned distribution and seeded noise to waveform;
|
|
@@ -26,7 +28,7 @@ seeded latent sampling, speed and variation controls, punctuation-aware
|
|
| 26 |
long-text chunking, and CPU/CUDA/DirectML provider selection. eSpeak-ng remains
|
| 27 |
the non-neural English frontend and is not embedded in the ONNX graphs.
|
| 28 |
|
| 29 |
-
See [`
|
| 30 |
for installation and verified parity measurements.
|
| 31 |
|
| 32 |
## Why only verified FP32 ships
|
|
|
|
| 6 |
| --- | --- | --- |
|
| 7 |
| PyTorch FP32 | Supported | Canonical weights and the fully tested runtime. |
|
| 8 |
| PyTorch FP16/BF16 | Not released | Smaller in memory, but not validated for waveform quality or CPU compatibility. |
|
| 9 |
+
| ONNX FP32 | Supported | Separate official [`Inflect-Micro-v2-ONNX`](https://huggingface.co/owensong/Inflect-Micro-v2-ONNX) repository with verified dynamic-length graphs, a torch-free Python runner, and a reproducible exporter. |
|
| 10 |
| INT8 / INT4 | Not released | Naive weight quantization can audibly damage the integrated waveform decoder. |
|
| 11 |
| GGUF | Not applicable today | GGUF targets transformer/LLM runtimes and is not a drop-in container for this VITS-family convolutional waveform model. |
|
| 12 |
| Core ML / TFLite | Not released | Requires separate conversion and target-device validation. |
|
| 13 |
|
| 14 |
## Supported ONNX package
|
| 15 |
|
| 16 |
+
The official
|
| 17 |
+
[`Inflect-Micro-v2-ONNX`](https://huggingface.co/owensong/Inflect-Micro-v2-ONNX)
|
| 18 |
+
package contains:
|
| 19 |
|
| 20 |
- `onnx/duration.onnx`: token sequence to aligned acoustic distribution;
|
| 21 |
- `onnx/decode.onnx`: aligned distribution and seeded noise to waveform;
|
|
|
|
| 28 |
long-text chunking, and CPU/CUDA/DirectML provider selection. eSpeak-ng remains
|
| 29 |
the non-neural English frontend and is not embedded in the ONNX graphs.
|
| 30 |
|
| 31 |
+
See the [`Inflect-Micro-v2-ONNX` model card](https://huggingface.co/owensong/Inflect-Micro-v2-ONNX)
|
| 32 |
for installation and verified parity measurements.
|
| 33 |
|
| 34 |
## Why only verified FP32 ships
|
onnx/README.md
CHANGED
|
@@ -1,120 +1,10 @@
|
|
| 1 |
-
#
|
| 2 |
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
The neural model is split into two graphs:
|
| 6 |
|
| 7 |
-
|
| 8 |
-
| --- | --- | ---: |
|
| 9 |
-
| `duration.onnx` | Text tokens to aligned acoustic distribution | 7.32 MB |
|
| 10 |
-
| `decode.onnx` | Acoustic distribution and seeded noise to 24 kHz waveform | 30.43 MB |
|
| 11 |
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
## Install
|
| 16 |
-
|
| 17 |
-
```bash
|
| 18 |
-
python -m pip install -r onnx/requirements.txt
|
| 19 |
-
```
|
| 20 |
-
|
| 21 |
-
ONNX Runtime CPU is installed by default. For another execution provider, use
|
| 22 |
-
the appropriate ONNX Runtime package for the target platform.
|
| 23 |
-
|
| 24 |
-
## Generate speech
|
| 25 |
-
|
| 26 |
-
From the repository root:
|
| 27 |
-
|
| 28 |
-
```bash
|
| 29 |
-
python onnx/inference_onnx.py \
|
| 30 |
-
--text "A compact voice can still sound clear and expressive." \
|
| 31 |
-
--output sample.wav \
|
| 32 |
-
--provider cpu \
|
| 33 |
-
--speed 1.0 \
|
| 34 |
-
--variation 0.667 \
|
| 35 |
-
--seed 7
|
| 36 |
-
```
|
| 37 |
-
|
| 38 |
-
Python:
|
| 39 |
-
|
| 40 |
-
```python
|
| 41 |
-
import sys
|
| 42 |
-
|
| 43 |
-
sys.path.insert(0, "onnx")
|
| 44 |
-
from inference_onnx import InflectONNX
|
| 45 |
-
|
| 46 |
-
tts = InflectONNX(".", provider="cpu")
|
| 47 |
-
sample_rate, waveform = tts.synthesize(
|
| 48 |
-
"Inflect runs through ONNX Runtime.",
|
| 49 |
-
speed=1.0,
|
| 50 |
-
variation=0.667,
|
| 51 |
-
seed=7,
|
| 52 |
-
)
|
| 53 |
-
```
|
| 54 |
-
|
| 55 |
-
Accepted provider aliases are `cpu`, `cuda`, and `directml`. The corresponding
|
| 56 |
-
provider must exist in the installed ONNX Runtime build.
|
| 57 |
-
|
| 58 |
-
## What the export includes
|
| 59 |
-
|
| 60 |
-
- Dynamic text and waveform lengths
|
| 61 |
-
- The duration predictor, acoustic generator, flow, and waveform decoder
|
| 62 |
-
- Seeded stochastic latent sampling in the Python wrapper
|
| 63 |
-
- The same punctuation-aware long-text chunking used by the PyTorch package
|
| 64 |
-
- 24 kHz mono `float32` waveform output
|
| 65 |
-
|
| 66 |
-
The English normalization and phonemization frontend remains Python code and
|
| 67 |
-
uses eSpeak-ng on the CPU. The ONNX files contain the learned neural graphs,
|
| 68 |
-
not eSpeak-ng itself.
|
| 69 |
-
|
| 70 |
-
## Verification
|
| 71 |
-
|
| 72 |
-
The packaged graphs pass:
|
| 73 |
-
|
| 74 |
-
- ONNX checker validation at opset 17
|
| 75 |
-
- Dynamic-length inference tests
|
| 76 |
-
- Short- and long-form end-to-end synthesis
|
| 77 |
-
- Same-seed determinism and different-seed variation checks
|
| 78 |
-
- Direct numerical comparison with the canonical PyTorch checkpoint
|
| 79 |
-
- Independent re-export comparison from the published `model.pth`
|
| 80 |
-
|
| 81 |
-
The direct PyTorch comparison measured a maximum absolute waveform error of
|
| 82 |
-
`1.7583e-05`, mean absolute error of `2.2595e-07`, and correlation above
|
| 83 |
-
`0.9999999999`. An independent re-export matched this package within
|
| 84 |
-
`1.0617e-07` maximum waveform error.
|
| 85 |
-
|
| 86 |
-
See [`parity_report.json`](parity_report.json), [`SOURCE.json`](SOURCE.json),
|
| 87 |
-
and [`checksums.sha256`](checksums.sha256) for machine-readable details.
|
| 88 |
-
|
| 89 |
-
## Re-export
|
| 90 |
-
|
| 91 |
-
The export script is included for reproducibility:
|
| 92 |
-
|
| 93 |
-
```bash
|
| 94 |
-
python -m pip install torch==2.4.1 onnx==1.19.1
|
| 95 |
-
python onnx/export_onnx.py \
|
| 96 |
-
--model-dir . \
|
| 97 |
-
--output-dir onnx-reexport \
|
| 98 |
-
--model-name Inflect-Micro-v2 \
|
| 99 |
-
--source-repository owensong/Inflect-Micro-v2 \
|
| 100 |
-
--source-revision 3e8c567f9bce1a309eea12d38ca9fbf7050df72e
|
| 101 |
-
```
|
| 102 |
-
|
| 103 |
-
Serialization hashes can differ across exporter versions even when numerical
|
| 104 |
-
outputs match. Validate outputs rather than expecting byte-identical files.
|
| 105 |
-
|
| 106 |
-
## Browser implementation and credit
|
| 107 |
-
|
| 108 |
-
Robert Bak produced the original ONNX conversion and a complete browser runner:
|
| 109 |
-
[robertbak/webtts-inflect](https://github.com/robertbak/webtts-inflect).
|
| 110 |
-
The official package preserves that conversion credit and adds checkpoint
|
| 111 |
-
provenance, metadata, reproducible export tooling, and Python ONNX Runtime
|
| 112 |
-
validation. The browser project is Apache-2.0 and runs synthesis locally with
|
| 113 |
-
ONNX Runtime Web and an eSpeak-ng WASM frontend.
|
| 114 |
-
|
| 115 |
-
## Quantization status
|
| 116 |
-
|
| 117 |
-
Only verified FP32 ONNX graphs are released. INT8 and INT4 are not included:
|
| 118 |
-
naive quantization of the flow and waveform decoder can produce audible
|
| 119 |
-
degradation, and no quantized build has passed matched listening and
|
| 120 |
-
intelligibility validation yet.
|
|
|
|
| 1 |
+
# ONNX export moved
|
| 2 |
|
| 3 |
+
The official verified FP32 ONNX Runtime package now lives in its own model
|
| 4 |
+
repository:
|
|
|
|
| 5 |
|
| 6 |
+
## [owensong/Inflect-Micro-v2-ONNX](https://huggingface.co/owensong/Inflect-Micro-v2-ONNX)
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
+
This canonical repository remains the PyTorch base model. The separate ONNX
|
| 9 |
+
repository includes both graphs, the torch-free runner, export script, parity
|
| 10 |
+
report, checksums, runtime files, and license notices.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
onnx/SOURCE.json
DELETED
|
@@ -1,16 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"format": "inflect_v2_onnx_source_v1",
|
| 3 |
-
"model": "Inflect-Micro-v2",
|
| 4 |
-
"source_repository": "owensong/Inflect-Micro-v2",
|
| 5 |
-
"source_revision": "3e8c567f9bce1a309eea12d38ca9fbf7050df72e",
|
| 6 |
-
"source_model_file": "model.pth",
|
| 7 |
-
"source_model_sha256": "3eede065c9ccfa88ade0a5a9a5c23de34afcbbb32213e59aad44d5cf100fdee8",
|
| 8 |
-
"conversion_repository": "https://github.com/robertbak/webtts-inflect",
|
| 9 |
-
"conversion_commit": "60832b1978115e4ed7fdddce4d9b9fd9161f9fe7",
|
| 10 |
-
"conversion_author": "Robert Bak",
|
| 11 |
-
"official_packaging": "Owen Song",
|
| 12 |
-
"precision": "FP32",
|
| 13 |
-
"onnx_opset": 17,
|
| 14 |
-
"sample_rate_hz": 24000,
|
| 15 |
-
"license": "Apache-2.0"
|
| 16 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
onnx/checksums.sha256
DELETED
|
@@ -1,8 +0,0 @@
|
|
| 1 |
-
7940923add86f76e7fa78d910b0632ca1779f8cc9a2ca2b49236381a9ca77183 decode.onnx
|
| 2 |
-
b728ca2564b9e5b7d6cf5e446f65e02a6fe2f1880ba281466fec93a667dd2388 duration.onnx
|
| 3 |
-
77bd27277861c8cae670247e3c9e3b976134a4886506fb069331aa6828752be6 export_onnx.py
|
| 4 |
-
cf48c4812f68e8315d80568e93650c64a7ad3fdfb9a0051c61a0d1d0746df274 inference_onnx.py
|
| 5 |
-
10c88564d24fe65ea730dc6ec98aa19ec74d3590efa427595a3c122c4c0f7039 parity_report.json
|
| 6 |
-
ecfaf6a3edca6ffc3152a61ccc6c27240f42daaa0ddf6efc08e1f9b277295734 README.md
|
| 7 |
-
43abd2c128e4296f1f0171f12946f3fc7dee3f8c8e2bb1858bcae6b43b62b3c9 requirements.txt
|
| 8 |
-
e950bf44034b011a64bba3773b388e386418ef952c3cd17ceefda5bfa94a3aaa SOURCE.json
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
onnx/decode.onnx
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:7940923add86f76e7fa78d910b0632ca1779f8cc9a2ca2b49236381a9ca77183
|
| 3 |
-
size 30427790
|
|
|
|
|
|
|
|
|
|
|
|
onnx/duration.onnx
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:b728ca2564b9e5b7d6cf5e446f65e02a6fe2f1880ba281466fec93a667dd2388
|
| 3 |
-
size 7322687
|
|
|
|
|
|
|
|
|
|
|
|
onnx/export_onnx.py
DELETED
|
@@ -1,261 +0,0 @@
|
|
| 1 |
-
from __future__ import annotations
|
| 2 |
-
|
| 3 |
-
import argparse
|
| 4 |
-
import hashlib
|
| 5 |
-
import json
|
| 6 |
-
import logging
|
| 7 |
-
import sys
|
| 8 |
-
from pathlib import Path
|
| 9 |
-
|
| 10 |
-
import onnx
|
| 11 |
-
import torch
|
| 12 |
-
from torch import nn
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
PACKAGE_ROOT = Path(__file__).resolve().parents[1]
|
| 16 |
-
commons = None
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
class DurationGraph(nn.Module):
|
| 20 |
-
def __init__(self, model: nn.Module) -> None:
|
| 21 |
-
super().__init__()
|
| 22 |
-
self.enc_p = model.enc_p
|
| 23 |
-
self.dp = model.dp
|
| 24 |
-
|
| 25 |
-
def forward(
|
| 26 |
-
self,
|
| 27 |
-
tokens: torch.Tensor,
|
| 28 |
-
lengths: torch.Tensor,
|
| 29 |
-
length_scale: torch.Tensor,
|
| 30 |
-
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 31 |
-
hidden, m_p, logs_p, x_mask = self.enc_p(tokens, lengths)
|
| 32 |
-
logw = self.dp(hidden, x_mask, g=None)
|
| 33 |
-
durations = torch.ceil(torch.exp(logw) * x_mask * length_scale)
|
| 34 |
-
y_lengths = torch.clamp_min(torch.sum(durations, [1, 2]), 1).long()
|
| 35 |
-
if commons is None:
|
| 36 |
-
raise RuntimeError("Runtime helpers were not initialized.")
|
| 37 |
-
y_mask = torch.unsqueeze(commons.sequence_mask(y_lengths, None), 1).to(
|
| 38 |
-
x_mask.dtype
|
| 39 |
-
)
|
| 40 |
-
attention_mask = torch.unsqueeze(x_mask, 2) * torch.unsqueeze(y_mask, -1)
|
| 41 |
-
attention = commons.generate_path(durations, attention_mask)
|
| 42 |
-
m_p_exp = torch.matmul(
|
| 43 |
-
attention.squeeze(1),
|
| 44 |
-
m_p.transpose(1, 2),
|
| 45 |
-
).transpose(1, 2)
|
| 46 |
-
logs_p_exp = torch.matmul(
|
| 47 |
-
attention.squeeze(1),
|
| 48 |
-
logs_p.transpose(1, 2),
|
| 49 |
-
).transpose(1, 2)
|
| 50 |
-
return m_p_exp, logs_p_exp, y_mask
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
class DecodeGraph(nn.Module):
|
| 54 |
-
def __init__(self, model: nn.Module) -> None:
|
| 55 |
-
super().__init__()
|
| 56 |
-
self.flow = model.flow
|
| 57 |
-
self.decoder = model.dec
|
| 58 |
-
|
| 59 |
-
def forward(
|
| 60 |
-
self,
|
| 61 |
-
m_p_exp: torch.Tensor,
|
| 62 |
-
logs_p_exp: torch.Tensor,
|
| 63 |
-
y_mask: torch.Tensor,
|
| 64 |
-
zp_noise: torch.Tensor,
|
| 65 |
-
noise_scale: torch.Tensor,
|
| 66 |
-
) -> torch.Tensor:
|
| 67 |
-
z_p = m_p_exp + zp_noise * torch.exp(logs_p_exp) * noise_scale
|
| 68 |
-
z = self.flow(z_p, y_mask, g=None, reverse=True)
|
| 69 |
-
return self.decoder(z * y_mask, g=None)
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
def sha256(path: Path) -> str:
|
| 73 |
-
digest = hashlib.sha256()
|
| 74 |
-
with path.open("rb") as handle:
|
| 75 |
-
for block in iter(lambda: handle.read(1024 * 1024), b""):
|
| 76 |
-
digest.update(block)
|
| 77 |
-
return digest.hexdigest()
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
def load_model(root: Path) -> nn.Module:
|
| 81 |
-
global commons
|
| 82 |
-
runtime_root = root / "runtime"
|
| 83 |
-
sys.path.insert(0, str(runtime_root))
|
| 84 |
-
sys.path.insert(0, str(root))
|
| 85 |
-
import commons as runtime_commons
|
| 86 |
-
import utils
|
| 87 |
-
from models import SynthesizerTrn
|
| 88 |
-
from text.symbols import symbols
|
| 89 |
-
|
| 90 |
-
commons = runtime_commons
|
| 91 |
-
hps = utils.get_hparams_from_file(str(root / "config.json"))
|
| 92 |
-
model = SynthesizerTrn(
|
| 93 |
-
len(symbols),
|
| 94 |
-
hps.data.filter_length // 2 + 1,
|
| 95 |
-
hps.train.segment_size // hps.data.hop_length,
|
| 96 |
-
**hps.model,
|
| 97 |
-
).cpu().eval()
|
| 98 |
-
logger = logging.getLogger()
|
| 99 |
-
previous_level = logger.level
|
| 100 |
-
try:
|
| 101 |
-
logger.setLevel(logging.WARNING)
|
| 102 |
-
utils.load_checkpoint(str(root / "model.pth"), model, None)
|
| 103 |
-
finally:
|
| 104 |
-
logger.setLevel(previous_level)
|
| 105 |
-
return model
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
def attach_metadata(
|
| 109 |
-
path: Path,
|
| 110 |
-
*,
|
| 111 |
-
model_name: str,
|
| 112 |
-
source_repository: str,
|
| 113 |
-
source_revision: str,
|
| 114 |
-
source_model_sha256: str,
|
| 115 |
-
) -> None:
|
| 116 |
-
graph = onnx.load(path)
|
| 117 |
-
metadata = {
|
| 118 |
-
"model_name": model_name,
|
| 119 |
-
"source_repository": source_repository,
|
| 120 |
-
"source_revision": source_revision,
|
| 121 |
-
"source_model_sha256": source_model_sha256,
|
| 122 |
-
"license": "Apache-2.0",
|
| 123 |
-
"sample_rate_hz": "24000",
|
| 124 |
-
"precision": "FP32",
|
| 125 |
-
"onnx_opset": "17",
|
| 126 |
-
"conversion_author": "Robert Bak (github.com/robertbak)",
|
| 127 |
-
"official_packaging": "Owen Song (huggingface.co/owensong)",
|
| 128 |
-
}
|
| 129 |
-
del graph.metadata_props[:]
|
| 130 |
-
for key, value in metadata.items():
|
| 131 |
-
item = graph.metadata_props.add()
|
| 132 |
-
item.key = key
|
| 133 |
-
item.value = value
|
| 134 |
-
onnx.checker.check_model(graph)
|
| 135 |
-
onnx.save(graph, path)
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
def export(
|
| 139 |
-
root: Path,
|
| 140 |
-
output_dir: Path,
|
| 141 |
-
*,
|
| 142 |
-
model_name: str,
|
| 143 |
-
source_repository: str,
|
| 144 |
-
source_revision: str,
|
| 145 |
-
) -> None:
|
| 146 |
-
torch.manual_seed(7)
|
| 147 |
-
model = load_model(root)
|
| 148 |
-
duration = DurationGraph(model).eval()
|
| 149 |
-
decode = DecodeGraph(model).eval()
|
| 150 |
-
|
| 151 |
-
output_dir.mkdir(parents=True, exist_ok=True)
|
| 152 |
-
duration_path = output_dir / "duration.onnx"
|
| 153 |
-
decode_path = output_dir / "decode.onnx"
|
| 154 |
-
|
| 155 |
-
tokens = torch.tensor(
|
| 156 |
-
[[0, 18, 0, 61, 0, 55, 0, 48, 0, 44, 0, 46, 0]],
|
| 157 |
-
dtype=torch.long,
|
| 158 |
-
)
|
| 159 |
-
lengths = torch.tensor([tokens.shape[1]], dtype=torch.long)
|
| 160 |
-
length_scale = torch.tensor(1.0, dtype=torch.float32)
|
| 161 |
-
|
| 162 |
-
torch.onnx.export(
|
| 163 |
-
duration,
|
| 164 |
-
(tokens, lengths, length_scale),
|
| 165 |
-
duration_path,
|
| 166 |
-
input_names=["tokens", "lengths", "length_scale"],
|
| 167 |
-
output_names=["m_p_exp", "logs_p_exp", "y_mask"],
|
| 168 |
-
dynamic_axes={
|
| 169 |
-
"tokens": {1: "text_len"},
|
| 170 |
-
"m_p_exp": {2: "mel_len"},
|
| 171 |
-
"logs_p_exp": {2: "mel_len"},
|
| 172 |
-
"y_mask": {2: "mel_len"},
|
| 173 |
-
},
|
| 174 |
-
opset_version=17,
|
| 175 |
-
do_constant_folding=True,
|
| 176 |
-
dynamo=False,
|
| 177 |
-
)
|
| 178 |
-
|
| 179 |
-
with torch.no_grad():
|
| 180 |
-
m_p_exp, logs_p_exp, y_mask = duration(tokens, lengths, length_scale)
|
| 181 |
-
zp_noise = torch.randn_like(m_p_exp)
|
| 182 |
-
noise_scale = torch.tensor(0.667, dtype=torch.float32)
|
| 183 |
-
torch.onnx.export(
|
| 184 |
-
decode,
|
| 185 |
-
(m_p_exp, logs_p_exp, y_mask, zp_noise, noise_scale),
|
| 186 |
-
decode_path,
|
| 187 |
-
input_names=[
|
| 188 |
-
"m_p_exp",
|
| 189 |
-
"logs_p_exp",
|
| 190 |
-
"y_mask",
|
| 191 |
-
"zp_noise",
|
| 192 |
-
"noise_scale",
|
| 193 |
-
],
|
| 194 |
-
output_names=["waveform"],
|
| 195 |
-
dynamic_axes={
|
| 196 |
-
"m_p_exp": {2: "mel_len"},
|
| 197 |
-
"logs_p_exp": {2: "mel_len"},
|
| 198 |
-
"y_mask": {2: "mel_len"},
|
| 199 |
-
"zp_noise": {2: "mel_len"},
|
| 200 |
-
"waveform": {2: "wav_len"},
|
| 201 |
-
},
|
| 202 |
-
opset_version=17,
|
| 203 |
-
do_constant_folding=True,
|
| 204 |
-
dynamo=False,
|
| 205 |
-
)
|
| 206 |
-
|
| 207 |
-
source_hash = sha256(root / "model.pth")
|
| 208 |
-
for path in (duration_path, decode_path):
|
| 209 |
-
attach_metadata(
|
| 210 |
-
path,
|
| 211 |
-
model_name=model_name,
|
| 212 |
-
source_repository=source_repository,
|
| 213 |
-
source_revision=source_revision,
|
| 214 |
-
source_model_sha256=source_hash,
|
| 215 |
-
)
|
| 216 |
-
|
| 217 |
-
manifest = {
|
| 218 |
-
"format": "inflect_v2_onnx_export_v1",
|
| 219 |
-
"model_name": model_name,
|
| 220 |
-
"source_repository": source_repository,
|
| 221 |
-
"source_revision": source_revision,
|
| 222 |
-
"source_model_sha256": source_hash,
|
| 223 |
-
"torch_version": torch.__version__,
|
| 224 |
-
"onnx_version": onnx.__version__,
|
| 225 |
-
"opset": 17,
|
| 226 |
-
"files": {
|
| 227 |
-
duration_path.name: {
|
| 228 |
-
"bytes": duration_path.stat().st_size,
|
| 229 |
-
"sha256": sha256(duration_path),
|
| 230 |
-
},
|
| 231 |
-
decode_path.name: {
|
| 232 |
-
"bytes": decode_path.stat().st_size,
|
| 233 |
-
"sha256": sha256(decode_path),
|
| 234 |
-
},
|
| 235 |
-
},
|
| 236 |
-
}
|
| 237 |
-
(output_dir / "export_manifest.json").write_text(
|
| 238 |
-
json.dumps(manifest, indent=2) + "\n",
|
| 239 |
-
encoding="utf-8",
|
| 240 |
-
)
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
def main() -> None:
|
| 244 |
-
parser = argparse.ArgumentParser(description="Export Inflect v2 to ONNX.")
|
| 245 |
-
parser.add_argument("--model-dir", type=Path, default=PACKAGE_ROOT)
|
| 246 |
-
parser.add_argument("--output-dir", type=Path, required=True)
|
| 247 |
-
parser.add_argument("--model-name", required=True)
|
| 248 |
-
parser.add_argument("--source-repository", required=True)
|
| 249 |
-
parser.add_argument("--source-revision", required=True)
|
| 250 |
-
args = parser.parse_args()
|
| 251 |
-
export(
|
| 252 |
-
args.model_dir.resolve(),
|
| 253 |
-
args.output_dir.resolve(),
|
| 254 |
-
model_name=args.model_name,
|
| 255 |
-
source_repository=args.source_repository,
|
| 256 |
-
source_revision=args.source_revision,
|
| 257 |
-
)
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
if __name__ == "__main__":
|
| 261 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
onnx/inference_onnx.py
DELETED
|
@@ -1,229 +0,0 @@
|
|
| 1 |
-
from __future__ import annotations
|
| 2 |
-
|
| 3 |
-
import argparse
|
| 4 |
-
import re
|
| 5 |
-
import sys
|
| 6 |
-
from pathlib import Path
|
| 7 |
-
|
| 8 |
-
import numpy as np
|
| 9 |
-
import onnxruntime as ort
|
| 10 |
-
import soundfile as sf
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
PACKAGE_ROOT = Path(__file__).resolve().parents[1]
|
| 14 |
-
RUNTIME_ROOT = PACKAGE_ROOT / "runtime"
|
| 15 |
-
sys.path.insert(0, str(RUNTIME_ROOT))
|
| 16 |
-
sys.path.insert(0, str(PACKAGE_ROOT))
|
| 17 |
-
|
| 18 |
-
from inflect_vits_frontend import run_vits_frontend # noqa: E402
|
| 19 |
-
from text.symbols import symbols # noqa: E402
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
SAMPLE_RATE = 24_000
|
| 23 |
-
SYMBOL_TO_ID = {symbol: index for index, symbol in enumerate(symbols)}
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
def split_text(text: str, limit: int = 280) -> list[str]:
|
| 27 |
-
normalized = " ".join(text.split())
|
| 28 |
-
sentences = [
|
| 29 |
-
part.strip()
|
| 30 |
-
for part in re.split(r"(?<=[.!?;:])\s+", normalized)
|
| 31 |
-
if part.strip()
|
| 32 |
-
]
|
| 33 |
-
chunks: list[str] = []
|
| 34 |
-
for sentence in sentences or [normalized]:
|
| 35 |
-
while len(sentence) > limit:
|
| 36 |
-
search = sentence[: limit + 1]
|
| 37 |
-
punctuation = max(search.rfind(mark) for mark in (",", ";", ":"))
|
| 38 |
-
split_at = (
|
| 39 |
-
punctuation + 1
|
| 40 |
-
if punctuation >= limit // 2
|
| 41 |
-
else sentence.rfind(" ", 0, limit + 1)
|
| 42 |
-
)
|
| 43 |
-
if split_at < limit // 2:
|
| 44 |
-
split_at = limit
|
| 45 |
-
chunks.append(sentence[:split_at].strip())
|
| 46 |
-
sentence = sentence[split_at:].strip()
|
| 47 |
-
if sentence:
|
| 48 |
-
chunks.append(sentence)
|
| 49 |
-
return chunks
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
def boundary_pause_seconds(chunk: str) -> float:
|
| 53 |
-
ending = chunk.rstrip()[-1:] if chunk.strip() else ""
|
| 54 |
-
return {
|
| 55 |
-
"?": 0.28,
|
| 56 |
-
"!": 0.24,
|
| 57 |
-
".": 0.22,
|
| 58 |
-
";": 0.16,
|
| 59 |
-
":": 0.13,
|
| 60 |
-
",": 0.09,
|
| 61 |
-
}.get(ending, 0.08)
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
def edge_fade(
|
| 65 |
-
waveform: np.ndarray,
|
| 66 |
-
sample_rate: int = SAMPLE_RATE,
|
| 67 |
-
milliseconds: float = 5.0,
|
| 68 |
-
) -> np.ndarray:
|
| 69 |
-
frames = min(round(sample_rate * milliseconds / 1000.0), waveform.size // 2)
|
| 70 |
-
if frames <= 0:
|
| 71 |
-
return waveform
|
| 72 |
-
output = waveform.copy()
|
| 73 |
-
ramp = np.linspace(0.0, 1.0, frames, endpoint=True, dtype=np.float32)
|
| 74 |
-
output[:frames] *= ramp
|
| 75 |
-
output[-frames:] *= ramp[::-1]
|
| 76 |
-
return output
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
def phonemes_to_tokens(phoneme_text: str) -> np.ndarray:
|
| 80 |
-
sequence = [SYMBOL_TO_ID[symbol] for symbol in phoneme_text]
|
| 81 |
-
if not sequence:
|
| 82 |
-
raise ValueError("The text frontend produced no speakable tokens.")
|
| 83 |
-
with_blanks = np.zeros(len(sequence) * 2 + 1, dtype=np.int64)
|
| 84 |
-
with_blanks[1::2] = sequence
|
| 85 |
-
return with_blanks[None, :]
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
def available_provider(name: str) -> str:
|
| 89 |
-
aliases = {
|
| 90 |
-
"cpu": "CPUExecutionProvider",
|
| 91 |
-
"cuda": "CUDAExecutionProvider",
|
| 92 |
-
"directml": "DmlExecutionProvider",
|
| 93 |
-
}
|
| 94 |
-
provider = aliases.get(name.lower(), name)
|
| 95 |
-
available = ort.get_available_providers()
|
| 96 |
-
if provider not in available:
|
| 97 |
-
raise ValueError(
|
| 98 |
-
f"Provider {provider!r} is unavailable. Installed providers: {available}"
|
| 99 |
-
)
|
| 100 |
-
return provider
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
class InflectONNX:
|
| 104 |
-
"""Torch-free Inflect v2 inference through ONNX Runtime."""
|
| 105 |
-
|
| 106 |
-
def __init__(
|
| 107 |
-
self,
|
| 108 |
-
model_dir: str | Path = PACKAGE_ROOT,
|
| 109 |
-
provider: str = "cpu",
|
| 110 |
-
) -> None:
|
| 111 |
-
root = Path(model_dir).resolve()
|
| 112 |
-
onnx_dir = root / "onnx"
|
| 113 |
-
selected_provider = available_provider(provider)
|
| 114 |
-
providers = [selected_provider]
|
| 115 |
-
if selected_provider != "CPUExecutionProvider":
|
| 116 |
-
providers.append("CPUExecutionProvider")
|
| 117 |
-
self.duration = ort.InferenceSession(
|
| 118 |
-
str(onnx_dir / "duration.onnx"),
|
| 119 |
-
providers=providers,
|
| 120 |
-
)
|
| 121 |
-
self.decode = ort.InferenceSession(
|
| 122 |
-
str(onnx_dir / "decode.onnx"),
|
| 123 |
-
providers=providers,
|
| 124 |
-
)
|
| 125 |
-
|
| 126 |
-
def _synthesize_chunk(
|
| 127 |
-
self,
|
| 128 |
-
text: str,
|
| 129 |
-
*,
|
| 130 |
-
speed: float,
|
| 131 |
-
variation: float,
|
| 132 |
-
seed: int,
|
| 133 |
-
) -> np.ndarray:
|
| 134 |
-
phoneme_text = run_vits_frontend(text).phoneme_text
|
| 135 |
-
tokens = phonemes_to_tokens(phoneme_text)
|
| 136 |
-
duration_outputs = self.duration.run(
|
| 137 |
-
["m_p_exp", "logs_p_exp", "y_mask"],
|
| 138 |
-
{
|
| 139 |
-
"tokens": tokens,
|
| 140 |
-
"lengths": np.asarray([tokens.shape[1]], dtype=np.int64),
|
| 141 |
-
"length_scale": np.asarray(1.0 / speed, dtype=np.float32),
|
| 142 |
-
},
|
| 143 |
-
)
|
| 144 |
-
m_p_exp, logs_p_exp, y_mask = duration_outputs
|
| 145 |
-
rng = np.random.default_rng(seed)
|
| 146 |
-
latent_noise = rng.standard_normal(m_p_exp.shape, dtype=np.float32)
|
| 147 |
-
waveform = self.decode.run(
|
| 148 |
-
["waveform"],
|
| 149 |
-
{
|
| 150 |
-
"m_p_exp": m_p_exp,
|
| 151 |
-
"logs_p_exp": logs_p_exp,
|
| 152 |
-
"y_mask": y_mask,
|
| 153 |
-
"zp_noise": latent_noise,
|
| 154 |
-
"noise_scale": np.asarray(variation, dtype=np.float32),
|
| 155 |
-
},
|
| 156 |
-
)[0]
|
| 157 |
-
return edge_fade(np.asarray(waveform, dtype=np.float32).reshape(-1))
|
| 158 |
-
|
| 159 |
-
def synthesize(
|
| 160 |
-
self,
|
| 161 |
-
text: str,
|
| 162 |
-
*,
|
| 163 |
-
speed: float = 1.0,
|
| 164 |
-
variation: float = 0.667,
|
| 165 |
-
seed: int = 0,
|
| 166 |
-
) -> tuple[int, np.ndarray]:
|
| 167 |
-
normalized = " ".join(text.split())
|
| 168 |
-
if not normalized:
|
| 169 |
-
raise ValueError("Text must not be empty.")
|
| 170 |
-
if not 0.5 <= speed <= 2.0:
|
| 171 |
-
raise ValueError("speed must be between 0.5 and 2.0")
|
| 172 |
-
if not 0.0 <= variation <= 1.0:
|
| 173 |
-
raise ValueError("variation must be between 0.0 and 1.0")
|
| 174 |
-
|
| 175 |
-
chunks = split_text(normalized)
|
| 176 |
-
pieces: list[np.ndarray] = []
|
| 177 |
-
for index, chunk in enumerate(chunks):
|
| 178 |
-
if index:
|
| 179 |
-
pause = boundary_pause_seconds(chunks[index - 1])
|
| 180 |
-
pieces.append(
|
| 181 |
-
np.zeros(round(SAMPLE_RATE * pause), dtype=np.float32)
|
| 182 |
-
)
|
| 183 |
-
pieces.append(
|
| 184 |
-
self._synthesize_chunk(
|
| 185 |
-
chunk,
|
| 186 |
-
speed=speed,
|
| 187 |
-
variation=variation,
|
| 188 |
-
seed=seed + index,
|
| 189 |
-
)
|
| 190 |
-
)
|
| 191 |
-
waveform = np.clip(np.concatenate(pieces), -1.0, 1.0)
|
| 192 |
-
return SAMPLE_RATE, waveform
|
| 193 |
-
|
| 194 |
-
def save(self, text: str, output: str | Path, **kwargs: object) -> Path:
|
| 195 |
-
destination = Path(output)
|
| 196 |
-
destination.parent.mkdir(parents=True, exist_ok=True)
|
| 197 |
-
sample_rate, waveform = self.synthesize(text, **kwargs)
|
| 198 |
-
sf.write(destination, waveform, sample_rate)
|
| 199 |
-
return destination
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
def main() -> None:
|
| 203 |
-
parser = argparse.ArgumentParser(description="Run Inflect v2 through ONNX Runtime.")
|
| 204 |
-
parser.add_argument("--model-dir", type=Path, default=PACKAGE_ROOT)
|
| 205 |
-
parser.add_argument("--text", required=True)
|
| 206 |
-
parser.add_argument("--output", type=Path, required=True)
|
| 207 |
-
parser.add_argument(
|
| 208 |
-
"--provider",
|
| 209 |
-
default="cpu",
|
| 210 |
-
help="cpu, cuda, directml, or an ONNX Runtime provider name",
|
| 211 |
-
)
|
| 212 |
-
parser.add_argument("--speed", type=float, default=1.0)
|
| 213 |
-
parser.add_argument("--variation", type=float, default=0.667)
|
| 214 |
-
parser.add_argument("--seed", type=int, default=0)
|
| 215 |
-
args = parser.parse_args()
|
| 216 |
-
|
| 217 |
-
engine = InflectONNX(args.model_dir, provider=args.provider)
|
| 218 |
-
engine.save(
|
| 219 |
-
args.text,
|
| 220 |
-
args.output,
|
| 221 |
-
speed=args.speed,
|
| 222 |
-
variation=args.variation,
|
| 223 |
-
seed=args.seed,
|
| 224 |
-
)
|
| 225 |
-
print(f"wrote {args.output} at {SAMPLE_RATE} Hz")
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
if __name__ == "__main__":
|
| 229 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
onnx/parity_report.json
DELETED
|
@@ -1,31 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"format": "inflect_v2_onnx_parity_v1",
|
| 3 |
-
"model": "Inflect-Micro-v2",
|
| 4 |
-
"onnx_checker": "pass",
|
| 5 |
-
"dynamic_lengths": "pass",
|
| 6 |
-
"end_to_end_short_text": "pass",
|
| 7 |
-
"end_to_end_long_text": "pass",
|
| 8 |
-
"same_seed_determinism": "pass",
|
| 9 |
-
"different_seed_variation": "pass",
|
| 10 |
-
"pytorch_parity": {
|
| 11 |
-
"duration_m_p_exp_max_abs_error": 9.5367431640625e-07,
|
| 12 |
-
"duration_logs_p_exp_max_abs_error": 3.5762786865234375e-07,
|
| 13 |
-
"duration_mask_exact": true,
|
| 14 |
-
"waveform_max_abs_error": 1.75834e-05,
|
| 15 |
-
"waveform_mean_abs_error": 2.2595e-07,
|
| 16 |
-
"waveform_correlation": 0.9999999999394602
|
| 17 |
-
},
|
| 18 |
-
"independent_reexport_parity": {
|
| 19 |
-
"duration_outputs_max_abs_error": [0.0, 0.0, 0.0],
|
| 20 |
-
"waveform_max_abs_error": 1.0617077350616455e-07,
|
| 21 |
-
"waveform_mean_abs_error": 4.823611288173879e-09,
|
| 22 |
-
"waveform_correlation": 0.9999999999995599
|
| 23 |
-
},
|
| 24 |
-
"validation_environment": {
|
| 25 |
-
"python": "3.12",
|
| 26 |
-
"onnx": "1.19.1",
|
| 27 |
-
"onnxruntime": "1.22.1",
|
| 28 |
-
"torch_for_reference": "2.4.1+cpu",
|
| 29 |
-
"provider": "CPUExecutionProvider"
|
| 30 |
-
}
|
| 31 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
onnx/requirements.txt
DELETED
|
@@ -1,7 +0,0 @@
|
|
| 1 |
-
numpy>=1.26,<3
|
| 2 |
-
onnxruntime>=1.18,<2
|
| 3 |
-
soundfile>=0.13
|
| 4 |
-
phonemizer>=3.3
|
| 5 |
-
espeakng-loader>=0.2.4
|
| 6 |
-
num2words>=0.5.14
|
| 7 |
-
Unidecode>=1.3.8
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
release_manifest.json
CHANGED
|
@@ -29,8 +29,8 @@
|
|
| 29 |
},
|
| 30 |
{
|
| 31 |
"path": "README.md",
|
| 32 |
-
"bytes":
|
| 33 |
-
"sha256": "
|
| 34 |
},
|
| 35 |
{
|
| 36 |
"path": "THIRD_PARTY_NOTICES.md",
|
|
@@ -144,8 +144,8 @@
|
|
| 144 |
},
|
| 145 |
{
|
| 146 |
"path": "docs/EXPORTS.md",
|
| 147 |
-
"bytes":
|
| 148 |
-
"sha256": "
|
| 149 |
},
|
| 150 |
{
|
| 151 |
"path": "docs/FINETUNING.md",
|
|
@@ -339,48 +339,8 @@
|
|
| 339 |
},
|
| 340 |
{
|
| 341 |
"path": "onnx/README.md",
|
| 342 |
-
"bytes":
|
| 343 |
-
"sha256": "
|
| 344 |
-
},
|
| 345 |
-
{
|
| 346 |
-
"path": "onnx/SOURCE.json",
|
| 347 |
-
"bytes": 633,
|
| 348 |
-
"sha256": "e950bf44034b011a64bba3773b388e386418ef952c3cd17ceefda5bfa94a3aaa"
|
| 349 |
-
},
|
| 350 |
-
{
|
| 351 |
-
"path": "onnx/checksums.sha256",
|
| 352 |
-
"bytes": 653,
|
| 353 |
-
"sha256": "d74c3c8d3a148f240d795dc603e18ac2c152a3f7ad3e45dcd66d3e14ee335b6b"
|
| 354 |
-
},
|
| 355 |
-
{
|
| 356 |
-
"path": "onnx/decode.onnx",
|
| 357 |
-
"bytes": 30427790,
|
| 358 |
-
"sha256": "7940923add86f76e7fa78d910b0632ca1779f8cc9a2ca2b49236381a9ca77183"
|
| 359 |
-
},
|
| 360 |
-
{
|
| 361 |
-
"path": "onnx/duration.onnx",
|
| 362 |
-
"bytes": 7322687,
|
| 363 |
-
"sha256": "b728ca2564b9e5b7d6cf5e446f65e02a6fe2f1880ba281466fec93a667dd2388"
|
| 364 |
-
},
|
| 365 |
-
{
|
| 366 |
-
"path": "onnx/export_onnx.py",
|
| 367 |
-
"bytes": 7884,
|
| 368 |
-
"sha256": "77bd27277861c8cae670247e3c9e3b976134a4886506fb069331aa6828752be6"
|
| 369 |
-
},
|
| 370 |
-
{
|
| 371 |
-
"path": "onnx/inference_onnx.py",
|
| 372 |
-
"bytes": 7333,
|
| 373 |
-
"sha256": "cf48c4812f68e8315d80568e93650c64a7ad3fdfb9a0051c61a0d1d0746df274"
|
| 374 |
-
},
|
| 375 |
-
{
|
| 376 |
-
"path": "onnx/parity_report.json",
|
| 377 |
-
"bytes": 1026,
|
| 378 |
-
"sha256": "10c88564d24fe65ea730dc6ec98aa19ec74d3590efa427595a3c122c4c0f7039"
|
| 379 |
-
},
|
| 380 |
-
{
|
| 381 |
-
"path": "onnx/requirements.txt",
|
| 382 |
-
"bytes": 126,
|
| 383 |
-
"sha256": "43abd2c128e4296f1f0171f12946f3fc7dee3f8c8e2bb1858bcae6b43b62b3c9"
|
| 384 |
},
|
| 385 |
{
|
| 386 |
"path": "requirements-tested.txt",
|
|
@@ -496,7 +456,7 @@
|
|
| 496 |
"format": "inflect_v2_release_manifest_v1",
|
| 497 |
"onnx_release": {
|
| 498 |
"format": "FP32 ONNX opset 17",
|
| 499 |
-
"
|
| 500 |
"verified": true,
|
| 501 |
"released": "2026-07-25"
|
| 502 |
}
|
|
|
|
| 29 |
},
|
| 30 |
{
|
| 31 |
"path": "README.md",
|
| 32 |
+
"bytes": 20268,
|
| 33 |
+
"sha256": "d0ba29dd9605ff524fdda51ab38167c1f80cd47404a7b1ada4f9c3c2d49ad1a2"
|
| 34 |
},
|
| 35 |
{
|
| 36 |
"path": "THIRD_PARTY_NOTICES.md",
|
|
|
|
| 144 |
},
|
| 145 |
{
|
| 146 |
"path": "docs/EXPORTS.md",
|
| 147 |
+
"bytes": 2991,
|
| 148 |
+
"sha256": "b32a8ca478e72ee4e35a0415ef6214e18843d3253ded8cb773f7cb6e134320ae"
|
| 149 |
},
|
| 150 |
{
|
| 151 |
"path": "docs/FINETUNING.md",
|
|
|
|
| 339 |
},
|
| 340 |
{
|
| 341 |
"path": "onnx/README.md",
|
| 342 |
+
"bytes": 410,
|
| 343 |
+
"sha256": "933af35f9c1ce5fda876e40e183a7cd3a6dc55cc9b0684062cbf40dfdd3e5008"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 344 |
},
|
| 345 |
{
|
| 346 |
"path": "requirements-tested.txt",
|
|
|
|
| 456 |
"format": "inflect_v2_release_manifest_v1",
|
| 457 |
"onnx_release": {
|
| 458 |
"format": "FP32 ONNX opset 17",
|
| 459 |
+
"repository": "owensong/Inflect-Micro-v2-ONNX",
|
| 460 |
"verified": true,
|
| 461 |
"released": "2026-07-25"
|
| 462 |
}
|