zoukankan      html  css  js  c++  java
  • 朴素贝叶斯

    朴素贝叶斯分类器是一种利用概率论知识实现的分类器,之所以称之为“朴素”,是因为整个形式化过程只做最原始、最简单的假设。下面将从原理到实战进行详细讲解。

    # 基于贝叶斯决策理论的分类方法

    ​ 在讲述朴素贝叶斯之前,贝叶斯决策理论的核心思想,即选择具有最高概率的决策。如上图的AB桶,若是问一个出自其中的一个黑球更可能来自哪个桶?由先验知识P(black|bucketA) 和 P(black|bucketB),我们不难由贝叶斯公式得到,P(bucketA|black)=3/7,P(bucketB|black)=4/7。

    ​ 可以看出,贝叶斯准则就是通过交换条件概率中的条件与结果,即如果已知 P(x|c),来求P(c|x)。具体地,应用贝叶斯准则得到:

    [p(c_{i}|x,y) = frac{p(x,y|c_{i})p(c_{i})}{p(x,y)} ]

    ​ 使用这些定义,可以定义贝叶斯分类准则为:

    • 如果p(c1|x, y) > p(c2|x, y) ,那么属于类别c1。
    • 如果p(c1|x, y) < p(c2|x, y) ,那么属于类别c2。

    # 使用朴素贝叶斯进行文档分类

    ​ 机器学习的一个重要应用就是文档的自动分类。在文档分类中,整个文档(如一封电子邮件)是实例,而电子邮件中的某些元素则构成特征。虽然电子邮件是一种会不断增加的文本,但我们同样也可以对新闻报道、用户留言、政府公文等其他任意类型的文本进行分类。我们可以观察文档中出现的词,并把每个词的出现或者不出现作为一个特征,这样得到的特征数目就会跟词汇表中的词目一样多,一个文本便是一个特征向量。朴素贝叶斯是上节介绍的贝叶斯分类器的一个扩展,是用于文档分类的常用算法。

    ​ 假设词汇表中有1000个单词。要得到好的概率分布,就需要足够的数据样本,假定样本数为 N。前面讲到的约会网站示例中有1000个实例,手写识别示例中每个数字有200个样本,而决策树 示例中有24个样本。其中,24个样本有点少,200个样本好一些,而1000个样本就非常好了。约会网站例子中有三个特征。由统计学知,如果每个特征需要N个样本,那么对于10个特征将需要 N10个样本,对于包含1000个特征的词汇表将需要N1000个样本。可以看到,所需要的样本数会随着特征数目增大而迅速增长。

    ​ 如果特征之间相互独立,那么样本数就可以从N^1000减少到1000×N。所谓独立(independence) 指的是统计意义上的独立,即一个特征或者单词出现的可能性与它和其他单词相邻没有关系。举个例子讲,假设单词bacon出现在unhealthy后面与出现在delicious后面的概率相同。当然,我们知道这种假设并不正确,bacon常常出现在delicious附近,而很少出现在unhealthy附近,这个假设正是朴素贝叶斯分类器中朴素(naive)一词的含义。朴素贝叶斯分类器中的另一个假设是,每个特征同等重要。其实这个假设也有问题。 如果要判断留言板的留言是否得当,那么可能不需要看完所有的1000个单词,而只需要看10~20个特征就足以做出判断了。尽管上述假设存在一些小的 瑕疵,但朴素贝叶斯的实际效果却很好。

    ​ 下面我们便以一个实例来分析它的实现过程:

    准备数据:从文本中构建词向量

    ​ 首先是生成文档数据,然后求得对应的词汇表,最后根据词汇表得到特征向量。

    def loadDataSet():
        '''
        创建实验样本
        :return: 第一个变量是进行词条切分后的文档集合;第二个变量是一个类别标签的集合  		
        '''
        postingList = [
            ['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],
            ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
            ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'loev', 'him'],
            ['stop', 'posting', 'stupid', 'worthless', 'garbage'],
            ['mr','licks', 'ate', 'my', 'steak', 'how', 'to', 'sotp', 'him'],
            ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']
        ]
        # 1 代表侮辱性文字,0代表正常言论
        classVec = [0, 1, 0, 1, 0, 1]
        return postingList, classVec
    
    def createVocabList(dataSet):
        '''
        生成特征文档的词库列表
        :param dataSet:
        :return: 词库列表
        '''
        #创建一个空集
        vocabSet = set([])
        # 创建两个集合的并集
        for document in dataSet:
            vocabSet = vocabSet | set(document)
        return list(vocabSet)
    
    def bagOfWords2VecMN(vocabList, inputSet):
        '''
        统计inputset中的词在vocablsit中出现次数,词袋模型
        :param vocabList: 词汇表
        :param inputSet: 某个文档
        :return: 文档向量,统计了文档中各个特征出现的次数
        '''
        # 创建一个其中所含元素均为0的向量,用于保存文档各特征出现的次数
        returnVec = [0]*len(vocabList)
        for word in inputSet:
            if word in vocabList:
                returnVec[vocabList.index(word)] += 1
            else:
                print("the word: %s is not in my Vocabulary!" % word)
        return returnVec
    
    训练算法:从词向量计算概率

    [p(c_{i}|w) = frac{p(w|c_{i})p(c_{i})}{p(w)}\ p(c_{i}|w) = frac{p(w_{0}|c_{i})p(w_{1}|c_{i})...p(w_{N}|c_{i})p(c_{i})}{p(w)} ]

    ​ 现在 已经知道一个词是否出现在一篇文档中,也知道该文档所属的类别。我们将使用上述公式,对每个类计算该值,然后比较这两个概率值的大小。实现伪代码如下:

    计算每个类别中的文档数目

    对每篇训练文档:

    ​  对每个类别:

    ​   如果词条出现在文档中→ 增加该词条的计数值

    ​   增加所有词条的计数值

    ​  对每个类别:

    ​   对每个词条:

    ​    将该词条的数目除以总词条数目得到条件概率

    ​  返回每个类别的条件概率

    ​ 根据现实情况修改分类器:

    ​ ①利用贝叶斯分类器对文档进行分类时,要计算多个概率的乘积以获得文档属于某个类别的概 率,即计算p(w0|1)p(w1|1)p(w2|1)。如果其中一个概率值为0,那么最后的乘积也为0。为降低这种影响,可以将所有词的出现数初始化为1,并将分母初始化为2。

    ​ ②另一个遇到的问题是下溢出,这是由于太多很小的数相乘造成的。当计算乘积 p(w0|ci)p(w1|ci)p(w2|ci)...p(wN|ci)时,由于大部分因子都非常小,所以程序会下溢出或者得到不正确的答案。一 种解决办法是对乘积取自然对数。在代数中有ln(a*b) = ln(a)+ln(b),于是通过求对数可以 避免下溢出或者浮点数舍入导致的错误。

    def trainNB0(trainMatrix, trainCategory):
        '''
        朴素贝叶斯分类器训练函数
        :param trainMatrix:文档矩阵
        :param trainCategory:由文档类别标签所构成的向量
        :return: 两个向量和一个概率
        '''
        # numTrainDocs 训练文档的数量,numWords 词库长度
        numTrainDocs = len(trainMatrix)
        numWords = len(trainMatrix[0])
        # 计算p(0)的概率
        pAbusive = sum(trainCategory) / float(numTrainDocs)
        # 计算p(wi|c0) 和 p(wi|c1), 初始化参数
        # p0Num = np.zeros(numWords)  # 分子, 某个词出现次数
        # p1Num = np.zeros(numWords)
        # p0Denom = 0.0   # 分母, 某一类出现词的个数
        # p1Denom = 0.0
        # 减少概率值为0的影响
        p0Num = np.ones(numWords)
        p1Num = np.ones(numWords)
        p0Denom = 2.0
        p1Denom = 2.0
        for i in range(numTrainDocs):
            if trainCategory[i] == 1:
                p1Num += trainMatrix[i]
                p1Denom += sum(trainMatrix[i])
            else:
                p0Num += trainMatrix[i]
                p0Denom += sum(trainMatrix[i])
        # 避免下溢出或者浮点数舍入导致的错误,采用对数的方式
        p1Vect = np.log(p1Num / p1Denom)
        p0Vect = np.log(p0Num / p0Denom)
        return p0Vect, p1Vect, pAbusive
    
    测试算法:朴素贝叶斯分类函数检验
    def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
        '''
        朴素贝叶斯分类函数
        :param vec2Classify: 待分类的向量
        :param p0Vec: 第0类各特征的概率向量
        :param p1Vec: 第1类各特征的概率向量
        :param pClass1: 文档属于class=1的概率
        :return: 分类结果
        '''
        # 由bayes公式可知,分母相同不用比较,分子取对数比较大小
        p1 = np.sum(vec2Classify * p1Vec) + np.log(pClass1)
        p0 = np.sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1)
        if p1 > p0:
            return 1
        else:
            return 0
    
    def testingNB():
        '''
        测试函数
        '''
        listOposts, listClasses = loadDataSet() # 数据集和标签
        myVocabList = createVocabList(listOposts) # 词库
        trainMat = []   # 数据集转换成词库01的序列集合
        for postinDoc in listOposts:
            trainMat.append(bagOfWords2VecMN(myVocabList, postinDoc))
        # 0类各词出现概率, 1类各词出现概率, 1类出现的概率
        p0V, p1V, pAb = trainNB0(trainMat, listClasses)
        testEntry = ['love', 'my', 'dalmation']
        thisDoc = bagOfWords2VecMN(myVocabList, testEntry)
        print(testEntry, "classified as:", classifyNB(thisDoc, p0V, p1V, pAb))
        testEntry = ['stupid', 'garbage']
        thisDoc = bagOfWords2VecMN(myVocabList, testEntry)
        print(testEntry, "classified as:", classifyNB(thisDoc, p0V, p1V, pAb))
    
    if __name__ == '__main__':
        testingNB()
    
    

    ​ 以上便是利用朴素贝叶斯分类器的实现过程,总结一下便是利用贝叶斯公式计算概率,取值最大的分类作为分类器的预测结果。

  • 相关阅读:
    POJ 1469 COURSES 二分图最大匹配
    POJ 1325 Machine Schedule 二分图最大匹配
    USACO Humble Numbers DP?
    SGU 194 Reactor Cooling 带容量上下限制的网络流
    POJ 3084 Panic Room 求最小割
    ZOJ 2587 Unique Attack 判断最小割是否唯一
    Poj 1815 Friendship 枚举+求最小割
    POJ 3308 Paratroopers 最小点权覆盖 求最小割
    1227. Rally Championship
    Etaoin Shrdlu
  • 原文地址:https://www.cnblogs.com/wys7541/p/13619663.html
Copyright © 2011-2022 走看看