AI Hints
Home›NLP Tutorial›Language from Counting
NLP · Lesson 44 of 106

Hidden Structure

Tagging with Viterbi.

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

The word book can be a noun or a verb. To decide which, a tagger has to look at the whole sentence. Trying every combination would be impossibly slow, even for a short sentence. In nineteen sixty seven, Andrew Viterbi found a shortcut, which is now in every mobile phone on Earth.

Hidden Structure
From the video: Hidden Structure
  • Book: noun or verb?
  • Every combination is too slow
  • Viterbi's shortcut

One word, many roles

Here's the problem in action. The same word is used in three sentences. Run it. SpaCy tags book as a verb, a noun, and a verb, purely from context. How can a program do that? Neighbouring tags help. After the word a, a noun is much more likely than a verb.

ambiguity.pypython
import spacy

nlp = spacy.load("en_core_web_sm")

for text in ["I book a flight.", "I read a book.", "Book the table."]:
    print([(t.text, t.pos_) for t in nlp(text) if t.lower_ == "book"], text)
Output
[('book', 'VERB')] I book a flight.
[('book', 'NOUN')] I read a book.
[('Book', 'NOUN')] Book the table.

The Viterbi algorithm

A hidden Markov model has two tables. How likely each tag is to follow another, and how likely each tag is to produce each word. Viterbi walks left to right, and at each step, it keeps only the best path to each tag. That turns an explosion of possibilities into simple steps. Run it. It finds that fish is a verb after people, and a noun before swim. The same word, tagged correctly by the sentence around it.

viterbi.pypython
import numpy as np

tags = ["NOUN", "VERB"]
start = np.array([0.7, 0.3])
trans = np.array([[0.3, 0.7],
                  [0.8, 0.2]])
emit = {"fish": [0.7, 0.3], "swim": [0.2, 0.8], "people": [0.9, 0.1]}

def viterbi(words):
    v = start * emit[words[0]]
    back = []
    for w in words[1:]:
        scores = v[:, None] * trans * np.array(emit[w])
        back.append(scores.argmax(0))
        v = scores.max(0)
    path = [int(v.argmax())]
    for b in reversed(back):
        path.append(int(b[path[-1]]))
    return [tags[i] for i in reversed(path)]

for s in ["people fish", "fish swim", "people swim"]:
    print(s, "->", viterbi(s.split()))
Output
people fish -> ['NOUN', 'NOUN']
fish swim -> ['NOUN', 'VERB']
people swim -> ['NOUN', 'VERB']

Fixing spelling

Here's one more classic that you use every day. How does a spell checker work? It generates every word within one edit of the typo, by removing, swapping, replacing, or adding a letter. Then it picks the most common real word. That's counting again. Run it. Four misspellings fixed, using nothing more than word frequencies from some medical discussion posts. A few lines, and it works surprisingly well.

spelling.pypython
import re
from collections import Counter
from sklearn.datasets import fetch_20newsgroups

text = " ".join(fetch_20newsgroups(subset="train", categories=["sci.med"]).data).lower()
freq = Counter(re.findall(r"[a-z]+", text))
letters = "abcdefghijklmnopqrstuvwxyz"

def edits(w):
    split = [(w[:i], w[i:]) for i in range(len(w) + 1)]
    gone = [a + b[1:] for a, b in split if b]
    swap = [a + b[1] + b[0] + b[2:] for a, b in split if len(b) > 1]
    sub = [a + c + b[1:] for a, b in split if b for c in letters]
    add = [a + c + b for a, b in split for c in letters]
    return set(gone + swap + sub + add)

def fix(w):
    if w in freq:
        return w
    return max((e for e in edits(w) if e in freq), key=freq.get, default=w)

for w in ["docter", "medecine", "paitent", "helth"]:
    print(f"{w:<10} -> {fix(w)}")
Output
docter     -> doctor
medecine   -> medicine
paitent    -> patient
helth      -> health

Summary

Viterbi finds the best path, and counting fixes spelling. Those classics still run all around you. Next, back to Scribe, and a very practical job. Pulling out the important words and the key points from a long document, with no language model at all.

Key takeaways
  • Context resolves ambiguity
  • Viterbi keeps the best path
  • Spell check: edits plus counts
Next up: Keywords and Extractive Summaries — finding the key points