機器學習之樸素貝葉斯分類器/貝葉斯網絡

1、貝葉斯判定準則

定義:爲最小化總體風險,只需在每個樣本上選擇那個能使條件風險R(c|x)最小的類別標記,即:
在這裏插入圖片描述
此時,h*稱爲貝葉斯最優分類器。

2.樸素貝葉斯分類器

樸素貝葉斯是基於貝葉斯定理特徵條件獨立假設的分類方法.
首先學習輸入/輸出的聯合概率分佈,然後基於此模型,對給定的輸入x,利用貝葉斯定理求出後驗概率最大的輸出y.屬於生成模型.
樸素貝葉斯法的精髓在於後驗概率最大化。

基本方法:
假設輸入空間X⊆Rn爲n維向量的集合,輸出空間爲類標記集合Y=c1,c2,…,cK。輸入爲特徵向量x∈X,輸出爲類標記y∈Y。X是定義在輸入空間X上的隨機向量,Y是定義在輸出空間Y上的隨機變量。P(X,Y)是X和Y的聯合概率分佈。訓練數據集T=(x(1),y(1)),(x(2),y(2)),…,(x(m),y(m))是由P(X,Y)獨立同分布產生的,其中每個x=(x1,x2,…,xn)是n維向量。樸素貝葉斯法通過對給定的輸入x,通過學習到的模型計算後驗概率分佈P(Y=ck|X=x),然後將後驗概率最大的類作爲x的輸出。計算後驗概率:
在這裏插入圖片描述
其中k=1,2,…,K,可以看到分母對於所有的類標記ck都是相同的,則可以得到輸出
在這裏插入圖片描述
其中:
在這裏插入圖片描述
是先驗概率分佈。
在這裏插入圖片描述
是條件概率分佈(似然函數)。假定條件概率分佈中的每個特徵是條件獨立的,則
在這裏插入圖片描述
這一假設使得樸素貝葉斯法變得簡單,但是會犧牲一定的分類準確率。於是代入,可以得到:
在這裏插入圖片描述
注:樸素貝葉斯法屬於生成模型(模型給定了輸入X產生輸出Y的生成關係,區別於判別模型
在這裏插入圖片描述

2.模型原理

首先學習先驗概率分佈,
在這裏插入圖片描述
然後學習條件概率分佈.
在這裏插入圖片描述
如果估計實際,需要指數級的計算,所以樸素貝葉斯法對條件概率分佈作了條件獨立性的假設,上式變成.
在這裏插入圖片描述
在分類時,通過學習到的模型計算後驗概率分佈,由貝葉斯定理得到,
在這裏插入圖片描述
將條件獨立性假設得到的等式代入,並且注意到分母都是相同的,所以得到樸素貝葉斯分類器:

在這裏插入圖片描述
在這裏插入圖片描述

3.算法

在這裏插入圖片描述
(1)使用極大似然估計法估計相應的先驗概率:
在這裏插入圖片描述
其中I(yi=ck)是指示函數,當yi=ck時值爲1,其他情況下爲0。m爲數據集裏的數據量。
假定輸入的n維特徵向量x的第j維可能的取值爲xj1,xj2,…xjsj,則條件概率P(Xj=xjl|Y=ck)的極大似然估計是:
在這裏插入圖片描述
令參數P(Y=ck)=θk, k=1,2,…,K。則隨機變量Y的概率可以用參數來表示爲P(Y)=∑k=1KθkI(Y=ck),其中I是指示函數。極大似然函數:
在這裏插入圖片描述
(2)貝葉斯估計
用極大似然估計可能會出現所要估計的概率值爲0的情況,在累乘後會影響後驗概率的計算結果,使分類產生偏差.可以採用貝葉斯估計,在隨機變量各個取值的頻數上賦予一個正數.
在這裏插入圖片描述
Sj爲j屬性可能取值數量,當λ=0時就是極大似然估計.常取λ=1,稱爲拉普拉斯平滑.如果是連續值的情況,可以假設連續變量服從高斯分佈,然後用訓練數據估計參數.
在這裏插入圖片描述
示例:
在這裏插入圖片描述
採用極大似然估計法
在這裏插入圖片描述
採用拉普拉斯平滑估計:
在這裏插入圖片描述
引入拉普拉斯平滑的原因?
解決零概率問題,就是在計算實例的概率時,如果某個量x,在觀察樣本庫(訓練集)中沒有出現過,會導致整個實例的概率結果是0。在文本分類的問題中,當一個詞語沒有在訓練樣本中出現,該詞語調概率爲0,使用連乘計算文本出現概率時也爲0。這是不合理的,不能因爲一個事件沒有觀察到就武斷的認爲該事件的概率是0。
也就是計算過程中,分子分母會加上一個數。

4.代碼實現

在這裏插入圖片描述
基本步驟:
(1)將數據集切分成訓練數據集和測試數據集。
(2)預先提取出所有的數據裏面單詞構成單詞向量。
(3)然後分別將訓練數據集和測試數據集的輸入,分詞,並轉換稱單詞向量。然後進行訓練,訓練時計算各個單詞的數量,然後除以總單詞數,並使用lamda=1。
(4)然後進行測試,採樣log的加和來使得避免連乘溢出。

# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import numpy as np
import os
import re
import random

class NativeBayes(object):

    def __init__(self):
        self._train_x = []  # 訓練數據x
        self._train_y = []  # 訓練數據y
        self._test_x = []   # 測試數據x
        self._test_y = []   # 測試數據y
        self._all_words = None # 所有的單詞
        self._all_words_num = 0 # 所以單詞數量

    def split_to_word(self, text): # 將文本切分成單詞
        words = re.split(r'\W*', text)
        #return [word.lower() for word in words if len(word) > 2]
        return [word.lower() for word in words if len(word) > 2 and re.match(r'[a-zA-Z]', word)]

    def words_to_vector(self, words): # 將切分後的單詞轉換稱單詞向量
        vector = [0]*self._all_words_num
        for word in words:
            if word in self._all_words:
                vector[self._all_words.index(word)]  += 1
        return vector

    def load_data(self, positive_dir, nagetive_dir): 
        train_files = os.listdir(positive_dir)
        input_x = []
        input_y = []
        all_input_x = []
        for i in train_files:
            with open('{}/{}'.format(positive_dir,i)) as f:
                text = f.read()
                words = self.split_to_word(text)
                input_x.append(words)
                all_input_x.extend(words)
                input_y.append(1)

        train_files = os.listdir(nagetive_dir)
        for i in train_files:
            with open('{}/{}'.format(nagetive_dir,i)) as f:
                text = f.read()
                words = self.split_to_word(text)
                input_x.append(words)
                all_input_x.extend(words)
                input_y.append(-1)

        self._all_words = list(set(all_input_x)) # 獲得數據裏面所有的單詞列表
        self._all_words_num = len(self._all_words) # 單詞列表裏面的單詞數量

        total = len(input_y)
        test_x = []
        test_y = []
        for i in range(10):  # 將數據集分爲訓練數據和測試數據
            index = random.randint(0, total-1)
            test_x.append(input_x[index])
            test_y.append(input_y[index])
            del(input_x[index])
            del(input_y[index])
            total -= 1
            
        self._train_x = []
        self._train_y = input_y
        train_num = len(input_y)
        print('train data num', train_num)
        for i in range(train_num):  # 將訓練數據單詞列表轉換稱單詞向量
            vector = self.words_to_vector(input_x[i])
            self._train_x.append(vector)

        self._test_x = []
        self._test_y = test_y
        test_num = len(test_y)
        print('test data num', test_num)
        for i in range(test_num): # 將測試數據單詞列表轉換稱單詞向量
            vector = self.words_to_vector(test_x[i])
            self._test_x.append(vector)
            
    def train(self):
        train_data_num = len(self._train_y)
        p_positive = np.ones(self._all_words_num) # 貝葉斯估計,所有單詞初始化lamda=1
        p_negative = np.ones(self._all_words_num) # 貝葉斯估計,lamda=1
        positive_words_total = self._all_words_num # 同時所有的單詞數量響應增加
        negative_words_total = self._all_words_num # 原書中此處爲0,應該是錯誤的
        total_positive = 0
        for i in range(train_data_num):
            if self._train_y[i] == 1:
                p_positive += self._train_x[i]
                positive_words_total += sum(self._train_x[i])
                total_positive += 1
            else:
                p_negative += self._train_x[i]
                negative_words_total += sum(self._train_x[i])
        p_positive = np.log(p_positive/positive_words_total) # 計算各個單詞的條件概率
        p_negative = np.log(p_negative/negative_words_total)
        positive_class = total_positive/float(train_data_num) # 計算分類概率
        print('train positive percent',positive_class)
        return p_positive,p_negative,positive_class
        
    def classify(self, p_positive, p_negative,  positive_class, vector):
        # 分別計算各個子類的概率
        positive = np.sum(p_positive*vector) + np.log(positive_class)
        nagative = np.sum(p_negative*vector) + np.log(1 - positive_class)
        print(positive,nagative)
        if positive > nagative:
            return 1
        else:
            return -1

    def test_data(self):
        p_positive,p_negative,positive_class = self.train()
        total_test = len(self._test_y)
        error_num = 0
        for i in range(total_test):
            vector = self._test_x[i]
            predict = self.classify(p_positive, p_negative, positive_class, vector)
            if predict != self._test_y[i]:
                error_num += 1
        print('predict error num', error_num)

bayes = NativeBayes()
postive = "/path_to/input/4.NaiveBayes/email/ham/"
nagative = "/path_to/input/4.NaiveBayes/email/spam/"
bayes.load_data(postive, nagative)
bayes.test_data()

5.貝葉斯網絡

首先說下貝葉斯網絡和樸素貝葉斯算法的主要區別:通過上面我們瞭解我們知道樸素貝葉斯的一大特點就是特徵的條件獨立性假設,然而在現實情況下,條件獨立假設過於嚴格,在實際問題中很難成立,因此特徵之間的相關性極大限制了樸素貝葉斯的性能,所以有了貝葉斯網絡,放寬了條件獨立假設的限制。

那麼一個貝葉斯網絡結構大概如何呢?
通常由有向無環圖DAG和節點對應的概率表組成,其中DAG由節點和有向邊組成,節點表示特徵屬性或隨機變量,有向邊表示各變量之間的依賴關係。
特點:當一個節點的父節點概率分佈確定之後,該節點條件獨立於其它所有的非直接父節點。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章