1. 程式人生 > >《統計學習方法》 樸素貝葉斯 極大使然估計 Python實現

《統計學習方法》 樸素貝葉斯 極大使然估計 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實現。