Unigram Language Model Tokenization › Viterbi Decoding: Finding the Best Segmentation
Language AI HandbookAll chapters Unigram Language Model Tokenization › Viterbi Decoding: Finding the Best SegmentationOne section · Sections in this chapter3 / 11 ↓
Reading tools Definitions & inline links
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
At inference time, given a new word, we want to find its most probable segmentation. This is not a trivial problem: a word of length has up to possible segmentations, and even a 10-character word could have hundreds of valid tokenization paths through the vocabulary. Checking all of them explicitly would be far too slow.
The solution is the Viterbi algorithm, a classic dynamic programming technique adapted here to sequence segmentation. Think of Viterbi as a smart path-finding algorithm. Imagine you are walking from one end of a word to the other, stepping through it one character at a time. At each position, you can "land" by completing a valid vocabulary token that ends at that position. Your goal is to find the sequence of landings that maximizes your total log-probability score.
Imagine writing out the characters of a word as positions where is the word length. We want to find the path from position 0 to position that passes through subword tokens with the highest combined log-probability.
Define as the log-probability of the highest-probability segmentation of the prefix . We build this table up character by character, from left to right. At each position , we ask: for every possible last token that ends at position , what is the best we could do if we used that token? The answer is the best score for the prefix ending before that token, plus the log-probability of the token itself:
where:
- : the maximum log-probability achievable for the prefix ending at position
- : the start of the last token in the optimal segmentation of
- : the substring from position to , which must be a valid token in the vocabulary
- : the log-probability of that token, which contributes to the total score
- : the best log-probability for the prefix , already computed in an earlier step
We initialize because the empty string has log-probability . Then we fill in using this recurrence. While filling the table, we track which starting position achieved the maximum at each , allowing us to backtrack and recover the actual best segmentation.
Why does this formula make sense? Notice that we are computing the best possible score for prefix by considering every valid token that ends at position . For each such token, the best total score we could achieve is the best score for the prefix before the token (already computed) plus the token's own log-probability. Taking the maximum over all valid tokens gives us the globally optimal score for , and the key insight is that this optimal score at position only depends on optimal scores at earlier positions, never on positions after . This is the "optimal substructure" property that makes dynamic programming applicable.
This dynamic programming approach runs in time in the word length (or where is the maximum token length), making it fast in practice since words are rarely longer than a few dozen characters. Even for a 20-character word with a maximum token length of 16, the algorithm only needs to examine pairs.
Backtracking the Optimal Path
After filling the table, we recover the actual segmentation by backtracking. Starting at position (the end of the word), we follow the stored pointers backward: the pointer at position tells us where the last token started, say at position . Then we follow the pointer at to find where the second-to-last token started, and so on, until we reach position 0. Reversing the collected tokens gives us the optimal segmentation in left-to-right order.
This backtracking step is and is the same technique used in Viterbi decoding for hidden Markov models, sequence-to-sequence alignment, and many other applications. The combination of forward dynamic programming to fill the table and backward tracing to recover the solution is one of the most versatile techniques in algorithm design.