gtcrn / README.md
vpermilp's picture
Upload README.md with huggingface_hub
43f1e94 verified
|
Raw
History Blame Contribute Delete
4.11 kB
---
license: mit
language: en
tags:
- speech-enhancement
- audio
- speech
- svod
- gtcrn
- real-time
pipeline_tag: audio-to-audio
library_name: safetensors
base_model:
- Xiaobin-Guang/GTCRN
---
# GTCRN β€” Grouped Temporal Convolutional Recurrent Network (svod port)
Speech enhancement model ported to run natively in [**svod**](https://github.com/npatsakula/svod) β€”
a pure-Rust inference stack on top of a JIT-compiled tensor runtime. This repo
holds the converted weights only; the model implementation lives in
`svod-model` (`model/src/gtcrn/`).
Ultralight real-time noise suppression: **48.8 K parameters**, **33.0 MMACs/s**.
Takes a noisy 16 kHz mono waveform, returns an enhanced waveform.
## Source
A faithful Rust port of the upstream
[Xiaobin-Guang/GTCRN](https://github.com/Xiaobin-Guang/GTCRN), checkpoint
`model_trained_on_dns3.tar` (trained on the DNS-Challenge 3 dataset). The
upstream architecture and training are unchanged; this repo republishes the
weights in `safetensors` with the small remaps described below.
## Architecture
```
noisy WAV β†’ STFT(n_fft=512, hop=256, √hann)
β†’ ERB analysis (257 bins β†’ 129 bands)
β†’ SFE (subband unfold)
β†’ Encoder: 2Γ— ConvBlock + 3Γ— GTConvBlock (ShuffleNetV2)
β†’ 2Γ— DPGRNN (dual-path grouped RNN: intra-frame bidir GRU + inter-frame GRU)
β†’ Decoder: 3Γ— GTConvBlock (transpose) + 2Γ— ConvBlock (transpose)
β†’ ERB synthesis (129 β†’ 257)
β†’ complex ratio mask Γ— input spectrogram
β†’ ISTFT β†’ enhanced WAV
```
## Usage
```bash
# Build svod-model and run the bundled example (noisy.wav β†’ enhanced.wav):
cargo run -p svod-model --release --example gtcrn_enhance -- \
--in noisy.wav --out enhanced.wav --hub
```
In Rust:
```rust
use svod_model::gtcrn::{Gtcrn, GtcrnJit};
use svod_model::jit::InputSpec;
let model = Gtcrn::from_hub()?; // pulls gtcrn.safetensors from this repo
let mut jit = GtcrnJit::new(model);
jit.prepare(InputSpec::f32(&[1, 257, T, 2]))?; // T = number of STFT frames
// copy the [1, 257, T, 2] complex spectrogram into jit.spec_mut()?, then:
jit.execute()?;
let enhanced = jit.output()?;
```
STFT/ISTFT run eagerly on the host via `realfft`; the network forward pass is
JIT-compiled. See `model/examples/gtcrn_enhance.rs` for the full waveform β†’
waveform pipeline (it processes long audio in fixed-size frame chunks because
the in-graph GRU recurrence unrolls one IR node per time step).
## Files
| File | Description |
|---|---|
| `gtcrn.safetensors` | Converted model weights (249 tensors, 48.8 K params). |
| `golden.safetensors` | PyTorch reference output for the parity test (a 24-frame slice of `mix.wav`). |
## Conversion notes
Generated by `scripts/convert_gtcrn.py` (run `uv run scripts/convert_gtcrn.py
--selfcheck` to reproduce). Two remaps from the upstream PyTorch checkpoint:
1. **GRU gate order.** PyTorch `nn.GRU` stores gate rows as `[reset, update,
new]`; svod's `gru()` op expects `[z, r, h]`. The first two hidden-sized
gate blocks of every GRU weight/bias are swapped.
2. **Bidirectional key split.** The DPGRNN's bidirectional grouped RNNs expose
`rnn1`/`rnn2` modules whose reverse-direction weights
(`*_l0_reverse`) are renamed to separate `rnn1_b`/`rnn2_b` keys, matching
svod's representation of a bidirectional GRU as two unidirectional passes
(forward over the sequence + forward over the time-flipped sequence,
concatenated).
`num_batches_tracked` entries are dropped; BatchNorm `running_var` is kept
verbatim (svod folds it into `invstd = 1/√(var+Ρ)` at load time). The `--selfcheck`
flag verifies the GRU remap against svod's documented recurrence equations.
## Verification
The svod forward matches the upstream PyTorch `GTCRN.forward` to ~6 significant
figures on a fixed-frame slice (`max |Ξ”|` relative < 1e-3), validated by the
`gtcrn::parity` test in `svod-model`.
## License
MIT β€” same as the upstream model. Weights Β© their respective authors; this repo
only republishes them in a converted format for use with svod.