Voice Activity Detection
Transformers
Safetensors
nemotron3_diarization
audio-frame-classification
speaker-diarization
streaming-sortformer
nemotron
medical
Instructions to use Aduomas/nemotron-3-diarization with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Aduomas/nemotron-3-diarization with Transformers:
# pip install -U transformers accelerate # Load model directly from transformers import AutoProcessor, AutoModelForAudioFrameClassification processor = AutoProcessor.from_pretrained("Aduomas/nemotron-3-diarization") model = AutoModelForAudioFrameClassification.from_pretrained("Aduomas/nemotron-3-diarization", device_map="auto") - Notebooks
- Google Colab
- Kaggle
Download decoder.py from Aduomas/nemotron-3-diarization: direct link, hf CLI and curl.
- Browser
- Download file 2.01 kB
-
https://huggingface.co/Aduomas/nemotron-3-diarization/resolve/main/decoder.py
- Command line
-
hf download hf://Aduomas/nemotron-3-diarization/decoder.py
-
curl -L -o decoder.py https://huggingface.co/Aduomas/nemotron-3-diarization/resolve/main/decoder.py
2.01 kB
| """Turn Nemotron 3 frame probabilities into speaker segments, replicating Omi's published decoding behaviour. | |
| Omi's output differs from NVIDIA's plain 0.5 threshold by a slightly higher threshold and by bridging short | |
| same-speaker pauses: always below `bridge_s`, and up to `bridge_clear_s` when no other speaker is active inside. | |
| """ | |
| from dataclasses import dataclass | |
| import numpy as np | |
| FRAME_S = 0.01 | |
| class DecoderParams: | |
| threshold: float = 0.5 | |
| bridge_s: float = 0.0 | |
| bridge_clear_s: float = 0.0 | |
| def _runs(active: np.ndarray) -> tuple[np.ndarray, np.ndarray]: | |
| edges = np.diff(np.r_[False, active, False].astype(np.int8)) | |
| return np.flatnonzero(edges == 1), np.flatnonzero(edges == -1) | |
| def activity(probs: np.ndarray, params: DecoderParams) -> np.ndarray: | |
| """Boolean (frames, speakers) activity after thresholding and pause bridging.""" | |
| raw = probs > params.threshold | |
| active = raw.copy() | |
| bridge, bridge_clear = round(params.bridge_s / FRAME_S), round(params.bridge_clear_s / FRAME_S) | |
| for speaker in range(raw.shape[1]): | |
| others = np.delete(raw, speaker, axis=1).any(axis=1) | |
| starts, ends = _runs(raw[:, speaker]) | |
| for gap_start, gap_end in zip(ends[:-1], starts[1:]): | |
| length = gap_end - gap_start | |
| if length < bridge or (length < bridge_clear and not others[gap_start:gap_end].any()): | |
| active[gap_start:gap_end, speaker] = True | |
| return active | |
| def decode(probs: np.ndarray, params: DecoderParams) -> list[dict]: | |
| """Segments in seconds; speakers that never activate are dropped, so the count stays automatic.""" | |
| active = activity(probs, params) | |
| segments = [] | |
| for speaker in range(active.shape[1]): | |
| starts, ends = _runs(active[:, speaker]) | |
| segments.extend( | |
| {"start": round(float(s * FRAME_S), 2), "end": round(float(e * FRAME_S), 2), "speaker": f"spk_{speaker}"} | |
| for s, e in zip(starts, ends) | |
| ) | |
| return segments | |