【機器學習基礎】樸素貝葉斯對B站彈幕分類(是否永久封禁)

YancyKahn發表於2020-10-08

樸素貝葉斯分類


前言

樸素貝葉斯分類是對於貝葉斯概率理論的一個應用,他可以對資料進行分類,是一個很常用的分類演算法。

一、使用條件概率進行分類

計算在不同分類下的條件概率,那個分類的概率大那麼久將其歸到概率大的分類一方。
說明

  • 在實際計算時候使用概率的對數進行計算,這樣可以避免乘法下溢。
  • 初始化是對計算概率的分子和分母初始化值不為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
  3. https://www.cnblogs.com/jpcflyer/p/11069659.html

相關文章