朴素贝叶斯算法——拼写检查器

Posted 刘小绪同学

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了朴素贝叶斯算法——拼写检查器相关的知识,希望对你有一定的参考价值。

贝叶斯分类是一类分类算法的总称,这类算法都以贝叶斯理论为基础,统称为贝叶斯分类。这里我们讨论的是贝叶斯分类中最简单的一种:朴素贝叶斯分类。(不知道为啥,写到这里想起了“素面朝天毕淑敏”)。

贝叶斯理论

    一般看到像拉格朗日中值定理、帕斯卡三角、洛必达法则等等,自然而然的就知道后面其实是个人,这里也不例外,贝叶斯也是个数学家。

    贝叶斯为了解决逆向概率的问题,写了一篇文章(贝叶斯理论),但是在那个年代,好东西不一定立马就被大家认可,不然哥白尼的下场也会好一点了。

我们现在有一个数据集,它由两类数据组成,
用 p1(x,y) 表示数据点 (x,y) 属于类别 1的概率,
用 p2(x,y) 表示数据点 (x,y) 属于类别2的概率,
那么对于一个新数据点 (x,y),可以用下面的规则来判断它的类别:

如果 p1(x,y) > p2(x,y) ,那么类别为1
如果 p2(x,y) > p1(x,y) ,那么类别为2

也就是说,我们会选择高概率对应的类别。
这就是贝叶斯决策理论的核心思想,即选择具有最高概率的决策。

    正向概率:比如我们现在知道学校的学生总数,也知道学校有多少男生,随机抽一个学生,计算是男生的概率有多大。

    逆向概率:现在我们事先不知道男女生的比例,而是通过随机抽一个(或多个)学生,通过观察抽出来的学生,来推测学校男女比例。

贝叶斯公式推导

    现在刚刚高考完,再过 10 天左右又该填报志愿了。假设今年参加高考的总数为 U,其中能考上大学的学生中,南方占比为 P(南方),北方占比 p(北方)。

    那么南方能上大学的学生就为:

U X P(南方) X P(上大学|南方)

    北方的准大学生就为:

U X P(北方) X P(上大学|北方)

    全国的准学生总数为:

[U X P(南方) X P(上大学|南方)] + [U X P(北方) X P(上大学|北方)]

    现在的问题变了,我们要算的是上大学的南方学生占比多少?

P(南方|上大学) = [U X P(南方) X P(上大学|南方)] / 全国准大学生总数



朴素贝叶斯算法——拼写检查器

    把上面的过程泛化一下,表现的更简洁一点,就变成了下面的样子,应该学过高中数学的人对这个式子都很熟悉,是不是有一种确认过眼神,这就是对的算式的感觉。

朴素贝叶斯算法——拼写检查器

拼写检查器

    相信很多人在使用搜索引擎或者在一些网站上面填写表单的时候,系统会自动的给你提示,有的提示是自动补全,有的提示是改正了你的错误。这里就用朴素贝叶斯实现一个超简单的拼写检查器。

    下面要实现的就是输入一个错误的单词 w,找到一个正确的单词 c 使得 P(c|w) 最大,那么这个单词c可能就是用户真正想要输入的单词。由前面所推导的公式可以知道:

朴素贝叶斯算法——拼写检查器

    具体实现如下所示,写的很简单,只做练习,了解基本原理够用了,实际上就是在已知的单词中找到一个正确的单词使得 P(c|w) 最大。

class SpellCheck():
   """
   P(c|w) = P(w|c)P(c) / P(w)
   P(c):文章出现正确拼写词c的概率,程序中直接用词频表示
   P(w|c):用户把词c错敲成w的概率
   """

   alphabet = list('abcdefghijklmnopqrstuvwxyz')

   # 读取数据
   def get_data(self):
       fp = open('big.txt', 'r', encoding='utf-8')
       return fp.read()

   # 只拿文本中的单词
   def get_words(self):
       text = self.get_data().lower()
       return re.findall('[a-z]+', text)

   # 词频统计,返回的是词语和出现的次数
   def train(self):
       result = collections.defaultdict(lambda: 1)
       for word in self.get_words():
           result[word] += 1
       return result

   def edit_first(self, word):
       """
       只编辑一次就把一个单词变为另一个单词
       :return: 所有与单词word编辑距离为1的集合
       """

       length = len(word)
       return set([word[0:i] + word[i + 1:] for i in range(length)] +  # 删除一个字母
                  [word[0:i] + word[i + 1] + word[i] + word[i + 2:] for i in range(length - 1)] +  # 调换两个字母
                  [word[0:i] + c + word[i + 1:] for i in range(length) for c in self.alphabet] +  # 修改一个字母
                  [word[0:i] + c + word[i:] for i in range(length + 1) for c in self.alphabet])  # 插入一个字母

   def edit_second(self, word):
       """
       :return: 编辑两次的集合
       """

       words = self.train()
       return set(e2 for e1 in self.edit_first(word) for e2 in self.edit_first(e1) if e2 in words)

   def already_words(self, word):
       """
       :return: 返回已知的和错误单词相近的正确单词集合,允许进行两次编辑
       """

       words = self.train()
       return set(w for w in word if w in words)

   def check(self, word):
       words = self.train()
       neighborhood = self.already_words([word]) \
                      or self.already_words(self.edit_first(word)) \
                      or self.already_words(self.edit_second(word)) \
                      or [word]
       # 取概率最大的正确单词,即词频最多的
       return max(neighborhood, key=lambda w: words[w])


if __name__ == '__main__':
   s = SpellCheck()
   print(s.check('learb'))

    简单测试了一下,假设用户本来想输入单词learn,但是不小心打成了learb,上面的程序很巧妙的就把learb改成了learn

写在最后

    上面只是一个简单的案例,拼写检查现在用的地方很多,可能更多的是听说使用朴素贝叶斯实现垃圾邮件过滤,也可以用它来做推荐。

    需要注意的是,朴素贝叶斯的重要的假设,那就是特征之间是相互独立的,比如我们中的出现的概率与这两个字相邻没有任何关系,这一点就是朴素的含义,另外一个假设是,各个特征是同等重要的,即权重相同。

推荐阅读









参考内容:https://github.com/apachecn/MachineLearning/blob/master/docs/4.朴素贝叶斯.md


以上是关于朴素贝叶斯算法——拼写检查器的主要内容,如果未能解决你的问题,请参考以下文章

利用朴素贝叶斯实现拼写纠错器

机器学习实战3:基于朴素贝叶斯实现单词拼写修正器(附Python代码)

机器学习:基于朴素贝叶斯实现单词拼写修正器(附Python代码)

机器学习——朴素贝叶斯算法

机器学习贝叶斯算法详解 + 公式推导 + 垃圾邮件过滤实战 + Python代码实现

朴素贝叶斯分类器原理