简单的朴素贝叶斯算法实现英文文本分类(Python实现)

简单的朴素贝叶斯算法实现英文文本分类(Python实现)


前言

本文主要记录一下我完成人工智能课的一个作业的过程,会对算法和实现的思路稍作讲解。由于本人水平不是很高,所以只是实现了一个非常简陋的贝叶斯文本分类器,泛用性并不高,如有错误也欢迎大家指出。
首先来简要介绍一下朴素贝叶斯算法:
**朴素贝叶斯算法(Naive Bayes)**是一种基于概率论和贝叶斯算法来预测文本的分类(如新闻或客户评论)的概率算法。贝叶斯算法将文本概率化——这种算法计算给定文本为每种分类的概率,然后输出具有最高概率值的分类。

作为初学者,我将使用最简单的多项式朴素贝叶斯算法来完成这次文本分类。

一、朴素贝叶斯算法的简要介绍

本文将通过这个例子来简要介绍朴素贝叶斯算法:1

假设我们正在构建一个分类器,该分类器将会判断文本是否与体育有关。我们的训练数据有以下5句话:
text为文本,tag为文本的类别,这里简单的分为运动类和非运动类
我们将根据这五句话的分类,来判断“A very close game”这个句子应该被分为哪一个类别。
作为一个概率分类器,我们要计算“A very close game”一句属于体育(Sports)类别的概率,以及它不是体育(Not sports)的概率。计算完毕后,我们将会把他分类到概率值更大的一类中。而朴素贝叶斯算法的根据是贝叶斯定理:
这个公式的意思大概是,在该句子是 “A very close game”的先验前提下,句子的分类为Sports的概率,如果有想要更深入的了解,请各位自行百度贝叶斯定理。

1.主要步骤

要计算出这个概率,我们只需要以下简单的几步:

(1)创建你的特征——数字化你的要素

要利用一个公式进行数学计算,我们首先需要的就是数字,但是对于一个文本来讲,我们并没有直接的要素可以得到,所以我们首先要创建一个可以由数字表达同时可以体现出文本信息的特征
对于英文来说,数字化文本信息最简单的一种方法就是计算一段文本中各个词语的词频。也就是说,我们可以简单地忽略掉单词的顺序和句子的结构,将每段文本粗暴地看做一组单词的集合。于是我们提炼的特征将是每个单词出现的次数。这种方法看起来非常简单粗暴,然而效果却出奇的好。

(2)由贝叶斯定理得到概率公式

事实上,在我们的数据中,我们没有办法直接得到P(sports|“A very close game”),但是贝叶斯定律提供了一种计算条件概率的方法:在这里插入图片描述
所以根据这个公式,我们可以把我们的P(sports|“A very close game”)转化为:
在这里插入图片描述
由于sports和notsports的计算式中的分母相同,因此我们在比较时可以舍弃除数,然后直接进行比较,即:在这里插入图片描述
这两个式子中的概率我们都可以直接根据我们的数据得到,如P(a very close game|Sports)只需计算“A very close game”一句出现在“Sport”分类中的次数,再除以总数即可,P(Sports)只需计算sports类型的文本除以文本的总数即可。至此,我们所需的数据就全部得到了。
不过仍然存在一个问题:我们的训练数据中没有出现“A very close game”,因此直接计算的话,我们计算出的概率将为零。除非我们要分类的每个句子都出现在我们的训练数据中,否则该模型将不会非常有效,我们需要一个泛用性更加强大的模型。

(3)将模型朴素化

于是,朴素化的模型应运而生:我们假定句子中的每个单词都独立于其他单词。 这意味着我们不再查看整个句子,而是查看单个单词。
根据前文思想,我们可以写出这个公式:在这里插入图片描述
事实上,这个假设确实十分有效,同时使得朴素贝叶斯模型能够很好地处理少量数据与可能分类错误的数据。接下来就是将其应用于我们之前的工作:
在这里插入图片描述
这样一来,所有的单词实际上都在我们的训练数据中出现了几次,于是我们便可以计算出概率来了。

(4)计算最终概率

至此,我们只需要计算每种分类(Sports与not sports)的概率,然后看看哪个概率更大。
我仅仅取用了训练数据中的数据来进行计算。
首先,我们计算每个类别的先验概率:对于我们训练数据中的给定句子,其为Sports
P(Sports)的概率为3/5。而P(Not sports)为2/5。
然后,计算 ,即计算“game”一词在Sports的文本中出现的次数(2次)除以Sports文本中的单词总数(11次)。 因此,可以得到:
在这里插入图片描述

这样下去我们又会遇到一个问题:“close”没有出现在任何Sports文本中。这样计算的话该概率将是0。而我们最终的式子需要几个概率相乘:
在这里插入图片描述
这样的话我们之前的努力就会因为一个不曾出现的单词而白费。
不过拉普拉斯平滑法(Laplace smoothing)可以很好的解决这个问题:即我们为每个计数加1,这样一来他们永远不会为零。为了平衡这一点,我们将可能的单词的数目添加到除数中去(训练样本中出现的单词数。请注意:如果某个特定单词在训练样本中出现多次,则该单词的数目将被计为1次)。因此除法的值永远不会大于1。在我们的数据中,可能的词是:
[‘a’,‘great’,‘very’,‘over’,‘it’,‘but’,‘game’,‘election’,‘clean’,‘close’,‘the’,‘was’,'forgetable‘,“match”]。
由于所有可能的单词数是14,因此应用平滑处理可以得出:
在这里插入图片描述
最终得到的结果是:
在这里插入图片描述
这样就只剩最后一步了,现在我们将所有概率计算出来,看看哪个个概率更大:
在这里插入图片描述
可以看到,0.0000276>0.00000572,故而我们的分类器将文本"A very close game"分类为与Sports相关的文本了。

二、Python代码实现

(1)源代码:

'''计算单词在tag类中出现的次数'''
def countWord(word,tag):
    count = 0
    for sentence in tag:
        for w in sentence.split():
            if(word == w):
                count = count + 1
    print(word,":  ",count)
    return count

'''计算文本str属于tag类的朴素贝叶斯概率'''
def caculateNBP(str,tag,Ptag,All,tagNum):
    str = str.lower()
    words = str.split(" ")
    pList = []
    for word in words:
        P = (1+countWord(word,tag))/(All + tagNum)
        pList.append(P)
    NBP = 1
    print(pList)
    for P in pList:
        NBP = NBP * P
    NBP = NBP*Ptag
    return NBP
'''训练数据'''
texts = ["A great game", "The election was over", "Very clean match", "A clean but forgettable game", "It was a close election"]
Sports = ["A great game", "Very clean match", "A clean but forgettable game"]
NotSports = ["The election was over", "It was a close election"]
'''转小写'''
texts2 = [s.lower() for s in texts if isinstance(s,str)==True]
Sports2 = [s.lower() for s in Sports if isinstance(s,str)==True]
NotSports2 = [s.lower() for s in NotSports if isinstance(s,str)==True]
'''计算P(Sports)和P(NOT SPORTS)'''
Psports = len(Sports)/len(texts)
PnotSports = len(NotSports)/len(texts)
str = "100"
allWords = []#所有可能的单词
for sentence in texts2:
    temp = sentence.split(" ")
    allWords = allWords + temp
allWords = set(allWords)
allWords = list(allWords)
lenAllWords = len(allWords)#所有可能的单词数量

numofSports = 0#Sports类文本的单词数
for sentence in Sports2:
    temp = len(sentence.split())
    numofSports = numofSports + temp

numofNotSports = 0#notSports类文本的单词数
for sentence in NotSports2:
    temp = len(sentence.split())
    numofNotSports = numofNotSports + temp

str = "A very close game"
print("计算sports概率:---------------------------------------------")
pSports = caculateNBP(str,Sports2,Psports,numofSports,lenAllWords)
print("P(STR|SPORTS)= ",pSports)
print("计算not sports概率:---------------------------------------------")
pNotSports = caculateNBP(str,NotSports2,PnotSports,numofNotSports,lenAllWords)
print("P(STR|NOTSPORTS)= ", pNotSports)
print("分类结果:   ")
if(pSports > pNotSports):
    print("文本  ",str,"  可以被分为Sports类")
else:
    print("文本  ",str,"  可以被分为Not Sports类")

(2)运行结果截图:

在这里插入图片描述
可以看到,我们计算出来的概率和文中计算的概率完全一致。

总结

以上就是一个简陋的朴素贝叶斯文本分类器的实现。这种方法仅适用于英文文本的分类,对于中文文本来讲,词语并不想英文那样分明,我们可能需要一个词库来进行进一步的处理。然而即便是尽可能地使用了最简化的算法,我的程序也非常的不完善,现在主要的文本分类可能都是用文件夹/文件来存储文本并分类,亦或许是从网页中爬取数据分类而不是像我这样直接存到列表里。再如我这个分类器仅仅可以分两种类而不能再作更多等等,不过对我来说作业已经交上去了所以改进还是放到日后再说罢。同时,贝叶斯算法还可以融入一些更高级的技术来使得这个算法的强度可以媲美一些更加高深的算法(如神经网络等),这里我这个初学者就不再做更深入的探讨,有感兴趣的萌新可以自行查阅。
本文的理论部分是根据我们老师发的一篇文章来写的,用例也是原文中的用例,原文是英文的也许我的理解可能有不到位的地方,附上文章地址,感兴趣的同学可以查看一下: https://monkeylearn.com/blog/practical-explanation-naive-bayes-classifier/

程序小白,同时也是论坛小白,文章和程序都有许多菜的地方,还请各位大佬不要嫌弃,感谢各位的观看。


  1. 本文理论部分是根据我们老师发的一个文档来写的,我将在结尾附上原文地址 ↩︎

猜你喜欢

转载自blog.csdn.net/weixin_44405843/article/details/109365790
今日推荐