贝叶斯③——Python实现贝叶斯文本分类(伯努利&多项式模型对比)

贝叶斯机器学习系列:
贝叶斯①——贝叶斯原理篇(联合概率&条件概率&贝叶斯定理&拉普拉斯平滑)
贝叶斯②——贝叶斯3种分类模型及Sklearn使用(高斯&多项式&伯努利)
贝叶斯④——Sklean新闻分类(TF-IDF)
贝叶斯⑤——搜狗新闻分类实战(jieba + TF-IDF + 贝叶斯)
贝叶斯⑥——银行借贷模型(贝叶斯与决策树对比)

虽然sklearn能直接调用贝叶斯分类,但是若能用Python代码敲出来,那也是非常有成就感的一件事,而且能进一步加深对算法过程的理解~

一、算法流程及函数解释

loadData():导入分类样本,如果样本是文档,先用jieba进行分词
vecabulary():用集合生成词典(set|set会生成并集且自动删除重复元素)
Word2Vetorm()&Word2Vetor_m():将样本转化成向量(用词典中的index[词语]获得下标),前者是伯努利模型,出现记录1,不出现记录0;后者是多项式模型,出现多少次就记多少,未出现则是0
cal_prob()&cal_prob_m():前者是用伯努利模型计算先验概率和类条件概率,后者是用多项式,这里使用了拉普拉斯平滑(参数为1)来解决零概率问题,以及取log对数化乘法为加法,简化计算
naive_byes_classify():生成分类器, 对样本分别计算不同类别的概率,概率更大者即是最终类别
testNB():总函数,将之前的函数全都调用一遍,得到分类器,再对测试样本进行预测

二、Python代码

① 导入训练样本

def loadData():
    postingList = [['伤心', '难受', '难过', '伤心', '郁闷'],
                   ['高兴', '愉悦', '欢欣', '开心', '开心', '明亮'],
                   ['难受', '低沉', '平静', '伤心'],
                   ['愉悦', '运动', '笑容', '明媚', '欢乐'],
                   ['低沉', '真坏', '噘嘴', '生气', '伤心'],
                   ['真好', '开心', '愉悦', '笑容', '甜美']]
    classVec = [0,1,0,1,0,1]  # 类别
    return postingList,classVec  # 返回已经分过词的文本矩阵和类别

② 根据训练样本生成词典

def vecabulary(dataset):
    vecabulary=set([])  # 使用集合生成词典
    for data in dataset:
        vecabulary = set(data) | vecabulary  # set|set可以生成并集并自动删除重复元素
    return list(vecabulary)  # 将集合变为列表

③ 根据词典将训练样本向量化(伯努利或多项式)

# 伯努利模型,出现为1,不出现为0
def Word2Vetor(dataset,vecabulary):
    sample = []
    for data in dataset:
        x = [0]*len(vecabulary)
        for feature in data:
            if feature in vecabulary:
                x[vecabulary.index(feature)]=1  # 这一步很灵活,以词典中词语的下标来作为样本中词语的下标
        sample.append(x)  # 将样本组合成矩阵
    return sample
    
# 多项式模型,计算出现次数,没有出现的是0
def Word2Vetor_m(dataset,vecabulary):
    sample = []
    for data in dataset:
        x = [0]*len(vecabulary)
        for feature in data:
            if feature in vecabulary:
                x[vecabulary.index(feature)] +=1  # 与伯努利区别在于这一步,伯努利是出现了就是1,不管出现多少次,这里是出现多少次就加多少
        sample.append(x)
    return sample

④ 计算类别概率和类条件概率(伯努利和多项式)

# 伯努利模型
def cal_prob(dataset,classVec):
    category_1 = sum(classVec)/len(classVec)  # 伯努利模型是以类别数/总数作为类别概率
    Pvec_1 = np.ones(len(dataset[0]))
    Pvec_0 = np.ones(len(dataset[0]))
    Pnum_1,Pnum_0 = 2.0,2.0  # 伯努利模型中分母是加上2,来减少分子加1后带来的概率偏差
    for i, data in enumerate(dataset):
        if classVec[i] == 1:  # 对于类别1
            Pvec_1 += data     # 样本向量加上1,拉普拉斯平滑
            Pnum_1 += sum(data)  # 类别1中单词总数
        else:
            Pvec_0 += data
            Pnum_0 += sum(data)
    Prob_1 = np.log(Pvec_1/Pnum_1)  # 类别1的条件概率
    Prob_0 = np.log(Pvec_0/Pnum_0)  #  类别0的条件概率
    return category_1,Prob_1,Prob_0
    
# 多项式模型
def cal_prob_m(dataset,classVec):
    Pvec_1 = np.ones(len(dataset[0]))
    Pvec_0 = np.ones(len(dataset[0]))
    s = len(dataset[0])  # 多项式分母是加上训练样本的单词总数,这里用词典的长度来作为单词数量
    Pnum_1,Pnum_0 =np.array([s]*len(dataset[0])),np.array([s]*len(dataset[0]))
    for i, data in enumerate(dataset):
        if classVec[i] == 1:
            Pvec_1 += data
            Pnum_1 += sum(data)
        else:
            Pvec_0 += data
            Pnum_0 += sum(data)
    Prob_1 = np.log(Pvec_1/sum(Pnum_1))  # 多项式类条件概率是用某单词在此类文档中出现的次数之和/此类文档的单词总数(包括重复的),这里取对数是为了后面方便计算
    Prob_0 = np.log(Pvec_0/sum(Pnum_0))
    category_1 = sum(Pvec_1)/(sum(Pnum_1) + sum(Pnum_0))  # 多项式类别概率是用类别所有词的出现次数之和/整个训练样本所有词的出现次数之和,也就是类别单词总数/训练样本单词总数
    return category_1,Prob_1,Prob_0

⑤ 生成贝叶斯模型

def naive_byes_classify(vec,category_1,Prob_1,Prob_0):
    test_class = [0]*len(vec)  # 先假设样本是一个全为0的向量
    for i,ve in enumerate(vec):
        test_1 = sum(Prob_1*ve) + np.log(category_1)  # 代入计算属于每一类别的概率
        test_0 = sum(Prob_0*ve) + np.log(1-category_1)
        if test_1 > test_0:  # 概率大的即为分类结果
            test_class[i]=1
    return test_class

⑥ 调用及测试

def testNB():
    postingList, classVec = loadData()
    veca = vecabulary(postingList)
    print(veca[:10])
    dataset = Word2Vetor(postingList, veca)
    print(dataset[:1])
    category_1, Prob_1, Prob_0 = cal_prob(dataset, classVec)
    print('训练集结果:', naive_byes_classify(dataset, category_1, Prob_1, Prob_0))
    test_1 = [['开心', '快乐', '明媚', '平静', '欢乐'],
              ['低沉', '难受', '伤心']]
    print('测试集结果:', naive_byes_classify(Word2Vetor(test_1,veca), category_1, Prob_1, Prob_0))

伯努利模型结果如下
在这里插入图片描述
训练样本的分类准确率是100% ,测试集类别也是100%

多项式模型结果如下:
在这里插入图片描述
训练样本模型分类准确率为100%,但是测试集分类准确率仅为50%,准确性不及伯努利。

三、两个模型差异分析

① 当训练样本数较少且文本单词量过少时,多项式模型中会出现大量的0或1,这个与伯努利是一样的,但是如果有极个别词出现次数特别多,就会导致概率向这个词倾斜,而伯努利因为只记录出现与否,不会记录次数,所以就不会有这个问题。

② 但是鉴于伯努利没有将样本的词频信息纳入考量,因此在样本较多且文档较长(比如新闻分类)时,多项式模型的劣势会被削弱,优势就能体现出来

因此,在训练样本数较少且文本单词量过少时,伯努利模型优于多项式模型;当样本足够丰富且文本单词量较多时,多项式模型优于伯努利模型

你可能感兴趣的:(贝叶斯,贝叶斯文本文类,python实现贝叶斯,伯努利与多项式在文本分类,贝叶斯分类,词典)