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

  • 2019 年 10 月 3 日
  • 筆記

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同時發生的概率:

條件概率:指在A事件發生的條件下,事件B發生的概率,用符號表示:

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

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

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

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

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

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

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

二、貝葉斯定理

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

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

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

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

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

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

證明起來也不複雜

1、根據條件概率的定義,在事件 B 發生的條件下事件 A 發生的概率為:

2、同樣地,在事件 A 發生的條件下事件 B 發生的概率為:

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

4、上式兩邊同除以 P(A),若P(A)是非零的,我們可以得到貝葉斯定理:

在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)設
    為待分類項,其中a為x的一個特徵屬性

2)類別集合為:

3)根據貝葉斯公式,計算

4)如果
,則x屬於這
一類.

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

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

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

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

![](https://img2018.cnblogs.com/blog/743008/201908/743008-20190805191943676-1417644618.png)    此時,我們只需要計算各個類別下的特徵劃分的均值和標準差.
  1. 多項式樸素貝葉斯(一般使用在特徵屬性離散的情況下)

    所謂多項式樸素貝葉斯,就是特徵屬性服從多項式分布,進而對於每一個類別y,參數
    ,其中n為特徵屬性數目,那麼P(xi|y)的概率為θyi。

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

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

四、樸素貝葉斯實戰

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  


我們依次解釋一下每一列的含義:

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,如下圖所示:

(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