朴素贝叶斯分类

 

 


前言

朴素贝叶斯分类是对于贝叶斯概率理论的一个应用,他可以对数据进行分类,是一个很常用的分类算法。

一、使用条件概率进行分类

计算在不同分类下的条件概率,哪个分类的概率大那么就将其归到概率大的分类一方。
【机器学习基础】朴素贝叶斯对B站弹幕分类(是否永久封禁)_机器学习

  • 在实际计算时候使用概率的对数进行计算,这样可以避免乘法下溢。
  • 初始化是对计算概率的分子和分母初始化值不为0,但要保证分母>分子。这样可以避免有0值和除0。
  • 在比较不同的条件概率时,可以只计算分子(分母是一样的)。
二、获取B站小黑屋弹幕数据

见博客爬取B站小黑屋
获取到的数据为以下json格式

[
{'type': xxx     	// 表示封禁信息
'article': xxx}		//表示实际弹幕信息
]

本文中获取到的数据一共有2108组,使用前2050组为训练集,剩下的数据做测试数据集。
由于单词划分方式是直接进行对汉字和符号进行划分的(不够合理),错误率也比较高(25%左右)

三、代码
import numpy as np
import json

class Bayes():
    
    def __init__(self):
        pass

    def loadData(self, url):
        #导入json文件
        with open(url, 'r', encoding='utf-8') as f:
            data_dect = json.load(f)
        # print(data_dect)
        
        label = []
        posting_list = []
        self.data_size = len(data_dect)

        for item in data_dect:    
            label.append(item['type'])
            posting_list.append([word for word in item['article']])

        return_label = []

        # 分为永久封禁和非永久封禁
        for example in label:
            if example == '永久封禁':
                return_label.append(1)
            else:
                return_label.append(0)
        # print(label)
        # print(posting_list)
        return posting_list, return_label

    def creat_vocabulary_list(self, data_set):
        # 创建单词表
        vocabulary_list = set([])
        for document in data_set:
            # 合并两个集合
            vocabulary_list = vocabulary_list | set(document)
        
        return list(vocabulary_list)

    def is_word_in_vocab(self, vocab_list, input_set):
        # 某句话中是否有某个单词 构建词向量
        return_vector = [0] * len(vocab_list)

        for word in input_set:
                if word in vocab_list:
                    return_vector[vocab_list.index(word)] = 1
                else:
                    print("Word: %s have not deteced!" % word)
        return return_vector

    def train_naive_bayes(self, train_set, train_category_set):
        # 训练模型,返回先验概率,及
        # 永久封禁的总概率
        # 在永久封禁弹幕的条件下,词向量表中每个单词出现的概率
        # 在非永久封禁弹幕条件下,词向量表中每个带刺出现的概率
        # train_set 文件词向量矩阵
        # train_category_set 文档类型列表


        num_train_document = len(train_set)
        num_words = len(train_set[0])

        # 永久封禁弹幕的总概率
        p_abusive = sum(train_category_set) / num_train_document

        # 在所有文档某个单词总出现次数(永久/非永久)
        p_abusive_num = np.ones(num_words)
        p_unabusive_num = np.ones(num_words)

        
        # 在所有文档中单词出现总次数(永久/非永久)
        p_abusive_all_num = 2.0
        p_unabusive_all_num = 2.0

        for i in range(num_train_document): 
            if train_category_set[i] == 1: # 永久封禁弹幕
                p_abusive_num += train_set[i]
                p_abusive_all_num += np.sum(train_set[i])
            else:
                p_unabusive_num += train_set[i]
                p_unabusive_all_num += np.sum(train_set[i])

        # 永久封禁弹幕 计算log, 防止乘法下溢
        p_abusive_vec = np.log(p_abusive_num/p_abusive_all_num)
        # 非永久封禁弹幕
        p_unabusive_vec = np.log(p_unabusive_num/p_unabusive_all_num)

        # print(p_unabusive_vec, p_abusive_vec, p_abusive)
        return p_abusive, p_abusive_vec, p_unabusive_vec

    def classify(self, input_x, p_abusive, p_abusive_vec, p_unabusive_vec):
        # input_x 为一个词向量
        # 这里可以只比较分子, 分母相同

        p1 = np.sum(input_x * p_abusive_vec) + np.log(p_abusive)
        p0 = np.sum(input_x * p_unabusive_vec) + np.log(1.0 - p_abusive)

        # print(p1, p0)
        if p1 > p0:
            return 1
        else:
            return 0

    def test(self):
        # 测试
        URL = r'2020\ML\ML_action\\3.NaiveBayes\data\blackroom.json'
        posting_list, class_vec = self.loadData(URL)
        vocab_list = self.creat_vocabulary_list(posting_list)
        print("len = ", len(posting_list))
        train_size = 2050
        # 划分数据
        test_list = posting_list[train_size:]
        test_label = class_vec[train_size:]
        posting_list = posting_list[:train_size]
        class_vec = class_vec[:train_size]

        train_set = []
        for posting_document in posting_list:
            train_set.append(self.is_word_in_vocab(vocab_list, posting_document))
        # print(train_set, class_vec)

        index = 0
        rate = 0
        pAb, p1v, p0v = self.train_naive_bayes(np.array(train_set), np.array(class_vec))
        print(pAb, p1v, p0v)
        for example in test_list:
            test_input_data = np.array(self.is_word_in_vocab(vocab_list, example))
            print(example)
            # print(test_input_data)
            test_result = self.classify(test_input_data, pAb, p1v, p0v)
            # print("Bayes send back: %s, real class %s" % (test_result, test_label[index]))
            if test_result != test_label[index]:
                rate += 1
            index += 1

        print("error rate: %f" % float(rate/len(test_label)))
     
DEBUG = True
nb = Bayes()
if DEBUG:
    nb.test()
参考文献
  1. 机器学习实战书籍
  2. https://github.com/apachecn/AiLearning/blob/master/docs/ml/4.%E6%9C%B4%E7%B4%A0%E8%B4%9D%E5%8F%B6%E6%96%AF.md