Choosing a Kaldi Acoustic Model Recipe: GMM-HMM Baseline vs Chain TDNN
A decision guide for new Kaldi projects: when a GMM-HMM baseline is enough, when to go straight to a chain TDNN recipe, and how to validate the choice with a dev-set WER comparison.
24 Dec 2025, 21:59 UTC

If you are starting a speech recognition project with Kaldi, the first real engineering decision is which acoustic modeling recipe to build on. Kaldi ships reference recipes under egs/, not turnkey models, so you are choosing a training pipeline, not downloading a product. The two supported paths are the classic GMM-HMM recipes and the chain (TDNN) recipes, and the right choice depends mostly on two constraints: how much training data you have and whether you have a GPU.
The decision and its constraints
The decision is: do you stop at a GMM-HMM system (Gaussian Mixture Model emitting probabilities for Hidden Markov Model states), or do you train a chain model — a time-delay neural network (TDNN) trained with the lattice-free MMI objective, which is Kaldi's standard modern recipe?
The constraints that actually drive the choice:
- Data volume. GMM-HMM systems train well on tens of hours. Chain models want more data to shine, though with speed perturbation they also work on moderate corpora.
- Hardware. Chain training is only practical with CUDA-enabled GPU support compiled in. CPU-only chain training runs, but slowly enough to be a real blocker.
- Maintenance horizon. Kaldi development has slowed as the community moved toward successor toolkits (k2/icefall). If long-term support matters, factor that in now.
Comparing the supported options
| Aspect | GMM-HMM (triphone, SAT/fMLLR) | Chain TDNN / TDNN-F |
|---|---|---|
| Training hardware | CPU is fine | GPU (CUDA) strongly recommended |
| Small corpora (<100h) | Well suited, fast iteration | Possible, but needs speed perturbation |
| Word error rate | Baseline | Typically clearly better on the same data |
| Frame rate | Standard 10 ms shift | Lower frame rate (~30 ms shift) |
| Pretraining | Staged alignments (mono → tri → SAT) | None (no cross-entropy stage) |
| Role in recipes | Baseline; produces neural alignments | Final model in most modern recipes |
Note that these are not fully exclusive: most chain recipes use a GMM system earlier in the pipeline to generate alignments and build the denominator graph. The practical question is whether GMM is your final system or just a stage on the way to a chain model.
Trade-offs that bite in practice
Recipe fragility. Scripts under egs/ are version- and data-sensitive. Flags, directory layouts, and stage numbers differ between examples (e.g., egs/wsj/s5 vs egs/librispeech/s5). Copying steps between recipes without reading them usually breaks. Pick one recipe close to your data profile and adapt it, rather than merging several.
Speed perturbation is not optional for chain models. Chain recipes normally rely on 3-way speed perturbation (0.9x, 1.0x, 1.1x) of the training audio. Skipping it to save time typically degrades WER noticeably.
WER numbers do not transfer. Published recipe results are corpus- and setup-specific. Treat them as evidence the pipeline works, not as a promise for your data.
Decoding is shared either way. Both paths decode with an HCLG graph — a composed finite-state transducer combining HMM topology (H), context dependency (C), lexicon (L), and grammar/language model (G). For streaming use, the online2 decoding binaries support i-vector-based speaker adaptation, and chain models are the better fit there because of their lower frame rate.
A concrete validation path
Before committing, verify the pipeline end-to-end on a small corpus. The yesno example is the standard smoke test; a Librispeech subset (e.g., the 100h split) is the realistic benchmark.
First, confirm CUDA is detected before attempting chain training. Run from the Kaldi src/ directory, with build tools and the CUDA toolkit installed:
cd kaldi/src
./configure --shared
make depend -j$(nproc)
make -j$(nproc)Check the configure output for a line confirming CUDA was found; if it reports CPU-only, chain training will be impractically slow and you should fix the CUDA path before proceeding. Do not assume success — read the output.
Then run a recipe end-to-end as the unprivileged user that owns the data directories:
cd kaldi/egs/yesno/s5
./run.shExpected check: the script completes all stages and prints a WER report (yesno should approach 0% — it is a tiny toy corpus, so anything else indicates a broken install). For a real comparison, run a Librispeech-style recipe and compare the dev-set WER of the GMM stage against the chain stage on the same split:
# after the GMM stages and after local/chain/run_tdnn.sh complete:
grep -H "%WER" exp/tri*/decode_*dev*/scoring_kaldi/best_wer \
exp/chain/tdnn*/decode_*dev*/scoring_kaldi/best_werDirectory names vary by recipe; adjust the globs to your exp/ layout. The expected outcome is a lower WER for the chain model. If the chain model is not better, suspect data preparation, missing speed perturbation, or a misconfigured GPU run before blaming the recipe.
Finally, decode a held-out audio file with the final HCLG graph and inspect the output. A CTM file (time-marked word list) with plausible alignments against the reference transcript is the practical acceptance check.
Limitations
This guidance reflects well-established Kaldi behavior, but Kaldi itself is in maintenance mode; if you are starting a multi-year project, evaluate k2/icefall alongside it. Exact script names and flags differ across recipe versions, so verify against the run.sh in your checkout rather than any external tutorial. And because WER is data-dependent, the only trustworthy validation is the dev-set comparison on your own corpus.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.