WeSpeaker's four CLI tasks, and the version stamp stuck at 0.0.1
Research and Production Oriented Speaker Verification, Recognition and Diarization Toolkit
At a glance
- What is it?
- WeSpeaker turns audio into fixed-length speaker embeddings and then scores, clusters or verifies with them, through one pip package and a tree of Kaldi-style recipes. It serves research teams that train and evaluate speaker models, and it frustrates product teams on versioning, install coverage and diarization output.
- Who is it for?
- Adopt WeSpeaker if your team already lives in Kaldi-style recipes and can install PyTorch: the four CLI tasks, the Python wrapper and the recipe tree cover embedding extraction, scoring and diarization without glue code, and the recipes span four corpora. Do not adopt it when you need a package version that tracks the code, a documented CPU install or named speakers in the output, because none of those exist.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 33 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Fixed-length vectors are the deliverable, and the audience is research teams
Speaker recognition here is not transcription. None of the four entry points returns words; each returns one fixed-length vector for an utterance, and everything else in the repository is built around comparing those vectors or grouping them.
Two feature paths support that. The toolkit either extracts features online while reading audio, or it loads pre-extracted features in kaldi-format, which suits teams already sitting in that data layout. Recipe folders cover cnceleb, sre, voxceleb and voxconverse, so the core audience is groups reproducing published verification experiments. The numbers attached to those recipes are specific rather than vague: 2.627% EER on vox1-O-clean for ECAPA_TDNN_GLOB_c1024 trained without any labels, and 0.447% EER with 0.043 minDCF for a 293-layer deep r-vector.
A Huggingface demo space and a Modelscope studio are linked so a model can be tried before code is written. What is not here is a voiceprint library. Nothing in the tree ships enrolled identities of real people, and there is no database to query.
Four CLI tasks separate extraction, similarity and diarization
Work on the command line is split by task name, which keeps batch jobs away from single-file calls. `--task embedding` reads one wav and writes vectors to `--output_file`; `--task embedding_kaldi` reads a `--wav_scp` list and writes into a directory; `--task similarity` compares two files; `--task diarization` takes one file and segments it. `-h` lists the parameters these four examples leave out.
$ wespeaker --task embedding --audio_file audio.wav --output_file embedding.txt
$ wespeaker --task embedding_kaldi --wav_scp wav.scp --output_file /path/to/embedding
$ wespeaker --task similarity --audio_file audio.wav --audio_file2 audio2.wav
$ wespeaker --task diarization --audio_file audio.wavRead those lines together and the gaps appear. The diarization call passes no speaker count and no overlap setting, so nothing in the invocation lets you declare how many people are in the room. A news entry dated 2024.08.20 records that the VoxConverse recipe was reworked around umap dimensionality reduction and hdbscan clustering, and hdbscan takes no cluster count, so the number of speakers comes out of the density structure rather than a setting. silero-vad sits in the dependency list for voice activity, yet the README does not say how that voice activity reaches the diarization output.
Model choice is a bare string and weights land in a hidden directory
Programmatic use goes through one object. `wespeaker.load_model('chinese')` takes a model name as a bare string, and the wrapper it returns exposes four methods that map one to one onto the four CLI tasks.
import wespeaker
model = wespeaker.load_model('chinese')
embedding = model.extract_embedding('audio.wav')
utt_names, embeddings = model.extract_embedding_list('wav.scp')
similarity = model.compute_similarity('audio1.wav', 'audio2.wav')
diar_result = model.diarize('audio.wav')Model weights download on first use rather than shipping inside the package, and `WESPEAKER_HOME` sets where they land, with `$HOME/.wespeaker` as the default path. That makes the first call depend on outbound network access and a writable home directory, and a container image has to pre-warm that directory or carry it in the layer. The four commands above are the quick path; `docs/python_package.md` holds the longer flag list, and `docs/pretrained.md` is where the available pretrained models are enumerated.
One git URL installs the package, and no CPU path is documented
The short install is a single line, and it pulls from the git remote rather than from a package index:
pip install git+https://github.com/wenet-e2e/wespeaker.gitThen `wespeaker --task embedding --audio_file audio.wav --output_file embedding.txt` turns one wav file into a text file of vectors, and you choose the output path. Two constraints deserve a check before anything else. setup.py sets python_requires to at least 3.8 and lists classifiers through 3.11, while the README badge advertises 3.8 and 3.9 and the conda recipe creates python=3.9, so the supported range gets described three different ways. And no CPU-only install appears anywhere in the documented setup, because both torch lines in the longer path point at a CUDA wheel index for 11.x or 12.x. A reader without an NVIDIA card has to work that out from surrounding text.
Two CUDA wheels, sox and a torchnet build isolation escape
Setup for deployment is longer, and it pins a GPU stack before anything else happens.
git clone https://github.com/wenet-e2e/wespeaker.git
conda create -n wespeaker python=3.9
conda activate wespeaker
pip install torch==2.7.1 torchaudio==2.7.1 --index-url https://download.pytorch.org/whl/cu118 # cuda 11.X
pip install torch==2.7.1 torchaudio==2.7.1 --index-url https://download.pytorch.org/whl/cu128 # cuda 12.X
conda install -c conda-forge sox
pip install -r requirements.txtThe final two steps are the unusual ones. torchnet==0.0.4 goes in separately with build isolation switched off, and pre-commit installs the formatting hooks.
pip install --no-build-isolation torchnet==0.0.4
pre-commit installA comment above that line gives the reason: visdom is a torchnet dependency, it needs pkg_resources at build time, and the workaround exists so pip reuses the setuptools already on the machine instead of building a fresh one. torchnet appears in neither requirements.txt nor setup.py, so anyone who installs only from the pinned requirements file ends up with no torchnet and no visdom at all. It is two lines, and it decides whether the training extras work.
Three dependency declarations disagree about the same packages
Three files describe dependencies and they do not agree. A pip install from git reads setup.py. A developer follows requirements.txt. A build reads pyproject.toml.
setup.py install_requires lists tqdm, kaldiio, hdbscan>=0.8.40, umap-learn==0.5.6, torch>=2.0.0, torchaudio>=2.0.0, silero-vad, s3prl, openai-whisper, peft and accelerate, holding the two torch packages at a floor rather than a pin. requirements.txt pins the same names to exact versions and adds entries setup.py never mentions, among them lmdb==1.3.0, onnxruntime, soundfile, pypeln==0.4.9 and fire==0.4.0. pyproject.toml keeps the build narrow:
[build-system]
requires = ["setuptools>=75.1.0,<=81", "wheel"]
build-backend = "setuptools.build_meta"The dev extras in that same file name flake8 and pre-commit with no version at all, beside a requirements.txt that pins flake8==3.8.2 and pre-commit==3.5.0. That file also hard-locks numpy==1.22.4, matplotlib==3.5.1 and scipy==1.10.0 while leaving silero-vad and s3prl floating, so one environment mixes exact locks with unpinned names. Pick a list and own the result, because combining a git install with a pinned numpy is one way to end up with a tree the training scripts will not accept.
The build metadata declares version 0.0.1 and never moves
Build metadata never moved with the code. setup.py carries version="0.0.1" and describes the package with the narrower phrase Speaker Embedding Learning Toolkit, dropping verification, recognition and diarization.
The last push was on 2026-07-08, with news entries dated 2026.07.03 for ReDimNet2 and 2026.06.15 for HAM-Softmax, yet the same setup.py still declares version="0.0.1" while the newest tag is v1.2.0, dated 2023-07-23. Pinning wespeaker in a lockfile pins a number that stopped moving well before the newest commits. pip reports 0.0.1 for a tree that has since gained ReDimNet2, w2v-bert2, Xi-vector and SimAM_ResNet.
The Apache-2.0 terms declared in setup.py require the licence text and any NOTICE file to travel with a redistribution, which matters when models or runtime binaries built from this tree ship inside a product. The classifiers set more honest expectations: Development Status :: 4 - Beta, Intended Audience :: Developers, Intended Audience :: Science/Research and Operating System :: OS Independent.
The runtime directory is a second deployment route with its own engines
Two other routes sit inside the repository. The first is deployment: a runtime/ folder is linked from the README, CPPLINT.cfg and .clang-format sit at the top level for C++ work, onnxruntime appears in requirements.txt, and a news entry dated 2024.04.23 records MNN inference engine support inside that runtime. The Python path fetches weights through WESPEAKER_HOME and hands vectors back to your process, whereas the runtime path is a separate build with its own engines, so an embedded deployment does not inherit the Python environment.
The second is scoring. compute_similarity compares two vectors directly, while a news entry dated 2023.07.18 records kaldi-compatible PLDA and unsupervised adaptation, and 2024.05.15 adds quality-aware score calibration. PLDA reframes the comparison as a likelihood ratio under a model of speaker variability, and calibration moves the decision threshold, which is what decides whether a score from one corpus survives contact with your own traffic. Neither route has a command example anywhere in the README, so a new user starts with the four CLI tasks and the recipe tree.
Editorial conclusion
Adopt WeSpeaker if your team already lives in Kaldi-style recipes and can install PyTorch: the four CLI tasks, the Python wrapper and the recipe tree cover embedding extraction, scoring and diarization without glue code, and the recipes span four corpora. Do not adopt it when you need a package version that tracks the code, a documented CPU install or named speakers in the output, because none of those exist. First step for any adopter: compare the version declared in setup.py against the commits you actually clone.
Frequently asked questions
How do I install WeSpeaker, and where do the downloaded models go?
Install it with pip install git+https://github.com/wenet-e2e/wespeaker.git. Downloaded model weights land in the path set by WESPEAKER_HOME, which defaults to $HOME/.wespeaker, so the first model load writes into the user's home directory.
What is the difference between `wespeaker --task embedding` and `wespeaker --task embedding_kaldi`?
The first takes a single --audio_file and writes vectors to the path given in --output_file. The second takes a --wav_scp list of utterances and writes the batch into the --output_file path.
Does WeSpeaker need an NVIDIA GPU?
The documented setup installs torch==2.7.1 and torchaudio==2.7.1 from CUDA 11.X and CUDA 12.X wheel indexes. The README gives no CPU-only install, so a machine without an NVIDIA card has no written path.
Can WeSpeaker tell me which named person is speaking in a multi-speaker recording?
The diarization task groups an utterance into segments by speaker, and the Python wrapper exposes model.diarize('audio.wav') for it. No documented step attaches names to those speakers, and hdbscan clustering discovers the speaker count from the data instead of taking one as input.
How can I score two utterances other than by direct similarity?
A news entry dated 2023.07.18 records kaldi-compatible PLDA and unsupervised adaptation, and one dated 2024.05.15 adds quality-aware score calibration. Both change the scoring path beyond the direct vector comparison returned by compute_similarity.
Which datasets do the shipped WeSpeaker recipes cover?
Four example folders exist: examples/cnceleb/, examples/sre/, examples/voxceleb/ and examples/voxconverse/. The README does not document how to obtain those corpora, so the download step is left to the reader.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/wenet-e2e-wespeaker)Was this useful?