100天搞定機器學習|Day15 樸素貝葉斯

jpld發表於2019-08-05

Day15,開始學習樸素貝葉斯,先了解一下貝爺,以示敬意。

托馬斯·貝葉斯 (Thomas Bayes),英國神學家、數學家、數理統計學家和哲學家,1702年出生於英國倫敦,做過神甫;1742年成為英國皇家學會會員;1763年4月7日逝世。貝葉斯曾是對概率論與統計的早期發展有重大影響的兩位(貝葉斯和布萊斯·帕斯卡Blaise Pascal)人物之一。

貝葉斯在數學方面主要研究概率論。他首先將歸納推理法用於概率論基礎理論,並創立了貝葉斯統計理論,對於統計決策函式、統計推斷、統計的估算等做出了貢獻。1763年發表了這方面的論著,對於現代概率論和數理統計都有很重要的作用。貝葉斯的《An essay towards solving a problem in the doctrine of chances》發表於1758年,貝葉斯所採用的許多術語被沿用至今。貝葉斯對統計推理的主要貢獻是使用了"逆概率"這個概念,並把它作為一種普遍的推理方法提出來,即貝葉斯定理。

一、回顧概率統計基礎知識

獨立事件:在一次實驗中,一個事件的發生不會影響到另一事件發生的概率,二者沒有任何關係。如果A1,A2,A3…An相互獨立,則A1~ An同時發生的概率:

100天搞定機器學習|Day15 樸素貝葉斯

條件概率:指在A事件發生的條件下,事件B發生的概率,用符號表示:100天搞定機器學習|Day15 樸素貝葉斯
100天搞定機器學習|Day15 樸素貝葉斯

全概率公式:如果事件A1、A2、A3…An 構成一個完備事件組,即它們兩兩互不相容,其和為全集Ω;並且P(Ai) > 0,則對任一試驗B有:

100天搞定機器學習|Day15 樸素貝葉斯

其他概率基礎,大家如有興趣請移步:

【溫故知新】概率筆記1——獨立事件下的簡單概率

【溫故知新】概率筆記2——古典概型

【溫故知新】概率筆記3——幾何概型

【溫故知新】概率筆記4——重要公式

【溫故知新】概率筆記5——概率分佈

二、貝葉斯定理

貝葉斯定理(Bayes’s Rule):如果有k個互斥且有窮個事件 B1,B2···,Bk,並且,P (B1) + P(B2) + · · · + P(Bk) = 1和一個可以觀測到的事件A,那麼有:

100天搞定機器學習|Day15 樸素貝葉斯

這就是貝葉斯公式,其中:

P(Bi) 為先驗概率,即在得到新資料前某一假設的概率;

P(Bi|A) 為後驗概率,即在觀察到新資料後計算該假設的概率;

P(A|Bi)為似然度,即在該假設下得到這一資料的概率;

P(A)為標準化常量,即在任何假設下得到這一資料的概率。

證明起來也不復雜

1、根據條件概率的定義,在事件 B 發生的條件下事件 A 發生的概率為:
100天搞定機器學習|Day15 樸素貝葉斯

2、同樣地,在事件 A 發生的條件下事件 B 發生的概率為:
100天搞定機器學習|Day15 樸素貝葉斯

3、結合這兩個方程式,我們可以得到:

100天搞定機器學習|Day15 樸素貝葉斯

4、上式兩邊同除以 P(A),若P(A)是非零的,我們可以得到貝葉斯定理:
100天搞定機器學習|Day15 樸素貝葉斯

在B出現的前提下,A出現的概率等於A出現的前提下B出現的概率乘以A出現的概率再除以 B 出現的概率。通過聯絡 A 與 B,計算從一個事件發生的情況下另一事件發生的概率,即從結果上溯到源頭(也即逆向概率)。

貝葉斯公式以及由此發展起來的一整套理論與方法,在概率統計中被稱為貝葉斯學派,與概率學派有著完全不同思考問題方式。

頻率學派:研究的是事件本身,所以研究者只能反覆試驗去逼近它從而得到結果。比如:想要計算拋擲一枚硬幣時正面朝上的概率,我們需要不斷地拋擲硬幣,當拋擲次數趨向無窮時正面朝上的頻率即為正面朝上的概率。

貝葉斯學派:研究的是觀察者對事物的看法,所以你可以用先驗知識和收集到的資訊去描述他,然後用一些證據去證明它。還是比如拋硬幣,當小明知道一枚硬幣是均勻的,然後賦予下一次丟擲結果是正面或反面都是50%的可信度(概率分佈),可能是出於認為均勻硬幣最常見這種信念,然後比如小明隨機拋了1000次,發現結果正是這樣,那麼它就通過這些證據驗證了自己的先驗知識。(也有存在修改的時候,比如發現硬幣的材質不一致,總之就是這麼一個過程)

舉個例子

假設有兩個各裝了100個球的箱子,甲箱子中有70個紅球,30個綠球,乙箱子中有30個紅球,70個綠球。假設隨機選擇其中一個箱子,從中拿出一個球記下球色再放回原箱子,如此重複12次,記錄得到8次紅球,4次綠球。問題來了,你認為被選擇的箱子是甲箱子的概率有多大?

剛開始選擇甲乙兩箱子的先驗概率都是50%,因為是隨機二選一(這是貝葉斯定理二選一的特殊形式)。即有:

P(甲) = 0.5, P(乙) = 1 - P(甲);

這時在拿出一個球是紅球的情況下,我們就應該根據這個資訊來更新選擇的是甲箱子的先驗概率:

P(甲|紅球1) = P(紅球|甲) × P(甲) / (P(紅球|甲) × P(甲) + (P(紅球|乙) × P(乙)))

P(紅球|甲):甲箱子中拿到紅球的概率

P(紅球|乙):乙箱子中拿到紅球的概率

因此在出現一個紅球的情況下,選擇的是甲箱子的先驗概率就可被修正為:

P(甲|紅球1) = 0.7 × 0.5 / (0.7 × 0.5 + 0.3 × 0.5) = 0.7

即在出現一個紅球之後,甲乙箱子被選中的先驗概率就被修正為:

P(甲) = 0.7, P(乙) = 1 - P(甲) = 0.3;

如此重複,直到經歷8次紅球修正(概率增加),4此綠球修正(概率減少)之後,選擇的是甲箱子的概率為:96.7%。

Python 程式碼來解這個問題:

def bayesFunc(pIsBox1, pBox1, pBox2):
return (pIsBox1 * pBox1)/((pIsBox1 * pBox1) + (1 - pIsBox1) * pBox2)
def redGreenBallProblem():
pIsBox1 = 0.5
# consider 8 red ball
for i in range(1, 9):
pIsBox1 = bayesFunc(pIsBox1, 0.7, 0.3)
print " After red %d > in 甲 box: %f" % (i, pIsBox1)
# consider 4 green ball
for i in range(1, 5):
pIsBox1 = bayesFunc(pIsBox1, 0.3, 0.7)
print " After green %d > in 甲 box: %f" % (i, pIsBox1)
redGreenBallProblem()

執行結果如下:

After red 1 > in 甲 box: 0.700000
After red 2 > in 甲 box: 0.844828
After red 3 > in 甲 box: 0.927027
After red 4 > in 甲 box: 0.967365
After red 5 > in 甲 box: 0.985748
After red 6 > in 甲 box: 0.993842
After red 7 > in 甲 box: 0.997351
After red 8 > in 甲 box: 0.998863
After green 1 > in 甲 box: 0.997351
After green 2 > in 甲 box: 0.993842
After green 3 > in 甲 box: 0.985748
After green 4 > in 甲 box: 0.967365

很明顯可以看到紅球的出現是增加選擇甲箱子的概率,而綠球則相反。

三、樸素貝葉斯演算法

樸素貝葉斯(Naive Bayesian)是基於貝葉斯定理和特徵條件獨立假設的分類方法,它通過特徵計算分類的概率,選取概率大的情況進行分類,因此它是基於概率論的一種機器學習分類方法。因為分類的目標是確定的,所以也是屬於監督學習。樸素貝葉斯假設各個特徵之間相互獨立,所以稱為樸素。它簡單、易於操作,基於特徵獨立性假設,假設各個特徵不會相互影響,這樣就大大減小了計算概率的難度。

  1. 樸素貝葉斯演算法的執行流程如下:
    1)設100天搞定機器學習|Day15 樸素貝葉斯
    為待分類項,其中a為x的一個特徵屬性

2)類別集合為:100天搞定機器學習|Day15 樸素貝葉斯

3)根據貝葉斯公式,計算100天搞定機器學習|Day15 樸素貝葉斯

4)如果100天搞定機器學習|Day15 樸素貝葉斯
,則x屬於這100天搞定機器學習|Day15 樸素貝葉斯
一類.

  1. 高斯樸素貝葉斯(一般使用在特徵屬性連續的情況下)

    上面的演算法流程中可以看出,樸素貝葉斯演算法就是對貝葉斯公式的一種運用,它沒有進行任何的改變.

    在計算條件概率時,對於離散的資料特徵可以使用大數定理(頻率代替概率的思想).但是,怎麼處理連續的特徵呢?這裡我們一般使用高斯樸素貝葉斯.

所謂高斯樸素貝葉斯,就是當特徵屬性為連續值並且服從高斯分佈時,可以使用高斯分佈的概率公式直接計算條件概率的值。

![](https://img2018.cnblogs.com/blog/743008/201908/743008-20190805191943676-1417644618.png)

此時,我們只需要計算各個類別下的特徵劃分的均值和標準差.
  1. 多項式樸素貝葉斯(一般使用在特徵屬性離散的情況下)

    所謂多項式樸素貝葉斯,就是特徵屬性服從多項式分佈,進而對於每一個類別y,引數100天搞定機器學習|Day15 樸素貝葉斯
    ,其中n為特徵屬性數目,那麼P(xi|y)的概率為θyi。

100天搞定機器學習|Day15 樸素貝葉斯

  1. 伯努利樸素貝葉斯(一般使用在缺失值較多的情況下)

    與多項式模型一樣,伯努利模型適用於離散特徵的情況,所不同的是,伯努利模型中每個特徵的取值只能是1和0(以文字分類為例,某個單詞在文件中出現過,則其特徵值為1,否則為0).

100天搞定機器學習|Day15 樸素貝葉斯

四、樸素貝葉斯實戰

sklearn中有3種不同型別的樸素貝葉斯:

高斯分佈型:用於classification問題,假定屬性/特徵服從正態分佈的。

多項式型:用於離散值模型裡。比如文字分類問題裡面我們提到過,我們不光看詞語是否在文字中出現,也得看出現次數。如果總詞數為n,出現詞數為m的話,有點像擲骰子n次出現m次這個詞的場景。

伯努利型:最後得到的特徵只有0(沒出現)和1(出現過)。

極簡Scikit-Learn入門

例1 我們使用iris資料集進行分類

from sklearn.naive_bayes import GaussianNB
from sklearn.model_selection import cross_val_score
from sklearn import datasets
iris = datasets.load_iris()
gnb = GaussianNB()
scores=cross_val_score(gnb, iris.data, iris.target, cv=10)
print("Accuracy:%.3f"%scores.mean())

輸出: Accuracy:0.953

例2 Kaggle比賽之“舊金山犯罪分類預測”

題目背景:『水深火熱』的大米國,在舊金山這個地方,一度犯罪率還挺高的,然後很多人都經歷過大到暴力案件,小到東西被偷,車被劃的事情。當地警方也是努力地去總結和想辦法降低犯罪率,一個挑戰是在給出犯罪的地點和時間的之後,要第一時間確定這可能是一個什麼樣的犯罪型別,以確定警力等等。後來乾脆一不做二不休,直接把12年內舊金山城內的犯罪報告都丟帶Kaggle上,說『大家折騰折騰吧,看看誰能幫忙第一時間預測一下犯罪型別』。犯罪報告裡面包括日期,描述,星期幾,所屬警區,處理結果,地址,GPS定位等資訊。當然,分類問題有很多分類器可以選擇,我們既然剛講過樸素貝葉斯,剛好就拿來練練手好了。

(1) 首先我們來看一下資料

import pandas as pd  
import numpy as np  
from sklearn import preprocessing  
from sklearn.metrics import log_loss  
from sklearn.cross_validation import train_test_split
train = pd.read_csv('/Users/liuming/projects/Python/ML資料/Kaggle舊金山犯罪型別分類/train.csv', parse_dates = ['Dates'])  
test = pd.read_csv('/Users/liuming/projects/Python/ML資料/Kaggle舊金山犯罪型別分類/test.csv', parse_dates = ['Dates'])  
train  

100天搞定機器學習|Day15 樸素貝葉斯
我們依次解釋一下每一列的含義:

Date: 日期
Category: 犯罪型別,比如 Larceny/盜竊罪 等.
Descript: 對於犯罪更詳細的描述
DayOfWeek: 星期幾
PdDistrict: 所屬警區
Resolution: 處理結果,比如說『逮捕』『逃了』
Address: 發生街區位置
X and Y: GPS座標
train.csv中的資料時間跨度為12年,包含了將近90w的記錄。另外,這部分資料,大家從上圖上也可以看出來,大部分都是『類別』型,比如犯罪型別,比如星期幾。
(2)特徵預處理
sklearn.preprocessing模組中的 LabelEncoder函式可以對類別做編號,我們用它對犯罪型別做編號;pandas中的get_dummies( )可以將變數進行二值化01向量,我們用它對”街區“、”星期幾“、”時間點“進行因子化。

#對犯罪類別:Category; 用LabelEncoder進行編號  
leCrime = preprocessing.LabelEncoder()  
crime = leCrime.fit_transform(train.Category)   #39種犯罪型別  
#用get_dummies因子化星期幾、街區、小時等特徵  
days=pd.get_dummies(train.DayOfWeek)  
district = pd.get_dummies(train.PdDistrict)  
hour = train.Dates.dt.hour  
hour = pd.get_dummies(hour)  
#組合特徵  
trainData = pd.concat([hour, days, district], axis = 1)  #將特徵進行橫向組合  
trainData['crime'] = crime   #追加'crime'列  
days = pd.get_dummies(test.DayOfWeek)  
district = pd.get_dummies(test.PdDistrict)  
hour = test.Dates.dt.hour  
hour = pd.get_dummies(hour)  
testData = pd.concat([hour, days, district], axis=1)  
trainData 

特徵預處理後,訓練集feature,如下圖所示:
100天搞定機器學習|Day15 樸素貝葉斯

(3) 建模

from sklearn.naive_bayes import BernoulliNB
import time
features=['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday', 'BAYVIEW', 'CENTRAL', 'INGLESIDE', 'MISSION',  
 'NORTHERN', 'PARK', 'RICHMOND', 'SOUTHERN', 'TARAVAL', 'TENDERLOIN']  
X_train, X_test, y_train, y_test = train_test_split(trainData[features], trainData['crime'], train_size=0.6)  
NB = BernoulliNB()  
nbStart = time.time()  
NB.fit(X_train, y_train)  
nbCostTime = time.time() - nbStart  
#print(X_test.shape)  
propa = NB.predict_proba(X_test)   #X_test為263415*17;那麼該行就是將263415分到39種犯罪型別中,每個樣本被分到每一種的概率  
print("樸素貝葉斯建模%.2f秒"%(nbCostTime))  
predicted = np.array(propa)  
logLoss=log_loss(y_test, predicted)  
print("樸素貝葉斯的log損失為:%.6f"%logLoss)  

輸出:
樸素貝葉斯建模0.55秒
樸素貝葉斯的log損失為:2.582561

例3 文字分類——垃圾郵件過濾

收集資料:提供文字檔案
準備資料:將文字檔案解析成詞條向量
分析資料;檢查詞條確保解析的正確性
訓練演算法:使用之前建立的trainNB0()函式
測試演算法:使用classifyNB(),並且構建一個新的測試函式來計算文件集的錯誤率
使用演算法:構建一個完整的程式對一組文件進行分類,將錯分的文件輸出到螢幕上

準備資料:切分文字

使用正規表示式切分,其中分隔符是除單詞、數字外的任意字元

import re
mySent = 'This book is the best book on Python or M.L. I have ever laid eyes upon.'
regEx = re.compile('\\W*')
listOfTokens = regEx.split(mySent)
# 去掉長度小於0的單詞,並轉換為小寫
[tok.lower() for tok in listOfTokens if len(tok) > 0]
[out]
['this', 'book', 'is', 'the', 'best', 'book', 'on', 'python', 'or', 'm', 'l', 'i', 'have', 'ever', 'laid', 'eyes', 'upon']

切分郵件

emailText = open('email/ham/6.txt').read()
listOfTokens = regEx.split(emailText)

測試演算法:使用樸素貝葉斯進行交叉驗證

import randomdef textParse(bigString):
    '''
    字串解析
    '''
    import re    # 根據非數字字母的任意字元進行拆分
    listOfTokens = re.split(r'\W*', bigString)    # 拆分後字串長度大於2的字串,並轉換為小寫
    return [tok.lower() for tok in listOfTokens if len(tok) > 2]def spamTest():
    '''
    貝葉斯分類器對垃圾郵件進行自動化處理
    '''
    docList = []
    classList = []
    fullText = []    for i in range(1, 26):        # 讀取spam資料夾下的檔案,並轉換為特徵和標籤向量
        wordList = textParse(open('email/spam/%d.txt' % i).read())
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(1)        # 讀取ham資料夾下的檔案,並轉換為特徵和標籤向量
        wordList = textParse(open('email/ham/%d.txt' % i).read())
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(0)    # 轉換為詞列表
    vocabList = createVocabList(docList)    # 初始化訓練集和測試集
    trainingSet = range(50);
    testSet = []    # 隨機抽取測試集索引
    for i in range(10):
        randIndex = int(random.uniform(0, len(trainingSet)))
        testSet.append(trainingSet[randIndex])        del(trainingSet[randIndex])

    trainMat = []
    trainClasses = []    # 構造訓練集
    for docIndex in trainingSet:
        trainMat.append(setOfWords2Vec(vocabList, docList[docIndex]))
        trainClasses.append(classList[docIndex])    # 樸素貝葉斯分類模型訓練
    p0V, p1V, pSpam = trainNB0(np.array(trainMat), np.array(trainClasses))
    errorCount = 0

    # 樸素貝葉斯分類模型測試
    for docIndex in testSet:
        wordVector = setOfWords2Vec(vocabList, docList[docIndex])        if classifyNB(np.array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:
            errorCount += 1
            print 'classification error', docList[docIndex]    print 'the error rate is: ',float(errorCount)/len(testSet)

由於SpamTest()構造的測試集和訓練集是隨機的,所以每次執行的分類結果可能不一樣。如果發生錯誤,函式會輸出錯分文件的詞表,這樣就可以瞭解到底哪篇文件發生了錯誤。這裡出現的錯誤是將垃圾郵件誤判為了正常郵件。

import randomdef textParse(bigString):
    '''
    字串解析
    '''
    import re    # 根據非數字字母的任意字元進行拆分
    listOfTokens = re.split(r'\W*', bigString)    # 拆分後字串長度大於2的字串,並轉換為小寫
    return [tok.lower() for tok in listOfTokens if len(tok) > 2]def spamTest():
    '''
    貝葉斯分類器對垃圾郵件進行自動化處理
    '''
spamTest()
[out]
classification error ['benoit', 'mandelbrot', '1924', '2010', 'benoit', 'mandelbrot', '1924', '2010', 'wilmott', 'team', 'benoit', 'mandelbrot', 'the', 'mathematician', 'the', 'father', 'fractal', 'mathematics', 'and', 'advocate', 'more', 'sophisticated', 'modelling', 'quantitative', 'finance', 'died', '14th', 'october', '2010', 'aged', 'wilmott', 'magazine', 'has', 'often', 'featured', 'mandelbrot', 'his', 'ideas', 'and', 'the', 'work', 'others', 'inspired', 'his', 'fundamental', 'insights', 'you', 'must', 'logged', 'view', 'these', 'articles', 'from', 'past', 'issues', 'wilmott', 'magazine']
the error rate is:  0.1spamTest()
[out]
the error rate is:  0.0

參考文獻:

https://blog.csdn.net/fisherming/article/details/79509025
https://blog.csdn.net/qq_32241189/article/details/80194653
http://blog.csdn.net/kesalin/article/details/40370325
100天搞定機器學習|Day15 樸素貝葉斯

相關文章