《統計學習方法》 樸素貝葉斯 極大使然估計 Python實現
程式碼可在Github上下載:程式碼下載
今天看了一下《統計學習方法》的樸素貝葉斯的演算法,然後結合參考了《機器學習實戰》一些程式碼。用Python實現了一下例4.1。
實現的是P50頁的例4.1,先簡單說下公式。
$$y=argmax_{y_k}{P(Y=y_k)\prod_j{P(X^{(j)}=x^{(j)}|Y=y_k)}}$$
有了這個公式我們大概知道了怎麼判斷輸入的資料是屬於哪一類的。
先別忘了匯入numpy包和編碼集
# coding:utf-8
import numpy as np
好了,來看一下執行的程式碼,瞭解下這個演算法的步驟。
dataSet = [[1, "S"], [1, "M"], [1, "M"], [1, "S"], [1, "S"], [2, "S"], [2, "M"], [2, "M"], [2, "L"], [2, "L"], [3, "L"], [3, "M"], [3, "M"], [3, "L"], [3, "L"]] labels = [-1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1] bayes = bayes() bayes.train(dataSet, labels) print bayes.predict([2, "S"])
資料集和測試樣本都是書上例4.1的。
首先我們需要建立一個詞條,就是一個包含著樣本所有特徵的詞彙表。這裡的詞條是[1, 2, 3, 'S', 'M', 'L']。
def createVocabList(self, dataSet): #建立詞彙表
vocabSet = set([])
for document in dataSet:
vocabSet = vocabSet | set(document) #|是求去重複的並集(每個詞只會出現一次)
return list(vocabSet)
可以看到,為了得到詞條,我們需要對每一個樣本進行取特徵,但是詞條裡是不重複的,這裡用了set函式(元素不重複的集合)來儲存詞條。
def setOfWord2Vec(self, vocabList, inputSet): #詞彙表向量
returnVec = [0] * len(vocabList) #vocablist大小的零向量
for word in inputSet: #遍歷輸入樣本的每個特徵
if word in vocabList:
returnVec[vocabList.index(word)] = 1 #如果發現有匹配的值就設定為1
return returnVec
這個是詞條向量,詞條裡面有所有特徵,那麼詞條向量則是一個樣本在詞條中的出現特徵的次數,比如[1, 'S'],那麼詞條是[1, 2, 3, 'S', 'M', 'L'],那麼詞條向量為[1, 0, 0, 1, 0, 0]。
接下來來看一下訓練的程式碼。
def train(self, dataSet, labels): #訓練樣本
self.vocabList = self.createVocabList(dataSet) #建立特徵詞彙表
trainMatrix = [] #多條詞條向量的矩陣(一個詞條向量代表著一個樣本在詞條中出現的次數)
for line in dataSet: #將每個訓練樣本轉換為詞條向量
trainMatrix.append(self.setOfWord2Vec(self.vocabList, line))
n = len(self.vocabList) #詞條的特徵數
pN1Num = np.zeros(n) #在類別為-1時,出現特徵的次數向量(N1 = negative 1)
p1Num = np.zeros(n)
numOfPN1 = 0 #標籤中出現-1的次數
numOfP1 = 0
for i in range(len(trainMatrix)):
if labels[i] == 1:
p1Num += trainMatrix[i] #與詞條向量相加
numOfP1 += 1
else:
pN1Num += trainMatrix[i]
numOfPN1 += 1
# print trainMatrix[i]
self.p1Vect = p1Num / numOfP1 #p1的各個隨機向量(特徵)的概率分佈
self.pN1Vect = pN1Num / numOfPN1
self.pClass1 = numOfP1 / float(len(labels)) #p(y=1)的概率
這段程式碼有點複雜,這段是訓練樣本的程式碼,主要是計算出訓練樣本的p(y=1)和p(y=-1)的概率,以及p(x=xi|y=1), p(x=xi|y=-1)的概率分佈,並且儲存起來。
來我們來看一下是怎麼寫的吧,我們記得一個樣本對應一個詞條向量,詞條向量的一個直觀理解就是這個樣本的特徵在詞條中出現的次數。那麼m個訓練樣本對應著m*n(特徵數)的矩陣,也就是trainMatrix。
接下來我們定義了一個pN1Num,這個是一個用zeros初始化的向量,我們這個例子中的特徵數是6個,那麼p1Num=[0,0,0,0,0,0],接下來我們來遍歷trainMatrix,判斷類別為1,還是-1。因為我們需要求出以及p(x=xi|y=1)和p(x=xi|y=-1)的概率分佈,當類別為1時,p1Num跟詞條向量相加,就得出1類別時,這個樣本的特徵出現的個數。比如樣本[1, 'M']對應[1, 0, 0, 1, 0, 0]與p1Num[0, 0, 0, 0, 0, 0]相加=[1, 0, 0, 1, 0, 0],當第二個類別為1的樣本時[1, 'S']對應[1, 1, 0, 0, 0, 0],相加=[2, 1, 0, 1, 0, 0],那麼用這個向量除以1類別出現的次數,就可以得到概率分佈了。
最後我們求出p(y=1)的概率之後,就可以,這裡為什麼不求出p(y=-1)的概率,是因為就2個類別,p(y=-1) = 1-p(y=1)。
好了,我們有了概率分佈和類別的概率之後就可以用這個來進行預測了。我們要判別的樣本是[2, "S"]。
def predict(self, inputData): #預測函式
inputVec = self.setOfWord2Vec(self.vocabList, inputData)#測試樣本的詞條向量
# np.multiply(self.p1Vect ,inputVec)
p1 = self.pClass1 #按照公式需要乘以p(y=1)的值,我們就以此為初始值
pN1 = (1 - self.pClass1)
for num in np.multiply(self.p1Vect ,inputVec): #概率分佈和詞條向量進行相乘,得出p(x=xi|y=1)的概率,然後相乘
if (num > 0):
p1 *= num
for num in np.multiply(self.pN1Vect ,inputVec):
if (num > 0):
pN1 *= num
print p1, pN1
if (p1 > pN1): #相比,誰大就傾向誰
return 1
else:
return -1
我們有了概率分佈,再跟詞條向量點乘,就得出了後面p(x=xi|y=1)的概率了。
按照公式,我們需要計算2個類別的概率,然後判斷誰大就屬於誰。
以上就是樸素貝葉斯的python實現。