1. 程式人生 > 其它 >樸素貝葉斯與貝葉斯網路

樸素貝葉斯與貝葉斯網路

1、樸素貝葉斯

樸素貝葉斯是一種基於貝葉斯定理和特徵條件獨立假設的分類演算法。

簡單而言,對於給定的訓練資料,樸素貝葉斯先基於特徵條件獨立假設學習輸入和輸出的聯合概率分佈,然後基於此分佈對於新的例項,利用貝葉斯定理計算出最大的後驗概率。樸素貝葉斯不會直接學習輸入輸出的聯合概率分佈,而是通過學習類的先驗概率類條件概率來完成。

所謂樸素貝葉斯中樸素的含義,即特徵條件獨立假設,條件獨立假設就是說用於分類的特徵在類確定的條件下都是條件獨立的。這一假設使得樸素貝葉斯的學習成為可能。樸素貝葉斯演算法具體步驟如下。

首先計算類先驗概率:

類先驗概率可直接用極大似然估計進行計算。

然後計算類條件概率:

最後給定新的例項,計算其對應的最大後驗概率,然後判斷其所屬的類別:

以上就是樸素貝葉斯的基本原理。下面基於numpy和pandas來實現樸素貝葉斯演算法。

匯入package

import numpy as np
import pandas as pd

構造資料集

### 來自於李航統計學習方法表4.1
x1 = [1,1,1,1,1,2,2,2,2,2,3,3,3,3,3]
x2 = ['S','M','M','S','S','S','M','M','L','L','L','M','M','L','L']
y = [-1,-1,1,1,-1,-1,-1,1,1,1,1,1,1,1,-1]

df = pd.DataFrame({'x1':x1, 'x2':x2, 'y':y})
df.head()
x1 x2 y
0 1 S -1
1 1 M -1
2 1 M 1
3 1 S 1
4 1 S -1

取出特徵和標籤

X = df[['x1', 'x2']]
y = df[['y']]

定義樸素貝葉斯訓練過程

def nb_fit(X, y):
    classes = y[y.columns[0]].unique()
    class_count = y[y.columns[0]].value_counts()
    class_prior = class_count/len(y)
    
    prior = dict()
    for col in X.columns:
        for j in classes:
            p_x_y = X[(y==j).values][col].value_counts()
            for i in p_x_y.index:
                prior[(col, i, j)] = p_x_y[i]/class_count[j]
    return classes, class_prior, prior

擬合示例如下

nb_fit(X, y)
(array([-1,  1], dtype=int64),
  1    0.6
 -1    0.4
 Name: y, dtype: float64,
 {('x1', 1, -1): 0.5,
  ('x1', 2, -1): 0.3333333333333333,
  ('x1', 3, -1): 0.16666666666666666,
  ('x1', 3, 1): 0.4444444444444444,
  ('x1', 2, 1): 0.3333333333333333,
  ('x1', 1, 1): 0.2222222222222222,
  ('x2', 'S', -1): 0.5,
  ('x2', 'M', -1): 0.3333333333333333,
  ('x2', 'L', -1): 0.16666666666666666,
  ('x2', 'M', 1): 0.4444444444444444,
  ('x2', 'L', 1): 0.4444444444444444,
  ('x2', 'S', 1): 0.1111111111111111})

最後定義預測函式

classes, class_prior, prior = nb_fit(X, y)

def predict(X_test):
    res = []
    for c in classes:
        p_y = class_prior[c]
        p_x_y = 1
        for i in X_test.items():
            p_x_y *= prior[tuple(list(i)+[c])]
        res.append(p_y*p_x_y)
    return classes[np.argmax(res)]

給定測試例項並進行預測

X_test = {'x1': 2, 'x2': 'S'}
classes, class_prior, prior = nb_fit(X, y)
print('測試資料預測類別為:', predict(X_test))

測試資料預測類別為: -1

sklearn實現

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import GaussianNB
from sklearn.metrics import accuracy_score
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, random_state=0)
gnb = GaussianNB()
y_pred = gnb.fit(X_train, y_train).predict(X_test)
print("Accuracy of GaussianNB in iris data test:",accuracy_score(y_test, y_pred))

2、貝葉斯網路

2.1 引例

在上一講中,我們講到了經典的樸素貝葉斯演算法。樸素貝葉斯的一大特點就是特徵的條件獨立假設,但在現實情況下,條件獨立這個假設通常過於嚴格,在實際中很難成立。特徵之間的相關性限制了樸素貝葉斯的效能, 所以本節將繼續介紹一種放寬了條件獨立假設的貝葉斯演算法——貝葉斯網路(Bayesian Network)。

先以一個例子進行引入。假設我們需要通過頭像真實性、粉絲數量和動態更新頻率來判斷一個微博賬號是否為真實賬號。各特徵屬性之間的關係如下圖所示:

上圖是一個有向無環圖(DAG),每個節點表示一個特徵或者隨機變數,特徵之間的關係則是用箭頭連線來表示,比如說動態的更新頻率、粉絲數量和頭像真實性都會對一個微博賬號的真實性有影響,而頭像真實性又對粉絲數量有一定影響。但僅有各特徵之間的關係還不足以進行貝葉斯分析。除此之外,貝葉斯網路中每個節點還有一個與之對應的概率表。

假設賬號是否真實和頭像是否真實有如下概率表:

A = 0 A = 1
0.13 0.87
H = 0 H = 1
A = 0 0.88 0.12
A = 1 0.25 0.75

第一張概率表表示的是賬號是否真實,因為該節點沒有父節點,可以直接用先驗概率來表示,表示賬號真實與否的概率。(A = 0賬號為真,A = 1賬號為假)

第二張概率表表示的是賬號真實性對於頭像真實性的條件概率。比如說在頭像為真實頭像的條件下,賬號為真的概率為0.88。(H = 0 頭像為真,H = 1頭像為假)在有了DAG和概率表之後,我們便可以利用貝葉斯公式進行定量的因果關係推斷。假設我們已知某微博賬號使用了虛假頭像,那麼其賬號為虛假賬號的概率可以推斷為:

利用貝葉斯公式,我們可知在虛假頭像的情況下其賬號為虛假賬號的概率為0.345。

2.2 貝葉斯網路性質

上面的例子可以讓大家直觀的感受到貝葉斯網路的作用。一個貝葉斯網路通常由有向無環圖(DAG)和節點對應的概率表組成。其中DAG由節點(node)和有向邊(edge)組成,節點表示特徵屬性或隨機變數,有向邊表示各變數之間的依賴關係。貝葉斯網路的一個重要性質是:當一個節點的父節點概率分佈確定之後,該節點條件獨立於其所有的非直接父節點。這個性質方便於我們計算變數之間的聯合概率分佈。

一般來說,多變數非獨立隨機變數的聯合概率分佈計算公式如下:

當有了上述性質之後,該式子就可以簡化為:

基於先驗概率、條件概率分佈和貝葉斯公式,我們便可以基於貝葉斯網路進行概率推斷。

2.3 基於pgmpy的貝葉斯網路實現

本節我們基於pgmpy來構造貝葉斯網路和進行建模訓練。pgmpy是一款基於Python的概率圖模型包,主要包括貝葉斯網路和馬爾可夫蒙特卡洛等常見概率圖模型的實現以及推斷方法。本節使用pgmpy包來實現簡單的貝葉斯網路。

我們以學生獲得推薦信質量這樣一個例子來進行貝葉斯網路的構造。具體有向圖和概率表如下圖所示:

考試難度、個人聰明與否都會影響到個人成績,另外個人聰明與否也會影響到SAT分數,而個人成績好壞會直接影響到推薦信的質量。

學生例子中包含5個隨機變數, 如下所示:

變數 含義 取值
Difficulty 課程本身難度 0=easy, 1=difficult
Intelligence 學生聰明程度 0=stupid, 1=smart
Grade 學生課程成績 1=A, 2=B, 3=C
SAT 學生高考成績 0=low, 1=high
Letter 可否獲得推薦信 0=未獲得, 1=獲得

下面我們直接來用pgmpy實現上述貝葉斯網路。

匯入相關模組:

# 匯入pgmpy相關模組
from pgmpy.factors.discrete import TabularCPD
from pgmpy.models import BayesianModel
letter_model = BayesianModel([('D', 'G'),
                               ('I', 'G'),
                               ('G', 'L'),
                               ('I', 'S')])

構建模型框架,指定各變數之間的依賴關係:

letter_model = BayesianModel([('D', 'G'),
                               ('I', 'G'),
                               ('G', 'L'),
                               ('I', 'S')])

構建各個節點和傳入概率表並指定相關引數:

# 學生成績的條件概率分佈
grade_cpd = TabularCPD(
    variable='G', # 節點名稱
    variable_card=3, # 節點取值個數
    values=[[0.3, 0.05, 0.9, 0.5], # 該節點的概率表
    [0.4, 0.25, 0.08, 0.3],
    [0.3, 0.7, 0.02, 0.2]],
    evidence=['I', 'D'], # 該節點的依賴節點
    evidence_card=[2, 2] # 依賴節點的取值個數
)
# 考試難度的條件概率分佈
difficulty_cpd = TabularCPD(
            variable='D',
            variable_card=2,
            values=[[0.6], [0.4]]
)
# 個人天賦的條件概率分佈
intel_cpd = TabularCPD(
            variable='I',
            variable_card=2,
            values=[[0.7], [0.3]]
)
# 推薦信質量的條件概率分佈
letter_cpd = TabularCPD(
            variable='L',
            variable_card=2,
            values=[[0.1, 0.4, 0.99],
            [0.9, 0.6, 0.01]],
            evidence=['G'],
            evidence_card=[3]
)
# SAT考試分數的條件概率分佈
sat_cpd = TabularCPD(
            variable='S',
            variable_card=2,
            values=[[0.95, 0.2],
            [0.05, 0.8]],
            evidence=['I'],
            evidence_card=[2]
)

將包含概率表的各節點新增到模型中:

# 將各節點新增到模型中,構建貝葉斯網路
letter_model.add_cpds(
    grade_cpd, 
    difficulty_cpd,
    intel_cpd,
    letter_cpd,
    sat_cpd
)

獲取模型的條件概率分佈:

letter_model.get_cpds()

[<TabularCPD representing P(G:3 | I:2, D:2) at 0x7fba52206700>,
<TabularCPD representing P(D:2) at 0x7fba522066a0>,
<TabularCPD representing P(I:2) at 0x7fba52206760>,
<TabularCPD representing P(L:2 | G:3) at 0x7fba522066d0>,
<TabularCPD representing P(S:2 | I:2) at 0x7fba52206790>]

獲取模型各節點之間的依賴關係:

# 獲取模型各節點之間的依賴關係
letter_model.get_independencies()

(G ⟂ S | I)
(G ⟂ S | D, I)
(G ⟂ S | I, L)
(G ⟂ S | D, I, L)
(D ⟂ S, I)
(D ⟂ L | G)
(D ⟂ I | S)
(D ⟂ S | I)
(D ⟂ L | G, S)
(D ⟂ S, L | G, I)
(D ⟂ S | I, L)
(D ⟂ L | G, S, I)
(D ⟂ S | G, I, L)
(S ⟂ D)
(S ⟂ L | G)
(S ⟂ G, D, L | I)
(S ⟂ L | G, D)
(S ⟂ D, L | G, I)
(S ⟂ G, L | D, I)
(S ⟂ G, D | I, L)
(S ⟂ L | G, D, I)
(S ⟂ D | G, I, L)
(S ⟂ G | D, I, L)
(I ⟂ D)
(I ⟂ L | G)
(I ⟂ D | S)
(I ⟂ L | G, D)
(I ⟂ L | G, S)
(I ⟂ L | G, D, S)
(L ⟂ D, S, I | G)
(L ⟂ S | I)
(L ⟂ S, I | G, D)
(L ⟂ D, I | G, S)
(L ⟂ D, S | G, I)
(L ⟂ S | D, I)
(L ⟂ I | G, D, S)
(L ⟂ S | G, D, I)
(L ⟂ D | G, S, I)

進行貝葉斯推斷:

# 匯入pgmpy貝葉斯推斷模組
from pgmpy.inference import VariableElimination
# 貝葉斯網路推斷
letter_infer = VariableElimination(letter_model)
# 天賦較好且考試不難的情況下推斷該學生獲得推薦信質量的好壞
prob_G = letter_infer.query(
            variables=['G'],
            evidence={'I': 1, 'D': 0})
print(prob_G)

Finding Elimination Order: :

0/0 [05:27<?, ?it/s]

0/0 [00:00<?, ?it/s]

+------+----------+
| G    |   phi(G) |
+======+==========+
| G(0) |   0.9000 |
+------+----------+
| G(1) |   0.0800 |
+------+----------+
| G(2) |   0.0200 |
+------+----------+

可見當聰明的學生碰上較簡單的考試時,獲得第一等成績的概率高達0.9。