樸素貝葉斯(Naive Bayes)

從機器學習到深度學習--概述


Sheldon鎮(zhèn)樓~~~

image

貝葉斯定理是英國數(shù)學家貝葉斯提出的是趴,當時的目標是要解決所謂「逆概率」問題。在前貝葉斯時代處理概率問題的時候掸驱,總是先取一定假設(shè)(比如拋硬幣時毕贼,每次出現(xiàn)正反面的概率相同)鬼癣,然后在假設(shè)下討論一定事件的概率(比如說連續(xù)出現(xiàn) 10 次正面的概率)待秃≌掠簦「逆概率」則反過來考慮問題,比如說聊替,如果連續(xù)出現(xiàn) 10 次正面佃牛,我們想知道一次拋硬幣時出現(xiàn)正反面的概率俘侠。貝葉斯定理的相關(guān)論文在貝葉斯去世后才發(fā)表,此后法國大數(shù)學家拉普拉斯對這一理論進行了深入的研究爷速,使之成為我們今天使用的形式惫东,如下圖所示。

image

此公式表示兩個互換的條件概率之間的關(guān)系颓遏,他們通過聯(lián)合概率關(guān)聯(lián)起來叁幢,這樣使得我們知道P(D|H)的情況下去計算P(H|D)成為了可能,而我們的貝葉斯模型便是通過貝葉斯準則去計算某個樣本在不同類別條件下的條件概率并取具有最大條件概率的那個類型作為分類的預(yù)測結(jié)果黍判。

實現(xiàn)簡單貝葉斯分類器

下面以進行文本分類為目的使用Python實現(xiàn)一個樸素貝葉斯文本分類器.

為了計算條件概率顷帖,我們需要計算各個特征的在不同類別下的條件概率以及類型的邊際概率窟她,這就需要我們通過大量的訓練數(shù)據(jù)進行統(tǒng)計獲取近似值了,這也就是我們訓練我們樸素貝葉斯模型的過程.

針對不同的文本蔼水,我們可以將所有出現(xiàn)的單詞作為數(shù)據(jù)特征向量震糖,統(tǒng)計每個文本中出現(xiàn)詞條的數(shù)目(或者是否出現(xiàn)某個詞條)作為數(shù)據(jù)向量。這樣一個文本就可以處理成一個整數(shù)列表趴腋,并且長度是所有詞條的數(shù)目吊说,這個向量也許會很長,用于本文的數(shù)據(jù)集中的短信詞條大概一共3000多個單詞优炬。

def get_doc_vector(words, vocabulary):
    ''' 根據(jù)詞匯表將文檔中的詞條轉(zhuǎn)換成文檔向量
    :param words: 文檔中的詞條列表
    :type words: list of str
    :param vocabulary: 總的詞匯列表
    :type vocabulary: list of str
    :return doc_vect: 用于貝葉斯分析的文檔向量
    :type doc_vect: list of int
    '''
    doc_vect = [0]*len(vocabulary)
    for word in words:
        if word in vocabulary:
            idx = vocabulary.index(word)
            doc_vect[idx] = 1
    return doc_vect

統(tǒng)計訓練的過程:

def train(self, dataset, classes):
    ''' 訓練樸素貝葉斯模型
    :param dataset: 所有的文檔數(shù)據(jù)向量
    :type dataset: MxN matrix containing all doc vectors.
    :param classes: 所有文檔的類型
    :type classes: 1xN list
    :return cond_probs: 訓練得到的條件概率矩陣
    :type cond_probs: dict
    :return cls_probs: 各種類型的概率
    :type cls_probs: dict
    '''
    # 按照不同類型記性分類
    sub_datasets = defaultdict(lambda: [])    # defaultdict:當字典里的key不存在但被查找時颁井,返回的不是keyError而是一個默認值
    cls_cnt = defaultdict(lambda: 0)
    for doc_vect, cls in zip(dataset, classes):
        sub_datasets[cls].append(doc_vect)
        cls_cnt[cls] += 1
    # 計算類型概率
    cls_probs = {k: v/len(classes) for k, v in cls_cnt.items()}
    # 計算不同類型的條件概率
    cond_probs = {}
    dataset = np.array(dataset)
    for cls, sub_dataset in sub_datasets.items():
        sub_dataset = np.array(sub_dataset)
        # Improve the classifier.
        cond_prob_vect = np.log((np.sum(sub_dataset, axis=0) + 1)/(np.sum(dataset) + 2))
        cond_probs[cls] = cond_prob_vect
    return cond_probs, cls_probs

注意這里對于基本的條件概率直接相乘有兩處改進:

  • 各個特征的概率初始值為1,分母上統(tǒng)計的某一類型的樣本總數(shù)的初始值是1蠢护,這是為了避免如果有一個特征統(tǒng)計的概率為0雅宾,則聯(lián)合概率也為零那自然沒有什么意義了, 如果訓練樣本足夠大時,并不會對比較結(jié)果產(chǎn)生影響.
  • 由于各個獨立特征的概率都是小于1的數(shù)眉抬,累積起來必然會是個更小的書,這會遇到浮點數(shù)下溢的問題,因此在這里我們對所有的概率都取了對數(shù)處理,這樣在保證不會有損失的情況下避免了下溢的問題垃你。

獲取了統(tǒng)計概率信息后,我們便可以通過貝葉斯準則預(yù)測我們數(shù)據(jù)的類型了毒坛,這里我并沒有直接計算每種情況的概率腿箩,而是通過統(tǒng)計得到的向量與數(shù)據(jù)向量進行內(nèi)積獲取條件概率的相對值并進行相對比較做出決策的暇韧。

def classify(self, doc_vect, cond_probs, cls_probs):
    ''' 使用樸素貝葉斯將doc_vect進行分類.
    '''
    pred_probs = {}
    for cls, cls_prob in cls_probs.items():
        cond_prob_vect = cond_probs[cls]
        pred_probs[cls] = np.sum(cond_prob_vect*doc_vect) + np.log(cls_prob)
    return max(pred_probs, key=pred_probs.get)

進行短信分類

已經(jīng)構(gòu)建好了樸素貝葉斯模型涂乌,我們就可以使用此模型來統(tǒng)計數(shù)據(jù)并用來預(yù)測了。這里我使用了SMS垃圾短信語料庫中的垃圾短信數(shù)據(jù), 并隨機抽取90%的數(shù)據(jù)作為訓練數(shù)據(jù)荧库,剩下10%的數(shù)據(jù)作為測試數(shù)據(jù)來測試我們的貝葉斯模型預(yù)測的準確性。

當然在建立模型前我們需要將數(shù)據(jù)處理成模型能夠處理的格式:

ENCODING = 'ISO-8859-1'
TRAIN_PERCENTAGE = 0.9
def get_doc_vector(words, vocabulary):
    ''' 根據(jù)詞匯表將文檔中的詞條轉(zhuǎn)換成文檔向量
    :param words: 文檔中的詞條列表
    :type words: list of str
    :param vocabulary: 總的詞匯列表
    :type vocabulary: list of str
    :return doc_vect: 用于貝葉斯分析的文檔向量
    :type doc_vect: list of int
    '''
    doc_vect = [0]*len(vocabulary)
    for word in words:
        if word in vocabulary:
            idx = vocabulary.index(word)
            doc_vect[idx] = 1
    return doc_vect
def parse_line(line):
    ''' 解析數(shù)據(jù)集中的每一行返回詞條向量和短信類型.
    '''
    cls = line.split(',')[-1].strip()
    content = ','.join(line.split(',')[: -1])
    word_vect = [word.lower() for word in re.split(r'\W+', content) if word]
    return word_vect, cls
def parse_file(filename):
    ''' 解析文件中的數(shù)據(jù)
    '''
    vocabulary, word_vects, classes = [], [], []
    with open(filename, 'r', encoding=ENCODING) as f:
        for line in f:
            if line:
                word_vect, cls = parse_line(line)
                vocabulary.extend(word_vect)
                word_vects.append(word_vect)
                classes.append(cls)
    vocabulary = list(set(vocabulary))
    return vocabulary, word_vects, classes

有了上面三個函數(shù)我們就可以直接將我們的文本轉(zhuǎn)換成模型需要的數(shù)據(jù)向量,之后我們就可以劃分數(shù)據(jù)集并將訓練數(shù)據(jù)集給貝葉斯模型進行統(tǒng)計。

# 訓練數(shù)據(jù) & 測試數(shù)據(jù)
ntest = int(len(classes)*(1-TRAIN_PERCENTAGE))
test_word_vects = []
test_classes = []
for i in range(ntest):
    idx = random.randint(0, len(word_vects)-1)
    test_word_vects.append(word_vects.pop(idx))
    test_classes.append(classes.pop(idx))
train_word_vects = word_vects
train_classes = classes
train_dataset = [get_doc_vector(words, vocabulary) for words in train_word_vects]

訓練模型:

cond_probs, cls_probs = clf.train(train_dataset, train_classes)

剩下我們用測試數(shù)據(jù)來測試我們貝葉斯模型的預(yù)測準確度:

# 測試模型
error = 0
for test_word_vect, test_cls in zip(test_word_vects, test_classes):
    test_data = get_doc_vector(test_word_vect, vocabulary)
    pred_cls = clf.classify(test_data, cond_probs, cls_probs)
    if test_cls != pred_cls:
        print('Predict: {} -- Actual: {}'.format(pred_cls, test_cls))
        error += 1
print('Error Rate: {}'.format(error/len(test_classes)))

參考鏈接:
https://www.jiqizhixin.com/articles/2017-09-19-6
https://zhuanlan.zhihu.com/p/27906640

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末逗噩,一起剝皮案震驚了整個濱河市片迅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖坝咐,帶你破解...
    沈念sama閱讀 222,865評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異泼各,居然都是意外死亡专筷,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,296評論 3 399
  • 文/潘曉璐 我一進店門掂铐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來全陨,“玉大人辱姨,你說我怎么就攤上這事雨涛÷乱蓿” “怎么了矩距?”我有些...
    開封第一講書人閱讀 169,631評論 0 364
  • 文/不壞的土叔 我叫張陵涣楷,是天一觀的道長迄汛。 經(jīng)常有香客問我,道長骤视,這世上最難降的妖魔是什么鞍爱? 我笑而不...
    開封第一講書人閱讀 60,199評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮专酗,結(jié)果婚禮上睹逃,老公的妹妹穿的比我還像新娘。我一直安慰自己祷肯,他們只是感情好沉填,可當我...
    茶點故事閱讀 69,196評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著佑笋,像睡著了一般翼闹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蒋纬,一...
    開封第一講書人閱讀 52,793評論 1 314
  • 那天猎荠,我揣著相機與錄音,去河邊找鬼蜀备。 笑死关摇,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的碾阁。 我是一名探鬼主播输虱,決...
    沈念sama閱讀 41,221評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼脂凶!你這毒婦竟也來了宪睹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,174評論 0 277
  • 序言:老撾萬榮一對情侶失蹤蚕钦,失蹤者是張志新(化名)和其女友劉穎亭病,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冠桃,經(jīng)...
    沈念sama閱讀 46,699評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡命贴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,770評論 3 343
  • 正文 我和宋清朗相戀三年道宅,在試婚紗的時候發(fā)現(xiàn)自己被綠了食听。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,918評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡污茵,死狀恐怖樱报,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情泞当,我是刑警寧澤迹蛤,帶...
    沈念sama閱讀 36,573評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響盗飒,放射性物質(zhì)發(fā)生泄漏嚷量。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,255評論 3 336
  • 文/蒙蒙 一逆趣、第九天 我趴在偏房一處隱蔽的房頂上張望蝶溶。 院中可真熱鬧,春花似錦宣渗、人聲如沸抖所。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,749評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽田轧。三九已至,卻和暖如春鞍恢,著一層夾襖步出監(jiān)牢的瞬間傻粘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,862評論 1 274
  • 我被黑心中介騙來泰國打工帮掉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留抹腿,地道東北人。 一個月前我還...
    沈念sama閱讀 49,364評論 3 379
  • 正文 我出身青樓旭寿,卻偏偏與公主長得像警绩,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子盅称,可洞房花燭夜當晚...
    茶點故事閱讀 45,926評論 2 361

推薦閱讀更多精彩內(nèi)容