天天看点

简单文档分类——朴素贝叶斯算法朴素贝叶斯算法简单文档分类实例步骤总结朴素贝叶斯分类调用(sklearn)

朴素贝叶斯算法

朴素贝叶斯法是基于贝叶斯定理与特征条件独立假设的分类方法。

最为广泛的两种分类模型是决策树模型(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)
运行结果

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