Spam Or Ham讀書筆記

fairjm發表於2016-01-24

《Machine Learning Projects for .NET Developers》書中的第二章,用了樸素貝葉斯的方法來識別簡訊是垃圾資訊還是正常資訊。

章節從零開始講,所以在最開始考慮如何入手這個問題。分析了測試資料,發現垃圾簡訊有一些在正常簡訊中不太會用到的單詞,用了FREE(全大寫)做測試。最後發現結果感人,有85%的正確率。將此作為baseline,接下來用更科學的方式,再怎麼差也不能比baseline差。

通過決策樹,分析通過垃圾簡訊中有FREE的概率能不能反推得到簡訊中有FREE這個詞,這條簡訊是垃圾簡訊的概率是多少。開始引入(樸素)貝葉斯定理。

公式比較簡單易懂: P(A|B) = P(B|A) x P(A) / P(B)
又只用FREE一個單詞似乎不科學,那可以使用多個單詞,多個單詞出現的概率獨立。那就可以使用公式
P(A and B) = P(A) x P(B)(A B相互獨立 也就是交集是空)
在想到處理一些稀有單詞,可能單詞不在測試資料中,可以引入拉普拉斯平滑,這裡我直接上程式碼好了: let laplace count total = float (count+1) / float (total+1)
也就是對分子分母各加1,這個函式本身不會改變原先結果的大小關係,所以使用也沒什麼問題。

直接使用公式計算不是很方便所以要對公式進行化簡。具體化簡的過程可以參考書本,我這裡只給出最後的結果: enter image description here 制定的工作流: enter image description here

上面free、txt是通過實現分類器,給定一些用來做分類的token(這裡就是free和txt),統計訓練集得到每個token的Laplace(SMS contains token|spam)Laplace(SMS contains token|ham)
然後對實際資料進行上面score函式的運算就可以了

第一個實現,分詞器用了最簡單的取每個單詞並且小寫化,用了一個單詞txt做分類用的token(也就是流程圖裡只有txt),得到的結果是86%(小數點後面我不記得了...86.5%好像...)

可以發現實現比baseline好了一點,但還是不夠。接下去考慮優化。

使用測試資料裡所有的token作為分類用token,結果效果更差了,所以token的選擇要更有針對性。

把分詞器變為取每個單詞,不區分大小寫,考慮到垃圾簡訊常常使用全大寫的單詞,效果所有提升。

在第一個優化中,想到free主要出現在垃圾簡訊中,所以可以考慮取垃圾簡訊和正常簡訊的出現次數top N的token,得到的結果達到90%。

分析top N的token,發現會有一大堆you、the、in之類的詞,考慮用停用詞。最後簡化將垃圾簡訊和正常簡訊的top N的結果取交集得到都出現的詞,然後取並集,再去除之間的交集。
程式碼如下:

    let topHam = ham |> top (hamCount / 10) casedTokenizer
    let topSpam = spam |> top (spamCount / 10) casedTokenizer
    let topTokens = Set.union topHam topSpam
    let commonTokens = Set.intersect topHam topSpam
    let specificTokens = Set.difference topTokens commonTokens

結果就更好了。

接下去的優化分析了出現最少的topN token,分析到垃圾資訊中會有大量的電話號碼,而正常簡訊中更多的是一些數字。針對電話號碼做單獨的處理,修改分詞器,讓分詞器再檢查到電話號碼時變為一個統一的token__PHONE__,取這個名字是不想讓普通的token和他衝突。得到了更好的結果。

後來的優化涉及到考慮簡訊的長度,因為簡訊的長度是一個連續的值,所以對其用區間的方式變為幾個分離的值。接著套用貝葉斯的方法。 P(長度的分類/Spam) -> P(Spam/長度的分類) ,加入到上面的公式當中。效果我忘了,因為看書的時候這部分好像被我跳過自己沒敲程式碼。

最後是關於錯誤率帶來的影響的問題,是識別垃圾更重要還是識別正確資訊更重要。後來得到正確簡訊的識別率更重要。於是可以將正確簡訊的topN token的N變大一些(除以的百分比變小),垃圾的N小一些。

最後是總結。

相關文章