1. 程式人生 > 其它 >樸素貝葉斯演算法——文字分類(離散型)

樸素貝葉斯演算法——文字分類(離散型)

樸素貝葉斯演算法:按照概率分類的演算法。

我們在豆瓣上經常能看到一些書籍和電影評價,有好的評價,也有差評。

關於影評好壞的結果是怎麼來的呢?後臺小姐姐一條條的看,然後進行分類嗎?利用我們的樸素貝葉斯演算法, 可以實現對文字的分類

在上程式碼之前先來進行一下數學預熱:

概率基礎複習

定義:概率定義為一件事發生的可能性,扔出一個硬幣,結果頭像朝上

P(X):取值在[0,1]

聯合概率、條件概率與相互獨立:

聯合概率:包含多個條件,且所有條件同時成立的概率

記作:P(A,B)

條件概率:就是事件A在另外一個事件B已經發生條件下的發生概率

記作:P(A|B)

相互獨立:

如果P(A,B) = P(A)P(B),則稱事件A與事件B相互獨立

樸素貝葉斯公式:

樸素貝葉斯,簡單理解,就是假定了特徵與特徵之間相互獨立的貝葉斯公式。

也就是說,樸素貝葉斯,之所以樸素,就在於假定了特徵與特徵相互獨立。

在這個案例中,需要用到的包:numpy

import numpy as np

def loadDataSet():
    """
    載入訓練資料, postingList是所有的訓練集, 每一個列表代表一條言論, 一共有6條言論 classVec代表每一條言論的類別,
     0是正常, 1是有侮辱性 返回 言論和類別
    :return:
    """
    # 步驟的缺失,和正常的比較,此處省略了文字的切割,都是直接弄好的,直接切割成單詞用了
    postingList=[['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']]
    classVec = [0,1,0,1,0,1]
    return postingList,classVec

def createVocabList(dataSet):
    """
    建立詞彙表, 就是把這個文件中所有的單詞不重複的放在一個列表裡面
    :param dataSet:
    :return:
    """
    vocabSet = set([])
    for data in dataSet:
        # 此處的|表示合併兩個集合
        vocabSet = vocabSet | set(data)
    return list(vocabSet)

def setofword(vacablist,inputdata):
    """
    製作詞向量矩陣
    將每一個文件轉換為詞向量, 然後放入矩陣中
    :param vocabSet:
    :param dataSet:
    :return:
    """
    vectors=[]
    # 兩個向量相乘,一開始預設都是0
    vectors = [0] * len(vacablist)
    for word in inputdata:
        if word in vacablist:
            vectors[vacablist.index(word)] = 1
        else:
            print(f'沒有{word}這個詞')
               # print('沒有 {} 這個詞'.format(word))
    return vectors

def trainNB1(trainMat,trainCategory):
    """
    根據上面set集合提取出來,一共有32個特徵
    trainMat是文件矩陣[]32位0,1,trainCategory是每篇文件類別標籤所構成的向量[0,1,0,1,0,1]
    """
    # 總的訓練語句數=文件矩陣的條數6
    numTrainDocs = len(trainMat)
    # 總的詞數 此處為32
    numWords = len(trainMat[0])
    # 矩陣的sum是所有元素相加,此處是0+1+0+1+0+1=3
    sumOfCate1 = sum(trainCategory)
    # 6-3 = 3
    sumOfCate0 = len(trainCategory) - sumOfCate1
    # 侮辱性詞彙的概率 = 3/6 =0.5
    pAbusive = sumOfCate1/float(numTrainDocs)
    # 建立一個32位的1矩陣,表示初始的p0,為了避免出現零的情況,根據拉普拉斯公式建立1填充的矩陣
    p0Num = np.ones(numWords)
    # 建立一個32位的1矩陣,表示初始的p1,為了避免出現零的情況,根據拉普拉斯公式建立1填充的矩陣
    p1Num = np.ones(numWords)
    # 建立一個變數p0Denom 非侮辱性詞彙總數,為了避免出現零的情況,根據拉普拉斯公式設定為2.0
    p0Denom = 2.0
    # 建立一個變數p1Denom 侮辱性詞彙總數,為了避免出現零的情況,根據拉普拉斯公式設定為2.0
    p1Denom = 2.0
    #計算每個詞在對應類別中出現的樣本數量
    for i in range(numTrainDocs):
        if trainCategory[i] == 1:
            # 最終得到一個侮辱性的32位矩陣
            p1Num += trainMat[i]
            p1Denom += sum(trainMat[i])
        else:
            # 最終得到一個非侮辱性的32位矩陣
            p0Num += trainMat[i]
            p0Denom += sum(trainMat[i])
    # log不能直接對矩陣進行操作,np.log
    # 詞出現的樣本數與一個分類樣本所有樣本數量的比
    # 得到侮辱性詞彙概率矩陣 (矩陣除以一個浮點數)
    # 為了防止下溢位的問題,把矩陣轉化成對數的形式,f(x)與ln(f(x))趨勢和極值點相同
    p1Vect = np.log(p1Num/p1Denom)
    # 得到非侮辱性詞彙概率矩陣 (矩陣除以一個浮點數)
    p0Vect = np.log(p0Num/p0Denom)
    return p0Vect,p1Vect,pAbusive

# 用得到的概率分類
def classfyNB(vec2Classify, p0Vect, p1Vect, pAbusive):
    """
    引數包括要分類的向量vec2Classify
    以及使用函式trainNB1()計算得到的三個概率
    """
    # 使用numpy陣列來計算兩個向量相乘的結果,這裡的相乘是指對應元素相乘,
    # 即先將兩個向量中的第一個元素相乘,然後將第二個元素相乘,以此類推
    p1 = sum(vec2Classify * p1Vect) + np.log(pAbusive)
    p0 = sum(vec2Classify * p0Vect) + np.log(1.0 - pAbusive)
    if p1 > p0:
        return 1
    else:
        return 0

#測試程式碼
def testingNB():
    print('數字0表示沒有侮辱性,數字1表示有侮辱性:')
    dataSet, listClasses = loadDataSet()
    myVocabList = createVocabList(dataSet)
    # print(f'我是詞袋長度{len(myVocabList)}')
    # print(f'我是詞袋{dataSet}')
    trainMat = []
    for data in dataSet:
        trainMat.append(setofword(myVocabList,data))
    # print(f'我是矩陣{trainMat}')
    p0V,p1V,pAb = trainNB1(trainMat, listClasses)
    # print(f'我是概率{p0V,p1V}')
    testEntry = ['love','my','dalmation']
    thisDOc =setofword(myVocabList,testEntry)
    p1 = classfyNB(thisDOc,p0V,p1V,pAb)
    print(f'當前詞條{testEntry}的結果為:{p1}')
    testEntry = ['dog','stupid']
    thisDOc =setofword(myVocabList, testEntry)
    p2 = classfyNB(thisDOc, p0V, p1V, pAb)
    print(f'當前詞條{testEntry}的結果為:{p2}')

testingNB()

在程式碼測試完以後,可以呼叫sklearn中自帶的byes的API驗證一下自己寫的程式碼準確性,MultinomialNB(alpha = 1.0),預設拉普拉斯公式為1;

注:上述程式碼是把所有特徵放到一個矩陣中進行運算的,利用numpy的array格式進行計算,速度遠遠快於迴圈遍歷(它不經過python的直譯器,numpy的底層是c程式碼)