Live data from Hacker News

How to Write a Spelling Corrector (2007)

norvig.com

11–20 of 25 posts

Re: How to Write a Spelling Corrector (2007)

#12

I've had a thought and am curious how people would solve it. Sometimes, if you copy words off a PDF lecture slide, all the words are mashed together (eg. Hello Foo bar → HelloFoobar). Is this an AI domain or can it solved by simple programming?

It's worth noting that different PDF-to-text tools (and different PDF-display tools that have text-copying) get different results and it can be worth trying a few. For the most part, the vector parts of a PDF and the association with the text is sufficient to discover spacing information.

Re: How to Write a Spelling Corrector (2007)

#14

I've had a thought and am curious how people would solve it. Sometimes, if you copy words off a PDF lecture slide, all the words are mashed together (eg. Hello Foo bar → HelloFoobar). Is this an AI domain or can it solved by simple programming?

It's called word segmentation. There are libraries for it:

https://github.com/grantjenks/python-wordsegment https://github.com/InstantDomain/instant-segment (rust)

I used the latter to process pdf plain text output quite successfully.

Re: How to Write a Spelling Corrector (2007)

#15
One of the more interesting parts of the post, for me, is the list of implementations in other languages, including: one for Clojure written by that language's author, Rich Hickey; an interesting one in R that clocks in at 2 lines (with a longer, more readable version further down in the linked post); and one written in functional Java. The first one in Awk is also interesting.

Re: How to Write a Spelling Corrector (2007)

#16

I've had a thought and am curious how people would solve it. Sometimes, if you copy words off a PDF lecture slide, all the words are mashed together (eg. Hello Foo bar → HelloFoobar). Is this an AI domain or can it solved by simple programming?

I got nerd-sniped and wrote a simple Python solver for this problem. You can find the ngram files at Norvig's site (https://norvig.com/ngrams/).

    import collections
    import math
    import heapq

    with open('count_1w.txt', 'r') as f:
        unigrams = [l.split() for l in f]
    unigram_map = collections.defaultdict(lambda: 0)
    for word, count in unigrams:
        unigram_map[word] = int(count)
    with open('count_2w.txt', 'r') as f:
        bigrams = [l.split() for l in f]
    bigram_map = collections.defaultdict(lambda: collections.defaultdict(lambda: {}))
    for word0, word1, count in bigrams:
        bigram_map[word0][word1] = int(count)
    log_p_unseen = collections.defaultdict(lambda: 0.)
    for word0, counts in bigram_map.items():
        for word1, count in counts.items():
            unigram_map[word1] += count
        total = sum(counts.values())
        #smoothing for unseen words
        mn, mx = min(counts.values()), max(counts.values())
        if mn == mx:
            p_unseen = 0.5
        else:
            #geometric series approximation
            r = (mn / mx) ** (1. / (len(counts) - 1))
            n = mn * r / (1. - r)
            p_unseen = n / (n + total)
        log_p_unseen[word0] = math.log(p_unseen)
        c = (1. - p_unseen) / total
        bigram_map[word0] = {word1: math.log(c * count) for word1, count in counts.items()}
    c = 1. / sum(unigram_map.values())
    unigram_map = {word: math.log(c * count) for word, count in unigram_map.items()}
    max_len = max(map(len, unigram_map))

    def optimal_parse(text):
        word_spans = {j: [] for j in range(len(text) + 1)}
        for i in range(len(text)):
            for j in range(i + 1, min(i + max_len, len(text)) + 1):
                if text[i:j] in unigram_map:
                    word_spans[i].append(j)
        min_cost = collections.defaultdict(lambda: float('inf'))
        parent = {}
        queue = [(0., 0, 0)]
        while queue:
            cost, i, j = heapq.heappop(queue)
            if cost > min_cost[(i, j)]:
                continue
            if j == len(text):
                break
            if j == 0:
                word0 = ''
            else:
                word0 = text[i:j]
            for k in word_spans[j]:
                word1 = text[j:k]
                if word1 in bigram_map[word0]:
                    word1_cost = -bigram_map[word0][word1]
                else:
                    #It would technically be more correct to normalize the unigram probability only over unseen words.
                    word1_cost = -(log_p_unseen[word0] + unigram_map[word1])
                cost1 = cost + word1_cost
                if cost1 

Re: How to Write a Spelling Corrector (2007)

#17
One of my most common spelling mistakes is physical mistypes on the keyboard, yet no spell checker seems to account for keyboard layout and locality of keys, or for something like my hand being one position off on the board but typing all the keys relatively correct only positionally shifted.

Re: How to Write a Spelling Corrector (2007)

#18

I've had a thought and am curious how people would solve it. Sometimes, if you copy words off a PDF lecture slide, all the words are mashed together (eg. Hello Foo bar → HelloFoobar). Is this an AI domain or can it solved by simple programming?

Short answer in Python: https://stackoverflow.com/questions/195010/how-can-i-split-m...

This version assumes non-dictionary "words" have probability 0. But it's the same basic idea as a fancier answer, and it's quick.

Re: How to Write a Spelling Corrector (2007)

#19
post #3

And after you've read that, here's a related blogpost: "A Spellchecker Used to Be a Major Feat of Software Engineering" [0], because Python being "fast enough" and having enough memory for large dictionaries hasn't always been the case. [0] https://prog21.dadgum.com/29.html

I'll repeat my feat in this area: a spelling corrector that had 32kB memory (because it was tied to a keyboard trap, or something like that) and could do four 8kB reads from CD-ROM before telling the user. It contained not only orthographic similarity (the actual letters), but also phonetic similarity. You rarely see that in older English spell checkers, because it's such an irregular language, but for other languages it's quite informative, although in extremely regular languages, such as Spanish, you could also put it in the weighting/probability model (e.g. vb is a pretty common confusion).

The CD-ROM spelling corrector was not really great, BTW, but at least it replied in 1s on a typical end-user PC.

Edit: this was late 1980s.

Post reply on HN