樸素貝葉斯算法 & 應用實例

轉載請註明出處:http://www.cnblogs.com/marc01in/p/4775440.htmlhtml

 

和師弟師妹聊天時常常說起,如有志於從事數據挖掘、機器學習方面的工做,在大學階段就要把基礎知識都帶上。 機器學習在大數據浪潮中逐漸展現她的魅力,其實《機率論》、《微積分》、《線性代數》、《運籌學》、《信息論》等幾門課程算是前置課程,固然要轉化爲工程應用的話,編程技能也是須要的,而做爲信息管理專業的同窗,對於信息的理解、數據的敏感都是很好的加分項。python

不過光說不練,給人的留下的印象是極爲淺薄的,從一些你們都熟悉的角度切入,或許更容易能讓人有所體會。算法

下面進入正題。編程

 

BTW,若是觀點錯誤或者引用侵權的歡迎指正交流。微信

 

1、樸素貝葉斯算法介紹

樸素貝葉斯,之因此稱爲樸素,是由於其中引入了幾個假設(不用擔憂,下文會說起)。而正由於這幾個假設的引入,使得模型簡單易理解,同時若是訓練得當,每每能收穫不錯的分類效果,所以這個系列以naive bayes開頭和你們見面。app

由於樸素貝葉斯是貝葉斯決策理論的一部分,因此咱們先快速瞭解一下貝葉斯決策理論。dom

假設有一個數據集,由兩類組成(簡化問題),對於每一個樣本的分類,咱們都已經知曉。數據分佈以下圖(圖取自MLiA):機器學習

 

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

咱們提出這樣的規則:post

若是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))。其對於的最大機率標籤,就是這個新點的分類啦。

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

沒錯,就是貝葉斯公式:

    

公式暫不推導,先描述這個轉換的重要性。紅色、藍色分類是爲了幫助理解,這裏要換成多維度說法了,也就是第二部分的實例:判斷一條微信朋友圈是否是廣告。

前置條件是:咱們已經擁有了一個平日廣大用戶的朋友圈內容庫,這些朋友圈當中,若是真的是在作廣告的,會被「熱心網友」打上「廣告」的標籤,咱們要作的是把全部內容分紅一個一個詞,每一個詞對應一個維度,構建一個高維度空間 (別擔憂,這裏未出現向量計算)。

當出現一條新的朋友圈new_post,咱們也將其分詞,而後投放到朋友圈詞庫空間裏。

這裏的X表示多個特徵(詞)x1,x2,x3...組成的特徵向量。

P(ad|x)表示:已知朋友圈內容而這條朋友圈是廣告的機率。

利用貝葉斯公式,進行轉換:

P(ad|X) = p(X|ad) p(ad) / p(X)

P(not-ad | X) = p(X|not-ad)p(not-ad) / p(X)

比較上面兩個機率的大小,若是p(ad|X) > p(not-ad|X),則這條朋友圈被劃分爲廣告,反之則不是廣告。

 

看到這兒,實際問題已經轉爲數學公式了。

看公式推導 (公式圖片引用):

樸素貝葉斯分類的正式定義以下:

      1、設爲一個待分類項,而每一個ax的一個特徵屬性。

      2、有類別集合

      3、計算

      4、若是,則

      那麼如今的關鍵就是如何計算第3步中的各個條件機率。咱們能夠這麼作:

      1、找到一個已知分類的待分類項集合,這個集合叫作訓練樣本集。

      2、統計獲得在各種別下各個特徵屬性的條件機率估計。即

      3、若是各個特徵屬性是條件獨立的,則根據貝葉斯定理有以下推導:

              

      由於分母對於全部類別爲常數,由於咱們只要將分子最大化皆可。又由於各特徵屬性是條件獨立的,因此有:

              

 

這裏要引入樸素貝葉斯假設了。若是認爲每一個詞都是獨立的特徵,那麼朋友圈內容向量能夠展開爲分詞(x1,x2,x3...xn),所以有了下面的公式推導:

  P(ad|X) = p(X|ad)p(ad) = p(x1, x2, x3, x4...xn | ad) p(ad)

假設全部詞相互條件獨立,則進一步拆分:

  P(ad|X) = p(x1|ad)p(x2|ad)p(x3|ad)...p(xn|ad) p(ad)

雖然現實中,一條朋友圈內容中,相互之間的詞不會是相對獨立的,由於咱們的天然語言是講究上下文的╮(╯▽╰)╭,不過這也是樸素貝葉斯的樸素所在,簡單的看待問題。

看公式p(ad|X)=p(x1|ad)p(x2|ad)p(x3|ad)...p(xn|ad) p(ad)

至此,P(xi|ad)很容易求解,P(ad)爲詞庫中廣告朋友圈佔全部朋友圈(訓練集)的機率。咱們的問題也就迎刃而解了。

 

2、構造一個文字廣告過濾器。

到這裏,應該已經有心急的讀者掀桌而起了,搗鼓半天,沒有應用。 (╯‵□′)╯︵┻━┻ 

"Talk is cheap, show me the code." 

邏輯均在代碼註釋中,由於用python編寫,和僞代碼沒啥兩樣,並且我也懶得畫圖……

 

  1 #encoding:UTF-8
  2 '''
  3 Author: marco lin
  4 Date: 2015-08-28
  5 '''
  6 
  7 from numpy import *
  8 import pickle
  9 import jieba
 10 import time
 11 
 12 stop_word = []
 13 '''
 14     停用詞集, 包含「啊,嗎,嗯」一類的無實意詞彙以及標點符號
 15 '''
 16 def loadStopword():
 17     fr = open('stopword.txt', 'r')
 18     lines = fr.readlines()
 19     for line in lines:
 20         stop_word.append(line.strip().decode('utf-8'))
 21     fr.close()
 22         
 23 '''
 24     建立詞集
 25     params:
 26         documentSet 爲訓練文檔集
 27     return:詞集, 做爲詞袋空間
 28 '''
 29 def createVocabList(documentSet):
 30     vocabSet = set([])
 31     for document in documentSet:
 32         vocabSet = vocabSet | set(document) #union of the two sets
 33     return list(vocabSet)
 34     
 35 '''
 36     載入數據
 37 '''
 38 def loadData():
 39     return None
 40     
 41 '''
 42    文本處理,若是是未處理文本,則先分詞(jieba分詞),再去除停用詞
 43 '''
 44 def textParse(bigString, load_from_file=True):    #input is big string, #output is word list
 45     if load_from_file:
 46         listOfWord = bigString.split('/ ')
 47         listOfWord = [x for x in listOfWord if x != ' ']
 48         return listOfWord
 49     else:
 50         cutted = jieba.cut(bigString, cut_all=False)
 51         listOfWord  = []
 52         for word in cutted:
 53             if word not in stop_word:
 54                 listOfWord.append(word)
 55         return [word.encode('utf-8') for word in listOfWord]
 56         
 57 '''
 58     交叉訓練
 59 '''
 60 CLASS_AD        = 1
 61 CLASS_NOT_AD    = 0
 62 
 63 def testClassify():
 64     listADDoc = []
 65     listNotADDoc = []
 66     listAllDoc = []
 67     listClasses = []
 68     
 69     print "----loading document list----"
 70     
 71     #兩千個標註爲廣告的文檔
 72     for i in range(1, 1001):
 73         wordList = textParse(open('subject/subject_ad/%d.txt' % i).read())
 74         listAllDoc.append(wordList)
 75         listClasses.append(CLASS_AD)
 76     #兩千個標註爲非廣告的文檔
 77     for i in range(1, 1001):
 78         wordList = textParse(open('subject/subject_notad/%d.txt' % i).read())
 79         listAllDoc.append(wordList)
 80         listClasses.append(CLASS_NOT_AD)
 81     
 82     print "----creating vocab list----"    
 83     #構建詞袋模型
 84     listVocab = createVocabList(listAllDoc)
 85     
 86     docNum = len(listAllDoc)
 87     testSetNum  = int(docNum * 0.1);
 88     
 89     trainingIndexSet = range(docNum)   # 創建與全部文檔等長的空數據集(索引)
 90     testSet = []                       # 空測試集
 91     
 92     # 隨機索引,用做測試集, 同時將隨機的索引從訓練集中剔除
 93     for i in range(testSetNum):
 94         randIndex = int(random.uniform(0, len(trainingIndexSet)))
 95         testSet.append(trainingIndexSet[randIndex])
 96         del(trainingIndexSet[randIndex])
 97     
 98     trainMatrix = []
 99     trainClasses = []
100    
101     for docIndex in trainingIndexSet:
102         trainMatrix.append(bagOfWords2VecMN(listVocab, listAllDoc[docIndex]))
103         trainClasses.append(listClasses[docIndex])
104     
105     print "----traning begin----"
106     pADV, pNotADV, pClassAD = trainNaiveBayes(array(trainMatrix), array(trainClasses))
107     
108     print "----traning complete----"
109     print "pADV:", pADV
110     print "pNotADV:", pNotADV
111     print "pClassAD:", pClassAD
112     print "ad: %d, not ad:%d" % (CLASS_AD, CLASS_NOT_AD)
113     
114     args = dict()
115     args['pADV'] = pADV
116     args['pNotADV'] = pNotADV
117     args['pClassAD'] = pClassAD
118     
119     fw = open("args.pkl", "wb")
120     pickle.dump(args, fw, 2)
121     fw.close()
122     
123     fw = open("vocab.pkl", "wb")
124     pickle.dump(listVocab, fw, 2)
125     fw.close()
126 
127     errorCount = 0
128     for docIndex in testSet:
129         vecWord = bagOfWords2VecMN(listVocab, listAllDoc[docIndex])
130         if classifyNaiveBayes(array(vecWord), pADV, pNotADV, pClassAD) != listClasses[docIndex]:
131             errorCount += 1
132             doc = ' '.join(listAllDoc[docIndex])
133             print "classfication error", doc.decode('utf-8', "ignore").encode('gbk')
134     print 'the error rate is: ', float(errorCount) / len(testSet)
135         
136 # 分類方法(這邊只作二類處理)
137 def classifyNaiveBayes(vec2Classify, pADVec, pNotADVec, pClass1):
138     pIsAD = sum(vec2Classify * pADVec) + log(pClass1)    #element-wise mult
139     pIsNotAD = sum(vec2Classify * pNotADVec) + log(1.0 - pClass1)
140     
141     if pIsAD > pIsNotAD:
142         return CLASS_AD
143     else: 
144         return CLASS_NOT_AD
145     
146 '''
147     訓練
148     params:
149         tranMatrix 由測試文檔轉化成的詞空間向量 所組成的 測試矩陣
150         tranClasses 上述測試文檔對應的分類標籤
151 '''
152 def trainNaiveBayes(trainMatrix, trainClasses):
153     numTrainDocs = len(trainMatrix)
154     numWords = len(trainMatrix[0]) #計算矩陣列數, 等於每一個向量的維數
155     numIsAD  = len(filter(lambda x: x == CLASS_AD, trainClasses))
156     pClassAD = numIsAD / float(numTrainDocs)
157     pADNum = ones(numWords); pNotADNum = ones(numWords)
158     pADDenom = 2.0; pNotADDenom = 2.0
159     
160     for i in range(numTrainDocs):
161         if trainClasses[i] == CLASS_AD:
162             pADNum += trainMatrix[i]
163             pADDenom += sum(trainMatrix[i])
164         else:
165             pNotADNum += trainMatrix[i]
166             pNotADDenom += sum(trainMatrix[i])
167         
168     pADVect = log(pADNum / pADDenom)
169     pNotADVect = log(pNotADNum / pNotADDenom)
170     
171     return pADVect, pNotADVect, pClassAD
172     
173 '''
174     將輸入轉化爲向量,其所在空間維度爲 len(listVocab)
175     params: 
176         listVocab-詞集
177         inputSet-分詞後的文本,存儲於set
178 '''
179 def bagOfWords2VecMN(listVocab, inputSet):
180     returnVec = [0]*len(listVocab)
181     for word in inputSet:
182         if word in listVocab:
183             returnVec[listVocab.index(word)] += 1
184     return returnVec
185     
186 '''
187     讀取保存的模型,作分類操做
188 '''
189 def adClassify(text):
190     fr = open("args.pkl", "rb")
191     args = pickle.load(fr)
192     pADV        = args['pADV']
193     pNotADV     = args['pNotADV']
194     pClassAD    = args['pClassAD']
195     fr.close()
196 
197     fr = open("vocab.pkl", "rb")
198     listVocab = pickle.load(fr)
199     fr.close()
200     
201     if len(listVocab) == 0:
202         print "got no args"
203         return
204         
205     text = textParse(text, False)
206     vecWord = bagOfWords2VecMN(listVocab, text)
207     class_type = classifyNaiveBayes(array(vecWord), pADV, pNotADV, pClassAD)
208         
209     print "classfication type:%d" % class_type
210     
211     
212 if __name__ == "__main__":
213     loadStopword()
214     while True:
215         opcode = raw_input("input 1 for training, 2 for ad classify: ")
216         if opcode.strip() == "1":
217             begtime = time.time()
218             testClassify()
219             print "cost time total:", time.time() - begtime
220         else:
221             text = raw_input("input the text:")
222             adClassify(text)
223             
View Code

 

代碼測試效果:

一、訓練。

二、實例測試。

分類爲1則歸爲廣告,0爲普通文本。

 

 

p.s.  

    此分類器的準確率,實際上是比較依賴於訓練語料的,機器學習算法就和純潔的小孩同樣,取決於其成長(訓練)條件,「吃的是草擠的是奶」,但,「不是全部的牛奶,都叫特侖蘇」。

相關文章
相關標籤/搜索