機器學習之樸素貝葉斯算法原理與代碼實現

                                    樸素貝葉斯算法原理與代碼實現

                                                   本文系作者原創,轉載請註明出處:https://www.cnblogs.com/further-further-further/p/9910417.html 

算法原理

樸素貝葉斯是經典的機器學習算法之一,也是爲數不多的基於概率論的分類算法。樸素貝葉斯原理簡單,也很容易實現,多用於文本分類,比如垃圾郵件過濾。

該算法的優點在於簡單易懂、學習效率高、在某些領域的分類問題中能夠與決策樹、神經網絡相媲美。

但由於該算法以自變量之間的獨立(條件特徵獨立)性和連續變量的正態性假設爲前提,就會導致算法精度在某種程度上受影響。

marco 博客https://www.cnblogs.com/marc01in/p/4775440.html的簡單例子能夠很形象說明其核心思想:

 

現在出現一個新的點new_point (x,y),其分類未知。我們可以用p1(x,y)表示數據點(x,y)屬於紅色一類的概率,同時也可以用p2(x,y)表示數據點(x,y)屬於藍色一類的概率。那要把new_point歸在紅、藍哪一類呢?

我們提出這樣的規則:

如果p1(x,y) > p2(x,y),則(x,y)爲紅色一類。

如果p1(x,y) <p2(x,y),  則(x,y)爲藍色一類。

換人類的語言來描述這一規則:選擇概率高的一類作爲新點的分類。這就是貝葉斯決策理論的核心思想,即選擇具有最高概率的決策。

用條件概率的方式定義這一貝葉斯分類準則:

如果p(red|x,y) > p(blue|x,y), 則(x,y)屬於紅色一類。

如果p(red|x,y) < p(blue|x,y), 則(x,y)屬於藍色一類。

也就是說,在出現一個需要分類的新點時,我們只需要計算這個點的

max(p(c1 | x,y),p(c2 | x,y),p(c3 | x,y)...p(cn| x,y))。其對於的最大概率標籤,就是這個新點的分類啦。

那麼問題來了,對於分類i 如何求解p(ci| x,y)?

沒錯,就是貝葉斯公式:

    

如果要確定某個樣本歸屬於哪一類,則需要計算出歸屬不同類的概率,再從中挑選出最大的概率。

我們把上面的貝葉斯公式寫出這樣,也許你能更好的理解:

        

而這個公式告訴我們,需要計算最大的後驗概率,只需要計算出分子的最大值即可,而不同水平的概率P(C)非常容易獲得,故難點就在於P(X|C)的概率計算。而問題的解決,正是聰明之處,即貝葉斯假設變量X間是條件獨立的,故而P(X|C)的概率就可以計算爲:

      

 

一般流程

1. 收集數據:提供數據源(一般訓練數據與測試數據比例爲7:3);

2. 準備數據:將數據源解析成詞條向量;

3. 分析數據:檢查詞條確保解析的正確性;

4. 訓練算法:用訓練數據生成的分類器;

5. 測試算法:用訓練生成的分類器預測測試數據的結果,對比真實結果,計算錯誤率,衡量分類器的準確度。

6. 使用算法:通過錯誤率來評估分類器;

代碼實現(python)

myBayes.py:代碼實現文件,代碼已做了詳細註釋,包含3個示例:

1. 過濾侮辱文檔

2. 過濾垃圾郵件

3. 尋找在線RSS源排名靠前的單詞

  1 # -*- coding: utf-8 -*-
  2 """
  3 Created on Mon Nov  5 14:02:18 2018
  4 
  5 @author: weixw
  6 """
  7 
  8 import numpy as np
  9 
 10 def loadDataSet():
 11     postingList=[['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],
 12                  ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
 13                  ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
 14                  ['stop', 'posting', 'stupid', 'worthless', 'garbage'],
 15                  ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
 16                  ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
 17     classVec = [0,1,0,1,0,1]    #1 侮辱性, 0 not
 18     return postingList,classVec
 19 
 20 #輸入:原始數據集
 21 #功能:創建一個包含所有原始數據集詞語集合,這個列表中詞語不重複。
 22 #輸出:不重複詞列表
 23 def createVocabList(dataSet):
 24     #創建一個空集合,這個集合中內容唯一,並且可動態擴展。
 25     vocabSet = set([])  #create empty set
 26     #獲取每一行數據list
 27     for document in dataSet:
 28         #將每一行數據取出,合併到vocabSet集合中,vocabSet會自動過濾重複詞
 29         vocabSet = vocabSet | set(document) #union of the two sets
 30     #將集合轉化爲列表list
 31     return list(vocabSet)
 32 
 33 #輸入:列表集合(唯一),輸入集合(一行文檔內容)
 34 '''
 35 功能:檢查輸入集合單詞是否在列表集合中,在則在列表集合對應位置設置爲1,否則爲0
 36 將每個詞在文檔中出現與否作爲一個特徵,稱爲詞集模型(set-of-words model)
 37 '''
 38 #輸出:向量列表(判斷輸入文檔中每個單詞是否在詞彙樣本中)
 39 def setOfWords2Vec(vocabList, inputSet):
 40     #初始化爲0,長度爲vocabList長度的集合
 41     returnVec = [0]*len(vocabList)
 42     #檢查inputSet中每個單詞是否在vocabList集合中,在則在vocabList對應位置置1,否則不作處理
 43     for word in inputSet:
 44         if word in vocabList:
 45             returnVec[vocabList.index(word)] = 1
 46         else: print ("the word: %s is not in my Vocabulary!" % word)
 47     return returnVec
 48 
 49 #輸入:trainMatrix(文檔數組(處理爲0,1向量)),trainCategory(列標籤數組)
 50 '''
 51 功能:生成分類器(獲取最佳訓練參數權值)
 52 訓練樣本以及測試樣本大小要相同,通過標籤指定行詞語類別來計算訓練樣本對應位置權重參數值。
 53 這裏有個假定前提,每一行詞語是相互獨立的,也就有
 54 p(w/c1) = p(w0,w1,w2...wn/c1) = p(w0/c1)*p(w1/c1)*p(w2/c1)***p(wn/c1)
 55 '''
 56 #輸出:p0Vect(文檔中非侮辱性詞語概率),p1Vect(侮辱性詞語概率),pAbusive(標籤類中侮辱性標籤概率) 
 57 def trainNB0(trainMatrix,trainCategory):
 58     #數據集行大小
 59     numTrainDocs = len(trainMatrix)
 60     #數據集列大小
 61     numWords = len(trainMatrix[0])
 62     #標籤類中侮辱性標籤概率(0:非侮辱,1:侮辱)
 63     #引入float強制轉換是使結果爲小數
 64     pAbusive = sum(trainCategory)/float(numTrainDocs)
 65     #初始化爲數據集列大小的單位矩陣
 66     #p0Num:非侮辱性矩陣,p1Num:侮辱性矩陣
 67     p0Num = np.ones(numWords); p1Num = np.ones(numWords)      #change to ones() 
 68     #使np.log > 1,防止np.log分母爲0而無法計算
 69     
 70     #防止分母爲0。因爲計算每個子項概率採用的對數log(防止下溢出),是以2爲底的,如果pADDenom = 2.0,則避免了分母爲0的可能。
 71     p0Denom = 2.0; p1Denom = 2.0                        #change to 2.0
 72     #遍歷數據集每一行
 73     for i in range(numTrainDocs):
 74         #如果標籤類中該行定義爲侮辱性標籤
 75         if trainCategory[i] == 1:
 76             #將數據集中指定侮辱性行對應數據迭代求和,結果還是矩陣
 77             #統計數據集中指定侮辱性行存在的詞語(爲1),並求和,結果是數字
 78             p1Num += trainMatrix[i]
 79             p1Denom += sum(trainMatrix[i])
 80         #如果標籤類中該行定義爲非侮辱性標籤
 81         else:
 82             #將數據集中指定非侮辱性行對應數據迭代求和,結果還是矩陣
 83             p0Num += trainMatrix[i]
 84             #統計數據集中指定非侮辱性行存在的詞語(爲1),並求和,結果是數字
 85             p0Denom += sum(trainMatrix[i])
 86     #這裏有個假定前提,每一行詞語是相互獨立的,也就有
 87     #p(w/c1) = p(w0,w1,w2...wn/c1) = p(w0/c1)*p(w1/c1)*p(w2/c1)***p(wn/c1)
 88     #計算數據集中侮辱性詞語概率p(w/c1)
 89     #取對數是防止下溢出,由於概率因子都非常小,當計算乘積p(W0/Ci)*p(W1/Ci)*p(W2/Ci)...*p(Wn/Ci)時,得到結果會更小,
 90     #四捨五入會爲0,所以採用log(a*b) = log(a) + log(b)
 91     p1Vect = np.log(p1Num/p1Denom)          #change to log()
 92     #計算數據集中非侮辱性詞語概率p(w/c0)
 93     p0Vect = np.log(p0Num/p0Denom)          #change to log()
 94 
 95     return p0Vect,p1Vect,pAbusive
 96 '''
 97 輸入:vec2Classify(測試文檔(0.1向量)),p0Vec(非侮辱性概率訓練參數),
 98 p1Vec(侮辱性概率訓練參數),pClass1(標籤類中侮辱性標籤概率)
 99 功能:比較侮辱性和非侮辱性後驗概率,判斷測試文檔所屬類別
100 樸素貝葉斯公式:p(Ci/W) = p(W/Ci)p(Ci)/p(W)
101 輸出:1:侮辱性文檔 0:非侮辱性文檔
102 '''
103 def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
104     #計算文檔屬於侮辱性標籤概率p(c1/w)
105     #np.log是以2爲底,求和實際是相乘
106     p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)    #element-wise mult
107     #計算文檔屬於非侮辱性標籤概率p(c0/w)
108     p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1)
109     if p1 > p0:
110         #u"侮辱文檔"
111         return 1 
112     else: 
113         #u"非侮辱文檔"
114         return 0 
115  
116 #輸入:列表集合(唯一),輸入集合(文檔或詞彙表)
117 '''
118 功能:檢查輸入集合單詞是否在列表集合中,在則在列表集合對應位置加1
119 如果一個詞在文檔中出現不止一次,這可能意味着包含該詞是否出現在文檔中所不能表達的某種信息,
120 稱爲詞袋模型(bags-of-words model)。在詞袋中,每個單子可以出現多次,而在詞集中,每個詞只能出現一次。
121 '''
122 #輸出:向量列表(統計輸入文檔中每個單詞在詞彙樣本中個數)
123 def bagOfWords2VecMN(vocabList, inputSet):
124     returnVec = [0]*len(vocabList)
125     for word in inputSet:
126         if word in vocabList:
127             returnVec[vocabList.index(word)] += 1
128     return returnVec
129 
130 #測試(過濾網站惡意留言)
131 def testingNB():
132     #獲取數據集(數組),標籤列表
133     listOPosts,listClasses = loadDataSet()
134     #將數據集數組轉化爲內容唯一的數據集list
135     #創建一個包含所有原始數據集詞語集合,這個列表中詞語不重複。
136     myVocabList = createVocabList(listOPosts)
137     trainMat=[]
138     #遍歷數據集中每一行數組,將詞語轉化爲向量0,1(1:該行數據中詞語在myVocabList存在)
139     #詞語文檔 => 向量文檔
140     for postinDoc in listOPosts:
141         trainMat.append(bagOfWords2VecMN(myVocabList, postinDoc))
142     #計算分類器訓練參數
143     p0V,p1V,pAb = trainNB0(np.array(trainMat),np.array(listClasses))
144     #測試1
145     testEntry = ['love', 'my', 'dalmation']
146     #詞語文檔 => 向量文檔
147     thisDoc = np.array(bagOfWords2VecMN(myVocabList, testEntry))
148     #結果分類
149     print (testEntry,'classified as: ',  u"0 (非侮辱文檔)" if(classifyNB(thisDoc,p0V,p1V,pAb) == 0)  else u"1(侮辱文檔)")
150     #測試2
151     testEntry = ['stupid', 'garbage']
152     #詞語文檔 => 向量文檔
153     thisDoc = np.array(bagOfWords2VecMN(myVocabList, testEntry))
154     #結果分類
155     print (testEntry,'classified as: ', u"0 (非侮辱文檔)" if(classifyNB(thisDoc,p0V,p1V,pAb) == 0)  else u"1(侮辱文檔)")
156     
157 
158 #輸入:文本字符串
159 '''
160 功能:文本處理,過濾掉一些不需要的字符(?,&,=...)以及URL中en和py這樣的單詞,
161 並全部轉換爲小寫
162 '''
163 #輸出:處理後的字符串集合
164 def textParse(bigString):    #input is big string, #output is word list
165     import re
166     #分隔符是除單詞、數字外的任意字符都是分隔符
167     #string.split()只是以" "分隔
168     #正則匹配
169     listOfTokens = re.split(r'\W*', bigString)
170     #過濾掉少於2個字符的字符串,並全部轉換爲小寫
171     return [tok.lower() for tok in listOfTokens if len(tok) > 2] 
172 
173 #測試(過濾垃圾郵件)
174 #說明:50封郵件中隨機選取10封作爲測試樣本,剩下40封作爲訓練樣本
175 def spamTest():
176     docList=[]; classList = []; fullText =[]
177     #email文件下26封正常郵件,26封垃圾郵件
178     for i in range(1,26):
179         #準備數據,文本預處理
180         wordList = textParse(open('email/spam/%d.txt' % i).read())
181         #保存爲list(list遞增 二維)
182         docList.append(wordList)
183         #保存爲array(擴展 一維)
184         fullText.extend(wordList)
185         #標記郵件類別,垃圾郵件
186         classList.append(1)
187         #準備數據,文本預處理
188         wordList = textParse(open('email/ham/%d.txt' % i).read())
189         #保存爲list(list遞增 二維)
190         docList.append(wordList)
191         #保存爲array(擴展 一維)
192         fullText.extend(wordList)
193         #標記郵件類別,正常郵件
194         classList.append(0)
195     #將數據集數組轉化爲內容唯一的數據集list
196     #創建一個包含所有原始數據集詞語集合,這個列表中詞語不重複。
197     vocabList = createVocabList(docList)#create vocabulary
198     #python3.x   range返回的是range對象,不返回數組對象
199     trainingSet = list(range(50)); testSet=[]           #create test set
200     #隨機選取10封郵件作爲測試數據
201     for i in range(10):
202         #在50封郵件中隨機抽選
203         randIndex = int(np.random.uniform(0,len(trainingSet)))
204         #保存list中
205         testSet.append(trainingSet[randIndex])
206         #選取並保存後,去除該郵件對應的索引
207         del(trainingSet[randIndex])  
208     trainMat=[]; trainClasses = []
209     #剩餘40封郵件作爲訓練樣本,訓練分類器參數
210     for docIndex in trainingSet:#train the classifier (get probs) trainNB0
211         #將處理後的文本轉化爲詞向量(詞袋模型),並保存
212         trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex]))
213         #保存對應標籤
214         trainClasses.append(classList[docIndex])
215     #訓練,獲取最終訓練參數
216     p0V,p1V,pSpam = trainNB0(np.array(trainMat),np.array(trainClasses))
217     
218     errRate = 0.0;iterNumber = 20
219     #迭代20次
220     for i in range(iterNumber):
221         errorCount = 0
222         #遍歷測試數據集
223         for docIndex in testSet:        #classify the remaining items
224             #將處理後的測試數據轉化爲詞向量(詞袋模型),並保存
225             wordVector = bagOfWords2VecMN(vocabList, docList[docIndex])
226             #將訓練後的參數導入分類器,輸入測試的詞向量,與測試數據對應標籤比較,統計錯誤率
227             result = classifyNB(np.array(wordVector),p0V,p1V,pSpam)
228             if result != classList[docIndex]:
229                 errorCount += 1
230 #                print ("docIndex:%d,classification error:%s"%(docIndex, docList[docIndex]))
231                 print ("docIndex:%d,classification error, predict result: %d mark class result %d:"%(docIndex, result, classList[docIndex]))
232         #每次錯誤率
233         print ('the error rate is: ',float(errorCount)/len(testSet))
234         errRate += float(errorCount)/len(testSet)  
235     #平均錯誤率
236     print ("the mean error rate is:", float(errRate)/iterNumber)
237     
238     #return vocabList,fullText
239     
240 
241 #輸入:vocabList(去重詞彙表),fullText(未去重所有單詞)
242 '''
243 功能:計算高頻詞彙。遍歷詞彙表中每個詞並統計它在文本中出現的次數,然後根據出現次數從高到低對詞典進行排序,
244 最後返回排序最高的30個單詞。
245 '''
246 #輸出:按降序排列的前30個單詞以及單詞對應出現次數
247 def calcMostFreq(vocabList,fullText):
248     import operator
249     freqDict = {}
250     #遍歷詞彙表中每個單詞,統計每個單詞出現的次數,然後以鍵值對保存
251     for token in vocabList:
252         freqDict[token]=fullText.count(token)
253     #以單詞出現次數倒敘排列
254     sortedFreq = sorted(freqDict.items(), key=operator.itemgetter(1), reverse=True) 
255     #返回前30個鍵值對對象
256     return sortedFreq[:30]       
257 
258 #輸入:feed1(RSS1源),feed0(RSS0源)
259 '''
260 功能:
261 1 根據樸素貝葉斯公式生成分類器;
262 2 判斷隨機抽選測試數據屬於源RSS0或者RSS1;
263 3 計算分類錯誤率;
264 '''
265 #輸出:vocabList(詞彙表),p0V(源RSS0概率),p1V(源RSS1概率)
266 def localWords(feed1,feed0):
267 #    import feedparser
268     docList=[]; classList = []; fullText =[]
269     #獲取源RSS0,RSS1最小行大小
270     minLen = min(len(feed1['entries']),len(feed0['entries']))
271     for i in range(minLen):
272         #文本處理
273         wordList = textParse(feed1['entries'][i]['summary'])
274         #保存list(二維)
275         docList.append(wordList)
276         #保存array(一維)
277         fullText.extend(wordList)
278         #添加標籤
279         classList.append(1) #NY is class 1
280          #文本處理
281         wordList = textParse(feed0['entries'][i]['summary'])
282         #保存list(二維)
283         docList.append(wordList)
284         #保存array(一維)
285         fullText.extend(wordList)
286         #添加標籤
287         classList.append(0)
288     #生成詞彙表
289     vocabList = createVocabList(docList)#create vocabulary
290     #獲取詞彙表中單詞在文本中出現次數,並截取排名前30鍵值對
291     top30Words = calcMostFreq(vocabList,fullText)   #remove top 30 words
292     #去除詞彙表中排名前30單詞
293     for pairW in top30Words:
294         if pairW[0] in vocabList: vocabList.remove(pairW[0])
295     #RSS0+RSS1
296     trainingSet = list(range(2*minLen)); testSet=[]           #create test set
297     #隨機選取20作爲測試數據,保存,然後在訓練集中去除
298     for i in range(20):
299         randIndex = int(np.random.uniform(0,len(trainingSet)))
300         testSet.append(trainingSet[randIndex])
301         del(trainingSet[randIndex])  
302     trainMat=[]; trainClasses = []
303     #除去20個測試數據,剩下作爲訓練數據
304     for docIndex in trainingSet:#train the classifier (get probs) trainNB0
305         trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex]))
306         trainClasses.append(classList[docIndex])
307     #生成分類器,獲取訓練參數結果
308     p0V,p1V,pSpam = trainNB0(np.array(trainMat),np.array(trainClasses))
309     errorCount = 0
310     #計算測試分類錯誤率
311     for docIndex in testSet:        #classify the remaining items
312         wordVector = bagOfWords2VecMN(vocabList, docList[docIndex])
313         if classifyNB(np.array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:
314             errorCount += 1
315     print ('the error rate is: ',float(errorCount)/len(testSet))
316     return vocabList,p0V,p1V
317 
318 #輸入:ny(源RSS0),sf(源RSS1)
319 #功能:根據貝葉斯公式,通過訓練分類器獲得的分類概率,找到排名靠前的單詞。
320 #輸出:RSS0和RSS1出現頻率排名靠前的單詞
321 def getTopWords(ny,sf):
322 #    import operator
323     #獲取訓練樣本詞彙表,以及RSS0,RSS1的概率
324     vocabList,p0V,p1V=localWords(ny,sf)
325     topNY=[]; topSF=[]
326     #設置概率閾值,保存滿足條件的鍵值對
327     for i in range(len(p0V)):
328         if p0V[i] > -6.0 : topSF.append((vocabList[i],p0V[i]))
329         if p1V[i] > -6.0 : topNY.append((vocabList[i],p1V[i]))
330     #RSS0最終結果排序
331     sortedSF = sorted(topSF, key=lambda pair: pair[1], reverse=True)
332     print (u"RSS0最終結果排序")
333     #打印RSS0頻率最高詞彙
334     for item in sortedSF:
335         print (item[0])
336     #RSS1最終結果排序
337     sortedNY = sorted(topNY, key=lambda pair: pair[1], reverse=True)
338     print (u"RSS1最終結果排序")
339     #打印RSS1頻率最高詞彙
340     for item in sortedNY:
341         print (item[0])
貝葉斯代碼實現

測試文件:testMyBayes.py

 

 1 # -*- coding: utf-8 -*-
 2 """
 3 Created on Mon Nov  5 14:08:32 2018
 4 
 5 @author: weixw
 6 """
 7 
 8 import myBayes as mb;
 9 import feedparser as fp
10 
11 #過濾網站惡意留言
12 mb.testingNB();
13 
14 #過濾垃圾郵件
15 mb.spamTest()
16 
17 
18 ny = fp.parse('http://www.people.com.cn/rss/politics.xml')
19 length = len(ny['entries'])
20 
21 sf = fp.parse('http://www.people.com.cn/rss/world.xml')
22 length = len(ny['entries'])
23 
24 #vocabList,pSF,pNY = bs.localWords(ny,sf)
25 
26 #找到排名靠前的單詞
27 mb.getTopWords(ny,sf);
測試代碼

測試運行結果

1. 過濾侮辱文檔

由給定的標籤類可以看出,預測分類結果是正確的。

2. 過濾垃圾郵件(40封作爲訓練樣本,10封作爲測試樣本),迭代次數:20

結果一:對垃圾郵件的過濾準確度平均只有80%,而且還會把正確郵件錯認爲垃圾郵件。

結果二:對垃圾郵件的過濾準確度100%。

出現兩種不同結果的原因是:訓練樣本太少,導致準確度不穩定,並且產生了將正確郵件錯認爲垃圾郵件。

 

結果二

 

 

 

3. 尋找在線RSS源排名靠前的單詞

通過RSS源http://www.people.com.cn/rss/politics.xml,http://www.people.com.cn/rss/world.xml

來校驗。

 

參考文獻

《機器學習實戰》

 博客:http://www.cnblogs.com/marc01in/p/4775440.html

 

    

 不要讓懶惰佔據你的大腦,不要讓妥協拖垮了你的人生。青春就是一張票,能不能趕上時代的快車,你的步伐就掌握在你的腳下。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章