当前位置: 首页 > 科技观察

自己写贝叶斯分类器对书籍进行分类

时间:2023-03-21 22:21:42 科技观察

背景和目的首先,这是一个机器学习初学者,非数学背景的非典型工程师的自学记录。所以这篇文章不会特别理论化,也不会把公式解释的太深,但是会很有目的性。对于一个特别现实的问题,我们会从头分享解决方案,包括一些优化方案。从问题出发我们要解决的问题是对书籍进行二分类。分类基于书籍的标签。这些标签可能来自专家、编辑或用户。比如“外国文学”、“侦探”、“电脑”、“蟒蛇”都属于标签。为了我们的小实验项目,为了简化问题,我们现在必须将书籍分为两类:“人文”或“非人文”。因此,如上所述,这是书籍的二元分类问题。比如《计算机科学导论》,它的标签是“计算机”、“科学”、“经典”、“介绍”,属于“非人文”。《麦田里的守望者》,标签为“小说”、“文学”、“美国”,属于“人文”类。我们的分类器能够根据标签自动将一本书分类为“人文”或“非人文”。尝试一下,这很有趣!为了解决这个问题,我们给出几个前提:任何一本书只能归类为“人文”或“非人文”之一1本书有1个或多个标签所有的书都没有“人文”和“非人文”-humanistic”标签(什么?你不信?你看看亚马逊京东)你需要的概率知识很少,比如什么是概率?什么是条件概率?使用python和numpy我们将使用python作为这个实验项目的编程语言。numpy是一个python科学计算库,需要自己安装。因为我们的程序涉及一些简单的矩阵运算,使用numpy可以大大简化编程工作量。基本原理贝叶斯分类器的工作原理还是需要一些理论知识的。别担心,这部分很快就会过去。我直接结合要解决的问题进行说明。基本上,使用贝叶斯分类就是解决这样一个问题:知道一本书有这些标签:tag1、tag2、tag3……它属于“人文”类别的概率是多少?属于“非人文”类别的概率如何?假设p1表示本例属于“人文”的概率,p2表示本例属于“非人文”的概率。如果p1>p2,那么这本书就属于“人文”,反之就是“非人文”。我们不考虑p1=p2的情况。这很容易,不是吗?那么,问题就变成了,如何通过tag1,tag2,tag3...来计算p1和p2?毕竟只要知道这两个值,我们最后的问题就解决了。条件概率其实这是一个条件概率的问题。所谓条件概率,就是在已知b发生的情况下,求a发生的概率。我们写:p(a|b)。结合我们的实际问题,即当tag1,tag2,tag3...已经发生时(即本书的tag为tag1,tag2,tag3...),本书属于“人文”和“非人文”概率。我们写:p(cate1|tag1,tag2,tag3...)表示在tag1,tag2,tag3...的情况下,这本书属于类别1的概率(cate1="Humanities")p(cate2|tag1,tag2,tag3...)表示在tag1,tag2,tag3...的情况下,这本书属于category2(cate2="non-humanities")的概率这里p(cate1|tag1,tag2,tag3...)其实就是上面说的p1,我们用更专业的方法写在这里。条件概率怎么求呢?这就是贝叶斯公式:p(a|b)=p(b|a)*p(a)/p(b)意思是:想求p(a|b),知道p(b|a),p(a)的值和p(b),则可以通过p(b|a)*p(a)/p(b)得到p(a|b)。换成我们要解决的实际问题,等于:p(cate1|tag1,tag2,tag3...)=p(tag1,tag2,tag3...|cate1)*p(cate1)/p(tag1,tag2,tag3...)翻译成人类的话,就是你要找p(cate1|tag1,tag2,tag3...),你现在知道p(tag1,tag2,tag3......|cate1),也就是知道当一本书已经被分类为“人文学科”时,tag1,tag2,tag3...一起出现的概率p(cate1),也就是所有的书都被标记为“人文学科”,(在训练集中所有书籍(“人文学科”和“非人文学科”)中的概率p(tag1,tag2,tag3...),即tag1,tag2,tag3...(在训练集中)在所有标签中出现的概率,就是说我们只要把以上三项一一找出来,就可以找到p(cate1|tag1,tag2,tag3...)。同理,p(cate2|tag1,tag2,tag3...)也可以得到,这里有一个值得注意的trick,上面三项中,第三项不需要我们去计算,因为我们的目的是com将p(cate1|tag1,tag2,tag3...)和p(cate2|tag1,tag2,tag3...)的大小相减,不是得到实际值,因为上式(tag1,tag2,tag3...)是一样的,所以我们只需要比较分子的大小。也就是比较一下:p(tag1,tag2,tag3...|cate1)*p(cate1),p(tag1,tag2,tag3...|cate2)*p(cate2)的大小可以为我们节省一些计算。朴素贝叶斯那么,如何计算p(tag1,tag2,tag3...|cate1)?这里使用了朴素贝叶斯的概念,也就是说,我们认为在一本书的标签中,每个标签都是相互独立的,与另一个是否出现无关。也就是说,“电脑”和“经典”出现的概率是没有关联的,不会因为出现“电脑”就出现“经典”的概率就高。由于它们相互独立,因此p(tag1,tag2,tag3...|cate1)等于:p(tag1|cate1)xp(tag2|cate1)xp(tag3|cate1)x...p(tag1,tag2,tag3...|cate2)等于:p(tag1|cate2)xp(tag2|cate2)xp(tag3|cate2)x...也就是说,我们可以计算每个标签,分别在"人文”和“非人文”类书籍的所有标签出现的概率,然后相乘得到我们想要的。比如我们现在有一本书《计算机科学导论》,它的标签是“计算机”、“科学”、“理论”、“经典”和“入门”。人文和非人文概率。那么我们已经拥有了什么?幸运的是,我们目前手头有10本书,其中已知有6本是“人文”,4本是“非人文”。这10本书,经过排序,一共有70个不同的标签,“计算机”、“科学”、“理论”、“导论”都在其中。据此,我们可以得出p(cate1)=6/10=0.6,p(cate2)=1-0.6=0.4。也就是说,“人文”书籍出现在所有书籍中的概率为0.6,“非人文”书籍出现的概率为0.4。接下来是p(tag1,tag2,tag3...|cate1)和p(tag1,tag2,tag3...|cate2)。即我们要计算“计算机”、“科学”、“理论”、“经典”、“概论”等标签出现在“人文”类书籍的所有标签中的概率。人文”类别。同样,我们还需要计算上述标签在“非人文”类别的所有图书中所有“非人文”图书的所有标签中出现的概率。计算方法是将每个标签出现在“人文”和“非人文”的概率相乘,然后分别乘以0.6和0.4。然后比较尺寸。即比较p(cate1)xp(tag1,tag2,tag3...|cate1)和p(cate2)xp(tag1,tag2,tag3...|cate2)的大小。开始1.准备训练集几乎所有的机器学习都需要训练集。贝叶斯分类也是如此。其实我们上面提到的关于我们##known##的数据就是训练集。上面例子中提到的10本书,以及这10本书之后的所有标签就是我们的训练集;而0.6和0.4的两个概率,p1(tag1,tag2,tag3...|cate1)和p2(tag1,tag2,tag3...|cate2)是根据训练集的数据计算的,即在机器学习中称为“训练”。根据我们的问题,我们需要准备100本书,人为地分为“人文”和“非人文”两大类,并收集这些书的所有标签。这些书是如何获得的?可以爬取亚马逊或豆瓣上的图书资源。2.形成标签集将上述标签在python中保存在一个列表中,我们将其做成dicts。dicts中的每个元素都是一个标签,例如:dicts=['science','theory','c++']这样表单。3.计算训练集中“人文”和“非人文”的概率很简单。如我们的例子,假设训练集中100本书中有60本是“人文学科”,那么p(cate1)=60/100=0.6。p(类别2)=1-p(类别1)=0.4。这里我们使用变量:pcate1=0.6pcate2=0.44。计算标签集中每个标签出现在训练集《人文》书籍标签中的概率。首先,我们根据训练集构建一个列表。此列表中的每一项都是A列表,此列表中的每一项不是1就是0。1表示该词典中该位置的标签是这本书的标签。例子:假设我们的dicts是这样的:['Computer','Fiction','Psychology','Science','Programming','Behavior','Introduction','Classic','TravelNotes','UnitedStates']我们有这样一个列表:tag_vector_cate1[[0,1,0,0,0,0,0,1,1],[0,0,1,0,0,0,0,1,0],...........]此列表对应于“人文”类别。每行代表训练集中“人文”类的一本书。***行对应的书是《麦田里的守望者》,它的标签是“小说”、“经典”、“美式”。第二行对应的书是《可预测的非理性》,它的标签是“Psychology”,“Behavior”,“America”。请注意,我们使用整个标签集字典来表示一本书的标签。因此,***行第一列的1(我们从0开始数)表示《每天里的守望者》有一个'fiction'标签(对应dicts中的第一列);***行第二列的0,表示《麦田里的守望者》在本书中没有'Psychology'这个标签(对应dicts的第二列)。同理,我们看到第一行第七列和第二行都是1,说明《麦田里的守望者》和《可预测的非理性》都有'美国'标签。有了这样的数据,我们就可以轻松计算了。现在以p(tag1|cate1)的计算为例。对于tag1,我们统计tag1在训练集中“人文”的所有书籍中出现了多少次。例如:在训练集中,有60本“人文”类书籍,其中40本带有“经典”标签,那么我们设置num_of_tag1=40。根据这个方法,我们找出每个标签出现了多少次,例如:num_of_tag2=32,num_of_tage=18...然后,我们找出“人文”类别中所有书籍的标签总数(注意它这里没有加权)。例如,“人文”类别有2本书,第一本书标有“散文”、“经典”和“外国”,第二本书为“经典”和“小说”。那么,所有书籍的标签总数为3+2=5。现在,我们找到训练集中所有100本书的标签总数。假设总数是700。我们设置total_cate1=700。因此,tag1出现在“人文”类别的概率为:p(tag1|cate1)=num_of_tag1/total_cate1=40/700=0.057。同理,我们得到p(tag2|cate1),p(tag3|cate1)...利用numpy,我们可以用几行代码轻松实现这个过程。fromnumpyimport*num_tags_cate1=ones(len(dicts))#(1)total_cate1=2.0#(2)foritemintag_vector_cate1:num_tags_cate1+=item#(3)total_cate1+=sum(item)#(4)p_tags_cate1=num_tags_cate1/total_cate1#(5)这是一个解释。(1)代码的意思是生成一个numpy数组。ones()是一个numpy函数,返回一个填充值为1的numpy数组,参数为数组的长度。例如:temp=ones(3),表示生成一个numpy数组[1,1,1]返回给temp。所以,(1)的代码就是以训练集的标签集dicts的长度为参数,生成一个填满1等于dicts长度的numpy数组,返回给num_tags_cate1。为什么要和dicts一起成长?请记住,我们将一本书表示为一个完整的词典集合。我们要计算的是这个dicts中每个标签出现的概率,放到一个数组中。num_tags_cate1就是这个数组。至于这个数组为什么要填1,后面会解释。(2)total_cate1=2.0。total_cate1是分母,分母不能为0,所以我们需要让它的初始值不为0,为什么是2.0呢?稍后会解释。(3)num_tags_cate1+=项目。item显然是一个python列表,也就是我们刚才说的[0,1,0,0,0,0,0,1,1]。当你将一个python列表添加到一个numpy数组中时,numpy会帮你计算出对应的项,相当于重载了+。例如,a是一个numpy数组:[1,2,3,5,0],b是一个python列表:[0,0,3,2,1]。a+b=[1,2,6,7,1],结果是一个numpy数组。在此示例中,“fiction”、“classic”和“American”的等效值分别递增1。(4)将每本书中出现的所有标签的编号相加。sum(item)也是numpy的一个函数,作用是将item中的每一项相加。例如:sum([2,5,-1]),结果为2+5+(-1)=6。如果item是这样一个列表:[0,1,0,0,0,0,0,1,1],对应《麦田里的守望者》,它的标签分别是“novel”,“classic”,“America”,等价标签总数增加了3个。(5)显然,我们用num_tags_cate1去掉total_cate1,这也是numpy的重载“/”运算符,比如[2,4,6]/2,相当于每一项除以2,***得到Anumpy数组,即[1,2,3]。在这个例子中,相当于用标签总数除以tag1,tag2,tag3...的出现次数,得到一个numpy数组p_tags_cate1。这个数组中的每一项都是一个概率值,代表它对应的标签出现在cate1(“人文”)类别中的概率。同样,我们可以计算出p_tags_cate2。即每个标签出现在cate2(“非人文”)的概率。5.现在我们有什么在这里我们已经拥有了几乎所有的东西。回想贝叶斯分类的公式:p(cate1|tag1,tag2,tag3...)=p(tag1,tag2,tag3...|cate1)xp(cate1)/p(tag1,tag2,tag3...)正如我们前面讨论的,分子可以忽略不计,即分母p(tag1,tag2,tag3...)不需要忽略。此外,根据朴素贝叶斯理论,分子等于:p(tag1,tag2,tag3...|cate1)xp(cate1)=p(tag1|cate1)xp(tag2|cate1)xp(tag3|cate1)x...xp(cate1)p(cate1)等于上面提到的pcate1。p(tag1|cate1),p(tag2|cate1)...就是我们上面得到的numpy数组p_tags_cate1中的每一项。我们只需要将它们相乘就可以得到p(tag1|cate1)xp(tag2|cate1)x......!说到这里,我们就得解释一下为什么上面的代码会把num_tags_cate1填成1。如果我们用0填充,当某个标签一直为0时(虽然理论上不可能),整个分子相乘的结果就是0,所以***的值就变成了0,影响了结果。所以,为了避免这种情况,我们认为每个标签至少要出现1次,所以我们用1来填充。因此,在最坏的情况下,num_tags_cate1=[1,1,1,....]。而total_cate1=2.0,对应num_tags_cate1=[1,1,1,...],那么我们认为每个标签出现的概率是0.5(1/2.0),这是一个可调参数,但是要记住不要设置total_cate1=1.0。如果是这样,那么每个标签出现的概率都变成了1,这就是个大问题。6.利用训练得到的数据对新书进行分类。最后,贝叶斯分类器就完成了。现在让我们看看如何对新书进行分类。所谓新书分类,就是在训练集训练完成后(记得吗?人工分类的100本书就是训练集),此时,我们需要对第101本书进行分类。这本书不是训练集中的书,是一本新书。我们根据前面计算的公式中的几个元素对其进行分类。同样,我们将新书的标签提取出来,保存在python中的一个列表中,记为:tagvects,其形式为:[1,0,0,1,0,0,1....].接下来我们将p_tags_cate1中的每一项乘以对应tagvects中的item:results_tags_cate1=p_tags_cate1*tagvects再将num_tags_cate1中的每一项乘以:temp1=1.0foriteminresults_tags_cate1:ifitem!=0:temp1=temp1*item同理计算temp2:results_tags_cate2=p_tags_cate2*tagvectstemp2=1.0foriteminresults_tags_cate2:ifitem!=0:temp2=temp2*item***,so:p_cate1_tags=temp1*pcate1p_cate2_tags=temp2xpcate2ifp_cate1_tags2_tags=temp2xpcate2ifp_cate1_tags2humanities'非人文'的大小,显然p_cate1_tags和p_cate2_tags,我们可以对新书进行分类,哪边的值大的就归到哪边。优化技巧是由于上面的公式,它是多个概率的乘积。当你的标签集dicts的长度非常大的时候(也就是当你的书有很多标签的时候),这是一个很糟糕的做法,因为每一项都是Decimals,当这么多小数相乘时,可能会溢出,或者数字太小,计算结果为0,这时候就需要一个trick来优化避免这种情况。我们采用了一种非常流行的数学方法,即取对数ln来改进我们的算法。在python中,对数函数是log()。可以在几个地方取对数。这里推荐这种方法,计算公式变为:ln(p(tag1|cate1)*p(tag2|cate1)*....*p(cate1)))展开后,变为:ln(p(tag1|cate1))+ln(p(tag2|cate1))+...+ln(pcate1)回想一下,p(tag1|cate1),p(tag2|cate1)...就是我们上面计算的每一项p_tags_cate1(p_tags_cate1是一个numpy数组,每一个代表对应标签出现在“人文”类别中的概率)。在我们上面的计算中:p_tags_cate1=num_tags_cate1/total_cate1现在我们对它取对数,所以把代码改成:p_tags_cate1=log(num_tags_cate1/total_cate1)注意这里的log是为numpy数组的每一项用来查找日志,结果仍然是一个数组。所以p_tags_cate1取对数后就变成了一个数组。然后找到ln(pcate1),把pcate1改成:pcate1=log(pcate1)所以,上面***分类的代码改成:results_tags_cate1=p_tags_cate1*tagvectstemp1=1.0foriteminresults_tags_cate1:ifitem!=0:temp1=temp1+item同理计算temp2:results_tags_cate2=p_tags_cate2*tagvectstemp2=1.0foriteminresults_tags_cate2:ifitem!=0:temp2=temp2+item然后就可以分类了:p_cate1_tags=temp1+pcate1p_cate2_tags=temp2+pcate2ifp_categ_categ1_ateg':print'non-human'总结很高兴你终于来了。本文力求简明扼要,尽量减少学习成本。但是一定有你第一次看的时候可能看不懂的可能。这个是正常的。凡事总有一个过程,尤其是机器学习领域,需要你反复咀嚼、思考和实践。在写这篇文章的过程中,我反复加深了对贝叶斯分类的理解,但是表述上还是有一些不清楚的地方。如果您有任何问题或建议,欢迎与我讨论。通过这篇文章,我们了解到:什么是朴素贝叶斯为了实现朴素贝叶斯分类,我们应该如何准备训练集,其中标签集非常重要?在训练数据的基础上,得到每个标签在“人文”和“非人文”中的出现概率利用这些出现概率对新文章进行分类巧妙地使用对数和一些初始值(比如ones())来优化算法