概率是許多機器學習算法的基礎,在前面生成決策樹的過程中使用了一小部分關于概率的知識,即統計特征在數據集中取某個特定值的次數,然后除以數據集的實例總數,得到特征取該值的概率。
**目錄:**
* 一.基于貝葉斯理論的分類方法
* 二.關于樸素貝葉斯的應用場景
* 三.基于Python和樸素貝葉斯的文本分類
1.準備數據
2.訓練算法
3.測試算法
* 四.小結
**以下進入正文:**
**一.基于貝葉斯理論的分類方法**
假設有兩類數據組成的數據集如下:

其中,假設兩個概率分布的參數已知,并用`p1(x,y)`表示當前數據點`(x,y)`屬于類別一的概率;用`p2(x,y)`表示當前數據點`(x,y)`屬于類別二的概率。
貝葉斯決策理論的核心思想是:選擇高概率所對應的類別,選擇具有最高概率的決策。有時也被總結成“多數占優”的原則。
具體到實例,對于一個數據點`(x,y)`,可以用如下規則判定它的類別:
若`p1(x,y)>p2(x,y)`,那么點`(x,y)`被判定為類別一。?
若`p1(x,y)<p2(x,y)`,那么點`(x,y)`被判定為類別二。
當然,在實際情況中,單單依靠以上的判定無法解決所有的問題,因為以上準則還不是貝葉斯決策理論的所有內容,使用`p1(x,y)`和`p2(x,y)`?只是為了簡化描述。更多的,我們使用`p(ci|x,y)`?來確定給定坐標的點`(x,y)`,該數據點來自類別`ci`的概率是多少。具體地,應用貝葉斯準則可得到,該準則可以通過已知的三個概率值來計算未知的概率值:

則以上判定法則可轉化為:
若`p(c1|x,y)>p(c2|x,y)`,那么點`(x,y)`被判定為類別一。?
若`p(c1|x,y)<p(c2|x,y)`,那么點`(x,y)`被判定為類別二。
**二.關于樸素貝葉斯的應用場景**
機器學習的一個重要應用就是文檔的自動分類,而樸素貝葉斯正是文檔分類的常用算法。基本步驟是遍歷并記錄下文檔中出現的詞,并把每個詞的出現或者不出現作為一個特征。這樣便有跟文檔中出現過詞匯的個數一樣多的特征。若有大量特征時,使用直方圖效果更好。以下是樸素貝葉斯的一般過程:
1. 收集數據:這里使用RSS源
2. 準備數據:需要數值型&布爾型數據
3. 分析數據:有大量特征時,繪制特征的作用不大,此時使用直方圖效果更好
4. 訓練算法:計算不同的獨立特征的條件概率
5. 測試算法:計算錯誤率
6. 使用算法:如文檔分類
講到這里,你也許還會帶著疑問,為什么貝葉斯前會加上“樸素”,其實,這是基于樸素貝葉斯的一個假設,即:**特征之間相互(統計意義上)獨立**,如一個單詞出現的可能性與其他單詞相鄰沒有關系,當然這在實際情況中不一定是正確的,但無數實驗表明,這種假設是有必要的,而且樸素貝葉斯的實際效果其實很好。
樸素貝葉斯的另外一個假設是:每個特征同等重要。當然,這個假設也有問題(不然就不叫假設了……),但確實有用的假設。
**三.基于Python和樸素貝葉斯的文本分類**
從文本中提取特征,首先需要將文本進行拆分,轉化為詞向量,某個詞存在表示為1,不存在表示為0,這樣,原來一大串字符串便轉為簡單的0,1序列的向量。這種情況下只考慮某個詞是否出現,當然,你也可以使用記錄詞的出現次數作為向量,或者記錄不同詞出現的頻率等等。
1.準備數據:從文本中構建詞向量,這里考慮出現在所有文檔中的所有單詞,并將每一篇文檔轉化為詞匯表上的向量。下面的代碼實現了功能,其中:
函數`loadDataSet()`?創建了一些實驗樣本`postingList`和對應的標簽`listClass`,有一些樣本被標簽為帶有侮辱性文字;?
函數`createNonRepeatedList()`?統計并保存一個列表`vocList`,該列表包含所有文檔中出現的詞(不重復),這里使用了Python的`set`函數;?
函數`detectInput(vocList, inputStream)`使用了詞列表`vocList`,?`inputStream`為待檢測的word串,輸出文檔向量,向量的每一元素為`1`或`0`,分別表示詞匯表中的單詞在輸入文檔中是否出現。
~~~
# -*- coding: utf-8 -*-
"""
Created on Tue Sep 08 16:12:55 2015
@author: Administrator
"""
from numpy import *
# 創建實驗樣本,可能需要對真實樣本做一些處理,如去除標點符號
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'],
['stop', 'posting', 'stupid', 'worthless', 'garbage'],
['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
listClass = [0, 1, 0, 1, 0, 1] # 1代表存在侮辱性的文字,0代表不存在
return postingList, listClass
# 將所有文檔所有詞都存到一個列表中,用set()函數去除重復出現的詞
def createNonRepeatedList(data):
vocList = set([])
for doc in data:
vocList = vocList | set(doc) # 兩集合的并集
return list(vocList)
def detectInput(vocList, inputStream):
returnVec = [0]*len(vocList) # 創建和vocabList一樣長度的全0列表
for word in inputStream:
if word in vocList: # 針對某段words進行處理
returnVec[vocList.index(word)] = 1 # ?
else:
print "The word :%s is not in the vocabulary!" % word
return returnVec
~~~

2.訓練算法:從詞向量計算概率,將之前的貝葉斯準則中的`(x,y)`改為向量`w`, 其長度為詞向量的長度,如以下公式:

計算分為類別`ci`的概率`p(ci)`。通過類別i中的文檔數除以總的文檔數可計算。及`label_i/sum(label)`。
已知某個類別c,計算w在類別`ci`中的概率`p(w|ci)`。由于樸素貝葉斯假設所有特征相互獨立,故有:
`p(w|ci) = p(w0,w1,...,wn|ci) = p(w0|ci)*p(w1|ci)*...*p(w0|ci)`計算每個詞wj在已知類別i的概率,然后再相乘。
偽代碼如下:
~~~
計算每個類別中的文檔數目
對每篇訓練文檔:
對每個類別:
如果詞條出現在文檔中 -> 增加該詞條的計數值
增加所有詞條的計數值
對每個類別:
將該詞條的數目除以總詞條數目得到條件概率
返回每個類別的條件概率
~~~
貝葉斯分類器訓練函數代碼如下:
~~~
def trainNaiveBayes(trainMatrix, classLabel):
numTrainDocs = len(trainMatrix)
numWords = len(trainMatrix[0])
pBase = sum(classLabel) / float(numTrainDocs)
# The following Settings aim at avoiding the probability of 0
p0Num = ones(numWords)
p1Num = ones(numWords)
p0Denom = 2.0
p1Denom = 2.0
for i in range(numTrainDocs):
if classLabel[i] == 1:
p1Num += trainMatrix[i]
p1Denom += sum(trainMatrix[i])
else:
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
p0 = log(p0Num / p0Denom)
p1 = log(p1Num / p1Denom)
return p0, p1, pBase
~~~

3.測試算法:測試分類器的效果
在?`p(w0|ci)*p(w1|ci)*...p(w0|ci)`中,如果某個值為0,則會使得最后的乘積為0,故將所有詞初始化為至少出現一次。分母初始化為2,這樣不改變實際效果。同時,`p(w0|ci)*p(w1|ci)*...*p(w0|ci)`?取對數,得到:`ln(p(w0|ci))+ln(p(w1|ci))+...+ln(p(w0|ci))`, 由于對數函數`ln(x)`為單調遞增函數,因此在計算過程中對乘法取對數對概率曲線的單調性沒有影響(高等數學中常用的性質)。修改代碼后進行測試:
~~~
def trainNaiveBayes(trainMatrix, classLabel):
numTrainDocs = len(trainMatrix)
numWords = len(trainMatrix[0])
pBase = sum(classLabel) / float(numTrainDocs)
# The following Settings aim at avoiding the probability of 0
p0Num = ones(numWords)
p1Num = ones(numWords)
p0Denom = 2.0
p1Denom = 2.0
for i in range(numTrainDocs):
if classLabel[i] == 1:
p1Num += trainMatrix[i]
p1Denom += sum(trainMatrix[i])
else:
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
p0 = log(p0Num / p0Denom)
p1 = log(p1Num / p1Denom)
return p0, p1, pBase
trainMat = []
for doc in loadData:
trainMat.append(detectInput(vocList, doc))
p0,p1,pBase = trainNaiveBayes(trainMat, dataLabel)
#print "trainMat : "
#print trainMat
# test the algorithm
def naiveBayesClassify(vec2Classify, p0, p1, pBase):
p0res = sum(vec2Classify * p0) + log(1 - pBase)
p1res = sum(vec2Classify * p1) + log(pBase)
if p1res > p0res:
return 1
else:
return 0
def testNaiveBayes():
loadData, classLabel = loadDataSet()
vocList = createNonRepeatedList(loadData)
trainMat = []
for doc in loadData:
trainMat.append(detectInput(vocList, doc))
p0, p1, pBase = trainNaiveBayes(array(trainMat), array(classLabel))
testInput = ['love', 'my', 'dalmation']
thisDoc = array(detectInput(vocList, testInput))
print testInput, 'the classified as: ', naiveBayesClassify(thisDoc, p0, p1, pBase)
testInput = ['stupid', 'garbage']
thisDoc = array(detectInput(vocList, testInput))
print testInput, 'the classified as: ', naiveBayesClassify(thisDoc, p0, p1, pBase)
testNaiveBayes()
~~~

最后,對兩組word串進行檢測,第一段被判定為非侮辱性用語,而第二段則被判定為侮辱性用語,分類正確。
**四.小結**
以上實驗基本實現了樸素貝葉斯分類器,并正確執行了文本分類,后面要進一步學習,將樸素貝葉斯運用到垃圾郵件過濾、個人廣告獲取區域傾向等實際應用。