監督學習——朴素貝葉斯分類理論與實踐


對於給定的訓練數據,首先基於特征條件獨立假設學習輸入/輸出的聯合概率分布,然后基於此模型,對給定的輸入x,利用貝葉斯定理求出后驗概率最大的輸出y。

條件概率:


條件概率應該比較熟悉,P(A|B) 表示事件B已經發生的條件下,事件A發生的概率。計算公式如下:

貝葉斯定理:


 

image

獨立性


事件的獨立性:

假設 A,B是兩個事件,如果滿足等式: P(AB)  = P(A)*P(B) ,則稱時間A,B相互獨立.

由條件概率公式知:如果兩個事件獨立則: P(A|B) = P(A) .   (B的發生與否對於A的結果沒有影響)

參數(隨機變量)的獨立性:

假設(x1,x2)是二位的隨機變量,其聯合分布函數為F(x1,x2),隨機變量x1,x2的邊緣分布函數為Fx1(x1)和Fx2(x2),如果對於任意x1,x2均有:

F(x1,x2) = Fx1(x1) * Fx2(x2)

該公式等價於: P{X1<x1,X2<x2} = P{X1<x1} * P{X2<x2}

則 x1, x2為相互獨立的隨機變量

在建模時,往往涉及到選取不同的特征變量(如顏色形狀等等),這些參數的獨立就是這里的參數獨立。

先驗概率


先驗概率(prior probability)是指根據以往經驗和分析得到的概率,如全概率公式,它往往作為"由因求果"問題中的"因"出現的概率。

后驗概率:


事情已經發生,要求這件事情發生的原因是由某個因素引起的可能性的大小,是后驗概率。

假設一個學校里有60%男生和40%女生。女生穿褲子的人數和穿裙子的人數相等,所有男生穿褲子。一個人在遠處隨機看到了一個穿褲子的學生。那么這個學生是女生的概率是多少?

使用貝葉斯定理,事件A是看到女生,事件B是看到一個穿褲子的學生。我們所要計算的是P(A
|B)。 P(A)是忽略其它因素,看到女生的概率,在這里是40% P(A')是忽略其它因素,看到不是女生(即看到男生)的概率,在這里是60% P(B|A)是女生穿褲子的概率,在這里是50% P(B|A')是男生穿褲子的概率,在這里是100% P(B)是忽略其它因素,學生穿褲子的概率,P(B) = P(B|A)P(A) + P(B|A')P(A'),在這里是0.5×0.4 + 1×0.6 = 0.8
根據貝葉斯定理,我們計算出后驗概率P(A
|B) P(A|B)=P(B|A)*P(A)/P(B)=0.25 可見,后驗概率實際上就是條件概率。

 

極大似然估計


用處:極大似然估計往往用來計算一個模型中的參數。 使用前提:“模型已定,參數未知”

原理:通過微分(如果有多個參數,可能需要解微分方程組)的方法尋找使樣本集合(x1,x2,x3,x4…..)出現概率最大的模型參數。下面是概率論中通過極大似然估計,計算高斯分布的兩個參數:(實際中的模型往往比高斯模型復雜的多,但是原理是一樣的)

image

至於為什么一階導數得到的方程組就能求得極大值,請參考這篇文章:http://www.cnblogs.com/hapjin/p/6633471.html

最大似然估計的一般求解過程:

  (1) 寫出似然函數;

  (2) 對似然函數取對數,並整理;

  (3) 求導數 ;

  (4) 解似然方程

 

朴素貝葉斯法


假如一個樣本的輸入為X(可以為一個向量x1,x2,x3…),輸出為Y(可以是分類問題,也可以是連續值)

假設: 條件獨立性假設:用於分類的特征在類確定額條件下都是條件獨立的。

朴素貝葉斯法通過訓練數據集學習聯合概率分布P(X,Y)。

先驗概率:   P(Y)

條件概率:  P(X = x|Y = y)

條件獨立性假設: 

image

最終得到后驗概率的計算公式(4.4) 及 貝葉斯分類器的計算公式(4.7)

image

注: 分母做了歸一下處理(比較雙方分母都化作1)

這里加一個問題: 為什么要條件獨立性假設,而不是直接利用數據求解條件概率 P(X = x|Y = y)?

這里X涉及到關於x所有屬性的聯合概率,直接根據樣本出現的概率來估計將會遇到嚴重的困難。例如,假設樣本d個屬性都是二值的將有2^d種可能的取值,在現實中這個往往大於訓練樣本數。

基於朴素貝葉斯的文本分類


需求

1. 郵件分為兩類,一種為正常郵件,另一種為侮辱性郵件(標簽分別用 0/1表示 )

2. 提供 正常郵件和侮辱性郵件(數據及其標簽)

3. 當輸入郵件數據時,系統需要輸出其標簽

文本數據預處理

1.  加載郵件數據以及他們的標簽(郵件分類:是否為惡意郵件)

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 is abusive, 0 not
    return postingList,classVec

2. 基於已有的郵件構建”單詞表“(所有郵件中包含的單詞)

def createVocabList(dataSet):
    vocabSet = set([])  #create empty set
    for document in dataSet:
        vocabSet = vocabSet | set(document) #union of the two sets
    return list(vocabSet)

3. 將已有的郵件映射到詞表中(如果郵件中某個單詞出現則此表中該 位置為 1 否則為0)

def setOfWords2Vec(vocabList, inputSet):
    returnVec = [0]*len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] = 1
        else: print "the word: %s is not in my Vocabulary!" % word
    return returnVec

訓練函數

將預處理好的數據輸入到該函數中,就能得到上面公式4.7中: P(x=xi | Y=1)或者 P(x=xi | Y=0) 的每項概率保存在返回的兩個數組。並且將 整體 的概率 P(Y=1) 也返回了。

# trainMatrix: 文本映射到“詞表”中的矩陣 
# trainCategory: 文本的 類別
def trainNB0(trainMatrix,trainCategory):
    numTrainDocs = len(trainMatrix)  # 文本的數量
    numWords = len(trainMatrix[0])   # 詞表的長度
    pAbusive = sum(trainCategory)/float(numTrainDocs)  # 類別為1的概率
    p0Num = ones(numWords);     # 存放計算的條件概率 P(xi|Y=0)
    p1Num = ones(numWords)      # 存放計算的條件概率 P(xi|Y=1)
    p0Denom = 2.0;
    p1Denom = 2.0
    for i in range(numTrainDocs):
        if trainCategory[i] == 1:
            p1Num += trainMatrix[i]
            p1Denom += sum(trainMatrix[i])
        else:
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i])
    p1Vect = log(p1Num/p1Denom)          #change to log()
    p0Vect = log(p0Num/p0Denom)          #change to log()
    return p0Vect,p1Vect,pAbusive

郵件分類

根據貝葉斯公式分別計算一封郵件是正常郵件還是侮辱性郵件的概率,如下所示:

def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    p1 = sum(vec2Classify * p1Vec) + log(pClass1)    #element-wise mult
    p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)
    if p1 > p0:
        return 1
    else:
        return 0

 

Scikit 關於貝葉斯開源代碼學習

navie_bayes源碼結構

bays類結構 (1)

 

BaseNB 接口

計算每種分類的似然概率

@abstractmethod
 def _joint_log_likelihood(self, X):
I.e. ``log P(c) + log P(x|c)`` for all rows x of X, as an array-like of
shape [n_classes, n_samples].

分類

 def predict(self, X):
        """
        Perform classification on an array of test vectors X.

        Parameters
        ----------
        X : array-like, shape = [n_samples, n_features]

        Returns
        -------
        C : array, shape = [n_samples]
            Predicted target values for X
        """
        jll = self._joint_log_likelihood(X)
        return self.classes_[np.argmax(jll, axis=1)]

 

針對源碼中定義了三個對外類

GaussianNB (當特征變量為高斯分布時,原理查看scikit網站中介紹)

父類中函數實現

#計算均值和方差的函數
@staticmethod
def _update_mean_variance(n_past, mu, var, X, sample_weight=None):
# 父類中_joint_log_likelihood函數實現
def _joint_log_likelihood(self, X):
    check_is_fitted(self, "classes_")

    X = check_array(X)
    joint_log_likelihood = []
    for i in range(np.size(self.classes_)):
        # P(y)
        jointi = np.log(self.class_prior_[i])  
        # siga_為方差 theta_為均值
        n_ij = - 0.5 * np.sum(np.log(2. * np.pi * self.sigma_[i, :]))
        n_ij -= 0.5 * np.sum(((X - self.theta_[i, :]) ** 2) /
                             (self.sigma_[i, :]), 1)
        joint_log_likelihood.append(jointi + n_ij)

    joint_log_likelihood = np.array(joint_log_likelihood).T
    return joint_log_likelihood

 

參考:

《機器學習實戰》

《統計學方法》

Scikit Learn: http://scikit-learn.org/stable/modules/naive_bayes.html#multinomial-naive-bayes


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM