文章出處

樸素貝葉斯

標簽(空格分隔): 機器學習


樸素貝葉斯算法適合給文檔分類。

詞向量

先對文檔分詞,所有文檔中所有不同的詞構成詞匯表。每個文檔根據詞匯表能形成一個詞向量,1表示對應維度的詞條出現在文檔中,0表示詞條未出現。

這種想法推廣開來,發現向量是新時代的數字
比如一個可能發生的事件集合,可以表示為一個向量,每個維度對應一個事件發生的概率。

問題描述

任給一個文檔,怎樣確定其分類?
每個文檔對應一個詞向量$\vec w$,需要確定$p(c_i|\vec w)$的最大值,即:已知這個文檔的條件下,把這個文檔歸為哪個類的概率最大?

樸素貝葉斯

$$
分類(\vec w) = p(c_i|\vec w)_{max}=\frac{p(\vec w|c_i)p(c_i)}{p(\vec w)}
$$
其中$\vec w$表示詞向量。對于任意一個分類$c_i$,$p(\vec w)$是相同的,因而只需要確定$p(\vec w|c_i)$和$p(c_i)$。

$p(c_i)$:從訓練數據中,用簡單除法得到。
$p(\vec w|c_i)$:根據樸素貝葉斯假設,特征向量$\vec w$在$c_i$條件下相互獨立,也就是:給定目標值時屬性之間相互條件獨立
$$
p(\vec w|c_i)=\Pi p(w_j|c_i)
$$
而對于$p(w_j|c_i)$,只需要在掃描訓練數據時,依據$c_i$過濾數據項,在這些被過濾出的數據中(都是$c_i$類的),用簡單統計的結果相除,就得到在$j$維上的概率權值$e(w_j|c_i)$。為什么不是$p(w_j|c_i)$?因為最終要計算的是一個未知分類的向量$\vec x$的分類,需要將$\vec x$與概率權值向量$\vec e$對應位相乘,這才得到$p(w_j|c_i)$序列。$\vec x$中的元素非1即0,如果元素為1,那么$p(w_j|c_i)$就取$e(w_j|c_i)$,否則取0。

簡單優化

條件概率乘積為0

$p(\vec w|c_i)=\Pi p(w_j|c_i)$這個公式中,一旦$p(w_j|c_i)$等于0,整個結果就是0。但是對于$\vec w$,你怎樣判斷$\Pi p(w_j|c_i)=0$的所有$c_i$,該如何判斷哪個$c_i$更好?萬一所有的$c_i$對應的$p(\vec w|c_i)=\Pi p(w_j|c_i)$都等于0,該怎樣確定最終的分類?因此,要避免某個$p(w_j|c_i)$等于0。一個辦法是使用拉普拉斯平滑:將所有詞的出現數初始化為1,并將分母初始化為2。對應《機器學習實戰》中文版P62的代碼:

p0Num = onew(numWords)
p1Num = ones(numWords)
p0Denom = 2.0
p1Denom = 2.0

條件概率乘積下溢

相乘的都是概率,都是小于1的數,乘不了幾次就得到非常小的數字,產生下溢,干擾比較。一個方法是用取對數。沒錯,就是這么老套的辦法,但是管用,因為$\forall x \in (0,1)$,x越小,$\log x$的絕對值越大,或者說負的程度越大。對于$p(\vec w|c_i)=\Pi p(w_j|c_i)$取對數,就是一堆負數相加。這種情形下對于任意的$\vec x$是很方便的。

當然,公式中還有一個$p(c_i)$,也需要對數處理后相加。

代碼

使用分類器

在訓練階段我們只計算$e(\vec w|c_i)$,這其實就是訓練出來的分類器。一旦完成訓練,就可以使用分類器:

def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    """
    @param vec2Classify: 需要被分類的詞向量
    @param p0Vec: 第0類對應的詞概率向量
    @param p1Vec: 第1類對應的詞概率向量
    @param 
    """
    p1=sum(vec2Classify*p1Vec) + np.log(pClass1)
    p0=sum(vec2Classify*p0Vec) + np.log(1.0-pClass1)
    if p1>p0:
        return 1
    else:
        return 0

訓練模型

使用訓練數據,訓練出分類器,也就是獲取$e(\vec w|c_i)$和$e(c_i)$。同時修正了《機器學習實戰》P61的一個bug,即對于$p1Vect和p0Vect的計算,分母應當是訓練數據對應類的向量總數,而不是元素總和$:

def trainNB0(trainMatrix, trainCategory):
    """
    樸素貝葉斯分類器訓練函數:計算P(\vec w|c_1)和P(\vec w|c_2)
    @param trainMatrix:訓練矩陣
    @param trainCategory: 和trainMatrix配套使用的向量,表示每個trainMatrix[i]所屬類別
    
    """
    numTrainDocs = len(trainMatrix)  #文檔總數
    numWords=len(trainMatrix[0])  #向量維數:不重復的單詞數量
    pAbusive=sum(trainCategory)/float(numTrainDocs)
    p0Num=np.ones(numWords)
    p1Num=np.ones(numWords)
    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 = p1Num/p1Denom  ##書上這句是錯的
    #p0Vect = p0Num/p0Denom  ##這句也是錯的
    
    num_c1 = sum(trainCategory)
    num_c0 = len(trainCategory) - num_c1
    p1Vect = log(p1Num / num_c1)   #這才正確的是p(\vec w|c_1)
    p0Vect = log(p0Num / num_c0)   #這才正確的是p(\vec w|c_0)
    
    return p0Vect, p1Vect, pAbusive #pAbusive就是p(c_1)

理論上到這里就算出了基本的表達式,可以用來比較了。

總結

感覺《機器學習實戰》第四章樸素貝葉斯對于取對數的說明不夠清楚,使用分類器時的向量相乘更是沒有任何說明,讓人一頭霧水。不過章節后面的垃圾郵件分類和詞語傾向性的例子還不錯~


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()