用快速使用Python写一个拼写检查器

来源:互联网 发布:养成游戏源码 编辑:程序博客网 时间:2024/05/07 16:21

简单介绍

拼写检查器是个非常常用的工具,现在几乎所有涉及到搜索的网站都已经实现了这个功能。那拼写检查器是不是很复杂呢?不是的,只要你明白一点贝叶斯公式的原理,你就能写出一个拼写检查器来。

什么是贝叶斯公式

贝叶斯定理是关于随机事件A和B的条件概率的一则定理。
P(c|w) = P(w|c) P(c) / P(w)

P(c|w):指的是w情况下c发生的概率
P(w|c):指的是已知c发生后,发生w的概率
P(c) , P(w):分别指的是c,w独立发生的概率。

这个公式用处非常广泛,概率论和统计学都离不了它。
那么在拼写检查器中贝叶斯公式起什么作用呢?
很简单,我们可以把 P(c|w)看成本来要输入c结果输入成w的概率。现在按照贝叶斯公式的展开,这个概率等于P(w|c) P(c) / P(w)
其中P(c) , P(w)分别为c和w两个单词在语料库中出现的概率,要求出任何一个单词出现的概率其实很简单,只要通过大量的语料训练就可以找出来了。而在这个地方,p(w)还可以省略掉,应为我们并不是要确切的求出一个概率来,我们只需要一个概率来帮助我们判断出哪个词是最可能正确的。而在一次输入错误中,p(w)是恒定的,所以可以省略。P(w|c) 则比较麻烦,它表示在拼写c的情况下,出现拼写错误w的概率。这里就需要概率学做理论支撑了。一般来说,看起来越像的单词拼错的概率越大,而一个单词有多少种拼错的可能呢?无非就少拼,多拼,位置不对,拼错四种可能。

love 少拼:lov,多拼:lovee,位置不对:loev,拼错:lova

当然现实生活中不一定只出现一个字母内的错误,但如果错误过多则效率则有可能将这个单词错拼为另一正确的单词。这里为了方便开发,我们就暂时允许用户出现小于等于两个字母的错误。这里就完成了我们这个拼写检查的第一部分。

这个方法也有这个方法的局限性,比如出现以下错误时

You wlere a student

程序如何选择出正确的单词呢?是选择where还是were?为了解决这个问题,我们可以设计一个有向图(directed graph),其中的节点(Node)表示单词,而节点之间的edge则表示单词之间的关系。让后通过大量的预料,我们就可以训练出一个可以理解上下文的拼写检查器,提升算法的正确性。

首先要解决的是单词的关系怎么统计,统计单词之间的关系有很多种方式。比较成熟的方式有通过分析句子中单词的词性,统计其前后单词所出现的次数,将这个次数最为距离。或者以这个单词为中心,将它紧埃着的单词作为距离为一的单词,间隔一个的距离为二,以此类推。然后统计距离。但今天我要用一个更简单的方法,仅仅使用一个单词在另一个单词之后出现的次数作为 edge ,这样的方法比较粗糙,但是我们的题目是‘快速‘,这个方法真的很快。

我们使用 networkx 中的 DiGraph 来构造有向图,networkx 是一个用于构造图的 python 扩展包,非常方便,大家可以了解下。然后我选择了《哈利波特和魔法石》,《飘》,《傲慢与偏见》作为预料。第一步就是读取这些预料,然后建立网络。在 networkx 的帮助下,这一步变得非常简单,关键代码只有一行。

ex:love you 在图中是这样表示的:graph[‘love‘][‘you‘]]['DISTANCE']

每出现一次,[‘DISTANCE’]就加一

第二步是写一个生成备选词的函数,前面已经提过,一的单词在一个字母内的拼写错误只会有四种情形,现在我们只需要枚举在这四种情形中出现的所有单词就好了。

def edit_distance_one(word):    alphabet = 'qwertyuioplkjhgfdsazxcvbnm'    splits = [(word[:i], word[i:]) for i in range(len(word) + 1)]    deletes = [a + b[1:] for a, b in splits if b]    transposes = [a + b[1] + b[0] + b[2:] for a, b in splits if len(b) > 1]    replaces = [a + c + b[1:] for a, b in splits for c in alphabet if b]    inserts = [a + c + b for a, b in splits for c in alphabet]    return set(deletes + transposes + replaces + inserts)

但是这样枚举出来的单词未必正确,我们还需要构造一个判断这个单词是否存在的函数,也很简单,就是读入一个单词表,然后看所给的单词在不在单词表中。

data 即为单词表return set(w for w in words if w in data)

到这里,前期准备已经基本完成了,现在要做的就是补充流程。
第一步,输入一句话,将其拆分成有独立单词构成的数组。
第二步,遍历这个数组,如果数组中的单词存在,返回垓单词。不存在,返回该单词的候选词。
第三部,返回候选词序列之间的笛卡尔积,返回distance最大的组合。

到这里,我们的spell corrector就算完成了,完整代码戳这里。目前的拼写检查器,虽然简单,但是也是可以用的。如果你想自行在提升一下精度,我这里还有几个建议。
1,扩充我们的语料库,三本书还是有点少。
2,考虑到构词法,比如形容词后缀,副词后缀之类的。
3,采用再燃语言识别的方法,考虑词性以及句子的语法结构。这样不仅能找出错误的词,甚至还能找出语法错误。

0 0
原创粉丝点击