vocal_helper.nemotron_asr module

Transcription par l’encodeur de Nemotron 3.5 Transcribe, sans torch : onnxruntime et numpy.

Pourquoi ce module

Nemotron 3.5 Transcribe est un LLM de parole de 30 milliards de paramètres, réservé aux GPU NVIDIA. Sous le LLM, son encodeur audio de 600 millions de paramètres porte une tête CTC qui transcrit seule, dans une quarantaine de langues, avec un horodatage par trame de 40 ms. C’est cette partie, exportée en ONNX par studies/nemotron_asr_export.py, que ce module fait tourner : le même fichier marche sous Linux, Windows et macOS, sur CPU comme sur CUDA, et se charge depuis Rust par la caisse ort.

Ce que l’hôte fait autour du graphe

Le graphe prend un log-mel normalisé et rend, toutes les 40 ms, le jeton le plus probable, sa log-probabilité et celle du blanc. Le reste est ici, en numpy pur, donc vérifiable sans modèle :

  • le front-end de NeMo, avec la fenêtre et le banc de filtres du point de contrôle, que le graphe porte dans ses métadonnées ;

  • la normalisation par bande, sur l’enregistrement entier ;

  • le découpage en fenêtres de 30 s, la longueur que l’encodeur a apprise ;

  • le décodage CTC glouton, puis le regroupement des pièces SentencePiece en mots horodatés, sans dépendre de sentencepiece.

Le vocabulaire contient aussi des balises : changement de locuteur (<spk:N>), événement (<ev:laughter>…), début et fin de style (<sty:stt:…>). Elles sont rendues à part, jamais mêlées aux mots.

class vocal_helper.nemotron_asr.AsrSettings(fb, window, vocab, blank_id, sample_rate=16000, n_fft=512, hop_length=160, preemphasis=0.97, log_guard=5.960464477539063e-08, stack=8, frame_s=0.04, chunk_s=30.0)[source]

Bases: object

Le front-end et le vocabulaire, tels que le graphe les porte.

Parameters:
fb

Banc de filtres mel (n_mels, n_fft // 2 + 1) du point de contrôle.

Type:

NDArray[np.float32]

window

Fenêtre d’analyse (win_length,), centrée dans la transformée.

Type:

NDArray[np.float32]

vocab

Les pièces SentencePiece, le blanc exclu.

Type:

tuple of str

blank_id

L’indice du blanc CTC, juste après le vocabulaire.

Type:

int

blank_id: int
chunk_s: float = 30.0
fb: ndarray[tuple[int, ...], dtype[float32]]
frame_s: float = 0.04
classmethod from_metadata(metadata)[source]

Lire les réglages des métadonnées du graphe.

Raises:

ValueError – Si le graphe ne suit pas le contrat attendu.

Parameters:

metadata (dict[str, str])

Return type:

AsrSettings

hop_length: int = 160
log_guard: float = 5.960464477539063e-08
n_fft: int = 512
preemphasis: float = 0.97
sample_rate: int = 16000
stack: int = 8
vocab: tuple[str, ...]
window: ndarray[tuple[int, ...], dtype[float32]]
class vocal_helper.nemotron_asr.CtcToken(id, first, last, logprob)[source]

Bases: object

Un jeton émis par le décodage glouton, avec ses trames.

Parameters:
first: int
id: int
last: int
logprob: float
class vocal_helper.nemotron_asr.NemotronAsrStage(graph=None, *, batch=False, max_chunk_s=30.0, precision='int8', providers=None, threads=None, warmup=False, **_ignored)[source]

Bases: object

L’étage de transcription de la pipeline, par le graphe Nemotron.

Même contrat que vocal_helper.asr.WhisperStage : il consomme des DiarizedSegment et rend une Utterance par segment, dans l’ordre, avec le locuteur du diariseur. On le choisit par asr={"engine": "nemotron"}.

Les segments consécutifs sont mis bout à bout dans des fenêtres d’au plus max_chunk_s secondes, un appel au graphe par fenêtre ; chaque mot revient au segment qui contient son milieu. Hors ligne c’est le chemin à plein régime ; en direct (batch=False) chaque segment part seul, dès qu’il arrive.

Le modèle ne dit pas la langue qu’il entend : language reste None.

Parameters:
  • graph (str or Path, optional) – Le graphe ; par défaut celui de resolve_graph().

  • precision (str) – "int8" (défaut) ou "fp32".

  • batch (bool) – Regrouper les segments (hors ligne) ou les traiter un par un (direct).

  • max_chunk_s (float) – Longueur maximale d’une fenêtre ; 30 s, celle que l’encodeur a apprise.

  • warmup (bool) – Ouvrir le graphe dès le démarrage plutôt qu’au premier segment, pour que la première énonciation en direct n’attende pas le chargement.

  • providers (list[str] | None) – Comme NemotronOnnxAsr.

  • threads (int | None) – Comme NemotronOnnxAsr.

  • _ignored (Any)

async run(inbox, outbox)[source]

Consommer les segments de inbox, pousser les énonciations dans outbox.

Parameters:
Return type:

None

transcribe_chunk(chunk)[source]

Transcrire des segments mis bout à bout ; une énonciation par segment.

Parameters:

chunk (list[dict])

Return type:

list[dict]

class vocal_helper.nemotron_asr.NemotronOnnxAsr(graph=None, *, precision='int8', providers=None, threads=None)[source]

Bases: object

Transcrire un fichier avec le graphe ONNX de l’encodeur Nemotron.

Parameters:
  • graph (str or Path, optional) – Le graphe. Par défaut resolve_graph() : $VH_NEMOTRON_ASR_GRAPH, sinon le cache, rempli une fois depuis deraison.ai/nemotron-onnx.

  • precision (str) – "int8" (défaut, 669 Mo) ou "fp32" (2,7 Go, exact).

  • providers (list of str, optional) – Les moteurs d’exécution ; même ordre d’autorité que le diariseur : l’argument, puis $VOCAL_HELPER_PROVIDER, puis l’ordre mesuré.

  • threads (int, optional) – Fils du moteur CPU ; None laisse onnxruntime choisir.

frames(audio, sample_rate)[source]

Jetons, log-probabilités et log-probabilités du blanc, toutes les 40 ms.

Normalisation sur l’enregistrement entier, puis fenêtres indépendantes de chunk_s secondes : l’encodeur de référence découpe ainsi.

Raises:

ValueError – Si la fréquence n’est pas celle du modèle.

Parameters:
  • audio (ndarray[tuple[int, ...], dtype[float32]])

  • sample_rate (int)

Return type:

tuple[ndarray[tuple[int, …], dtype[int32]], ndarray[tuple[int, …], dtype[float32]], ndarray[tuple[int, …], dtype[float32]]]

locate()[source]

Le chemin du graphe : l’argument, sinon resolve_graph().

Raises:

FileNotFoundError – Si le graphe désigné n’existe pas.

Return type:

Path

property settings: AsrSettings

Les réglages du graphe, lus sans charger ses poids.

streaming_frames(audio, sample_rate, *, hop_s=2.0, left_s=8.0, right_s=0.0, normalize='running')[source]

Les trames d’un flux simulé, et le retard de chacune.

Toutes les hop_s secondes, on transcrit les left_s + hop_s + right_s dernières secondes et on arrête les trames de [t - right_s - hop_s, t - right_s). Une trame attend donc entre right_s et right_s + hop_s avant d’être écrite, calcul en sus. La concaténation des trames arrêtées se décode comme un fichier.

Un flux ne connaît pas la suite, donc pas la moyenne de l’enregistrement que la normalisation hors ligne emploie. normalize="running" prend la moyenne et l’écart-type par bande de tout l’audio déjà vu, ce qui reste causal ; "window" les prend sur la fenêtre seule.

Returns:

ids, logprob, blank_logprob et delay_s, le retard de chaque trame entre la fin de son audio et son écriture, calcul compris.

Return type:

tuple

Raises:

ValueError – Si la fréquence n’est pas celle du modèle, si la fenêtre dépasse celle que l’encodeur a apprise, ou si normalize est inconnu.

Parameters:
transcribe(audio, sample_rate)[source]

Les mots horodatés, les balises, et la probabilité de parole par trame.

Parameters:
  • audio (ndarray[tuple[int, ...], dtype[float32]])

  • sample_rate (int)

Return type:

Transcript

class vocal_helper.nemotron_asr.Transcript(words=<factory>, tags=<factory>, speech=<factory>)[source]

Bases: object

Les mots, et à part les balises que la tête CTC émet.

Parameters:
speech: ndarray[tuple[int, ...], dtype[float32]]
tags: list[tuple[float, str]]
property text: str

Le texte, mots séparés par une espace.

words: list[Word]
class vocal_helper.nemotron_asr.Word(text, start_s, end_s, confidence)[source]

Bases: object

Un mot horodaté ; confidence est la moyenne des probabilités de ses pièces.

Parameters:
confidence: float
end_s: float
start_s: float
text: str
vocal_helper.nemotron_asr.greedy_ctc(ids, logprob, blank_id)[source]

Fusionner les répétitions et retirer les blancs, en gardant les trames.

Examples

>>> import numpy as np
>>> toks = greedy_ctc(np.array([9, 5, 5, 9, 5, 7]), np.zeros(6), blank_id=9)
>>> [(t.id, t.first, t.last) for t in toks]
[(5, 1, 2), (5, 4, 4), (7, 5, 5)]
Parameters:
  • ids (ndarray[tuple[int, ...], dtype[integer]])

  • logprob (ndarray[tuple[int, ...], dtype[floating]])

  • blank_id (int)

Return type:

list[CtcToken]

vocal_helper.nemotron_asr.log_mel(audio, settings)[source]

Le log-mel de NeMo, (n_mels, T), et le nombre de trames valides.

La recette de FilterbankFeatures : pré-accentuation, transformée centrée sur un remplissage constant (et non par réflexion, à la différence du front-end du diariseur), fenêtre de 400 points centrée dans 512, puissance, banc de filtres, log(x + 2**-24). Les trames au-delà de len(audio) // hop sont mises à zéro, comme NeMo le fait.

Examples

>>> import numpy as np
>>> s = AsrSettings(np.ones((128, 257), np.float32), np.hanning(400).astype(np.float32), (), 0)
>>> mel, valid = log_mel(np.zeros(16000, np.float32), s)
>>> mel.shape, valid
((128, 101), 100)
Parameters:
Return type:

tuple[ndarray[tuple[int, …], dtype[float32]], int]

vocal_helper.nemotron_asr.normalize_per_feature(mel, valid)[source]

Centrer et réduire chaque bande sur les trames valides, comme NeMo.

Écart-type sans biais (division par n - 1), plancher de 1e-5 ajouté après, trames invalides remises à zéro.

Examples

>>> import numpy as np
>>> out = normalize_per_feature(np.array([[1.0, 3.0, 9.0]], np.float32), 2)
>>> [round(float(v), 4) for v in out[0]]
[-0.7071, 0.7071, 0.0]
Parameters:
  • mel (ndarray[tuple[int, ...], dtype[float32]])

  • valid (int)

Return type:

ndarray[tuple[int, …], dtype[float32]]

vocal_helper.nemotron_asr.resolve_graph(precision='int8')[source]

Le graphe de la précision demandée, téléchargé une fois puis lu du cache.

Ordre : $VH_NEMOTRON_ASR_GRAPH s’il est défini, sinon le cache ($VH_CACHE_DIR/nemotron-onnx, par défaut ~/.cache/vocal-helper), rempli au besoin depuis $VH_NEMOTRON_ASR_URL ou DEFAULT_BASE_URL. Le téléchargement passe par os-helper : reprise, nouvelles tentatives, écriture atomique, et vérification de l’empreinte sur l’adresse par défaut.

Raises:

ValueError – Si la précision est inconnue.

Parameters:

precision (str)

Return type:

Path

vocal_helper.nemotron_asr.tokens_to_words(tokens, vocab, frame_s)[source]

Regrouper les pièces en mots horodatés, et sortir les balises à part.

Une pièce qui commence par ▁ ouvre un mot ; les autres le prolongent. Une balise (<spk:1>, <ev:laughter>…) clôt le mot en cours.

Examples

>>> vocab = ("<spk:1>", "▁bon", "jour", "▁à", "▁tous")
>>> toks = [CtcToken(1, 0, 1, 0.0), CtcToken(2, 3, 3, 0.0), CtcToken(0, 5, 5, 0.0),
...         CtcToken(3, 6, 6, 0.0), CtcToken(4, 8, 9, 0.0)]
>>> words, tags = tokens_to_words(toks, vocab, 0.04)
>>> [(w.text, w.start_s, w.end_s) for w in words]
[('bonjour', 0.0, 0.16), ('à', 0.24, 0.28), ('tous', 0.32, 0.4)]
>>> tags
[(0.2, '<spk:1>')]
Parameters:
Return type:

tuple[list[Word], list[tuple[float, str]]]