本文主要是对How to Write a Spelling Corrector部分翻译,详情请访问该网站。希望对大家有所帮助。
如果要实现这个功能,你还需要下载big.txt。链接:百度网盘 请输入提取码 提取码:7777
目录
代码
import re
from collections import Counter
def words(text): return re.findall(r'\w+', text.lower())
WORDS = Counter(words(open('big.txt').read()))
def P(word, N=sum(WORDS.values())):
"Probability of `word`."
return WORDS[word] / N
def correction(word):
"Most probable spelling correction for word."
return max(candidates(word), key=P)
def candidates(word):
"Generate possible spelling corrections for word."
return (known([word]) or known(edits1(word)) or known(edits2(word)) or [word])
def known(words):
"The subset of `words` that appear in the dictionary of WORDS."
return set(w for w in words if w in WORDS)
def edits1(word):
"All edits that are one edit away from `word`."
letters = 'abcdefghijklmnopqrstuvwxyz'
splits = [(word[:i], word[i:]) for i in range(len(word) + 1)]
deletes = [L + R[1:] for L, R in splits if R]
transposes = [L + R[1] + R[0] + R[2:] for L, R in splits if len(R)>1]
replaces = [L + c + R[1:] for L, R in splits if R for c in letters]
inserts = [L + c + R for L, R in splits for c in letters]
return set(deletes + transposes + replaces + inserts)
def edits2(word):
"All edits that are two edits away from `word`."
return (e2 for e1 in edits1(word) for e2 in edits1(e1))
输出如下
如何实现:需要一点儿概率理论
correction(w)尝试选择对w来说最有可能正确的拼写。因为没有办法100%保证改正的单词是正确的(例如,“lates”应该被改正为“late”、“latest”或者“lattes”,还是别的单词?),所以要使用概率。我们尝试根据原始单词w,从所有候选单词中,找到可能性最大的单词c。
根据贝叶斯公式,等于:
因为P(w)对所有可能的候选单词c来说是一样的(后面会有解释),我们得到如下式子:
这个公式的四个部分:
- 选择机制:argmax——我们选择总计可能性最高的候补单词。
- 候选模型:c ∈ candidates——告诉我们考虑哪些候选单词c。
- 语言模型:P(c)——在英语文本中单词c出现的可能性。例如,“the”在文本中出现的概率为7%,所以P(the)= 0.07。
- 错误模型:P(w|c)——当作者表达单词c时,打出单词w的概率。例如,P(teh|the)相对来说挺高的,但是P(theeexyz|the)就相对较低。
一个明显的问题是:为什么将P(c|w)替换为P(c)P(w|c)这样更麻烦的形式。假如有一个拼错的单词w="thew",和两个候选单词c="the"以及c="thaw"。哪一个有更高的P(c|w)呢?“thaw”似乎更高,因为它仅将“a”改成了“e”,这是一个小改变。另一个方面,"the"似乎也挺高,因为它是一个非常常见的单词,但是增加一个“w”似乎是更不可能犯的错误,或许打字人的手指从“e”划开点到了旁边的“w”。关键在于评估P(c|w),我们必须考虑c的可能性和从c改变到w的可能性。
译者在错误模型的详细叙述中谈谈对P(w|c)的理解。
python实现
选择机制:
使用python中的max函数和key关键字参数实现 'argmax'。
候选模型:
首先来一个新概念:一个单词的简单修改(仅修改一处)是删除(去掉一个字母)、换位(交换邻近字母位置)、替换(将一个字母换成其他字母)或者增加(增加一个字母)。函数edits1返回所有简单修改的字符串(不管是不是单词):
def edits1(word):
"All edits that are one edit away from `word`."
letters = 'abcdefghijklmnopqrstuvwxyz'
#将单词分成两部分,列表装的是将单词分成两部分的各种分法
splits = [(word[:i], word[i:]) for i in range(len(word) + 1)]
#删除一个字母的所有情况
deletes = [L + R[1:] for L, R in splits if R]
#相邻字母乱序的所有情况
transposes = [L + R[1] + R[0] + R[2:] for L, R in splits if len(R)>1]
#一个字母被替换的所有情况
replaces = [L + c + R[1:] for L, R in splits if R for c in letters]
#增加一个字母的所有情况
inserts = [L + c + R for L, R in splits for c in letters]
return set(deletes + transposes + replaces + inserts)
对于一个有长度n的单词来说,会有n种情况的删除,n-1种情况的换位、26n种情况的替换和26(n+1)种情况的增加。总共有54n+25种情况的修改。例如:
,
然而,如果我们限制不认识的单词——通过字典,长度会缩小:
我们也考虑到改正需要两个简单修改(two simple edit)。这个函数产生了更大的返回量,但是其中真正的单词只有很少一部分,像这样:
我们说edits2(w)的结果距离w有两个修改距离(即修改两处)。
语言模型:
我们可以估计一个单词的可能性,通过计算一个含有一百万个单词的文本文件中一个单词的数量,这个单词文本是big.txt。它是公共领域书里的摘录和一张常用单词表,分别来自Project Gutenberg和British National Corpus。函数words,并将其小写并提取文中的单词。变量WORDS有一个计数器,记录每个单词出现次数,返回的是一个字典。P在这个计数器的基础上,估计每个单词的可能性。如下图:
我们可以看到有32192个不同的单词,一共出现了1115504次。其中“the”是最常见的单词,出现了79808次(7%的可能性)。而其他的单词概率就没有这么高。
错误模型:
按照优先级顺序生成第一个非空的候选人列表:
- 已知的原始单词;否则
- 元组中的单词距离原始单词有一个修改长度;否则
- 元组中的单词距离原始单词有两个修改长度;否则
- 未知的原始单词
这个顺序是根据作者写的一个拼写错误模型得出的。
顺带一提,这个结论是根据作者的拼写错误模型得出的 。
看到这里,我认为所有一个修改长度的拼写错误发生的概率都是相等的,同理两个修改长度的拼写错误发生的概率也是相等的。
从P(teh|the)>P(theeexyz|the)来看,一个修改长度的拼写错误发生概率大于两个或多个修改长度的拼写错误发生概率。而对于一个被改正的字母组合来说,如果找得到距离它一个修改长度的正确单词,那么就在P(w|c)的条件下得到了candidatesP(c)。在其中找到频次最高的单词就是argmaxc。
如有理解有误的地方,请各位指教。
总结
后面的还有“发展”、“未来”两个板块,有兴趣的朋友可以自行查看。对于我来说,这个纠错器还是在计算正确单词在文中的概率。P(w|c)没办法去衡量,首先正确单词没办法确定,其次很难估计w的概率。