樸素貝葉斯(Naive Bayesian)是最爲普遍使用的分類方法,它以機率論爲基礎,是基於貝葉斯定理和特徵條件獨立假設的分類方法。
git
樸素貝葉斯(Naive Bayesian)是基於貝葉斯定理和特徵條件獨立假設原則的分類方法。經過給出的特徵計算分類的機率,選取機率大的狀況進行分類。也是基於機率論的一種機器學習分類方法。分類目標肯定,屬於監督學習。github
經過幾率來衡量事件發生的可能性。機率論和統計學剛好是兩個相反的概念,統計學是抽取部分樣本進行統計來估算整體的狀況,而機率論是經過整體狀況來估計單個事件或者部分事情的發生狀況。所以,機率論須要已知的數據去預測未知的事件。
例如,咱們看到天氣烏雲密佈,電閃雷鳴並陣陣狂風,在這樣的天氣特徵(F)下,咱們推斷下雨的機率比不下雨的機率大,也就是p(下雨)>p(不下雨),因此認爲待會兒會下雨。這個從經驗上看對機率進行判斷。
而氣象局經過多年長期積累的數據,通過計算,今天下雨的機率p(下雨)=85%,p(不下雨)=15%,一樣的,p(下雨)>p(不下雨),所以今天的天氣預報確定預報下雨。這是經過必定的方法計算機率從而對下雨事件進行判斷。
複製代碼
爲何叫樸素貝葉斯:簡單,易於操做,基於特徵獨立性假設,也即各個特徵彼此獨立,互相不影響發生。算法
某個事件已發生的狀況下另一個事件發生的機率。計算公式以下:P(A|B)=P(A∩B) / P(B) 簡單理解:畫維恩圖,兩個圓圈相交的部分就是A發生B也發生了,由於求的是B發生下A發生的機率。B至關於一個新的樣本空間。AB/B便可。bash
機率相乘法則:P(A∩B)=P(A)P(B|A) or P(A∩B)=P(B)P(A|B) 獨立事件的機率:P(A∩B)=P(A)P(B)app
若是有窮k個互斥事件,B1, B2,,,Bk 而且 P(B1)+P(B2)+⋅⋅⋅+P(Bk)=1和一個能夠觀測到的事件A,那麼有: less
基於機率論,二分類問題以下: 若是p1 > p2, 分入類別1; 不然分入類別2。機器學習
其次,貝葉斯定理,有 p(ci|x,y) = p(x,y|ci) * p(ci) / p(x,y)
x, y 表示特徵變量,以下例子中的單詞。Ci表示類別。p(ci | x, y) 即表示在特徵x, y出現的狀況下,分入類別Ci的機率。結合如上: p(ci | x, y) > p(cj | x, y), 分入類別i, 不然分入類別j。ide
貝葉斯定理最大的好處是能夠用已知的三個機率去計算未知的機率,而若是僅僅是爲了比較p(ci|x,y)和p(cj|x,y)的大小,只須要已知兩個機率便可,分母相同,比較p(x,y|ci)p(ci)和p(x,y|cj)p(cj)便可。
函數
樸素貝葉斯經常使用與對文檔分類。根據文檔中出現的詞彙,判斷文章屬於什麼類別。將詞彙出現的特徵條件用詞向量W表示,由多個值組成,值的個數和訓練集中的詞彙表個數相同。 上面的貝葉斯公式能夠表示爲: p(ci|ω)=p(ω|ci) * p(ci) / p(ω)
各個單詞的出現不會相互影響,則p(ω|ci) = p(ω0|ci)*p(ω1|ci)*...* p(ωk|ci)
post
import numpy as np
np.seterr(divide='ignore', invalid='ignore') #消除向量中除以0的警告
# 獲取數據
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']]
classVec = [0, 1, 0, 1, 0, 1] #1表示侮辱性言論,0表示正常
return postingList, classVec
複製代碼
根據文檔詞彙構建詞向量:
def createVocabList(dataSet):
vocabSet = set([])
for document in dataSet:
vocabSet = vocabSet | set(document)
return list(vocabSet)
# 對輸入的詞彙表構建詞向量
def setOfWords2Vec(vocabList, inputSet):
returnVec = np.zeros(len(vocabList)) #生成零向量的array
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] = 1 #有單詞,該位置填充1
else:
print("the word: %s is not in my Vocabulary" % word)
# pass
return returnVec #返回0,1的向量
if __name__ == '__main__':
listPosts, listClasses = loadDataSet()
myVocabList = createVocabList(listPosts)
print(myVocabList)
複製代碼
輸出結果以下: ['flea', 'ate', 'how', 'licks', 'quit', 'problems', 'dog', 'I', 'garbage', 'help', 'is', 'cute', 'steak', 'to', 'worthless', 'please', 'has', 'posting', 'buying', 'love', 'food', 'so', 'my', 'take', 'dalmation', 'stop', 'park', 'not', 'stupid', 'him', 'mr', 'maybe']
, 表示不一樣類別言論去重後獲得的詞向量。 [ 1. 0. 0. 0. 0. 0. 1. 0. 0. 0. 1. 1. 1. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]
: 表示詞聚集1中的單詞是否在詞向量中出現。
如上,這個方法只記錄了每一個詞是否出現,並無記錄出現次數,成爲詞集模型。若是記錄詞出現的次數,這樣的詞向量構建方法稱爲詞袋模型,以下。本文只使用詞集模型。
# 詞袋模型
def bagofWords2VecMN(vocabList, inputSet):
returnVec = [0] * len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] += 1
return vocabList #返回非負整數的詞向量
複製代碼
運用詞向量計算機率:
def trainNB0(trainMatrix, trainCategory):
numTrainDocs = len(trainMatrix) #文檔數目
numWord = len(trainMatrix[0]) #詞彙表數目
print(numTrainDocs, numWord)
pAbusive = sum(trainCategory) / len(trainCategory) #p1, 出現侮辱性評論的機率 [0, 1, 0, 1, 0, 1]
p0Num = np.zeros(numWord)
p1Num = np.zeros(numWord)
p0Demon = 0
p1Demon = 0
for i in range(numTrainDocs):
if trainCategory[i] == 0:
p0Num += trainMatrix[i] #向量相加
p0Demon += sum(trainMatrix[i]) #向量中1累加其和
else:
p1Num += trainMatrix[i]
p1Demon += sum(trainMatrix[i])
p0Vec = p0Num / p0Demon
p1Vec = p1Num / p1Demon
return p0Vec, p1Vec, pAbusive
if __name__ == '__main__':
listPosts, listClasses = loadDataSet()
myVocabList = createVocabList(listPosts)
trainMat = []
trainMat = []
for postinDoc in listPosts:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
print(trainMat)
p0Vec, p1Vec, pAbusive = trainNB0(trainMat, listClasses)
print(p0Vec, p1Vec, pAbusive)
複製代碼
輸出結果稍微有點多,慢慢來看: trainMat
:表示數據中六個給定的特徵在詞集模型中的出現狀況。
array([ 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 1., 0.,
0., 0., 1., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 1., 1.]), array([ 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 1.,
0., 1., 0., 1., 1., 1., 0., 0., 0., 1., 0., 0., 0.,
0., 0., 1., 0., 0., 0.]), array([ 1., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.,
1., 0., 0., 1., 0., 0., 0., 1., 0., 0., 0., 0., 0.,
1., 1., 0., 0., 0., 1.]), array([ 0., 1., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0.,
0., 0., 0., 0., 0., 0.]), array([ 0., 1., 0., 0., 0., 0., 0., 1., 0., 1., 0., 0., 0.,
0., 0., 0., 1., 0., 0., 1., 0., 1., 0., 0., 0., 0.,
0., 0., 1., 1., 0., 1.]), array([ 0., 0., 1., 0., 1., 0., 0., 0., 1., 0., 0., 0., 0.,
0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 1., 1.,
0., 0., 0., 0., 0., 0.])]
複製代碼
print(numTrainDocs, numWord)
: 6 32 (6個文檔,一共32個詞彙) print(p0Vec, p1Vec, pAbusive)
:pAbusive是文檔中是侮辱性言論的機率,爲0.5。 而p0Vec
表示類別0(非侮辱言論)中的詞在詞向量中出現的機率:
[ 0. 0.04166667 0.04166667 0.04166667 0.04166667 0.
0.08333333 0.04166667 0. 0.04166667 0. 0.04166667
0. 0.04166667 0. 0. 0.04166667 0.04166667
0.04166667 0.04166667 0.04166667 0. 0. 0.04166667
0.04166667 0.04166667 0. 0.125 0. 0.04166667
0.04166667 0.04166667]
複製代碼
def trainNB1(trainMatrix, trainCategory):
numTrainDocs = len(trainMatrix) #文檔數目
numWord = len(trainMatrix[0]) #詞彙表數目
pAbusive = sum(trainCategory) / len(trainCategory) #p1, 出現侮辱性評論的機率
p0Num = np.ones(numWord) #修改成1
p1Num = np.ones(numWord)
p0Demon = 2 #修改成2
p1Demon = 2
for i in range(numTrainDocs):
if trainCategory[i] == 0:
p0Num += trainMatrix[i] #向量相加
p0Demon += sum(trainMatrix[i]) #向量中1累加其和
else:
p1Num += trainMatrix[i]
p1Demon += sum(trainMatrix[i])
p0Vec = np.log(p0Num / p0Demon) #求對數
p1Vec = np.log(p1Num / p1Demon)
return p0Vec, p1Vec, pAbusive
複製代碼
注意:這裏獲得p0Vec多是沒有規律的,但其對最後的機率比較沒有影響。
def classifyNB(vec2Classify, p0Vc, p1Vc, pClass1):
p1 = sum(vec2Classify * p1Vc) * pClass1
p0 = sum(vec2Classify * p0Vc) * (1-pClass1)
# p1 = sum(vec2Classify * p1Vc) + np.log(pClass1) #取對數,防止結果溢出
# p0 = sum(vec2Classify * p0Vc) + np.log(1 - pClass1)
if p1 > p0:
return 1
else:
return 0
複製代碼
解釋一下:vec2Classify是所需分類文檔的詞量。根據公式 p(ci|ω)=p(ω|ci)p(ci) / p(ω)
, 已知特徵向量求分類的機率等於 p(ω|ci)p(ci)
。忽略分母:
p(ci)好求,用樣本集中,ci的數量/總樣本數便可
p(ω|ci)因爲各個條件特徵相互獨立且地位相同,`p(ω|ci)=p(w0|ci)p(w1|ci)p(w2|ci)......p(wN|ci)`,能夠分別求p(w0|ci),p(w1|ci),p(w2|ci),......,p(wN|ci),從而獲得p(ω|ci)。
而求p(ωk|ci)也就變成了求在分類類別爲ci的文檔詞彙表集合中,單個詞項ωk出現的機率。
複製代碼
使用兩個不一樣的樣原本測試分類函數:
# 構造樣本測試
def testingNB():
listPosts, listClasses = loadDataSet()
myVocabList = createVocabList(listPosts)
trainMat = []
for postinDoc in listPosts:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
p0v, p1v, pAb = trainNB0(trainMat, listClasses)
# print(p0v, p1v, pAb)
testEntry = ['love']
thisDoc = setOfWords2Vec(myVocabList, testEntry)
print(testEntry, 'classified as', classifyNB(thisDoc, p0v, p1v, pAb))
testEntry = ['stupid', 'garbage']
thisDoc = (setOfWords2Vec(myVocabList, testEntry))
print(testEntry, 'classified as:', classifyNB(thisDoc, p0v, p1v, pAb))
if __name__ == '__main__':
testingNB()
複製代碼
觀察結果,能夠看到將兩個文檔正確的分類。 完整代碼請查看: