貝葉斯算法的基本原理和算法實現
一. 貝葉斯公式推導
樸素貝葉斯分類是一種十分簡單的分類算法,叫它樸素是因為其思想基礎的簡單性:就文本分類而言,它認為詞袋中的兩兩詞之間的關系是相互獨立的,即一個對象 的特征向量中每個維度都是相互獨立的。例如,黃色是蘋果和梨共有的屬性,但蘋果 和梨是相互獨立的。這是樸素貝葉斯理論的思想基礎。現在我們將它擴展到多維的情況:
樸素貝葉斯分類的正式定義如下:
1.設 x={a1,a2,…,am}為一個待分類項,而每個 a 為 x 的一個特征屬性。
2.有類別集合 C={y1,y2,…,yn}。
3.計算 P( y1|x) ,P( y2|x),…, P( yn|x)。
4.如果 P( yk|x) =max{P( y1|x),P( y2|x),…, P( yn|x)},則 x∈yk。
那麽現在的關鍵就是如何計算第 3 步中的各個條件概率。我們可以這麽做:
(1) 找到一個已知分類的待分類項集合,也就是訓練集。
(2) 統計得到在各類別下各個特征屬性的條件概率估計。即:
P(a1|y1) , P(a2|y1),…, P(am|y1);
P(a1|y2) , P(a2|y2),…, P(am|y2);
P(am|yn) , P(am|yn),…, P(am|yn)。
(3) 如果各個特征屬性是條件獨立的(或者我們假設它們之間是相互獨立的),則根 據貝葉斯定理有如下推導:
因為分母對於所有類別為常數,只要將分子最大化皆可。又因為各特征屬性是條 件獨立的,所以有:
根據上述分析,樸素貝葉斯分類的流程可以表示如下: 第一階段:訓練數據生成訓練樣本集:TF-IDF
第二階段:對每個類別計算 P(yi)
第三階段:對每個特征屬性計算所有劃分的條件概率 第四階段:對每個類別計算 P( x | yi ) P( yi )
第五階段:以 P( x | yi ) P( yi ) 的最大項作為 x 的所屬類別
二. 樸素貝葉斯算法實現
使用簡單的英文語料作為數據集:
def loadDataSet(): postingList=[[‘my‘, ‘dog‘, ‘has‘, ‘flea‘, ‘problems‘, ‘help‘, ‘please‘], [‘maybe‘, ‘not‘, ‘take‘, ‘him‘, ‘to‘, ‘dog‘, ‘park‘, ‘stupid‘], [‘my‘, ‘dalmation‘, ‘is‘, ‘so‘, ‘cute‘, ‘I‘, ‘love‘, ‘him‘,‘my‘], [‘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] #1 is abusive, 0 not return postingList,classVec
postList 是訓練集文本,classVec 是每個文本對應的分類。
根據上節的步驟,逐步實現貝葉斯算法的全過程:
1.編寫一個貝葉斯算法類,並創建默認的構造方法:
class NBayes(object): def __init__(self): self.vocabulary= [] # 詞典 self.idf=0 # 詞典的 idf 權值向量 self.tf=0 # 訓練集的權值矩陣 self.tdm=0 # P(x|yi) self.Pcates = {} # P(yi)--是個類別字典 self.labels=[] # 對應每個文本的分類,是個外部導入的列表 self.doclength = 0 # 訓練集文本數 self.vocablen = 0 # 詞典詞長 self.testset = 0 # 測試集
2.導入和訓練數據集,生成算法必須的參數和數據結構:
def train_set(self,trainset,classVec): self.cate_prob(classVec) # 計算每個分類在數據集中的概率:P(yi)
self.doclength = len(trainset) tempset = set() [tempset.add(word) for doc in trainset for word in doc ] # Th成詞典 self.vocabulary= list(tempset) self.vocablen = len(self.vocabulary) self.calc_wordfreq(trainset) # 計算詞頻數據集 self.build_tdm() # 按分類累計向量空間的每維值:P(x|yi)
3.cate_prob 函數:計算在數據集中每個分類的概率:P(yi)
def cate_prob(self,classVec): self.labels = classVec labeltemps = set(self.labels) # 獲取全部分類 for labeltemp in labeltemps: # 統計列表中重復的分類:self.labels.count(labeltemp) self.Pcates[labeltemp] = float(self.labels.count(labeltemp))/float(len(self.labels))
4.calc_wordfreq 函數:生成普通的詞頻向量
# Th成普通的詞頻向量 def calc_wordfreq(self,trainset): self.idf = np.zeros([1,self.vocablen]) # 1*詞典數 self.tf = np.zeros([self.doclength,self.vocablen]) # 訓練集文件數*詞典數 for indx in xrange(self.doclength): # 遍歷所有的文本 for word in trainset[indx]: # 遍歷文本中的每個詞 self.tf[indx,self.vocabulary.index(word)] +=1 # 找到文本的詞在字典中的位置+1 for signleword in set(trainset[indx]): self.idf[0,self.vocabulary.index(signleword)] +=1
5.build_tdm 函數:按分類累計計算向量空間的每維值:P(x|yi)
#按分類累計向量空間的每維值:P(x|yi) def build_tdm(self): self.tdm = np.zeros([len(self.Pcates),self.vocablen]) # 類別行*詞典列 sumlist = np.zeros([len(self.Pcates),1]) # 統計每個分類的總值 for indx in xrange(self.doclength): self.tdm[self.labels[indx]] += self.tf[indx] # 將同一類別的詞向量空間值加總 # 統計每個分類的總值--是個標量 sumlist[self.labels[indx]]= np.sum(self.tdm[self.labels[indx]]) self.tdm = self.tdm/sumlist # Th成 P(x|yi)
6.map2vocab
函數:將測試集映射到當前詞典
def map2vocab(self,testdata): self.testset = np.zeros([1,self.vocablen]) for word in testdata: self.testset[0,self.vocabulary.index(word)] +=1
7.predict 函數:預測分類結果,輸出預測的分類類別
def predict(self,testset): if np.shape(testset)[1] != self.vocablen: # 如果測試集長度與詞典不相等,退出程序 print "輸入錯誤"
exit(0) predvalue = 0 # 初始化類別概率 predclass = "" # 初始化類別名稱 for tdm_vect,keyclass in zip(self.tdm,self.Pcates): # P(x|yi) P(yi) temp = np.sum(testset*tdm_vect*self.Pcates[keyclass]) # 變量 tdm,計算最大分類值 if temp > predvalue:
predvalue = temp predclass = keyclass return predclass
三. 算法改進
為普通的詞頻向量使用 TF-IDF 策略,使之有能力修正多種偏差。
4.calc_tfidf 函數:以 tf-idf 方式Th成向量空間:
# Th成 tf-idf def calc_tfidf(self,trainset): self.idf = np.zeros([1,self.vocablen]) self.tf = np.zeros([self.doclength,self.vocablen])
for indx in xrange(self.doclength): for word in trainset[indx]: self.tf[indx,self.vocabulary.index(word)] +=1 # 消除不同句長導致的偏差 self.tf[indx] = self.tf[indx]/float(len(trainset[indx]))
for signleword in set(trainset[indx]): self.idf[0,self.vocabulary.index(signleword)] +=1 self.idf = np.log(float(self.doclength)/self.idf) self.tf = np.multiply(self.tf,self.idf) # 矩陣與向量的點乘 tf x idf
四. 評估分類結果
# -*- coding: utf-8 -*- import sys import os from numpy import * import numpyas np from Nbayes_lib import * dataSet,listClasses = loadDataSet() # 導入外部數據集 # dataset: 句子的詞向量, # listClass 是句子所屬的類別 [0,1,0,1,0,1] nb = NBayes() # 實例化 nb.train_set(dataSet,listClasses) # 訓練數據集 nb.map2vocab(dataSet[0]) # 隨機選擇一個測試句 print nb.predict(nb.testset) # 輸出分類結果
分類結果
1
執行我們創建的樸素貝葉斯類,獲取執行結果
貝葉斯算法的基本原理和算法實現