天天看點

大道至簡:樸素貝葉斯分類器

萬物之始,大道至簡,衍化至繁。

       ——ifelse(is.element(this, 道德經), 道德經, unknown)

一、背景

提到貝葉斯分類,首先來看下貝葉斯其人,貝葉斯(Thomas Bayes,1701—1761)英國牧師、業餘數學家。可别小看了歐洲的牧師,孟德爾,被譽為“遺傳學之父”也曾為一名神父,假如你不記得孟德爾是誰,那麼你肯定記得高中生物上那個著名的豌豆實驗。

具有諷刺意味的是,當初貝葉斯發明機率統計理論是為了證明上帝的存在,而至死這個願望都沒有實作,不過感謝偉大的貝葉斯,因為他的無心插柳,才有了今天的貝葉斯公式。接下來,來一睹貝葉斯公式的風采,

P(B|A)=P(B)P(A|B)P(A)

公式看起來是不是很簡潔,看起來很有對稱美。記得上學那會數學老師的一句話,假如你算出來的答案不夠簡潔,那麼多半這道題你算錯了。貝葉斯公式有什麼意義呢?它解決了兩個事件條件機率的轉換問題。比如說,已知感冒導緻流鼻涕的機率,那麼流鼻涕有多大的機率感冒呢?貝葉斯可以解決這類問題。

二、貝葉斯分類

貝葉斯可以解決條件機率轉換,可是它怎麼與分類聯系起來的呢?

讓我以一個例子加以說明,假設有這樣一個資料集(本例來自樸素貝葉斯分類器的應用),

症狀(A1) 職業(A2) 疾病(B)

打噴嚏  護士   感冒

打噴嚏  農夫   過敏

頭痛   建築勞工 腦震蕩

頭痛   建築勞工 感冒

打噴嚏  教師   感冒

頭痛   教師   腦震蕩

那麼一個打噴嚏的建築勞工是感冒還是沒感冒呢?

根據貝葉斯定理,

P(感冒|打噴嚏x建築勞工) = P(打噴嚏x建築勞工|感冒) x P(感冒) / P(打噴嚏x建築勞工)

假定”打噴嚏”和”建築勞工”這兩個特征是獨立的,是以,上面的等式就變成了

P(感冒|打噴嚏x建築勞工) = P(打噴嚏|感冒) x P(建築勞工|感冒) x P(感冒) / P(打噴嚏) x P(建築勞工) = 0.66 x 0.33 x 0.5 / 0.5 x 0.33 = 0.66

同理,

P(非感冒|打噴嚏x建築勞工) = P(打噴嚏|非感冒) x P(建築勞工|非感冒) x P(非感冒) / P(打噴嚏) x P(建築勞工) = 0.33 x 0.33 x 0.5 / 0.5 x 0.33 = 0.33

因為P(感冒|打噴嚏x建築勞工) > P(非感冒|打噴嚏x建築勞工) ,是以我們更願意相信一個打噴嚏的建築勞工是感冒的。

從上面的例子可以看出,貝葉斯分類的步驟是這樣的:

  1. 設 x={a1,a2,⋯} 為一個待分類項,每個a為x的一個特征屬性。
  2. 有類别集合 C={y1,y2,⋯,yn} .
  3. 根據訓練集計算, P(y1|x),P(y2|x),⋯,P(yn|x) .
  4. 如果 P(yk|x)=max{P(y1|x),P(y2|x),⋯,P(yn|x)} ,則 x 的分類為yk。

說到貝葉斯分類,還有幾個需要注意的問題:

  1. 如果已知條件不止一個屬性,二是多個呢,這個時候貝葉斯公式可以寫作 P(y|a1a2⋯)=P(y)P(a1a2⋯|y)P(a1a2⋯)=P(y)P(a1|y)P(a2|y)⋯P(a1)P(a2)⋯

    上述公式假設特征屬性 a1,a2⋯ 互相獨立,這也是“樸素”一詞的由來。另外,可以看到對于不同的分類,分母都是恒定的,而我們隻想找到機率最大的類别,是以可以把分母省略,求條件機率的相對值, P(y|a1a2⋯)relative=P(y)P(a1|y)P(a2|y)⋯

  2. 不知道大家有沒有注意到,上面的已知條件都是離散值,如果是連續值呢,對于連續值通常有兩種辦法,一是将連續值截取為離散值,然後求機率,二是假定離散值服從高斯分布,即 f(x)=12π−−√σexp(−(x−μ)22σ2)

    因為我們隻需求機率的相對值,是以這裡隻需計算屬性的機率密度值即可。

  3. 還有一個問題,當某些類别下某個特征值計數為0,即 P(ai|yj) =0,這會使某些分類最終的機率為0,會降低分類器的準确性,為了解決這個問題,引入Laplace校準,就是對這些類别的某些特征值計數加1,這樣如果訓練樣本集數量充分大時,并不會對結果産生影響。

如果想更詳細的了解貝葉斯分類,請參考這兩篇文章分類算法之樸素貝葉斯分類和樸素貝葉斯分類器的應用。

接下來,我用R語言實作一個分類器并用一些資料集測試分類效果。

三、算法實作

程式主要由三部分組成:

分類器主要由下面幾個函數組成,具體的代碼見GitHub。

# 1.求各個分類機率P(ycol)
get.ytable <- function(ycol, trainset)
# 2.1求離散屬性xcol的條件機率P(xcol|ycol)
get.discrete.xtable <- function(xcol, ycol, trainset) 
# 2.2求連續屬性xcol的機率密度,假設服從高斯分布
get.continout.xdensity <- function(xcol, ycol, trainset)
# 3.對于某些機率為零的類别,采用Laplace校準設定預設值
get.defaultx <- function(ycol, trainset)
# 注:xcol特征屬性,ycol類别屬性,trainset訓練集
           

下面以基礎包裡的iris1資料集驗證一下分類器的效果,選取前四列為特征,預測鸢尾花的種類,

大道至簡:樸素貝葉斯分類器

圖上有兩條曲線,黑色為我實作的貝葉斯分類器,紅色虛線為e1071包裡的一個貝葉斯分類器實作。觀察可得,随着訓練集樣本數的增加,測試集的分類正确率越來越高。

再來看看特征屬性的選取對正确率的影響,

大道至簡:樸素貝葉斯分類器

這次隻選擇了第二列(花萼寬度)作為特征值,可以看到正确率明顯下降了。

再來看一個多分類問題,采用北京二手房這個資料集,

大道至簡:樸素貝葉斯分類器

通過房價和是否學區這兩列來預測房子所在的區,可以看到這兩個特征屬性的預測正确率穩定在0.4左右,下面再添加戶型、朝向、樓層三列,

大道至簡:樸素貝葉斯分類器

上圖顯示,添加了三個特征屬性後,正确率并沒有明顯的改善,但是如果再添加一個區域列(con),

大道至簡:樸素貝葉斯分類器

由圖觀察,添加了區域這一列後,正确率得到了大幅度的提升,事實上僅保留區域這一列,預測的正确率也很高,這是因為區域(con)與區(area)的相關性較強。

根據我實驗的結果,通常情況下,提高預測正确率的方法有兩種:

1. 增加訓練集樣本數,但是樣本到達一定的數目正确率就保持穩定,很難再提高了。

2. 選取恰當的特征,注意單純的增加特征數目并不能提高正确率,反而會引入更多的誤差造成過拟合。

  1. iris以鸢尾花的特征作為資料來源,該資料集共150條資料,包含了5個屬性:Sepal.Length(花萼長度),Sepal.Width(花萼寬度),Petal.Length(花瓣長度),Petal.Width(花瓣寬度),種類 ↩

繼續閱讀