樸素貝葉斯演算法——文字分類(離散型)
阿新 • • 發佈:2021-04-25
樸素貝葉斯演算法:按照概率分類的演算法。
我們在豆瓣上經常能看到一些書籍和電影評價,有好的評價,也有差評。
關於影評好壞的結果是怎麼來的呢?後臺小姐姐一條條的看,然後進行分類嗎?利用我們的樸素貝葉斯演算法, 可以實現對文字的分類。
在上程式碼之前先來進行一下數學預熱:
概率基礎複習
定義:概率定義為一件事發生的可能性,扔出一個硬幣,結果頭像朝上
P(X):取值在[0,1]
聯合概率、條件概率與相互獨立:
聯合概率:包含多個條件,且所有條件同時成立的概率
記作:P(A,B)
條件概率:就是事件A在另外一個事件B已經發生條件下的發生概率
記作:P(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程式碼)