樸素貝葉斯的那點事兒

在機器學習領域中,樸素貝葉斯是一種基於貝葉斯定理的簡單機率分類器(分類又被稱爲監督式學習,所謂監督式學習即從已知樣本數據中的特徵信息去推測可能出現的輸出以完成分類,反之聚類問題被稱爲非監督式學習),樸素貝葉斯在處理文本數據時能夠獲得較好的分類結果,因此它被普遍應用於文本分類/垃圾郵件過濾/天然語言處理等場景。html

樸素貝葉斯假設了樣本的每一個特徵之間是互相獨立、互不影響的,比方說,若是有一個水果是紅色的,形狀爲圓形,而且直徑大約爲70毫米,那麼它就有可能被認爲是蘋果(具備最高几率的類將會被認爲是最有可能的類,這被稱爲最大後驗機率 Maximum A Posteriori),即便上述的這些特徵可能會有依賴關係或有其餘特徵存在,樸素貝葉斯都會認爲這些特徵都獨立地貢獻了這個水果是一個蘋果的機率,這種假設關係太過於理想,因此這也是樸素貝葉斯的"Naive"之處。python

樸素貝葉斯的原名爲Naive Bayes Classifier,樸素自己並非一個正確的翻譯,之因此這樣翻譯是由於樸素貝葉斯雖然Naive,但不表明它的效率會差,相反它的優勢正在於實現簡單與只須要少許的訓練數據,還有另外一個緣由是它與貝葉斯網絡等算法相比,確實是「樸素」了些。git

在繼續探討樸素貝葉斯以前,咱們先須要理解貝葉斯定理與它的前置理論條件機率與全機率公式。github

本文做者爲SylvanasSun(sylvanas.sun@gmail.com),首發於SylvanasSun’s Blog。 原文連接:https://sylvanassun.github.io/2017/12/20/2017-12-20-naive_bayes/ (轉載請務必保留本段聲明,而且保留超連接。)算法

PS: 掘金目前還不支持使用LaTeX來生成數學公式,因爲本文中涉及到不少LaTeX生成的公式,因此建議到個人博客查看原文。bash

條件機率


條件機率(Conditional Probability)是指在事件B發生的狀況下,事件A發生的機率,用$P(A|B)$表示,讀做在B條件下的A的機率。網絡

在上方的文氏圖中,描述了兩個事件A和B,與它們的交集A ∩ B,代入條件機率公式,可推出事件A發生的機率爲$P(A|B) = \frac{P({A}\bigcap{B})}{P(B)}$。數據結構

對該公式稍做變換可推得${P({A}\bigcap{B})} = {P(A|B)}{P(B)}$與${P({A}\bigcap{B})} = {P(B|A)}{P(A)}$(P(B|A)爲在A條件下的B的機率)。併發

而後根據這個關係可推得${P(A|B)}{P(B)} = {P(B|A)}{P(A)}$。app

讓咱們舉個栗子,假設有兩我的在扔兩個個六面的骰子D1D2,咱們來預測D1D2的向上面的結果的機率。

Table1中描述了一個含有36個結果的樣本空間,標紅處爲D1的向上面爲2的6個結果,機率爲$P(D1=2) = \frac{6}{36} = \frac{1}{6}$。

Table2描述了D1 + D2 <= 5的機率,一共10個結果,用條件機率公式表示爲${P(D1+D2\leq5)} = \frac{10}{36}$。

Table3描述了知足Table2的條件同時也知足D1 = 2的結果,它選中了Table2中的3個結果,用條件機率公式表示爲${P(D1=2 | D1+D2\leq5)} = \frac{3}{10} = 0.3$。

全機率公式


全機率公式是將邊緣機率與條件機率關聯起來的基本規則,它表示了一個結果的總機率,能夠經過幾個不一樣的事件來實現。

全機率公式將對一復瑣事件的機率求解問題轉化爲了在不一樣狀況下發生的簡單事件的機率的求和問題,公式爲$P(B) = {\sum_{i=1}^n}P(A_i)P(B|A_i)$。

假定一個樣本空間S,它是兩個事件A與C之和,同時事件B與它們兩個都有交集,以下圖所示:

那麼事件B的機率能夠表示爲$P(B) = P({B}\bigcap{A}) + P({B}\bigcap{C})$

經過條件機率,能夠推斷出$P({B}\bigcap{A}) = P(B|A)P(A)$,因此$P(B) = P(B|A)P(A) + P(B|C)P(C)$

這就是全機率公式,即事件B的機率等於事件A與事件C的機率分別乘以B對這兩個事件的條件機率之和。

一樣舉個栗子來應用這個公式,假設有兩家工廠生產並對外提供電燈泡,工廠X生產的電燈泡在99%的狀況下可以工做超過5000小時,工廠Y生產的電燈泡在95%的狀況下可以工做超過5000小時。工廠X在市場的佔有率爲60%,工廠Y爲40%,如何推測出購買的燈泡的工做時間超過5000小時的機率是多少呢?

運用全機率公式,能夠得出: $$ \begin{equation}\begin{split} Pr(A) &=Pr(A | B_x) \cdot Pr(B_x) + Pr(A|B_y) \cdot Pr(B_y)\ &= \frac{99}{100} \cdot \frac{6}{10} + \frac{95}{100} \cdot \frac{4}{10}\ &= \frac{594 + 380}{1000}\ &= \frac{974}{1000} \end{split}\end{equation} $$

  • $Pr(B_x) = \frac{6}{10}$:購買到工廠X製造的電燈泡的機率。

  • $Pr(B_y) = \frac{4}{10}$:購買到工廠y製造的電燈泡的機率。

  • $Pr(A|B_x) = \frac{99}{100}$:工廠x製造的電燈泡工做時間超過5000小時的機率。

  • $Pr(A|B_y) = \frac{95}{100}$:工廠y製造的電燈泡工做時間超過5000小時的機率。

所以,能夠得知購買一個工做時間超過5000小時的電燈泡的機率爲97.4%。

貝葉斯定理


貝葉斯定理最先由英國數學家(同時也是神學家和哲學家)Thomas Bayes(1701-1761)提出,有趣的是他生前並無發表過什麼有關數學的學術文章,就連他最著名的成就貝葉斯定理也是由他的朋友Richard Price從他死後的遺物(筆記)中找到並發表的。

Thomas Bayes在晚年對機率學產生了興趣,所謂的貝葉斯定理只是他生前爲了解決一個逆機率問題(爲了證實上帝是否存在,彷佛哲學家們都很喜歡這個問題啊)所寫的一篇文章。在那個時期,人們已經可以計算出正向機率問題,比方說,有一個袋子中有X個白球,Y個黑球,你伸手進去摸到黑球的機率是多少?這就是一個正向機率問題,而逆機率問題正好反過來,咱們事先並不知道袋子中球的比例,而是不斷伸手去摸好幾個球,而後根據它們的顏色來推測黑球與白球的比例。

貝葉斯定理是關於隨機事件A和B的條件機率的一則定理。一般,事件A在事件B(發生)的條件下的機率,與事件B在事件A(發生)的條件下的機率是不同的,但它們二者之間是有肯定的關係的,貝葉斯定理陳述了這個關係。

貝葉斯定理的一個主要應用爲貝葉斯推理,它是一種創建在主觀判斷基礎之上的推理方法,也就是說,你只須要先預估一個值,而後再去根據實際結果去不斷修正,不須要任何客觀因素。這種推理方式須要大量的計算,所以一直遭到其餘人的詬病,沒法獲得普遍的應用,直到計算機的高速發展,而且人們發現不少事情都是沒法事先進行客觀判斷的,所以貝葉斯推理才得以東山再起。

說了這麼多理論知識(不少數學理論都像是在說繞口令),讓咱們來看一看公式吧,其實只須要把咱們在上面推導出的條件機率公式繼續進行推理,就能夠得出貝葉斯公式。

$$P(A|B) = \frac{P(B|A)P(A)}{P(B)}$$

  • $P(A|B)$:在B條件下的事件A的機率,在貝葉斯定理中,條件機率也被稱爲後驗機率,即在事件B發生以後,咱們對事件A機率的從新評估。

  • $P(B|A)$:在A條件下的事件B的機率,與上一條同理。

  • $P(A)$與$P(B)$被稱爲先驗機率(也被稱爲邊緣機率),即在事件B發生以前,咱們對事件A機率的一個推斷(不考慮任何事件B方面的因素),後面同理。

  • $\frac{P(B|A)}{P(B)}$被稱爲標準類似度,它是一個調整因子,主要是爲了保證預測機率更接近真實機率。

  • 根據這些術語,貝葉斯定理表述爲: 後驗機率 = 標準類似度 * 先驗機率。

讓咱們以著名的假陽性問題爲例,假設某種疾病的發病率爲0.001(1000我的中會有一我的得病),現有一種試劑在患者確實得病的狀況下,有99%的概率呈現爲陽性,而在患者沒有得病的狀況下,它有5%的概率呈現爲陽性(也就是假陽性),若有一位病人的檢驗成果爲陽性,那麼他的得病機率是多少呢?

代入貝葉斯定理,假定事件A表示爲得病的機率(P(A) = 0.001),這是咱們的先驗機率,它是在病人在實際注射試劑(缺少實驗的結果)以前預計的發病率,再假定事件B爲試劑結果爲陽性的機率,咱們須要計算的是條件機率P(A|B),即在事件B條件下的A機率,這就是後驗機率,也就是病人在注射試劑以後(獲得實驗結果)得出的發病率。

因爲還有未得病的機率,因此還須要假設事件C爲未得病的先驗機率(P(C) = 1 - 0.001 = 0.999),那麼P(B|C)後驗機率表示的是未得病條件下的試劑結果爲陽性的機率,以後再代入全機率公式就可得出最終結果。

$$ \begin{equation}\begin{split} P(A|B)&=\frac{P(B|A)P(A)}{P(B)}\ &= \frac{P(B|A)P(A)}{P(B|A)P(A) + P(B|C)P(C)}\ &= \frac{0.99 \times 0.001}{0.99 \times 0.001 + 0.05 \times 0.999}\approx 0.019 \end{split}\end{equation} $$

最終結果約等於2%,即便一個病人的試劑結果爲陽性,他的患病概率也只有2%而已。

樸素貝葉斯的機率模型


咱們設一個待分類項$X = {f_1,f_2,\cdots,f_n}$,其中每一個fX的一個特徵屬性,而後設一個類別集合$C_1,C_2,\cdots,C_m$。

而後須要計算$P(C_1|X),P(C_2|X),\cdots,P(C_m|X)$,咱們能夠根據一個訓練樣本集合(已知分類的待分類項集合),而後統計獲得在各種別下各個特徵屬性的條件機率:

$P(f_1|C_1),P(f_2|C_1),\cdots,P(f_n|C_1),\cdots,P(f_1|C_2),P(f_2|C_2),\cdots,P(f_n|C_2),\cdots,P(f_1|C_m),P(f_2|C_m),\cdots,P(f_n|C_m)$

若是$P(C_k|X) = MAX(P(C_1|X),P(C_2|X),\cdots,P(C_m|X))$,則${X}\in{C_k}$(貝葉斯分類其實就是取機率最大的那一個)。

樸素貝葉斯會假設每一個特徵都是獨立的,根據貝葉斯定理可推得:$P(C_i|X) = \frac{P(X|C_i)P(C_i)}{P(X)}$,因爲分母對於全部類別爲常數,所以只須要將分子最大化便可,又由於各特徵是互相獨立的,因此最終推得:

根據上述的公式推導,樸素貝葉斯的流程可以下圖所示:

接下來咱們經過一個案例來過一遍上圖的流程。

現有一網站想要經過程序自動識別出帳號的真實性(將帳號分類爲真實帳號與不真實帳號,所謂不真實帳號即帶有虛假信息或惡意註冊的小號)。

  • 首先須要肯定特徵屬性和類別,而後獲取訓練樣本。假設一個帳號具備三個特徵:日誌數量/註冊天數(F1)、好友數量/註冊天數(F2)、是否使用了真實的頭像(True爲1,False爲0)。

  • 該網站使用曾經人工檢測過的10000個帳號做爲訓練樣本,那麼計算每一個類別的機率爲$P(C_0) = 8900 \div 10000 = 0.89, P(C_1) = 1100 \div 10000 = 0.11$,C0爲真實帳號的類別機率也就是89%,C1爲虛假帳號的類別機率也就是11%。

  • 以後須要計算每一個類別下的各個特徵的條件機率,代入樸素貝葉斯分類器,可得$P(F_1|C)P(F_2|C)P(F_3|C)P(C)$,不過有一個問題是,F1F2是連續變量,不適宜按照某個特定值計算機率。解決方法爲將連續值轉化爲離散值,而後計算區間的機率,好比將F1分解爲[0,0.05]、[0.05,0.2]、[0.2,+∞]三個區間,而後計算每一個區間的機率便可。

  • 已知某一帳號的數據以下:$F_1 = 0.1,F_2 = 0.2,F_3 = 0$,推測該帳號是真實帳號仍是虛假帳號。在此例中,F1爲0.1,落在第二個區間內,因此在計算的時候,就使用第二個區間的發生機率。根據訓練樣本可得出結果爲:

$$ \begin{equation}\begin{split} P(F_1|C_0) = 0.5, P(F_1|C_1) = 0.1\ P(F_2|C_0) = 0.7, P(F_2|C_1) = 0.2\ P(F_3|C_0) = 0.2, P(F_3|C_1) = 0.9 \end{split}\end{equation} $$

  • 接下來使用訓練後的分類器可得出該帳號的真實帳號機率與虛假帳號機率,而後取最大機率做爲它的類別:

$$ \begin{equation}\begin{split} P(F_1|C_0)P(F_2|C_0)P(F_3|C_0)P(C_0) &= 0.5 \times 0.7 \times 0.2 \times 0.89\ &= 0.0623 \end{split}\end{equation} $$ $$ \begin{equation}\begin{split} P(F_1|C_1)P(F_2|C_1)P(F_3|C_1)P(C_1) &= 0.1 \times 0.2 \times 0.9 \times 0.11\ &= 0.00198 \end{split}\end{equation} $$

最終結果爲該帳號是一個真實帳號。

樸素貝葉斯的算法模型


在樸素貝葉斯中含有如下三種算法模型:

  • Gaussian Naive Bayes:適合在特徵變量具備連續性的時候使用,同時它還假設特徵聽從於高斯分佈(正態分佈)。舉個栗子,假設咱們有一組人體特徵的統計資料,該數據中的特徵:身高、體重和腳掌長度等都爲連續變量,很明顯咱們不能採用離散變量的方法來計算機率,因爲樣本太少,也沒法分紅區間計算,那麼要怎麼辦呢?解決方法是假設特徵項都是正態分佈,而後經過樣本計算出均值與標準差,這樣就獲得了正態分佈的密度函數,有了密度函數,就能夠代入值,進而算出某一點的密度函數的值。

  • MultiNomial Naive Bayes:與Gaussian Naive Bayes相反,多項式模型更適合處理特徵是離散變量的狀況,該模型會在計算先驗機率$P(C_m)$和條件機率$P(F_n|C_m)$時會作一些平滑處理。具體公式爲

    ,其中T爲總的樣本數,m爲總類別數,$T_{cm}$即類別爲$C_m$的樣本個數,a是一個平滑值。條件機率的公式爲
    n爲特徵的個數,T_cmfn爲類別爲C_m特徵爲F_n的樣本個數。當平滑值a = 10 < a < 1時,被稱做爲Laplace平滑,當a = 0時不作平滑。它的思想其實就是對每類別下全部劃分的計數加1,這樣若是訓練樣本數量足夠大時,就不會對結果產生影響,而且解決了$P(F|C)$的頻率爲0的現象(某個類別下的某個特徵劃分沒有出現,這會嚴重影響分類器的質量)。

  • Bernoulli Naive Bayes:Bernoulli適用於在特徵屬性爲二進制的場景下,它對每一個特徵的取值是基於布爾值的,一個典型例子就是判斷單詞有沒有在文本中出現。

樸素貝葉斯的實現


瞭解了足夠多的理論,接下來咱們要動手使用python來實現一個Gaussian Naive Bayes,目的是解決皮馬人(一個印第安人部落)的糖尿病問題,樣本數據(請從該超連接中獲取)是一個csv格式的文件,每一個值都是一個數字,該文件描述了從患者的年齡、懷孕次數和驗血結果等方面的即時測量數據。每一個記錄都有一個類別值(一個布爾值,以0或1表示),該值表述了患者是否在五年內得過糖尿病。這是一個在機器學習文獻中被大量研究過的數據集,一個比較好的預測精度應該在70%~76%。樣本數據的每列含義以下:

列1:懷孕次數
列2:在口服葡萄糖耐量試驗中,血漿葡萄糖的濃度(2小時)
列3:心臟的舒張壓((mm Hg))
列4:肱三頭肌皮膚褶皺厚度(mm)
列5:二小時內的血清胰島素(mu U/ml)
列6:體質指數 ((weight in kg/(height in m)^2))
列7:糖尿病家族做用
列8:年齡
列9:類別布爾值,0爲5年沒得過糖尿病,1爲5年內得過糖尿病
------------------------------------
6,148,72,35,0,33.6,0.627,50,1
1,85,66,29,0,26.6,0.351,31,0
8,183,64,0,0,23.3,0.672,32,1
1,89,66,23,94,28.1,0.167,21,0
0,137,40,35,168,43.1,2.288,33,1
.........
複製代碼

首先要作的是讀取這個csv文件,並解析成咱們能夠直接使用的數據結構。因爲樣本數據文件中沒有任何的空行和標記符號,每行都是對應的一行數據,只須要簡單地把每一行封裝到一個list中便可(返回結果爲一個list,它的每一項元素都是包含一行數據的list),注意該文件中的數據都爲數字,須要先作類型轉換。

import csv

def load_csv_file(filename):
    with open(filename) as f:
        lines = csv.reader(f)
        data_set = list(lines)
    for i in range(len(data_set)):
        data_set[i] = [float(x) for x in data_set[i]]
    return data_set
複製代碼

得到了樣本數據後,爲了評估模型的準確性還須要將它切分爲訓練數據集(樸素貝葉斯須要使用它來進行預測)與測試數據集。數據在切分過程當中是隨機選取的,但咱們會選擇一個比率來控制訓練數據集與測試數據集的大小,通常爲67%:33%,這是一個比較常見的比率。

import random

def split_data_set(data_set, split_ratio):
    train_size = int(len(data_set) * split_ratio)
    train_set = []
    data_set_copy = list(data_set)
    while len(train_set) < train_size:
        index = random.randrange(len(data_set_copy))
        train_set.append(data_set_copy.pop(index))
    return [train_set, data_set_copy]
複製代碼

切分了樣本數據後,還要對訓練數據集進行更細緻的處理,因爲Gaussian Naive Bayes假設了每一個特徵都遵循正態分佈,因此須要從訓練數據集中抽取出摘要,它包含了均值與標準差,摘要的數量由類別和特徵屬性的組合數決定,例如,若是有3個類別與7個特徵屬性,那麼就須要對每一個特徵屬性和類別計算出均值和標準差,這就是21個摘要。

在計算訓練數據集的摘要以前,咱們的第一個任務是要將訓練數據集中的特徵與類別進行分離,也就是說,構造出一個key爲類別,值爲所屬該類別的數據行的散列表。

def separate_by_class(data_set, class_index):
    result = {}
    for i in range(len(data_set)):
        vector = data_set[i]
        class_val = vector[class_index]
        if (class_val not in result):
            result[class_val] = []
        result[class_val].append(vector)
    return result
複製代碼

因爲已經知道了類別只有一個,並且在每行數據的最後一個,因此只須要將-1傳入到class_index參數便可。而後就是計算訓練數據集的摘要(每一個類別中的每一個特徵屬性的均值與標準差),均值會被做爲正態分佈的中間值,而標準差則描述了數據的離散程度,在計算機率時,它會被做爲正態分佈中每一個特徵屬性的指望分佈。

標準差就是方差的平方根,只要先求出方差(每一個特徵值與平均值的差的平方之和的平均值)就能夠得出標準差。

import math

def mean(numbers):
    return sum(numbers) / float(len(numbers))

def stdev(numbers):
    avg = mean(numbers)
    variance = sum([pow(x - avg, 2) for x in numbers]) / float(len(numbers))
    return math.sqrt(variance)    
複製代碼

有了這些輔助函數,計算摘要就很簡單了,具體步驟就是先從訓練數據集中構造出key爲類別的散列表,而後根據類別與每一個特徵進行計算求出均值與標準差便可。

def summarize(data_set):
    # 使用zip函數將每一個元素中的第n個屬性封裝爲一個元組
	# 簡單地說,就是把每列(特徵)都打包到一個元組中
    summaries = [(mean(feature), stdev(feature)) for feature in zip(*data_set)]
    del summaries[-1] # 最後一行是類別與類別的摘要 因此刪除
    return summaries

def summarize_by_class(data_set):
    class_map = separate_by_class(data_set, -1)
    summaries = {}
    for class_val, data in class_map.items():
        summaries[class_val] = summarize(data)
    return summaries
複製代碼

數據的處理階段已經完成了,下面的任務是要去根據訓練數據集來進行預測,該階段須要計算類機率與每一個特徵與類別的條件機率,而後選出機率最大的類別做爲分類結果。關鍵在於計算條件機率,須要用到正態分佈的密度函數,而它所依賴的參數(特徵,均值,標準差)咱們已經準備好了。

def calculate_probability(x, mean, stdev):
    exponent = math.exp(-(math.pow(x - mean, 2) / (2 * math.pow(stdev, 2))))
    return (1 / (math.sqrt(2 * math.pi) * stdev)) * exponent

def calculate_conditional_probabilities(summaries, input_vector):
    probabilities = {}
    for class_val, class_summaries in summaries.items():
        probabilities[class_val] = 1
        for i in range(len(class_summaries)):
            mean, stdev = class_summaries[i]
			# input_vector是test_set的一行數據,x爲該行中的某一特徵屬性
            x = input_vector[i]
			# 將機率相乘
            probabilities[class_val] *= calculate_probability(x, mean, stdev)
    return probabilities
複製代碼

函數calculate_conditional_probabilities()返回了一個key爲類別,值爲其機率的散列表,這個散列表記錄了每一個特徵類別的條件機率,以後只須要選出其中最大機率的類別便可。

def predict(summaries, input_vector):
    probabilities = calculate_conditional_probabilities(summaries, input_vector)
    best_label, best_prob = None, -1
    for class_val, probability in probabilities.items():
        if best_label is None or probability > best_prob:
            best_label = class_val
            best_prob = probability
    return best_label
複製代碼

最後咱們定義一個函數來對測試數據集中的每一個數據實例進行預測以預估模型的準確性,該函數返回了一個預測值列表,包含了每一個數據實例的預測值。根據這個返回值,就能夠對預測結果進行準確性的評估了。

def get_predictions(summaries, test_set):
    predictions = []
    for i in range(len(test_set)):
        result = predict(summaries, test_set[i])
        predictions.append(result)
    return predictions

def get_accuracy(predictions, test_set):
    correct = 0
    for x in range(len(test_set)):
		# 分類結果與測試數據集一致,調整值自增
        if test_set[x][-1] == predictions[x]:
            correct += 1
    return (correct / float(len(test_set))) * 100.0
複製代碼

完整代碼以下:

import csv, random, math

""" A simple classifier base on the gaussian naive bayes and problem of the pima indians diabetes. (https://archive.ics.uci.edu/ml/datasets/Pima+Indians+Diabetes) """

def load_csv_file(filename):
    with open(filename) as f:
        lines = csv.reader(f)
        data_set = list(lines)
    for i in range(len(data_set)):
        data_set[i] = [float(x) for x in data_set[i]]
    return data_set

def split_data_set(data_set, split_ratio):
    train_size = int(len(data_set) * split_ratio)
    train_set = []
    data_set_copy = list(data_set)
    while len(train_set) < train_size:
        index = random.randrange(len(data_set_copy))
        train_set.append(data_set_copy.pop(index))
    return [train_set, data_set_copy]

def separate_by_class(data_set, class_index):
    result = {}
    for i in range(len(data_set)):
        vector = data_set[i]
        class_val = vector[class_index]
        if (class_val not in result):
            result[class_val] = []
        result[class_val].append(vector)
    return result

def mean(numbers):
    return sum(numbers) / float(len(numbers))

def stdev(numbers):
    avg = mean(numbers)
    variance = sum([pow(x - avg, 2) for x in numbers]) / float(len(numbers))
    return math.sqrt(variance)

def summarize(data_set):
    summaries = [(mean(feature), stdev(feature)) for feature in zip(*data_set)]
    del summaries[-1]
    return summaries

def summarize_by_class(data_set):
    class_map = separate_by_class(data_set, -1)
    summaries = {}
    for class_val, data in class_map.items():
        summaries[class_val] = summarize(data)
    return summaries

def calculate_probability(x, mean, stdev):
    exponent = math.exp(-(math.pow(x - mean, 2) / (2 * math.pow(stdev, 2))))
    return (1 / (math.sqrt(2 * math.pi) * stdev)) * exponent

def calculate_conditional_probabilities(summaries, input_vector):
    probabilities = {}
    for class_val, class_summaries in summaries.items():
        probabilities[class_val] = 1
        for i in range(len(class_summaries)):
            mean, stdev = class_summaries[i]
            x = input_vector[i]
            probabilities[class_val] *= calculate_probability(x, mean, stdev)
    return probabilities

def predict(summaries, input_vector):
    probabilities = calculate_conditional_probabilities(summaries, input_vector)
    best_label, best_prob = None, -1
    for class_val, probability in probabilities.items():
        if best_label is None or probability > best_prob:
            best_label = class_val
            best_prob = probability
    return best_label

def get_predictions(summaries, test_set):
    predictions = []
    for i in range(len(test_set)):
        result = predict(summaries, test_set[i])
        predictions.append(result)
    return predictions

def get_accuracy(predictions, test_set):
    correct = 0
    for x in range(len(test_set)):
        if test_set[x][-1] == predictions[x]:
            correct += 1
    return (correct / float(len(test_set))) * 100.0

def main():
    filename = 'pima-indians-diabetes.data.csv'
    split_ratio = 0.67
    data_set = load_csv_file(filename)
    train_set, test_set = split_data_set(data_set, split_ratio)
    print('Split %s rows into train set = %s and test set = %s rows'
                %(len(data_set), len(train_set), len(test_set)))
    # prepare model
    summaries = summarize_by_class(train_set)
    # predict and test
    predictions = get_predictions(summaries, test_set)
    accuracy = get_accuracy(predictions, test_set)
    print('Accuracy: %s' % accuracy)

main()

複製代碼

參考文獻


相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息