1. 程式人生 > >貝葉斯2-樸素貝葉斯的python實現

貝葉斯2-樸素貝葉斯的python實現

OK,前文http://blog.csdn.net/lvhao92/article/details/50775860提到了一些圍繞貝葉斯的基礎概念,極大似然等等。這篇就是介紹大名鼎鼎的樸素貝葉斯分類器

寫文章之前百度了一下貝葉斯,發現大多數文章提到貝葉斯就是樸素貝葉斯。其實,這是有問題的。因為兩者是不一樣的。

如前文所述,計算後驗概率P(c|x)的最大難點就是條件概率P(x|c)是所有屬性上的聯合概率。這是很蛋疼的。所以,樸素貝葉斯為了避開這個,做了“條件獨立性假設”:對已知類別,假設所有屬性相互獨立。就是說每個屬性都能獨立的對分類結果發生影響。所以,這樣,我們的P(x|c)可以寫成(1)

d就是屬性的數目。Xi就是x在第i個屬性上的取值。前一篇文章為了簡單理解,所以就只取男生的一類屬性(身高)作為例子,其實男生有很多屬性:身高,體重,等等。因此,樸素貝葉斯分類器的表示式如下:

(2)

接下來我將通過一個很具體的例子--文件過濾來形象的幫助大家理解樸素貝葉斯。功能就是一篇文章,裡面有單詞,然後通過樸素貝葉斯判斷出來這篇是不是垃圾文章,可以用在E-mail的郵件過濾上。在這裡,上面的C就是好或者壞,好代表有用文章,壞代表垃圾文章。X就是文章,Xi是特徵,就是一個個的單詞,聰明的你會發現,其實這裡並不能用樸素貝葉斯來做這個事,因為前面所說樸素貝葉斯有個假設:組合的各個屬性概率要彼此獨立,在這裡就是說單詞出現於這個分類的概率應該是相互獨立的。事實上並不是如此,比如文章中有“醫院”這個單詞,很有可能就會有“病人”,“醫生”這樣的單詞,他們出現的概率是有相關影響的,並不是相互獨立的。但是我們只是在這裡舉個例子讓大家能理解,不要鑽牛角尖啦!

(語言是python,雖然我用的最多的工具是matlab,但是matlab對找工作並沒有什麼用,我也盡力所有的程式碼將慢慢的轉python等等,python也很簡單,我儘量多做註釋讓大家看懂)。

OK

用Notepad++新建一個bay.py檔案,接著在裡面新增函式

首先我們需要獲得單詞,下面這是一個以任何非字母類字元為分隔符將文字拆分成一個個單詞的函式。

<pre name="code" class="python">import re
import math
def getwords(doc):#doc就是文章啦。
  splitter=re.compile('\\W*')#python裡將正則表示式編譯成正則表示式物件
  words=[s.lower() for s in splitter.split(doc)<span style="font-size: 12.0000009536743px; font-family: Arial, Helvetica, sans-serif;">]</span><span style="font-size: 12.0000009536743px; font-family: Arial, Helvetica, sans-serif;"># split是劃分,就是對文章進行劃分,lower是小寫,並且將單詞小寫</span>
  return dict([(w,1) for w in words])#返回一組單詞
接下來就是對分類器進行訓練

首先,新建一個名為classifier的類:

class classifier:
  def __init__(self,getfeatures,filename=None):
    self.fc={}#統計特徵在各個分類中的數量
    self.cc={}#統計每個分類中的文件數量
    self.getfeatures=getfeatures#獲取特徵的方式,我們的例子中就是getwords
變數fc是不是不理解?舉個例子,‘am’:{‘bad’:1,‘good‘:4},就是’am’這個單詞在有用文章中出現4次在垃圾文章中出現1次。

下面新增的函式就是用以實現計數值的增加和獲取:

<pre name="code" class="python">  def incf(self,f,cat):#f為特徵,即為單詞,cat為型別
    self.fc.setdefault(f,{})
    self.fc[f].setdefault(cat,0)
    self.fc[f][cat]+=1

  def incc(self,cat):#增加某一分類的計數值
    self.cc.setdefault(cat,0)
    self.cc[cat]+=1 	

  def fcount(self,f,cat):#某一個特徵出現在某種分類中的次數
    if f in self.fc and cat in self.fc[f]:
      return float(self.fc[f][cat])
    return 0.0
	
  def catcount(self,cat):#返回某個分類中的內容數量
    if cat in self.cc:
      return float(self.cc[cat])
    return 0

  def categories(self):#所有分類的列表
    return self.cc.keys()

  def totalcount(self):#返回所有的值
    return sum(self.cc.values())
訓練函式則是
  def train(self,item,cat):
    features=self.getfeatures(item)
    for f in features:
      self.incf(f,cat)#針對這個分類下的每個特徵進行計數
    self.incc(cat)#增加該分類的計數值

我們來試試看


我們匯入了之後。發現。恩,經過三條語句的訓練,”lvhhh“這個詞在好的類別中出現了2次,在壞的類別中出現了0次。

現在,這個計數模組算是完成了。好的,繼續。

下面,將上述的計數轉化成概率,具體來說,這個概率就是單詞在這個分類中出現的概率,也就是用這個單詞在屬於某個分類的文件中出現的次數除以該分類文件的總述。(我的例子一條語句就是一個文件,簡單呀)程式碼也是非常的簡單。

  def fprob(self,f,cat):
    if self.catcount(cat)==0: return 0 #如果並不存在這個分類,那麼返回0
    return self.fcount(f,cat)/self.catcount(cat) #該單詞屬於特定分類除以該分類文件總數
試一下

這樣就能反饋出概率大小了。這就是我們希望的P(Xi|C)

接下來,我們就可以寫樸素貝葉斯的程式碼了

class naivebayes(classifier):
  def docprob(self,item,cat):
    features=self.getfeatures(item)   
    p=1
    for f in features: p*=self.fprob(f,cat)#根據樸素貝葉斯的各特徵獨立P(x|c)為所有特徵p(xi|c)的連乘。這個求出來的就是改類別C下,這條句子的概率P(文章|類別)
    return p
	
  def prob(self,item,cat):
    catprob=self.catcount(cat)/self.totalcount()#求的是P(類別),即這種類別的概率。
    docprob=self.docprob(item,cat)
    return docprob*catprob #就是P(類別|文章)=P(文章|類別)×P(類別)
最後,看一下結果


這樣,我們比如突然遇到一個句子“who is”,我們並不知道它到底是有用的句子還是垃圾句子,用僅由幾條語句訓練而成的樸素貝葉斯網路來判斷一下,發現更應該偏有用的句子的可能性大點。

總結一下,樸素貝葉斯與其他方法相比比較大的優勢在於它在接收大資料量訓練和查詢時所具備的高速度。比如,樸素貝葉斯允許我們每次僅使用一次訓練項,而其他演算法,比如SVM,一下子要把所有的訓練集都傳給它們訓練。這樣我們的樸素貝葉斯是可以不斷的更新的。

樸素貝葉斯還有一個優勢是,對於學習狀況的解釋也是相對比較簡單的。每個特徵的概率值都會被儲存起來,因此我們可以隨時檢視找到最合適的特徵來區分郵件垃圾與否。

不過,樸素貝葉斯也是有缺陷的,就比如它無法處理特徵組合帶來的變化。就是我前面提到的,有的詞單個出現的時候是有用的,而組合出現的時候往往暗示著這是垃圾郵件,這個時候樸素貝葉斯就很難辦。

好了。這就是樸素貝葉斯網路的簡單的介紹,你們,看明白了嗎?