朴素贝叶斯算法代码实现,朴素贝叶斯算法的作用

大家好,小编来为大家解答以下问题,朴素贝叶斯算法实例代码,朴素贝叶斯算法代码实现,现在让我们一起来看看吧!

 

本文主要介绍了贝叶斯算法的基本原理以及基于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)间进行转换。
    就文本分类而言,它认为每个词袋中的两两词之间的关系是相互独立的,因此得到了朴素贝叶斯的定义:

    1. 设 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的一个特征属性。
    2. 有类别集合 C={y1,y2,⋯,yn} < type="math/tex" id="MathJax-Element-13517">C=\{y_1,y_2,\cdots,y_n\}。
    3. 计算 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)。
    4. 如果 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)
    根据上述分析,朴素贝叶斯分类的流程可以表示如下:

    1. 训练数据产生训练样本集:TF-IDF;
    2. 对每个类别计算 P(yi) < type="math/tex" id="MathJax-Element-13524">P(y_i);
    3. 对每个特征属性计算所有划分的条件概率;
    4. 对每个类别计算 P(x|yi)P(yi) < type="math/tex" id="MathJax-Element-13525">P(x|y_i)P(y_i);
    5. 以 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倾向于过滤掉常见的词语,保持重要的词语。
  • 代码实现

    • 创建简单的英文语料作为数据集
      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
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值