大家好,小编来为大家解答以下问题,朴素贝叶斯算法实例代码,朴素贝叶斯算法代码实现,现在让我们一起来看看吧!
本文主要介绍了贝叶斯算法的基本原理以及基于Python的朴素贝叶斯分类算法的实现。
-
贝叶斯定理
我们先从理论方面来讲讲著名的贝叶斯定理- 条件概率
条件概率(又称后验概率)就是事件A在另外一个事件B已经发生条件下的发生概率ai伪原创api,如何用php调用。条件概率表示为P(A|B),读作“在B条件下A的概率”。
比如,在同一个样本空间 Ω < type="math/tex" id="MathJax-Element-13489">\Omega中的事件或者子集A与B,如果随机从 Ω < type="math/tex" id="MathJax-Element-13490">\Omega中选出的一个元素属于B,那么这个随机选择的元素还属于A的概率就定义为在B的前提下A的条件概率,所以: P(A|B)=|A∩B||B| < type="math/tex" id="MathJax-Element-13491">P(A|B)=\frac{|A\cap B|}{|B|},接着分子、分母都除以 Ω < type="math/tex" id="MathJax-Element-13492">\Omega得到:P(A|B)P(A∩B)P(B)< type="math/tex; mode=display" id="MathJax-Element-13493">P(A|B)\frac{P(A\cap B)}{P(B)} - 联合概率
联合概率表示两个事件共同发生的概率,如:A与B的联合概率表示为 P(A∩B) < type="math/tex" id="MathJax-Element-13494">P(A\cap B)或者 P(A,B) < type="math/tex" id="MathJax-Element-13495">P(A,B) - 边缘概率(先验概率)
边缘概率是某个事件发生的概率。在联合概率中,把最终结果中那些不需要的事件通过合并成它们的全概率,而消去它们(对离散随机变量用求和得全概率,对连续随机变量用积分得全概率),这称为边缘化(marginalization),比如A的边缘概率表示为 P(A) < type="math/tex" id="MathJax-Element-13496">P(A),B的边缘概率表示为 P(B) < type="math/tex" id="MathJax-Element-13497">P(B)。 - 贝叶斯公式
首先,在事件B发生之前,我们对事件A的发生有一个基本的概率判断,称为A的先验概率,用 P(A) < type="math/tex" id="MathJax-Element-13498">P(A)表示;同理,事件B的先验概率可用 P(B) < type="math/tex" id="MathJax-Element-13499">P(B)表示。
在事件B发生之后,我们对事件A的发生概率重新评估,称为A的后验概率,用 P(A|B) < type="math/tex" id="MathJax-Element-13500">P(A|B)表示;同理,事件B的后验概率为 P(B|A) < type="math/tex" id="MathJax-Element-13501">P(B|A)
根据条件概率的定义,在事件B发生的条件下事件A发生的概率是:P(A|B)=P(A∩B)P(B)< type="math/tex; mode=display" id="MathJax-Element-13502">P(A|B)=\frac{P(A\cap B)}{P(B)}
同理,事件A发生的条件下时间B发生的概率是:P(B|A)=P(A∩B)P(A)< type="math/tex; mode=display" id="MathJax-Element-13503">P(B|A)=\frac{P(A\cap B)}{P(A)}
整理与合并上述两条等式,便可以得到:P(A|B)P(B)=P(A∩B)=P(B|A)P(A)< type="math/tex; mode=display" id="MathJax-Element-13504">P(A|B)P(B)=P(A\cap B)=P(B|A)P(A)
若 P(B) < type="math/tex" id="MathJax-Element-13505">P(B)非零,我们便可以得到贝叶斯定理的表达式:P(A|B)=P(A)P(B|A)P(B)< type="math/tex; mode=display" id="MathJax-Element-13506">P(A|B)=\frac {P(A)P(B|A)}{P(B)}
应用:如 P(A)=0.5 < type="math/tex" id="MathJax-Element-13507">P(A)=0.5表示的是酒后驾驶的概率为 0.5 < type="math/tex" id="MathJax-Element-13508">0.5, P(B)=0.1 < type="math/tex" id="MathJax-Element-13509">P(B)=0.1表示的是交警抽查的概率(但要注意的是,条件A和条件B是相互独立的),那么 P(B|A)=P(A)×P(B)=0.05 < type="math/tex" id="MathJax-Element-13510">P(B|A)=P(A)\times P(B)=0.05则表示酒后驾驶被交警抽查到的概率为0.05。那么问题来了,已知一老司机被交警抽查到了,那么我们就可以用贝叶斯定理求出他酒后驾驶的概率( P(A|B) < type="math/tex" id="MathJax-Element-13511">P(A|B))是多少了。
- 条件概率
-
朴素贝叶斯分类
目前最为广泛的两种分类模型是决策树模型(Decision Tree Model)和朴素贝叶斯模型(Naive Bayesian Model,NBM)。而朴素贝叶斯法是基于贝叶斯定理与特征条件独立假设的分类方法。朴素贝叶斯分类器基于一个“朴素”的假定:给定目标值时属性之间相互条件独立。基于这个假定和贝叶斯定理,我们就可以在 P(A|B) < type="math/tex" id="MathJax-Element-13512">P(A|B)和 P(B|A) < type="math/tex" id="MathJax-Element-13513">P(B|A)间进行转换。
就文本分类而言,它认为每个词袋中的两两词之间的关系是相互独立的,因此得到了朴素贝叶斯的定义:- 设 x={a1,a2,⋯,am} < type="math/tex" id="MathJax-Element-13514">x=\{a_1,a_2,\cdots,a_m\}为一个待分类项,而每个 a < type="math/tex" id="MathJax-Element-13515">a为x< type="math/tex" id="MathJax-Element-13516">x的一个特征属性。
- 有类别集合 C={y1,y2,⋯,yn} < type="math/tex" id="MathJax-Element-13517">C=\{y_1,y_2,\cdots,y_n\}。
- 计算 P(y1|x),P(y2|x),⋯,P(yn|x) < type="math/tex" id="MathJax-Element-13518">P(y_1|x),P(y_2|x),\cdots,P(y_n|x)。
- 如果 P(yk|x)=max{P(y1|x),P(y2|x),⋯,P(yn|x)} < type="math/tex" id="MathJax-Element-13519">P(y_k|x)=max\{P(y_1|x),P(y_2|x),\cdots,P(y_n|x)\},则 x∈yk < type="math/tex" id="MathJax-Element-13520">x\in y_k。
那么问题来了,关键的第3步如何进行计算?
(1) 找到一个已知分类的待分类项集合(也就是训练集);
(2) 统计得到在各个类别下各个特征属性的条件概率估计,即:P(a1|y1),P(x2|y1),⋯,P(am|y1);P(a1|y2),P(x2|y2),⋯,P(am|y2);⋮P(a1|yn),P(x2|yn),⋯,P(am|yn);< type="math/tex; mode=display" id="MathJax-Element-13521"> P(a_1|y_1),P(x_2|y_1),\cdots,P(a_m|y_1);\\ P(a_1|y_2),P(x_2|y_2),\cdots,P(a_m|y_2);\\ \vdots\\ P(a_1|y_n),P(x_2|y_n),\cdots,P(a_m|y_n);
(3)如果各个特征属性是条件独立的(或者假设它们之间是相互独立的),则根据贝叶斯定理有如下推导:P(yi|x)=P(x|yi)P(yi)P(x)< type="math/tex; mode=display" id="MathJax-Element-13522"> P(y_i|x)=\frac {P(x|y_i)P(y_i)}{P(x)}
因为分母对于所有类别为常数,只要将分子最大化皆可。又因为诶各特征属性是条件独立的,所以有:P(x|yi)P(yi)=P(a1|y1)P(a2|y2)⋯P(am|yi)P(yi)=P(yi)∏j=1mP(aj|yi)< type="math/tex; mode=display" id="MathJax-Element-13523"> P(x|y_i)P(y_i)=P(a_1|y_1)P(a_2|y_2)\cdots P(a_m|y_i)P(y_i)=P(y_i)\prod_{j=1}^m P(a_j|y_i)
根据上述分析,朴素贝叶斯分类的流程可以表示如下:- 训练数据产生训练样本集:TF-IDF;
- 对每个类别计算 P(yi) < type="math/tex" id="MathJax-Element-13524">P(y_i);
- 对每个特征属性计算所有划分的条件概率;
- 对每个类别计算 P(x|yi)P(yi) < type="math/tex" id="MathJax-Element-13525">P(x|y_i)P(y_i);
- 以 P(x|yi)P(yi) < type="math/tex" id="MathJax-Element-13526">P(x|y_i)P(y_i)的最大项作为x的所属类别。
所谓的TF-IDF就是词频逆文档频率,如果某个词或短语在一篇文章中出现的频率高,并且在其他文章中很少出现,则认为此词或短语具有很好的类别区分能力,适合用来分类。- 词频(Term Frequency, TF)指的是某一个给定的词语在该文档中出现的频率。这个数字是对词数(Term Count)的归一化,以防止它偏向长的文件。对于在某一特定文件里的词语来说,它的重要性可表示为:
TFij=ni,j∑knk,j< type="math/tex; mode=display" id="MathJax-Element-13527"> TF_{ij}=\frac{n_{i,j}}{\sum_k n_{k,j}}
其中, ni,j < type="math/tex" id="MathJax-Element-13528">n_{i,j}是该词在文件中出现的次数, ∑knk,j < type="math/tex" id="MathJax-Element-13529">\sum_k n_{k,j}是文件中所有字词的出现次数之和。 - 逆向文件频率(Inverse Document Frequency, IDF)是一个词语普遍重要性的度量。某一特定词语的IDF,可以由总文件数目除以包含该词语的文件的数目,再将得到的商取对数得到:
IDFi=log∣D∣∣j:ti∈dj∣< type="math/tex; mode=display" id="MathJax-Element-13530"> IDF_i = log\frac {\lvert D \rvert}{\lvert {j:t_i\in d_j} \rvert}
其中, ∣D∣ < type="math/tex" id="MathJax-Element-13531">\lvert D\rvert表示语料库中的文件总数; j < type="math/tex" id="MathJax-Element-13532">j包含词语的文件数目(如果该词语不在语料库中,就会导致分母为零,因此一般情况下使用1+∣d∈D:t∈d∣< type="math/tex" id="MathJax-Element-13533">1+{\lvert {d\in D:t\in d} \rvert}作为分母)。 - TF-IDF权重策略就是计算TF与IDF的乘积,某一特定文件内的高词语频率,以及该词语在整个文件集合中的低文件频率( TFIDFij=TFijI×IDFij < type="math/tex" id="MathJax-Element-13534">TFIDF_{ij}=TF_{ij}I\times IDF_{ij}),可以产生高权重的TF-IDF。因此,TF-IDF倾向于过滤掉常见的词语,保持重要的词语。
- 词频(Term Frequency, TF)指的是某一个给定的词语在该文档中出现的频率。这个数字是对词数(Term Count)的归一化,以防止它偏向长的文件。对于在某一特定文件里的词语来说,它的重要性可表示为:
-
代码实现
- 创建简单的英文语料作为数据集
def loadDataSet(): postingList=[['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],\ ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],\ ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him','my'],\ ['stop', 'posting', 'stupid', 'worthless', 'garbage']\ ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],\ ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']] classVec = [0,1,0,1,0,1] #1 is abusive, 0 not return postingList,classVec
postingList是训练集文本,classVec是每个文本对应的分类。
-
编写一个贝叶斯算法类,创建默认的构造方法
class NBayes(object): def __init__(self): self.vocabulary = [] # 词典 self.idf=0 # 词典的idf权值向量 self.tf=0 # 训练集的权值矩阵 self.tdm=0 # P(x|yi) self.Pcates = {} # P(yi)--是个类别字典 self.labels=[] # 对应每个文本的分类,是个外部导入的列表 self.doclength = 0 # 训练集文本 self.vocablen = 0 # 词典词长 self.testset = 0 # 测试集
-
导入训练集,生成算法参数和数据结构
def train_set(self,trainset,classVec): self.cate_prob(classVec) # 计算每个分类在数据集中的概率:P(yi) self.doclength = len(trainset) tempset = set() [tempset.add(word) for doc in trainset for word in doc ] # 生成词典 self.vocabulary = list(tempset) self.vocablen = len(self.vocabulary) self.calc_wordfreq(trainset) # 计算词频数据集 self.build_tdm() # 按分类累计向量空间的每维值:P(x|yi)
-
计算在数据集中每个分类的高铝 P(yi) < type="math/tex" id="MathJax-Element-13535">P(y_i)的cate_prob函数
def cate_prob(self,classVec): self.labels = classVec labeltemps = set(self.labels) # 获取全部分类 for labeltemp in labeltemps: # 统计列表中重复的分类: self.labels.count(labeltemp) self.Pcates[labeltemp] = float(self.labels.count(labeltemp))/float(len(self.labels))
-
生成普通的词频向量的calc_wordfreq函数
# 生成普通的词频向量 def calc_wordfreq(self,trainset): self.idf = np.zeros([1,self.vocablen]) # 1*词典数 self.tf = np.zeros([self.doclength,self.vocablen]) # 训练集文件数*词典数 for indx in xrange(self.doclength): # 遍历所有的文本 for word in trainset[indx]: # 遍历文本中的每个词 self.tf[indx,self.vocabulary.index(word)] +=1 # 找到文本的词在字典中的位置+1 for signleword in set(trainset[indx]): self.idf[0,self.vocabulary.index(signleword)] +=1
-
按分类累计计算向量空间的每维值 P(x|yi) < type="math/tex" id="MathJax-Element-13536">P(x|y_i)的build_tdm函数
#按分类累计向量空间的每维值:P(x|yi) def build_tdm(self): self.tdm = np.zeros([len(self.Pcates),self.vocablen]) #类别行*词典列 sumlist = np.zeros([len(self.Pcates),1]) # 统计每个分类的总值 for indx in xrange(self.doclength): # 将同一类别的词向量空间值加总 self.tdm[self.labels[indx]] += self.tf[indx] # 统计每个分类的总值--是个标量 sumlist[self.labels[indx]]= np.sum(self.tdm[self.labels[indx]]) self.tdm = self.tdm/sumlist # 生成P(x|yi)
-
将测试集映射到当前词典的map2vocab函数
def map2vocab(self,testdata): self.testset = np.zeros([1,self.vocablen]) for word in testdata: self.testset[0,self.vocabulary.index(word)] +=1
-
预测分类结果,输出预测的分类类别的predict函数
def predict(self,testset): if np.shape(testset)[1] != self.vocablen: # 如果测试集长度与词典不相等,退出程序 print "输入错误" exit(0) predvalue = 0 # 初始化类别概率 predclass = "" # 初始化类别名称 for tdm_vect,keyclass in zip(self.tdm,self.Pcates): # P(x|yi) P(yi) # 变量tdm,计算最大分类值 temp = np.sum(testset*tdm_vect*self.Pcates[keyclass]) if temp > predvalue: predvalue = temp predclass = keyclass return predclass
-
使用TF-IDF策略改进算法
# 以生成 tf-idf方式生成向量空间的calc_tfidf函数 def calc_tfidf(self,trainset): self.idf = np.zeros([1,self.vocablen]) self.tf = np.zeros([self.doclength,self.vocablen]) for indx in xrange(self.doclength): for word in trainset[indx]: self.tf[indx,self.vocabulary.index(word)] +=1 # 消除不同句长导致的偏差 self.tf[indx] = self.tf[indx]/float(len(trainset[indx])) for signleword in set(trainset[indx]): self.idf[0,self.vocabulary.index(signleword)] +=1 self.idf = np.log(float(self.doclength)/self.idf) self.tf = np.multiply(self.tf,self.idf) # 矩阵与向量的点乘 tf x idf
-
分类结果
# -*- coding: utf-8 -*- import sys import os from numpy import * import numpy as np from Nbayes_lib import * dataSet,listClasses = loadDataSet() # 导入外部数据集 # dataset: 句子的词向量 # listClass是句子所属的类别 [0,1,0,1,0,1] nb = NBayes() # 实例化 nb.train_set(dataSet,listClasses) # 训练数据集 nb.map2vocab(dataSet[0]) # 随机选择一个测试句 print nb.predict(nb.testset) # 输出分类结果
分类结果为:
1
- 创建简单的英文语料作为数据集