Managing the HCLG Graph: Optimizing WFSTs in Kaldi
Learn how Kaldi uses WFST composition to build HCLG graphs, the role of determinization and minimization, and how to manage the memory trade-offs of large language models.
28 Jun 2026, 21:54 UTC

The Decoding Bottleneck: The HCLG Graph
When building a speech recognition system in Kaldi, the transition from acoustic models to actual text happens in the decoding graph. The core problem is search space: if the system had to calculate every possible combination of phonemes, words, and grammar rules in real-time, the latency would be unusable. The solution is the HCLG graph, a massive Weighted Finite State Transducer (WFST) that pre-calculates these paths.
The takeaway is that while the HCLG graph enables lightning-fast Viterbi decoding, it introduces a significant engineering trade-off: the more complex your language model, the more memory-intensive the graph compilation becomes. Understanding how to compose and minimize this graph is the difference between a production-ready model and a system that crashes during build time.
How the Composition Works
Kaldi doesn't treat the acoustic model and the dictionary as separate lookups. Instead, it uses composition—a mathematical operation that merges multiple transducers into one. The HCLG graph is the result of composing four distinct layers:
- H (HMM): Maps HMM states to phone states.
- C (Context-dependency): Maps context-dependent phones to phone states.
- L (Lexicon): Maps phones to words.
- G (Grammar): Maps words to sequences of words (usually based on an N-gram language model).
By composing these into a single HCLG.fst, the decoder only needs to traverse a single graph during runtime. It doesn't "know" it is moving from a phone to a word; it is simply moving from one state to another based on the lowest weight (highest probability).
Optimization: Determinization and Minimization
A raw composed graph is often redundant. If multiple paths lead to the same state with the same weight, the decoder wastes cycles exploring both. To fix this, Kaldi employs two critical steps:
Determinization ensures that for any state and any input symbol, there is at most one outgoing transition. This removes ambiguity in the search path. Minimization then collapses equivalent states to reduce the total number of states and arcs, shrinking the memory footprint of the final .fst file.
Practical Example: Building the Graph
In a standard Kaldi recipe, the construction of the HCLG graph is typically handled by the utils/compose_lm.sh script. To understand what is happening under the hood, you can observe the composition sequence. This is typically run on a build server with high RAM, as the intermediate composed graphs can be massive before they are minimized.
# Example sequence of operations (simplified)
# Run these in your kaldi/src directory or recipe folder
# 1. Compose L and G
fstcompose L.fst G.fst LGe.fst
# 2. Determinize and minimize the result
fstdeterminize LGe.fst LGe_det.fst
fstminimize LGe_det.fst LGe_min.fst
# 3. Compose H and C, then compose with the minimized LG
fstcompose H.fst C.fst HC.fst
fstcompose HC.fst LGe_min.fst HCLG.fst
Permissions: These commands require execute permissions for the Kaldi binaries and write access to the working directory.
Risk: Running fstdeterminize on a poorly structured grammar or an excessively large N-gram model can cause an exponential blow-up in states, leading to an out-of-memory (OOM) crash.
The Engineering Trade-offs
| Factor | Small/Constrained Grammar | Large N-gram Model |
|---|---|---|
| Build Time | Fast (Seconds/Minutes) | Slow (Hours) |
| RAM Usage | Low | Very High (during composition) |
| Accuracy | High for specific commands | Better for natural language |
| Flexibility | Rigid | Flexible |
Limitations and Verification
The primary limitation of the HCLG approach is staticity. If you add a single new word to your lexicon (L) or update your language model (G), you cannot simply "patch" the graph. You must re-compose and re-minimize the entire HCLG structure, which is computationally expensive.
To verify the result of your graph construction, use the ismfst tool. This allows you to inspect the number of states and arcs in the resulting file:
ismfst HCLG.fst
Check the output for the total state count. If the state count is significantly higher than expected compared to your L.fst and G.fst, it may indicate a failure in the minimization step or an overly complex grammar that is causing state explosion.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.