貝葉斯分類演算法例項 --根據姓名推測男女

zzzzMing發表於2018-11-29

一.從貝葉斯公式開始

貝葉斯分類其實是利用用貝葉斯公式,算出每種情況下發生的概率,再取概率較大的一個分類作為結果。我們先來看看貝葉斯公式:

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

其中P(A|B)是指在事件B發生的情況下事件A發生的概率。

在貝葉斯定理中,每個名詞都有約定俗成的名稱:

  • P(A|B)是已知B發生後A的條件概率,也由於得自B的取值而被稱作A的後驗概率。
  • P(A)是A的先驗概率(或邊緣概率)。之所以稱為"先驗"是因為它不考慮任何B方面的因素。
  • P(B|A)是已知A發生後B的條件概率,也由於得自A的取值而被稱作B的後驗概率。
  • P(B)是B的先驗概率或邊緣概率。

這裡可以用一個例子來說明這個公式。

看一個簡單的小例子來展示貝葉斯定理

病人的例子:
某個醫院早上收了八個門診病人,如下表。

症狀 職業 疾病
打噴嚏 護士   感冒
打噴嚏 農夫   過敏
頭痛  建築工人 腦震盪
頭痛  建築工人 感冒
打噴嚏  建築工人 過敏
打噴嚏 教師   感冒
頭痛  教師   腦震盪
打噴嚏 教師   過敏

現在又來了第九個病人,是一個打噴嚏的建築工人。請問他患上感冒的概率有多大?

根據貝葉斯定理:

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

可得滿足“打噴嚏”和“建築工人”兩個條件下,感冒的概率如下:

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

假定"打噴嚏"和"建築工人"這兩個特徵是獨立的(即這兩個條件沒有相關性,比如不存在說他是建築工人他打噴嚏的概率比較大或者比較小這種關係),因此,上面的等式就變成了。

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

通過統計可得:

 P(感冒|打噴嚏x建築工人) 
 = (2/3) x (1/3) x (3/8) / (5/8) x (3/8) 
 = (16/45)

通過貝葉斯公式算出了滿足條件下感冒的概率,那麼現在貝葉斯分類器如何實現呢?

接上面的例子,從上面我們得出了 P(感冒|打噴嚏x建築工人) 的值,那麼我們可以再算出
P(不感冒|打噴嚏x建築工人) 的值,計算結果如下:

 P(不感冒|打噴嚏x建築工人) 

 = P(打噴嚏|不感冒) x P(建築工人|不感冒) x P(不感冒)  /  P(打噴嚏) x P(建築工人)
 = (3/5) x (2/5) x (5/8) / (5/8) x (3/8) 
 = (16/25)

OK,現在我們知道來如果新來一個打噴嚏的建築工人,他患感冒的機率是P(感冒|打噴嚏x建築工人)= (16/45)。不患感冒的機率是P(不感冒|打噴嚏x建築工人)= (16/25)。

通過對概率的比較,我們就可以將打噴嚏的建築工人分類到“不感冒”人群中(不感冒的概率比較大)。 這就是樸素貝葉斯分類器的最簡單的應用了。當然你也看到了,貝葉斯分類器需要我們應用到統計所得的結果,這需要資料量比較大,大到能滿足大數定理(大數定理這裡就不多解釋啦,自行百度即可),以及樣本資料足夠客觀。

接下來我們看一個實際的例子,是我在 github 上看到的一個專案例子,根據姓名來對性別進行分類。看上去覺得很不可思議吧,其實也是用了上述說的貝葉斯分類的方法。

二.貝葉斯分類器根據姓名判別男女 -python

專案github地址:https://github.com/observerss/ngender

先說一下主要思路,我們日常從一個人的名字中,基本上能大致判斷這個名字的主人是男是女。比如李大志,這個名字一聽就很男性。為什麼呢?因為字和字男性名字用得比較多。雖然機器一眼看不出來,但它可以通過統計資訊來判斷。如果有足夠多的資料,我們就可以統計出字和字用作男性名字的比例,計算概率資訊。然後就可以用這些概率,運用上述的貝葉斯公式來進行計算,判定性別。

程式碼其實不難,各個字的統計資料已經計算好,在專案中給出。我們只需要讀取檔案資料,儲存到 python 的字典中,計算出概率,然後預測的時候進行計算即可。我們先看核心程式碼,稍後會有例子說明。

裡面核心程式碼檔案為:

這裡主要講一下核心程式碼的內容:https://github.com/observerss/ngender/blob/master/ngender/ngender.py

class Guesser(object):

    //初始化函式,呼叫下面的_load_model()函式
    def __init__(self):
        self._load_model()

    //初始化一些引數
    def _load_model(self):
        self.male_total = 0
        self.female_total = 0
        self.freq = {}

        //這裡載入charfreq.csv檔案,這個檔案存放的是一些漢字是男女的統計資訊
        with open(os.path.join(os.path.dirname(__file__),
                               'charfreq.csv'),
                  'rb') as f:
            # skip first line
            next(f)
            //將檔案中的資訊儲存,累加,以便稍後計算概率
            for line in f:
                line = line.decode('utf-8')
                char, male, female = line.split(',')
                char = py2compat(char)
                //計算男性總數
                self.male_total += int(male)
                //計算女性總數
                self.female_total += int(female)
                //一個漢字對應的那女數量
                self.freq[char] = (int(female), int(male))

        self.total = self.male_total + self.female_total

        //一個漢字是男女概率
        for char in self.freq:
            female, male = self.freq[char]
            self.freq[char] = (1. * female / self.female_total,
                               1. * male / self.male_total)

    def guess(self, name):
        name = py2compat(name)
         //去掉姓氏
        firstname = name[1:]
        //過濾掉不在這個unicode編碼範圍內的字元
        for char in firstname:
            assert u'\u4e00' <= char <= u'\u9fa0', u'姓名必須為中文'

        //貝葉斯分類器,分別計算出男的概率和女的概率
        pf = self.prob_for_gender(firstname, 0)
        pm = self.prob_for_gender(firstname, 1)

        //若名字為男的概率較大,則分類為男,反之則為女
        if pm  pf:
            return ('male', 1. * pm / (pm + pf))
        elif pm < pf:
            return ('female', 1. * pf / (pm + pf))
        else:
            return ('unknown', 0)

    //貝葉斯公式的應用
    def prob_for_gender(self, firstname, gender=0):
        p = 1. * self.female_total / self.total \
            if gender == 0 \
            else 1. * self.male_total / self.total

        for char in firstname:
            p *= self.freq.get(char, (0, 0))[gender]

        return p


guesser = Guesser()

上述程式碼還是比較簡單的,首先在初始化的時候會呼叫 _load_model() 函式,這個函式完成的是一些概率計算工作,比如先將每個字對應是男是女的概率計算好儲存在字典中。

然後在計算的時候,先過濾掉姓氏。然後分別計算出這個名字是男是女的概率,比如計算 P(男|李大志)和P(女|李大志),,對比哪個概率大一些,然後進行男女分類。

這裡放上一個例子:判斷

P(gender=男|name=本山) 
= P(name=本山|gender=男) * P(gender=男) / P(name=本山)
= P(name has 本|gender=男) * P(name has 山|gender=男) * P(gender=男) / P(name=本山)
  • 公式原理為貝葉斯公式,下面對公式中中各個項進行解答,首先明確我們已經統計得到P(gender=男),P(gender=女)的概率。

怎麼算 P(name has 本|gender=男)?

  • “本”在男性名字中出現的次數 / 男性字出現的總次數

怎麼算 P(gender=男)?

  • 男性名出現的次數 / 總次數

怎麼算 P(name=本山)?

這個概率對男女來說都是一樣的,所以沒必要算出來,即我們只需要比較P(name=本山|gender=男) * P(gender=男)和P(name=本山|gender=女) * P(gender=女)兩部分誰比較大即可做出判斷。

以上就是貝葉斯分類器介紹的全部內容啦。

參考文章:
http://www.ruanyifeng.com/blog/2013/12/naive_bayes_classifier.html


推薦閱讀:

python Kmeans演算法解析
從分治演算法到 MapReduce

相關文章