机器学习笔记(4)——朴素贝叶斯

Naive Bayes

朴素贝叶斯网络是贝叶斯分类器的一种,贝叶斯分类算法是统计学的一种分类方法,利用概率论和统计知识进行分类。其原理是利用贝叶斯公式根据样本的先验概率来计算其后验概率(即样本属于某一类的概率),然后选择具有最大后验概率的类作为该对象所属的类别。朴素贝叶斯分类以概率论为基础,有坚实的数学基础,以及稳定的分类效率,其优点是算法简单,在数据较少的情况下仍然准确。理论上朴素贝叶斯分类有最小的误差率,但实际贝叶斯假设样本之间的各个特征相互独立往往不成立,从而影响分类正确性。

1.贝叶斯理论

在贝叶斯理论中,假设X,Y两个事件:
P(X) X 先验概率
P(Y|X) 是已知 X 发生后Y 发生的条件概率,也称之为 Y 后验概率
贝叶斯理论中,常用公式:
乘法公式:P(XYZ)=P(Z|XY)P(Y|X)P(X)
全概率公式: P(X)=kP(X|Yi)P(Yi)
贝叶斯公式: P(Yi|X)=P(X|Yi)P(X)

2.朴素贝叶斯法的学习与分类

设输入空间 XRn n 维特征向量的集合,输出空间为分类标记集合Y={c1,c2,,ck}。 朴素贝叶斯分类器的输入为特征向量 xX ,输出为预测类标记 yY , 学习过程就是通过训练数据集 T={(x1,y1),(x2,y2),,(xN,yN)} 得到联合分布概率 P(X,Y) , 并以此来预测未知样本的标记。
具体地:
后验概率:

P(Y=ck|X=x)=P(X=x|Y=ck)P(Y=ck)P(X=x)

其中, P(Y=ck) 为类别标签的先验概率, P(X=x|Y=ck) 为条件概率分布,且在朴素贝叶斯中假设各个特征具有相同地位且各自出现的概率相互独立,因此:
P(X=x|Y=ck)=P(X(1)=x(1),X(2)=x(2),,X(n)=x(n)|Y=ck)=j=1nP(X(j)=x(j)|Y=ck)

P(X=x)=kP(Y=ck)P(X=x|Y=ck)=kP(Y=ck)j=1nP(X(j)=x(j)|Y=ck)

所以最终朴素贝叶斯分类器可以表示为:
y=f(x)=argmaxkP(Y=ck)nj=1P(X(j)=x(j)|Y=ck)kP(Y=ck)nj=1P(X(j)=x(j)|Y=ck)

即在所有的后验概率中找到最大的概率作为该特征预测的类别,而且我们注意到最终的分母是队所有的类别都是一样的,因此可以简化为:
y=f(x)=argmaxkP(Y=ck)j=1nP(X(j)=x(j)|Y=ck)

我们以两个类别来简要说明贝叶斯的分类准则:假设未知类别样本特征为 x ,分类标签为c1,c2 ,则样本为类别1的概率为:
P(c1|x)=P(x|c1)P(c1)P(x)

样本为类别2的概率为:
P(c2|x)=P(x|c2)P(c2)P(x)

其中 P(c1) 为所有训练集中类别 c1 出现的概率, P(x|c1) 为所有 c1 类别中特征 x 出现的概率。

  • 如果P(x|c1)P(c1)>P(x|c2)P(c2),那么该测试样本属于 c1
    • 如果 P(x|c1)P(c1)<P(x|c2)P(c2) ,那么该测试样本属于 c2
    • 3.朴素贝叶斯学习算法

      损失函数:
      通过上述介绍,我们知道朴素贝叶斯方法的分类规则就是后验概率最大化,这与经验风险最小化是相匹配的。我们定义朴素贝叶斯分类的损失函数为:

      L(Y,f(X))={1,0,if Yf(X)if Y=f(X)

      其中, Y 为样本实际类别,f(X) 为分类决策函数得到类别。这时,期望风险函数为:
      Rexp(f)=E[L(Y,f(X))]=Exk=1K[L(ck,f(X))]P(ck|X)

      其中, P(ck|X) 为各个预测的概率。为了使期望风险最小化,只需要使所有各个样本的误差极小即可,即:
      f(x)=argminyYk=1K[L(ck,y)]P(ck|X=x)=argminyYk=1KP(yck|X=x)=argminyY(1P(y=ck|X=x))=argmaxyYP(y=ck|X=x)

      由此可以看出,后验概率最大化准则符合损失函数最小化的要求。
      参数估计:
      通过上述介绍,我们知道朴素贝叶斯的学习过程就是估计先验概率 P(Y=ck) 和条件概率 P(Xj=xj|Y=ck) 。在实际学习中,我们假设训练样本总数为N,由极大似然估计可知:
      P(Y=ck)=Nl=1I(yl=ck)N

      然后在所有标记为 ck 的样本中计算条件概率:
      P(X(j)=ajl|Y=ck)=Nl=1I(X(j)=ajl,Y=ck)Nl=1I(yl=ck)

      其中,X^{(j)}是样本的第 j 个特征,本式计算在类别ck 中,不同特征所在比例,及其分布概率。
      算法实现:
      在本处,我们应用朴素贝叶斯方法来进行垃圾邮件分类。
      - 准备数据:首先准备一定量垃圾邮件和正常邮件并给予一定标记
      - 数据处理:由于本处文本量少,文本较为简单,直接统计文本中各个单词的数量来作为文本特征
      - 参数估计:首先统计训练样本中正负样本各占多少比例,即 P(Y=ck) ,再统计每个类别中各个特征所占的比例,即 P(X=x|Y=ck)
      - 模型测试:统计给定训练样本的特征,然后通过先验概率计算出属于各类的标签
      - 模型应用:可以编辑任意文本进行测试,看未标记文本是否分类正确
      以下是程序实现源码:

      # Project: Machine learning-Naive Bayes
      # Author: Lyndon
      # date: 2015/10/20
      
      from numpy import *
      import re,string,os
      
      # create the vocabulary
      # input: traing_data text
      # output: vocabulary list
      def creatVocabulary(dataset):
          vocabulary = set([])
          for document in dataset:
              vocabulary = vocabulary | set(document)
          return list(vocabulary) 
      
      # describe document using the bag-of-words model
      # input: vocabulary list, input document
      # output: descriptor of input document
      def document2Vec(vocabulary,inputData):
          vector = [0]*len(vocabulary)
          for word in inputData:
              if word in vocabulary:
                  vector[vocabulary.index(word)]+=1
              else:
                  print "the word: %s is not in the vocabulary!" %word
          return vector 
      
      # split the document to word list
      # input: document
      # output: word list
      def text2word(textString):
          textString.translate(None, string.punctuation)
          # split the document
          wordsplit = re.split(r'\s+',textString)
          #  retain the alphabetic characters
          #for i in range(len(wordsplit)):
              #wordsplit[i] = filter(str.isalpha,wordsplit[i])
          # return the lower case word 
          return [tok.lower() for tok in wordsplit if len(tok) >2]
      
      
      # process the dataset
      # input: NULL
      # output: document list, class label list, full text
      def proDocument():
          documentlist = []
          classlist = []
          fulltext = []
          path1 = "F:/Program/Python/Machine_Learning/Bayes/email/spam"
          for root, dirs, files in os.walk(path1):
              #print files
              numDocument1 = len(files)  
          for i in range(numDocument1):
              wordList = text2word(open(path1+'/%d.txt' %(i+1)).read())
              documentlist.append(wordList)
              fulltext.extend(wordList)
              classlist.append(1)
          path2 = "F:/Program/Python/Machine_Learning/Bayes/email/ham"
          for root, dirs, files in os.walk(path2):
              #print files
              numDocument2 = len(files)
          for i in range(numDocument2):
              wordList = text2word(open(path2+'/%d.txt' %(i+1)).read())
              documentlist.append(wordList)
              fulltext.extend(wordList)
              classlist.append(0)
          return documentlist,classlist,fulltext
      
      # training
      # input: training_vector, training_label
      # output: posterior probability P(Y=1), P(X=x|Y=1),P(X=x|y=0)
      def trainNB(trainMatrix,trainlabel):
          numTrain = len(trainMatrix)
          numWords = len(trainMatrix[0])
          pAbusive = sum(trainlabel)/float(numTrain)
          p0Num = ones(numWords); p1Num = ones(numWords)
          p0Denom = 2.0; p1Denom =2.0
          for i in range(numTrain):
              if trainlabel[i]==1:
                  p1Num += trainMatrix[i]
                  p1Denom += sum(trainMatrix[i])
              else:
                  p0Num += trainMatrix[i]
                  p0Denom += sum(trainMatrix[i])
          p1Vector = log(p1Num/p1Denom)
          p0Vector = log(p0Num/p0Denom)
          return p0Vector,p1Vector,pAbusive
      
      # testing 
      # input: testing_vector, posterior probability P(Y=1), P(X=x|Y=1),P(X=x|y=0)
      # output: testing_label
      def testNB(testVector,p0Vector,p1Vector,pAbusive):
          p1 = sum(testVector*p1Vector)+log(pAbusive)
          p0 = sum(testVector*p0Vector)+log(1-pAbusive)
          if p1>p0:
              return 1
          else:
              return 0
      
      # main function
      if __name__=="__main__":
          # dataset processing
          documentlist = []
          classlist = []
          fulltext = []
          documentlist,classlist,fulltext=proDocument()
          vocabulary = creatVocabulary(documentlist)
          # randomly select training_set and testing_set
          trainSet=range(len(documentlist));testSet=[]
          for i in range(int(len(documentlist)*0.2)):
              randIndex = int (random.uniform(0,len(documentlist)-i))
              testSet.append(trainSet[randIndex])
              del(trainSet[randIndex])
          trainMat=[];trainlabel=[]
          for docIndex in trainSet:
              trainMat.append(document2Vec(vocabulary, documentlist[docIndex]))
              trainlabel.append(classlist[docIndex])
          p0Vector,p1Vector,pAbusive = trainNB(trainMat,trainlabel)
          error = 0
          for docIndex in testSet:
              doc2Vector = document2Vec(vocabulary, documentlist[docIndex])
              result=testNB(doc2Vector,p0Vector,p1Vector,pAbusive)
              if result!=classlist[docIndex]:
                  error +=1
                  print "classification error:"+str(documentlist[docIndex])+"is "+str(classlist[docIndex])+" but classify to "+str(result)
          print "the error rate is:",float(error)/len(testSet)

      程序测试结果:
      这里写图片描述

      在实际处理过程中,可能会出现某个条件概率 P(X=x|Y=ck) 为0的情况,这样会使得独立分布相乘之后的结果为0,影响正常的后验概率,因此定义了如下的贝叶斯估计:

      P(X(j)=ajl|Y=ck)=Nl=1I(X(j)=ajl,Y=ck)+λNl=1I(yl=ck)+Sjλ

      λ=0 时就是极大似然估计,在 λ=1 时,成为拉普拉斯平滑。在本文的垃圾邮件处理中,我们初始化特征数都是1。并且,为了避免因为多数特征概率因子较小导致乘积下溢得不到正确的结果,我们将 nj=1P(X(j)=x(j)|Y=ck) 取对数,变成各特征的和。
      PS:
      本文为机器学习(4)总结笔记,主要通过Python编程实现了朴素贝叶斯方法实现垃圾邮件分类系统。理论主要参考李航《统计学习方法》,

你可能感兴趣的:(机器学习,朴素贝叶斯,Python)