《統計學習方法》 樸素貝葉斯 貝葉斯估計 Python實現

由於使用極大似然估計會出現概率值爲0的情況,這會影響後續的計算。比如當有一個後驗概率爲0的時候,那麼會使我們最後計算先驗概率P(y=1)或者P(y=-1)出錯。
爲了解決這個問題我們一般引入一個 λ (>>0,但是通常設爲1)。
然後條件概率的貝葉斯估計是:Pλ(X(j)=aji|Y=ck)=Ni=1I(x(j)=aji,yi=cki)+λKi=1I(yi=ck)+Sjλ
先驗概率的貝葉斯估計是:Pλ(Y=ck)=Ni=1I(yi=ck)+λN+Kλ
好了,來看下代碼吧。
我們這次主要修改了上次代碼的2個地方,一個是創建詞條的函數,一個是訓練函數,並增加了一個構造函數用來初始化我們的λ
先看一下構造函數。

def __init__(self, lamb = 1):
        if (lamb < 0):  #因爲lamb大於等於0
            self.lamb = 1
        else:
            self.lamb = lamb

主要是將λ 進行初始化,對於非法的λ 值,我們也要進行一些處理,就是把它初始化成我們常用的1。
然後看一下創建詞條的函數。這裏做了一些修改。

def createVocabList(self, dataSet): #創建詞彙表
        vocabSet = set([])
        print dataSet
        # for document in dataSet:
        #     print document
        #     vocabSet = vocabSet | set(document)
        #     print vocabSet
        m, n = np.shape(dataSet)    #獲得數據集的行和列
        self.S = [] #每個特徵在自己的維度裏出現的次數
        for i in range(n):  #按維度來創建詞條,第一維,第二維這樣
            column = set([row[i] for row in dataSet]) #按列讀取
            vocabSet = vocabSet | set(column)
            self.S.extend(list(np.ones(len(column)) * len(column)))
        return list(vocabSet)

這裏把之前創建詞條的方式給改了,之前是按行讀取數據,然後用set()把所有特徵都寫到詞條裏。
這次樓主是按列來進行特徵選擇,讀取出列的數據後,用set()提取特徵,並寫入詞條。這次的數據集是2維的,第一維的特徵有{1, 2, 3},第二維的特徵有{‘S’, ‘M’, ‘L’}。然後我加了一個S列表,對應着條件概率的 Sj ,不過我是以向量的形式存放,到時好用向量來相乘獲得概率分佈的向量。
最後看一下訓練函數,我就改了倒數幾行。來看一下。

self.p1Vect =  (p1Num + self.lamb) / (numOfP1 + np.array(self.S) * self.lamb)  #p1的各個隨機向量(特徵)的概率分佈
self.pN1Vect = (pN1Num + self.lamb) / (numOfPN1 + np.array(self.S)  * self.lamb)
self.pClass1 = (numOfP1 + self.lamb) / float(len(labels) + len(set(labels)) * self.lamb) #p(y=1)的概率
return self.p1Vect, self.pN1Vect, self.pClass1

這裏主要是按公式進行修改的。
看下self.p1Vect,分子加上了λ ,分母加上了Sj (爲j維度的特徵數目)乘以λ 。爲了方便計算,這裏樓主使用了S作爲特徵數向量,即[3, 3, 3, 3, 3, 3]。如果我們有第一維的特徵{1, 2, 3, 4},第二維的特徵有{‘S’, ‘M’, ‘L’}。那麼S將會是[4, 4, 4, 4, 3, 3, 3]。
看下self.pClass1,對應着p(y=1),我們按照公式將分子增加了λ ,分母加上類別數K(本分中我們的類別有2個,一個是1,一個是-1),然後乘以λ
好好體會一下,你可以的。
這裏順便貼出全部代碼。

# coding:utf-8
# Designed by Chen
# 2017-8-19
import numpy as np
class bayes:
    def __init__(self, lamb = 1):
        if (lamb < 0):  #因爲lamb大於等於0
            self.lamb = 1
        else:
            self.lamb = lamb

    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] #與詞條向量相加
                print trainMatrix[i]
                numOfP1 += 1
            else:
                pN1Num += trainMatrix[i]
                numOfPN1 += 1
            # print trainMatrix[i]
        print "-->%s" % (p1Num + self.lamb)
        self.p1Vect =  (p1Num + self.lamb) / (numOfP1 + np.array(self.S) * self.lamb)  #p1的各個隨機向量(特徵)的概率分佈
        self.pN1Vect = (pN1Num + self.lamb) / (numOfPN1 + np.array(self.S)  * self.lamb)
        self.pClass1 = (numOfP1 + self.lamb) / float(len(labels) + len(set(labels)) * self.lamb) #p(y=1)的概率
        return self.p1Vect, self.pN1Vect, self.pClass1

    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

    def createVocabList(self, dataSet): #創建詞彙表
        vocabSet = set([])
        print dataSet
        # for document in dataSet:
        #     print document
        #     vocabSet = vocabSet | set(document)
        #     print vocabSet
        m, n = np.shape(dataSet)    #獲得數據集的行和列
        self.S = [] #每個特徵在自己的維度裏出現的次數
        for i in range(n):  #按維度來創建詞條,第一維,第二維這樣
            column = set([row[i] for row in dataSet])
            vocabSet = vocabSet | set(column)
            self.S.extend(list(np.ones(len(column)) * len(column)))
        return list(vocabSet)

    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

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"])
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章