美文网首页
简单文档分类——朴素贝叶斯算法

简单文档分类——朴素贝叶斯算法

作者: 是我真的是我 | 来源:发表于2019-12-01 18:11 被阅读0次

    朴素贝叶斯算法

    朴素贝叶斯法是基于贝叶斯定理特征条件独立假设的分类方法。
    最为广泛的两种分类模型是决策树模型(Decision Tree Model)和朴素贝叶斯模型(Naive Bayesian Model,NBM)。和决策树模型相比,朴素贝叶斯分类器(Naive Bayes Classifier 或 NBC)发源于古典数学理论,有着坚实的数学基础,以及稳定的分类效率。同时,NBC模型所需估计的参数很少,对缺失数据不太敏感,算法也比较简单。理论上,NBC模型与其他分类方法相比具有最小的误差率。但是实际上并非总是如此,这是因为NBC模型假设属性之间相互独立,这个假设在实际应用中往往是不成立的,这给NBC模型的正确分类带来了一定影响。

    朴素贝叶斯算法(Naive Bayesian algorithm) 是应用最为广泛的分类算法之一。
    朴素贝叶斯方法是在贝叶斯算法的基础上进行了相应的简化,即假定给定目标值时属性之间相互条件独立。也就是说没有哪个属性变量对于决策结果来说占有着较大的比重,也没有哪个属性变量对于决策结果占有着较小的比重。虽然这个简化方式在一定程度上降低了贝叶斯分类算法的分类效果,但是在实际的应用场景中,极大地简化了贝叶斯方法的复杂性。

    贝叶斯方法

    贝叶斯方法是以贝叶斯原理为基础,使用概率统计的知识对样本数据集进行分类。由于其有着坚实的数学基础,贝叶斯分类算法的误判率是很低的。贝叶斯方法的特点是结合先验概率和后验概率,即避免了只使用先验概率的主观偏见,也避免了单独使用样本信息的过拟合现象。贝叶斯分类算法在数据集较大的情况下表现出较高的准确率,同时算法本身也比较简单。

    简单文档分类实例

    import numpy as np
    
    ## 简单文档分类——朴素贝叶斯算法
    
    '''
    创建实验数据集(假设每个样本已经被分割完成)
    返回值分别是 X_train, y_train
    X_train:表示训练的样本
    y_train:表示对应训练样本的分类结果
    '''
    def loadDataSet():
        X_train = [
            ['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],
            ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
            ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
            ['stop', 'posting', 'stupid', 'worthless', 'garbage'],
            ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
            ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']
        ]  # 切分好的词条
        y_train = [0, 1, 0, 1, 0, 1]  # 类别标签向量,1代表侮辱性词汇,0代表非侮辱性词汇
        return X_train, y_train
    
    '''
    创建词汇表
    trainData:训练集trainData(每句话代表一条数据),每句话已被切割成单词了
    vocabList:训练集中所有的单词(无重复)
    '''
    def createVocabList(trainData):
        vocabSet = set()                    # 创建一条空的集合
        # 遍历训练集dataSet(每句话代表一条数据)中的每句话(已被切割成单词了)
        for doc in trainData:
            vocabSet = vocabSet | set(doc)  # 取并集,重复的只记一遍
            # print(vocabSet)
            vocabList = list(vocabSet)
        # print(len(vocabList))
        return vocabList
    
    '''
    vocabList:词汇表
    inputSet:当前样本(话,包含多个已被分割的单词)
    returnVec:返回每个样本(话)所对应的结果向量
    '''
    def setofwords2Vec(vocabList, inputSet):
        returnVec = [0] * len(vocabList)    # 创建词汇表大小的零向量
        for word in inputSet:   # 遍历当前样本(话)中的每一个单词
            if word in vocabList:
                returnVec[vocabList.index(word)] += 1
            else:
                print(f'{word} is not in my vocabulary!')
        return returnVec
    
    '''
    生成训练向量集
    dataSet:训练集
    vocabList:词汇表
    trainMat:返回整个训练集的结果向量集
            (n*m维,n表示样本数,m表示词汇表中词的个数)
    '''
    def get_trainMat(dataSet, vocabList):
        trainMat = []   # 初始化向量列表
        for inputSet in dataSet:    # 遍历训练集中每句话(所有的单词)
            returnVec = setofwords2Vec(vocabList, inputSet)  # 将当前词条向量化
            trainMat.append(returnVec)
        return trainMat
    
    '''
    拉普拉斯平滑朴素贝叶斯分类器训练函数
    要计算多个概率的乘积以获取文档属于哪个类别,如果其中一个概率为0,则最终计算结果为0,显然是不合理的。
    因此使分子初始化为1,分母初始化为2,这种做法就叫做拉普拉斯平滑,又被称为加一平滑。它就是为了解决0概率问题
    trainMat:训练结果向量集
    classVec:每个训练样本的结果向量
    '''
    def trainNB(trainMat, classVec):
        trainNum = len(trainMat)   # 计算训练集的数目(总文档数)
        ClassNum = len(trainMat[0])    # 计算训练集中每一条的大小(总单词数)
    
        p0Num = np.ones(ClassNum)  # 非侮辱类每个单词词频初始化为0,为了防止出现0而初始化为1
        p1Num = np.ones(ClassNum)  # 侮辱类每个单词词频初始化为0,为了防止出现0而初始化为1
        p0Denom = 2  # 非侮辱类所有单词的词频,同理初始化为2
        p1Denom = 2  # 侮辱类所有单词的词频
        num0 = 0    # 各类训练集个数
        num1 = 0    # 同上
    
        pNumList = [p0Num, p1Num]
        pDenomList = [p0Denom, p1Denom]
        numList = [num0, num1]
    
        for i in range(trainNum):  # 遍历每一个训练样本
            for j in range(2):  # 遍历每个类别
                if classVec[i] == j:    # 如果第i个训练样本为j类样本
                    pNumList[j] += trainMat[i]    # 在该样本出现的所有单词词频+1
                    pDenomList[j] += sum(trainMat[i])     # 同理对分母增加的此样本所有单词词频
                    numList[j] += 1  # 该类文档数目加1
    
        # 取log对数仍然是拉普拉斯平滑原理
        pVect, pNum = [], []
        for index in range(2):
            pVect.append(np.log(pNumList[index] / pDenomList[index]))
            pNum.append(numList[index] / float(trainNum))
        return pVect, pNum    # 返回属于非侮辱类、侮辱类和整个训练样本属于侮辱类的概率
    
    '''
    测试朴素贝叶斯分类器
    vec2Classify:向量化的测试样本
    '''
    def classifyNB(vec2Classify, pVect, pNum):
        # 计算公式  log(P(F1|C))+log(P(F2|C))+....+log(P(Fn|C))+log(P(C))
        vpl = []  # 文档分类到各类的概率
        for x in range(2):
            vpl.append(sum(vec2Classify * pVect[x]) + np.log(pNum[x]))
    
        reslist = ['非侮辱类', '侮辱类']
        index = [vpl.index(res) for res in vpl if res == max(vpl)]
        return reslist[index[0]]    # 返回分类值
    
    '''
    朴素贝叶斯测试函数
    '''
    def testingNB(testVec):
        dataSet, classVec = loadDataSet()       # 创建实验样本
        vocabList = createVocabList(dataSet)    # 创建词汇表
        trainMat = get_trainMat(dataSet, vocabList)    # 将训练样本向量化
        pVect, vpl = trainNB(trainMat, classVec)    # 训练朴素贝叶斯分类器
    
        thisone = setofwords2Vec(vocabList, testVec)   # 测试样本向量化
        return classifyNB(thisone, pVect, vpl)
    
    if __name__  == '__main__':
        # 测试样本1
        testVec1 = ['love', 'my', 'dalmation']
        print('分类结果是:', testingNB(testVec1))
    
        # 测试样本2
        testVec2 = ['stupid', 'garbage']
        print('分类结果是:', testingNB(testVec2))
    

    步骤总结

    • 1. 收集数据并预处理
      针对本例来说,省略了收集数据和预处理的大多数步骤。若无则应对训练集文件夹内的文件读取,并去掉多余的符号(若有停用词则删去对应的词),最后采用分词如jieba进行切分,然后进行下一步骤。
    • 2. 向量化处理
      将得到的训练集(和测试集)列表构建不重复的单词集合,然后向量化每一个样本列表,构建训练数据的向量集。
    • 3. 训练模型
      利用贝叶斯公式计算对应的词频进行相应概率计算,注意可能出现0概率现象,因此需要拉普拉斯平滑处理,即特殊初始化操作。
    • 4. 测试验证模型
      将测试数据同样进行向量化处理,然后计算每类概率情况,取概率最大值分类即为预测的类别。本例样本少不再进行验证准确性。

    朴素贝叶斯分类调用(sklearn)

    '''以上涉及获取数据、求词汇表、求训练数据集等方法省略,即替代方法trainNB、classifyNB'''
    
    from sklearn.naive_bayes import GaussianNB
    from sklearn.naive_bayes import MultinomialNB
    from sklearn.naive_bayes import BernoulliNB
    
    '''调用sklearn'''
    def sklearnTest(testVec, testDoc):
        # 重复步骤
        dataSet, classVec = loadDataSet()
        vocabList = createVocabList(dataSet)
        trainMat = get_trainMat(dataSet, vocabList)
        testVec = setofwords2Vec(vocabList, testVec)
    
        X = np.array(trainMat)
        Y = np.array(classVec)
        testVec = np.array(testVec)
    
        if testDoc == 'GaussianNB':  # 高斯朴素贝叶斯
           clf = GaussianNB()
        elif testDoc == 'MultinomialNB':    # 多项分布贝叶斯
            clf = MultinomialNB()
        elif testDoc == 'BernoulliNB':  # 伯努利分布贝叶斯
            clf = BernoulliNB()
        clf.fit(X, Y)
    
        # 测试
        input = [testVec]
        index = clf.predict(input)
        resList = ['非侮辱类', '侮辱类']
        return resList[index[0]]
    
    if __name__  == '__main__':
        # 测试样本1
        testVec1 = ['love', 'my', 'dalmation']
        print('普通朴素贝叶斯分类结果是:', testingNB(testVec1))
        print('高斯朴素贝叶斯分类结果是:', sklearnTest(testVec1, 'GaussianNB'))
        print('多项朴素贝叶斯分类结果是:', sklearnTest(testVec1, 'MultinomialNB'))
        print('伯努利朴素贝叶斯分类结果是:', sklearnTest(testVec1, 'BernoulliNB'))
    
        # 测试样本2
        testVec2 = ['stupid', 'garbage']
        print('普通朴素贝叶斯分类结果是:', testingNB(testVec2))
        print('高斯朴素贝叶斯分类结果是:', sklearnTest(testVec2, 'GaussianNB'))
        print('多项朴素贝叶斯分类结果是:', sklearnTest(testVec2, 'MultinomialNB'))
        print('伯努利朴素贝叶斯分类结果是:', sklearnTest(testVec2, 'BernoulliNB'))
    
    运行结果

    参考:
    朴素贝叶斯算法
    一步步教你轻松学朴素贝叶斯模型算法Sklearn深度篇3

    相关文章

      网友评论

          本文标题:简单文档分类——朴素贝叶斯算法

          本文链接:https://www.haomeiwen.com/subject/qvmzwctx.html