From c79fd1d7aed8231ac82cfc7551cf341aee25e0ec Mon Sep 17 00:00:00 2001 From: arxaqapi Date: Thu, 30 Apr 2026 15:41:36 +0200 Subject: [PATCH 1/3] fix: threshold sliders improved --- docs/advanced/thresholds.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced/thresholds.md b/docs/advanced/thresholds.md index 7ce40a1..7b90082 100644 --- a/docs/advanced/thresholds.md +++ b/docs/advanced/thresholds.md @@ -46,7 +46,7 @@ uv run scripts/infer.py \ Drag each slider, or click directly on a chart, to set the decision threshold for each category. The macro-F1 number at the top updates live as you tune. - !!! note "What the curves show" From 2fc3b9d2c9b438989bd00e90c848f040e9b639e0 Mon Sep 17 00:00:00 2001 From: arxaqapi Date: Tue, 5 May 2026 14:40:16 +0200 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20added=20huggingface=5Fhug=20model?= =?UTF-8?q?=20downoad=20and=20cleaned=20CLI=20API=20=F0=9F=9B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 3 +- scripts/infer.py | 120 +++++++++++++++++++++++++++-------------------- uv.lock | 2 + 3 files changed, 73 insertions(+), 52 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d722f4d..d71820e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,6 +5,7 @@ description = "The inference code for the Voice Type Classifier model" readme = "README.md" requires-python = ">=3.13" dependencies = [ + "huggingface-hub>=0.35.0", "pyannote-audio>=3.4.0", "segma", "tqdm>=4.67.1", @@ -19,4 +20,4 @@ doc = [ ] [project.urls] -documentation = "https://docs.cognitive-ml.fr/fastabx" +documentation = "https://laac-lscp.github.io/VTC/" diff --git a/scripts/infer.py b/scripts/infer.py index 3910f68..d2b30a8 100644 --- a/scripts/infer.py +++ b/scripts/infer.py @@ -6,6 +6,7 @@ from typing import Literal import polars as pl +from huggingface_hub import snapshot_download from pyannote.core import Annotation, Segment from segma.inference import get_list_of_files_to_process, run_inference_on_audios from segma.utils.io import get_audio_info @@ -17,6 +18,11 @@ ) logger = logging.getLogger("inference") +MODEL_TO_REVISION = { + "2.1": "10265c5", + "2.0": "91e67b5", +} + def load_aa(path: Path): data = pl.read_csv( @@ -168,59 +174,75 @@ def check_audio_files(audio_files_to_process: list[Path]) -> None: def main( - output: str, - uris: Path | None = None, - config: str = "VTC-2/model/config.toml", - wavs: str = "data/debug/wav", - checkpoint: str = "VTC-2/model/best.ckpt", - save_logits: bool = False, + wavs: str, + output: str | Path, + model: Literal["2.1"] = "2.1", + device: Literal["gpu", "cuda", "cpu", "mps"] = "gpu", + batch_size: int = 128, + recursive_search: bool = False, high_precision: bool = False, thresholds: None | Path = Path("thresholds/f1.toml"), min_duration_on_s: float = 0.1, min_duration_off_s: float = 0.1, - batch_size: int = 128, write_empty: bool = True, - write_csv: bool = True, - recursive_search: bool = False, - device: Literal["gpu", "cuda", "cpu", "mps"] = "gpu", + save_logits: bool = False, keep_raw: bool = False, + uris: Path | None = None, *args, **kwargs, ): """Run sliding inference on the given files and then merges the created segments. Args: - uris (list[str]): list of uris to use for prediction. - config (str, optional): Config file to be loaded and used for inference. Defaults to "VTC-2.0/model/config.yml". - wavs (str, optional): _description_. Defaults to "data/debug/wav". - checkpoint (str, optional): Path to a pretrained model checkpoint. Defaults to "VTC-2.0/model/best.ckpt". - output (str, optional): Output Path to the folder that will contain the final predictions.. Defaults to "". + wavs (str): List of audio files to run inference on. + output (str | Path): Output Path to the folder that will contain the final predictions. + model (Literal["2.1"], optional): Version of the VTC model to use. Defaults to "2.1". + device (Literal["gpu", "cuda", "cpu", "mps"], optional): Device to run the model on. Defaults to "gpu". + batch_size (int, optional): Batch size to use during inference. Defaults to 128. + recursive_search (bool, optional): Recursively searches the wavs dir for audio files, can be time consuming. Defaults to False. + high_precision (bool, optional): Uses a high precision version of the VTC by using the high precision thresholds (`thresholds/hp.toml`). Defaults to False. + thresholds (None | Path, optional): Path to a thresholds dict, perform predictions using thresholding. Defaults to Path("thresholds/f1.toml"). + min_duration_on_s (float, optional): Remove speech segments shorter than that many seconds. Defaults to .1. + min_duration_off_s (float, optional): Fill same-speaker gaps shorter than that many seconds. Defaults to .1. + write_empty (bool, optional): Write an empty RTTM files to disk when nothing was detected in an audio. Defaults to True. save_logits (bool, optional): If the prediction scripts saves the logits to disk, can be memory intensive. Defaults to False. - thresholds (None | Path, optional): Path to a thresholds dict, perform predictions using thresholding. Defaults to None. - min_duration_on_s (float, optional): Remove speech segments shorter than that many seconds.. Defaults to .1. - min_duration_off_s (float, optional): Fill same-speaker gaps shorter than that many seconds.. Defaults to .1. - batch_size (int): Batch size to use during inference. Defaults to 128. - keep_raw (bool, optional): If True, keeps the RTTM files before segment merging. + keep_raw (bool, optional): If True, keeps the RTTM files before segment merging. Defaults to False. + uris (Path | None, optional): Given a `.txt` file containing a list of newline separated uris (filenames), performs inference only on these audio files. Defaults to None. Raises: - ValueError: _description_ - ValueError: _description_ - ValueError: _description_ + FileNotFoundError: Raised if the model checkpoint and config file where not correctly downloaded using `huggingface_hub.snapshot_download`. """ + output = Path(output) + output.mkdir(parents=True, exist_ok=True) + + logger.info(f"Using VTC model version {model}.") + model_path = snapshot_download( + repo_id="coml/VTC-2", + revision=MODEL_TO_REVISION[model], + ) + + config_path = Path(model_path) / "model" / "config.toml" + checkpoint_path = Path(model_path) / "model" / "best.ckpt" + if not config_path.exists() or not checkpoint_path.exists(): + raise FileNotFoundError( + f"Something went wrong when downloading the model, the files could not be found on disk:\n\tPath: {model_path}" + ) + + logger.info(f"VTC-{model} model is stored here: {model_path}.") + + # TODO - use assets if high_precision: thresholds = Path("thresholds/hp.toml") if thresholds: shutil.copy(str(thresholds), dst=output) logger.info(f"Using thresholds: {thresholds}") - output = Path(output) - - logger.info("Running inference on audio files.") + logger.info("Running inference on audio files...") processed_files = run_inference_on_audios( - config=config, + config=config_path, uris=uris, wavs=wavs, - checkpoint=checkpoint, + checkpoint=checkpoint_path, output=output, thresholds=thresholds, batch_size=batch_size, @@ -244,21 +266,20 @@ def main( shutil.rmtree(str(Path(output / "raw_rttm").absolute())) # NOTE - write RTTMs to `csv` files - if write_csv: - if keep_raw: - # NOTE - Raw RTTMs - raw_rttm_file_p = sorted(list((output / "raw_rttm").glob("*.rttm"))) - raw_rttm_file_dfs = [] - for rttm_file in raw_rttm_file_p: - raw_rttm_file_dfs.append(load_rttm(rttm_file)) - pl.concat(raw_rttm_file_dfs).write_csv(output / "raw_rttm.csv") - - # NOTE - merged RTTMs - rttm_file_p = sorted(list((output / "rttm").glob("*.rttm"))) - rttm_file_dfs = [] - for rttm_file in rttm_file_p: - rttm_file_dfs.append(load_rttm(rttm_file)) - pl.concat(rttm_file_dfs).write_csv(output / "rttm.csv") + if keep_raw: + # NOTE - Raw RTTMs + raw_rttm_file_p = sorted(list((output / "raw_rttm").glob("*.rttm"))) + raw_rttm_file_dfs = [] + for rttm_file in raw_rttm_file_p: + raw_rttm_file_dfs.append(load_rttm(rttm_file)) + pl.concat(raw_rttm_file_dfs).write_csv(output / "raw_rttm.csv") + + # NOTE - merged RTTMs + rttm_file_p = sorted(list((output / "rttm").glob("*.rttm"))) + rttm_file_dfs = [] + for rttm_file in rttm_file_p: + rttm_file_dfs.append(load_rttm(rttm_file)) + pl.concat(rttm_file_dfs).write_csv(output / "rttm.csv") logger.info(f"Inference finished, files can be found here: '{output.absolute()}/'") @@ -266,10 +287,11 @@ def main( if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument( - "--config", + "--model", type=str, - default="VTC-2/model/config.toml", - help="Config file to be loaded and used for inference.", + default="2.1", + choices=["2.1"], # "2.0" + help="Version of the model to use during inference. Defaults to `2.1`", ) parser.add_argument( "--uris", help="Path to a file containing the list of uris to use." @@ -279,14 +301,10 @@ def main( default="data/debug/wav", help="Folder containing the audio files to run inference on.", ) - parser.add_argument( - "--checkpoint", - default="VTC-2/model/best.ckpt", - help="Path to a pretrained model checkpoint.", - ) parser.add_argument( "--output", required=True, + type=Path, help="Output Path to the folder that will contain the final predictions.", ) parser.add_argument( diff --git a/uv.lock b/uv.lock index cbf0eda..bf81e7d 100644 --- a/uv.lock +++ b/uv.lock @@ -2261,6 +2261,7 @@ name = "vtc" version = "2.1.0" source = { virtual = "." } dependencies = [ + { name = "huggingface-hub" }, { name = "pyannote-audio" }, { name = "segma" }, { name = "tqdm" }, @@ -2273,6 +2274,7 @@ doc = [ [package.metadata] requires-dist = [ + { name = "huggingface-hub", specifier = ">=0.35.0" }, { name = "pyannote-audio", specifier = ">=3.4.0" }, { name = "segma", git = "https://github.com/arxaqapi/segma?rev=977225f2e0400465cd6a0c525f11c963223c8830" }, { name = "tqdm", specifier = ">=4.67.1" }, From d5ae2468dbce17b3142157fc437a853d1937dfcd Mon Sep 17 00:00:00 2001 From: arxaqapi Date: Tue, 5 May 2026 14:45:21 +0200 Subject: [PATCH 3/3] feat: removed VTC-2 git submodule --- .gitmodules | 3 --- VTC-2 | 1 - scripts/run.sh | 2 -- 3 files changed, 6 deletions(-) delete mode 100644 .gitmodules delete mode 160000 VTC-2 diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 731f895..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "VTC-2"] - path = VTC-2 - url = https://huggingface.co/coml/VTC-2 diff --git a/VTC-2 b/VTC-2 deleted file mode 160000 index 6b1a955..0000000 --- a/VTC-2 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6b1a95508302edc14c50f670cd9a30d66fa4f88a diff --git a/scripts/run.sh b/scripts/run.sh index 20737be..85391c1 100644 --- a/scripts/run.sh +++ b/scripts/run.sh @@ -4,8 +4,6 @@ audios_path=audio_folder output=out/$(date +%Y%m%d_%H%M)_inference_output -mkdir -p $output - uv run scripts/infer.py \ --wavs $audios_path \ --output $output