萬物之始,大道至簡,衍化至繁。
——ifelse(is.element(this, 道德經), 道德經, unknown)
一、背景
提到貝葉斯分類,首先來看下貝葉斯其人,貝葉斯(Thomas Bayes,1701—1761)英國牧師、業餘數學家。可別小看了歐洲的牧師,孟德爾,被譽爲“遺傳學之父”也曾爲一名神父,假如你不記得孟德爾是誰,那麼你肯定記得高中生物上那個著名的豌豆實驗。
具有諷刺意味的是,當初貝葉斯發明概率統計理論是爲了證明上帝的存在,而至死這個願望都沒有實現,不過感謝偉大的貝葉斯,因爲他的無心插柳,纔有了今天的貝葉斯公式。接下來,來一睹貝葉斯公式的風采,
公式看起來是不是很簡潔,看起來很有對稱美。記得上學那會數學老師的一句話,假如你算出來的答案不夠簡潔,那麼多半這道題你算錯了。貝葉斯公式有什麼意義呢?它解決了兩個事件條件概率的轉換問題。比如說,已知感冒導致流鼻涕的概率,那麼流鼻涕有多大的概率感冒呢?貝葉斯可以解決這類問題。
二、貝葉斯分類
貝葉斯可以解決條件概率轉換,可是它怎麼與分類聯繫起來的呢?
讓我以一個例子加以說明,假設有這樣一個數據集(本例來自樸素貝葉斯分類器的應用),
症狀(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建築工人) ,所以我們更願意相信一個打噴嚏的建築工人是感冒的。
從上面的例子可以看出,貝葉斯分類的步驟是這樣的:
- 設
x={a1,a2,⋯} 爲一個待分類項,每個a爲x的一個特徵屬性。 - 有類別集合
C={y1,y2,⋯,yn} . - 根據訓練集計算,
P(y1|x),P(y2|x),⋯,P(yn|x) . - 如果
P(yk|x)=max{P(y1|x),P(y2|x),⋯,P(yn|x)} ,則x 的分類爲yk 。
說到貝葉斯分類,還有幾個需要注意的問題:
- 如果已知條件不止一個屬性,二是多個呢,這個時候貝葉斯公式可以寫作上述公式假設特徵屬性
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)⋯ - 不知道大家有沒有注意到,上面的已知條件都是離散值,如果是連續值呢,對於連續值通常有兩種辦法,一是將連續值截取爲離散值,然後求概率,二是假定離散值服從高斯分佈,即因爲我們只需求概率的相對值,所以這裏只需計算屬性的概率密度值即可。
f(x)=12π−−√σexp(−(x−μ)22σ2) - 還有一個問題,當某些類別下某個特徵值計數爲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. 選取恰當的特徵,注意單純的增加特徵數目並不能提高正確率,反而會引入更多的誤差造成過擬合。
- iris以鳶尾花的特徵作爲數據來源,該數據集共150條數據,包含了5個屬性:Sepal.Length(花萼長度),Sepal.Width(花萼寬度),Petal.Length(花瓣長度),Petal.Width(花瓣寬度),種類 ↩