How to write a spelling corrector (2016)
norvig.com
norvig.com
Eye halve a spelling chequer It came with my pea sea It plainly marques four my revue Miss steaks eye kin knot sea.
Eye strike a quay and type a word And weight four it two say Weather eye am wrong oar write It shows me strait a weigh.
As soon as a mist ache is maid It nose bee fore two long And eye can put the error rite It's rare lea ever wrong.
Eye have run this poem threw it I am shore your pleased two no It's letter perfect awl the weigh My chequer tolled me sew.
I found it difficult to parse the initial couple of lines because I was constantly attempting to read by attaching meaning to the spellings: "Eye halve" is a bit frightening in that sense. But then I realised I can read the text as sounds and almost ignore the spellings. Listening to what the sounds made in my head allowed for a much faster pace of comprehension because I didn't have to keep correcting myself.
This poem demonstrated for me that it is possible to read and listen simultaneously, and that I don't usually do that. It seems that it would probably add to the experience of other poems to read them in this manner.
It plainly marques four my revueClearly the process was automated. Ingenious program. More complex than mere homophone substritution... it works syllable by phonetic syllable to string together words that, when spoken, sound pretty dang close to the original.
Naive spelling [auto]correctors correct by text distance, because people almost always make mistakes in text input by typing the right words but making the wrong motions to do so.
Slightly-less-naive autocorrect takes this approach further, and understands that e.g. "yjr" should become "the" because it's the same letters all shifted over by one.
And, as in the submitted article, the best autocorrectors just look at the whole sentence context and try to predict what word the input "should" have been given what it looks like—this is essentially a kind of compressed sensing (like fMRIs use!), though in practice it tends to be baked down to something like markov chains of levenstein automata with back-propagation on w>n.
...whereas this poem is more like the result of a very naive speech to text algorithm, one which parses each word independently without the context of the sentence-so-far.
(Side-note: I'm surprised that speech-to-text algorithms still work mostly in "real time" with only a limited buffer, unable to go back and change anything more than a few words in the past. It's why they fail to recognize names, for example. If speech-to-text algorithms would buffer the entire audio stream for a dictated document, showing an estimate of the output text so far, but continuing to re-estimate the entire document after every word/sentence/paragraph, they'd perform much better. The difference would be on the level of a standard web-renderer's line-at-a-time reflow algorithm, vs. TeX's whole-document reflow.)
https://github.com/wolfgarbe/SymSpellCompound/blob/master/RE...
I'm certainly not knocking that achievement, but the two issues I ran into fairly quickly were: 1. It (currently) doesn't handle words that are genuine words but which are contextually wrong 2. You need a high quality dictionary that's also well aligned with your domain or you'll have poor corrections (this last point is merely a matter of effort, so less of a concern)
An here a benchmark between Norvig's spelling corrector, BK-tree and SymSpell: https://towardsdatascience.com/symspell-vs-bk-tree-100x-fast...
While it's clever and concise, in terms of raw speed, indexing your vocabulary in a Trie, and then doing a traversal on it using a Levenshtein distance is way faster[2]. By using a trie, you eliminate lot of unwanted look-ups that Peter Norvig's brute-force approach takes.
The other interesting thing for me personally about this problem is that there is a statistical angle as well. You will often find words that are of the same edit distance from a given target word -- you will need to rely on some form of "popularity metric" to rank those tied corrections. E.g. how many times did people type this word and then change it to another word - that's precisely the kind of data that Google has and which makes its search suggestions and spell checking so intuitive.
[1]: https://typesense.org/ [2]: http://stevehanov.ca/blog/index.php?id=114
fst (finite state transducer) is even smaller. And for some languages like portuguese where a lot of suffixes are extremely common, the size reduction is dramatic!
>The other interesting thing for me personally about this problem is that there is a statistical angle as well. You will often find words that are of the same edit distance from a given target word
Indeed.
http://www.ling.helsinki.fi/~klinden/pubs/PirinenLrec2010.pd...
Especially on search engines, sometimes I am searching for words in another language, mixed-language results, or even doing an intentional search for misspelt words. In my native Dutch his first example would already cause problems: "spelling" means the same as in English, but "speling" means margin/give (the noun) and I wouldn't want one to be auto-corrected to the other. The fuzzy search for names on Facebook makes the search pretty much worthless especially in case you have an unusual variant of a common name in your search - you can be sure the actual person you're looking for is listed after half a billion bogus results.
The whole trend of second-guessing the user and saying "you tried to do A, but we assume you meant B, so we're gonna do B instead" is one of my biggest pet peeves.
My Siri is in English because I figured it was more reliable for everyday us then in French. But my mom's contact is as 'maman', can't use Siri to call her. Or I have to try and pronounce 'maman' with an english accent and hope Siri know what I'm talking about. And that's an easy one, try a name like 'Jean Brentois'.... But I assume setting up Siri in French will lead to the same problem, but with my non French contacts now. I have not tried however.
So obviously I get the challenge, but it makes it very hard to use (I can't use Siri like tools in parts because of this). Especially since so many of my contacts are international.
EDIT: a quick fix for contact pronounciation might be to ask the user how he pronounces a particular contact when adding it.
Similarly, why not allow for a special syntax allowing for multiple languages in a sentence ? Say my first language is English, my second is French, and my third is Spanish. Why not something like that: "Let's go to the $$casa $demain $avec $toi, what do you think ?"
You could even train it on past messages making it more tailored towards particular user.
A bad part about user-end autocorrection is that the end user is looking only at the case they have in front, not the overall average — if they have a case where it worked very well, they'll start overly trusting it and failing at producing good results. If they have a case where it wasn't useful, they'll be annoyed by it and feel they are doing more than they should. In all cases someone is going to be unhappy.
If I can't expect all ends to understand the value I'd mostly just look for pipelines where a-c could be an internal process and there was no need for the data provider and end user to be aware that it's happening. But then, ascertaining this is a problem on its own.
assert len(WORDS) == 32192
assert sum(WORDS.values()) == 1115504
assert WORDS.most_common(10) == [
('the', 79808),
('of', 40024),
('and', 38311),
('to', 28765),
('in', 22020),
('a', 21124),
('that', 12512),
('he', 12401),
('was', 11410),
('it', 10681)]
assert WORDS['the'] == 79808
Those aren't testing the file open, or Counter, or read, but instead are tightly-coupling the tests to the exact corpus. The code really should have not hard-coded the corpus, and the tests should have supplied a smaller corpus of known length where each word's frequency+probability was obvious from inspection.There's another overfitted test just before this:
assert Counter(words('This is a test. 123; A TEST this is.')) == (
Counter({'123': 1, 'a': 2, 'is': 2, 'test': 2, 'this': 2}))
What is this verifying? We know how a Counter works, we don't need to test it; and the previous test already established the correctness of words().I see a lot of junior engineers following this style of tightly-coupled, overfitted tests, and seen how a few years of growth can lead to tens of thousands of tests whose primary value is to provide Amazon with a predictable stream of AWS revenue (as we run all th tests) & make the engineer feel better for writing tests.
Since you speak of value, the value Peter Norvig is trying to provide is making readers understand the principles, he's not trying to provide some monetary value. So I don't think your criticisms apply here, those tests fit the purpose very well.
From that perspective, I agree the tightly coupled unit tests are a bit grating. Though they would not look out of place as doctests.
I think he's trying to show how to approach solving this kind of problem. Juniors will copy it, and will then write tests in a business-logic environment which have dubious value.
The code shows how to solve the problem very well. The tests do not.
Will that be obvious to a junior engineer? (I think that's the target audience for this article)
If I was writing such an article, I would (maybe) call that out as explicit, or (extremely likely) remove those tests so as not to accidentally implicitly recommend overfitted tests as a useful technique.
2. Opine on how blog code is not ready for production.
3. Post to HN.
4. Propser.
Just as a curiosity: I once did some text correction applied to tweets, using word embeddings (word vectors, word2vec), which are a language model too. In real life contexts, where you don't limit yourself to a dictionary, it's really interesting because word vectors can include many foreignisms and other "incorrect" words that you might want to keep in the text.
The article also mentions edit distances, the distances between words (in this case, the "incorrect" word and its possible corrections). In general, algorithms are based on common errors at the keyboard. Missing or adding a letter, transposing two of them, etc. Algorithms like Damerau-Levenshtein (pretty much the one applied in the article, even though it's not explicitly mentioned I think, and there are many variations) keep it simple by doing this, but it's interesting to notice that you can also fine-tune distances by considering which keys are closer to other keys in the keyboard (it's easier to accidentally type "a" instead of "s" in qwerty than "u"), but when doing more general text correction you can also use phonetic distances or even more sophisticated techniques like finite-state transducers that apply language-specific rules to suggest corrections. (I know the article is only meant as an introduction, but thought someone might find it interesting)
i.e. the amount of times I cant spell a word, and MS Word has no idea what I'm trying to say, so I "copy and paste" the wrong word into Google and it instantly knows what I meant and shows me the correct spelling.
I appreciate part of that is probably related to my previous searches on Google and it guesses based upon my history - but I'd be ok with using this for "good" like my spell checking.
Install one of the interpreters at the bottom of the page, then download the "story" file and open it in said interpreter.
especially for all the oldheads around here, if you ever played Zork or Hitchhiker's Guide or whatever, give it a shot. it's the same style, just as funny, with helpful features to make it far less frustrating than the old games could be, and it really takes advantage of not having to run on a TRS-80.
There's a spoiler-filled post about the design of the puzzles here. https://emshort.blog/2013/01/24/making-of-counterfeit-monkey...
It won Best Game, Best Setting, Best Puzzles, Best Individual Player Character, and Best Implementation in 2012! http://www.ifwiki.org/index.php/Counterfeit_Monkey There's a nod to this game in https://xkcd.com/1975/ Right-click the image, go to games -> advent.exe and start exploring :)
Had a pleasant surprise when Matthias Felleisen responded to a request on the Racket mailing list and practically rewrote the Racket version.
Also got to learn about the HAT-trie[1] data structure.
The author is an English speaker, internal to an English community, writing about spell checking.
Its unreasonable to wish every group on the planet to factor in the concerns of every other group, or add their group identifier to everything they write.
P(c|w) = P(c)P(w|c)/P(w),
where c is a correction, and w is the original word.
The author does implicitly talk about this when he explains that P(c|w) conflates the two factors, but it's also not that hard to see that getting a handle on P(w) -- the probability space of misspellings -- is harder than getting a hold of P(c) -- the probability space of actual words, and Bayes lets us get rid of the former during optimization.
https://en.wikibooks.org/wiki/Clojure_Programming/Examples/N...
Which function were you thinking were explicitly added to the language that are in use here?
Outside ;)
- Couldn't determine the exact date of that article
- Previous discussion here: https://news.ycombinator.com/item?id=12453535
I really enjoyed the code, it is incredibly clever and idiomatic.
I actively tried to avoid using libraries, and ended up writing most of it in vanilla JavaScript - including a bunch of DOM manipulations, which was suicide at the time. I even wrote my own XML-based DSL for defining the input text.
Would have loved to actually follow through with the research but phd's don't pay bills.
Is there an api service for these type of text analysis?
That kind of speaks volumes about modern academics.
It can't even make a suggestion when there is only one missing character.
I'm tempted to wonder whether speech-to-text is actually saving me enough time after all the fixing I need to do now to be worth it. It's more than a little frustrating.
elipsis (ellipsis), elipses (ellipses), independant (independent), disserning (discerning)...
How can it not find the correct word?
But yeah, I tackled the same problem (for Flickr tags) and did not at first use the "obvious" algorithm; I did something slower and suboptimal.
Most elegant code I've actually seen, and can see the lisp thinking apparent on the style.
I also recommend his Sudoku solver: http://norvig.com/sudoku.html, and his XKCD regex golf code: http://nbviewer.jupyter.org/url/norvig.com/ipython/xkcd1313.....