AI Hints
Home›NLP Tutorial›Classic Algorithms
NLP · Lesson 63 of 106

Tagging with Hidden Markov Models

The Viterbi algorithm.

⏱ 2 min read▶ Video at 1:53:16🎓 Free
Watch this lesson in the NLP courseWatch on YouTube ↗Full NLP course

Book is a noun in, read the book. And it's a verb in, book the table. So how does a computer tell them apart? You look at the neighbours. And the algorithm that does this was invented for sending signals through noisy telephone lines, in nineteen sixty seven. Today, it's still in your phone.

Tagging with Hidden Markov Models
From the video: Tagging with Hidden Markov Models
  • 'Book' can be a noun or a verb
  • Look at the neighbours
  • Invented for telephone lines

Finding the best path

The start table says how sentences tend to begin. The transition table says which tags follow which. After a determiner, a noun is very likely. And the emission table says which words each tag produces. Viterbi walks along the sentence, and for each tag, it keeps only the best path so far. Run it. In, book the dog, book is a verb. In, the book reads, the same word is a noun. It worked that out only from the surrounding tags. That's the power of a sequence model. Rather than deciding one word at a time, it finds the best whole path.

viterbi.pypython
import numpy as np

tags = ["DET", "NOUN", "VERB"]
start = {"DET": .7, "NOUN": .2, "VERB": .1}
trans = {"DET": {"DET": .01, "NOUN": .95, "VERB": .04},
         "NOUN": {"DET": .1, "NOUN": .3, "VERB": .6},
         "VERB": {"DET": .6, "NOUN": .3, "VERB": .1}}
emit = {"DET": {"the": .9, "a": .1},
        "NOUN": {"dog": .4, "book": .4, "bark": .2},
        "VERB": {"book": .3, "bark": .5, "reads": .2}}

def viterbi(words):
    best = {t: (np.log(start[t] * emit[t].get(words[0], 1e-6)), [t]) for t in tags}
    for w in words[1:]:
        best = {t: max((best[p][0] + np.log(trans[p][t] * emit[t].get(w, 1e-6)), best[p][1] + [t])
                       for p in tags) for t in tags}
    return max(best.values())[1]

print(viterbi("the dog bark".split()))
print(viterbi("book the dog".split()))
print(viterbi("the book reads".split()))
Output
['DET', 'NOUN', 'VERB']
['VERB', 'DET', 'NOUN']
['DET', 'NOUN', 'VERB']

Sequence thinking

Viterbi has a huge family. It decodes speech and mobile signals. Conditional random fields use the same trick, and they often sit on top of neural taggers. The beam search that large language models use is a close cousin too. Whenever each choice depends on the last one, think about finding the best path, rather than the best step.

Telephone and mobilesignal decoding
Speech recognitionbest word path
CRF layerson neural taggers
Beam searcha close cousin

Summary

Keep the best path to each state. Next, two messages that say nearly the same thing, and how to find them among millions.

Key takeaways
  • Tags depend on neighbours
  • Viterbi keeps the best path
  • Best path beats best step
Next up: Near Duplicates — finding copies among millions