貝葉斯分類器(Bayes Classifier)

一、貝葉斯定理

1、背景:

貝葉斯定理也稱貝葉斯推理,早在18世紀,英國學者貝葉斯(1702~1763)曾提出
引用:百度百科

  • 貝葉斯定理是關於隨機事件A和B的條件概率(或邊緣概率)的一則定理。其中P(A|B)是在B發生的情況下A發生的可能性。
  • 貝葉斯定理計算條件概率的公式用來解決如下一類問題:
	假設:
			H[1],H[2],H[n]互斥且構成一個完全事件
			已知它們的概率P(H[i]),i=1,2,,n,
			現觀察到某事件A與H[1],H[2],H[n]相伴隨機出現且已知條件概率P(A|H[i]),求P(H[i]|A)

1.2、貝葉斯公式:
在這裏插入圖片描述

  • 公式描述中:P(Bi)爲事件Bi發生的概率,事件Bi在已經發生的條件下A的概率爲P(A|B),事件A發生條件下事件Bi的概率爲P(Bi|A)

1.2、貝葉斯概率的定義與貝葉斯公式:

假設,A和B是兩個事件公式如下:

  • A發生的概率記作:P(A)
  • B發生的概率記作:P(B)
  • A和B同時發生的概率記作:P(AB)
  • 條件概率P(B|A)爲:
    在這裏插入圖片描述
  • 由上圖公式可得:P(AB)=P(B|A)P(A)
    在這裏插入圖片描述

1.3、貝葉斯公式的應用:

  • 1.3.1,雙色球盒問題:

     假設有5個盒子,其中各自裝了兩個球,球總共有兩種顏色,具體如下
     	已知樣本空間Ω={(紅、紅),(紅、白),(白、紅),(白、白),(白、白)}
     問題:
     	事件A爲{盒子中至少有一個紅球}
     	事件B爲{盒子中至少有一個白球}
     	盒子中至少有一個紅求的條件下,至少有一個白球的概率
     	
     	解:
     	P(A)={(紅、紅),(紅、白),(白、紅)}
     	P(B)={(白、白),(紅、白),(白、紅),(白、白)}	
     	
     	可盒子中至少有一個紅求的條件下,至少有一個白球的概率=P(B|A)
     	求得概率爲2/3,如下圖
    

    在這裏插入圖片描述

     實驗中樣本點的總數爲n,事件A所包含的樣本點數爲m(m>0)
     AB所包含的樣本點數爲k,則可推出:
    

    在這裏插入圖片描述

1.4、樸素貝葉斯分類的應用:

  • 1.4.2,優缺點:
    (1) 算法邏輯簡單,易於實現(算法思路很簡單,只要使用貝葉斯公式轉化即可!)
    (2)分類過程中時空開銷小(假設特徵相互獨立,只會涉及到二維存儲)

  • 1.4.2,bayes分類一個好公司問題,數據集如下:
    數據集:統計求職者選則公司的特徵

  • 1.4.3,構建數學模型(數學處理過程):

    ①問題:
    A公司滿足Ω={福利少、加班少、工資中、壓力小、發展前景好、氛圍好、位置近、環境好}求,該公司是否是好公司?
    解:
    公式A如下
    在這裏插入圖片描述在這裏插入圖片描述
    公式B如下
    在這裏插入圖片描述
    在這裏插入圖片描述

    ②拉普拉斯平滑係數:
    根據公式p(工資中|壞公司)=0,肯定不符合真實情況,所以需要“拉普拉斯平滑係數”:當我們在使用樸素貝葉斯算法去解決分類問題時,在訓練集上進行訓練時我們可以發現有可能出現某些特徵的概率P爲0的情況,無論是在全文檢索中某個字出現的概率,還是在垃圾郵件分類中,這種情況明顯是不太合理的,不能因爲一個事件沒有觀察到就武斷的認爲該事件的概率是0,拉普拉斯的理論支撐而拉布拉斯平滑處理正是處理這種情況下應運而生的。
    ③拉普拉斯平滑係數化:
    在這裏插入圖片描述
    在這裏插入圖片描述

1.4.4,代碼實現Naive Bayes 分類:

# -*- coding: utf-8 -*-
from numpy import *
from functools import reduce

adClass = 1


def loadDataSet():
    """加載數據集合及其對應的分類"""
    # 福利	加班	工資	工作壓力	發展前景	公司氛圍	公司位置	環境
    wordsList = [
        ['福利少', '加班多', '工資少', '壓力大', '發展前景差', '氛圍中', '位置中', '環境差'],
        ['福利好', '加班少', '工資多', '壓力小', '發展前景好', '氛圍好', '位置近', '環境好'],
        ['福利好', '加班中', '工資中', '壓力中', '發展前景好', '氛圍好', '位置近', '環境好'],
        ['福利中', '加班少', '工資少', '壓力小', '發展前景中', '氛圍好', '位置近', '環境好'],
        ['福利少', '加班中', '工資少', '壓力中', '發展前景差', '氛圍中', '位置遠', '環境中'],
        ['福利中', '加班多', '工資多', '壓力大', '發展前景中', '氛圍好', '位置遠', '環境中'],
        ['福利少', '加班多', '工資中', '壓力大', '發展前景差', '氛圍中', '位置中', '環境差'],
        ['福利中', '加班少', '工資少', '壓力小', '發展前景好', '氛圍中', '位置近', '環境好'],
        ['福利中', '加班多', '工資多', '壓力大', '發展前景中', '氛圍好', '位置遠', '環境中'],
        ['福利少', '加班少', '工資多', '壓力小', '發展前景好', '氛圍中', '位置中', '環境好'],
        ['福利少', '加班少', '工資少', '壓力小', '發展前景好', '氛圍差', '位置近', '環境好'],
        ['福利少', '加班少', '工資少', '壓力大', '發展前景好', '氛圍差', '位置中', '環境差'],

    ]

    # 1 是, 0 否
    classVec = [0,1, 1, 0,0, 1, 1, 0, 1, 1,1,0]
    return wordsList, classVec


# python中的& | 是位運算符   and or是邏輯運算符 當and的運算結果爲true時候返回的並不是true而是運算結果最後一位變量的值
# 當and返回的結果是false時候,如果A AND B 返回的是第一個false的值,如果a爲false 則返回a,如果a不是false,那麼返回b
# 如果a or b 爲true時候,返回的是第一個真的變量的值,如果a,b都爲真時候那麼返回a 如果a爲假b爲真那麼返回b
# a & b a和b爲兩個set,返回結果取a和b的交集  a|b a和b爲兩個set,返回結果爲兩個集合的不重複並集


def doc2VecList(docList):
    # 從第一個和第二個集合開始進行並集操作,最後返回一個不重複的並集
    # a = list(reduce(lambda x, y: set(x) | set(y), docList))
    a =['環境好', '氛圍中', '壓力大', '壓力小', '工資少', '氛圍好', '加班多', '福利少', '發展前景差', '福利好', '環境差', '加班少', '加班中', '位置遠', '工資中', '位置中', '氛圍差', '環境中', '壓力中', '發展前景中', '工資多', '位置近', '福利中', '發展前景好']
    return a


def words2Vec(vecList, inputWords):
    """把單子轉化爲詞向量"""
    # 轉化成以一維數組
    resultVec = [0] * len(vecList)
    for word in inputWords:
        if word in vecList:
            # 在單詞出現的位置上的計數加1
            resultVec[vecList.index(word)] += 1
        else:
            print('沒有發現此單詞')

    return array(resultVec)

#trainMat, classVec
def trainNB(trainMatrix, trainClass):
    """計算,生成每個詞對於類別上的概率"""
    # 類別行數
    numTrainClass = len(trainClass)
    # 列數
    numWords = len(trainMatrix[0])

    # 全部都初始化爲1, 防止出現概率爲0的情況出現
    # 見於韓家煒的數據挖掘概念與技術上的講解,避免出現概率爲0的狀況,影響計算,因爲在數量很大的情況下,在分子和分母同時+1的情況不會
    # 影響主要的數據
    p0Num = ones(numWords)
    p1Num = ones(numWords)
    # 相應的單詞初始化爲1
    # 爲了分子分母同時都加上某個數λ
    p0Words = 1.0
    p1Words = 1.0
    # 統計每個分類的詞的總數
    # 訓練數據集的行數作爲遍歷的條件,從1開始
    # 如果當前類別爲1,那麼p1Num會加上當前單詞矩陣行數據,依次遍歷
    # 如果當前類別爲0,那麼p0Num會加上當前單詞矩陣行數據,依次遍歷
    # 同時統計當前類別下單詞的個數和p1Words和p0Words
    for i in range(numTrainClass):
        if trainClass[i] == 1:
            # 數組在對應的位置上相加
            p1Num += trainMatrix[i]
            p1Words += sum(trainMatrix[i])

            # print("i=",i,",p1Words=",p1Words,",trainMatrix[i]=",trainMatrix[i])
        else:
            p0Num += trainMatrix[i]
            p0Words += sum(trainMatrix[i])
            # print("i=",i,",p1Num=",p1Num,",trainMatrix[i]=",trainMatrix[i])
            print("i=",i,',p0Num=',p0Num,",p0Words=",p0Words,",trainMatrix[i]=",trainMatrix[i])
    # 計算每種類型裏面, 每個單詞出現的概率
    # 樸素貝葉斯分類中,y=x是單調遞增函數,y=ln(x)也是單調的遞增的
    # 如果x1>x2 那麼ln(x1)>ln(x2)
    # 在計算過程中,由於概率的值較小,所以我們就取對數進行比較,根據對數的特性
    # ln(MN) = ln(M)+ln(N)
    # ln(M/N) = ln(M)-ln(N)
    # ln(M**n)= nln(M)
    # 注:其中ln可替換爲log的任意對數底
    print("p0Num==",p0Num)
    print("p0Num / p0Words===",p0Num / p0Words)

    p0Vec = log(p0Num / p0Words)
    p1Vec = log(p1Num / p1Words)
    # 計算在類別中1出現的概率,0出現的概率可通過1-p得到
    pClass1 = sum(trainClass) / float(numTrainClass)
    return p0Vec, p1Vec, pClass1


def classifyNB(testVec, p0Vec, p1Vec, pClass1):
    # 樸素貝葉斯分類, max(p0, p1)作爲推斷的分類
    # y=x 是單調遞增的, y=ln(x)也是單調遞增的。 , 如果x1 > x2, 那麼ln(x1) > ln(x2)
    # 因爲概率的值太小了,所以我們可以取ln, 根據對數特性ln(ab) = lna + lnb, 可以簡化計算
    # sum是numpy的函數,testVec是一個數組向量,p1Vec是一個1的概率向量,通過矩陣之間的乘機
    # 獲得p(X1|Yj)*p(X2|Yj)*...*p(Xn|Yj)*p(Yj)
    # 其中pClass1即爲p(Yj)
    # 此處計算出的p1是用對數表示,按照上面所說的,對數也是單調的,而貝葉斯分類主要是通過比較概率
    # 出現的大小,不需要確切的概率數據,因此下述表述完全正確


    # p1 = sum(testVec * p1Vec) + log(pClass1)
    # p0 = sum(testVec * p0Vec) + log(1 - pClass1)
    p1 = sum(testVec * p1Vec) + log(pClass1)
    p0 = sum(testVec * p0Vec) + log(1 - pClass1)
    print("p1=", p1, ",p0=", p1)
    if p0 > p1:
        return 0
    return 1


def printClass(words, testClass):
    if testClass == adClass:
        print(words, '推測爲:好公司')
    else:
        print(words, '推測爲:壞公司')


def tNB():
    # 從訓練數據集中提取出屬性矩陣和分類數據
    docList, classVec = loadDataSet()

    allWordsVec = doc2VecList(docList) #去重
    print("allWordsVec=",allWordsVec)

    # 構建詞向量矩陣
    # 計算docList數據集中每一行每個單詞出現的次數,其中返回的trainMat是一個數組的數組
    print("docList:", docList)
    trainMat = list(map(lambda x: words2Vec(allWordsVec, x), docList))
    print("trainMat:",trainMat)

    # 訓練計算每個詞在分類上的概率, p0V:每個單詞在非分類出現的概率, p1V:每個單詞在是分類出現的概率
    # 其中概率是以ln進行計算的
    # pClass1爲類別中是1的概率
    p0V, p1V, pClass1 = trainNB(trainMat, classVec)
    # 測試數據集

    print("p0V:", p0V)
    print("p1V:", p1V)
    print("pClass1:", pClass1)
    #福利	加班	工資	工作壓力	發展前景	公司氛圍	公司位置	環境


    # testWords = ['福利少', '加班多', '工資多', '壓力大', '發展前景差', '氛圍中', '位置中', '環境差']
    testWords = ['福利少', '加班多', '工資多', '壓力大', '發展前景差', '氛圍中', '位置中', '環境差']
    # testWords = ['公司', '聚餐', '討論', '貝葉斯']
    # 轉換成單詞向量,32個單詞構成的數組,如果此單詞在數組中,數組的項值置1

    testVec = words2Vec(allWordsVec, testWords)
    print("testWords=",testWords,",testVec=",testVec)
    # 通過將單詞向量testVec代入,根據貝葉斯公式,比較各個類別的後驗概率,判斷當前數據的分類情況
    testClass = classifyNB(testVec, p0V, p1V, pClass1)

    # 打印出測試結果
    printClass(testWords, testClass)
    # 福利	加班	工資	工作壓力	發展前景	公司氛圍	公司位置	環境


    # testWords = ['福利少', '加班少', '工資多', '壓力小', '發展前景好', '氛圍好', '位置近', '環境好']
    testWords = ['福利少', '加班少', '工資中', '壓力小', '發展前景好', '氛圍好', '位置近', '環境好']
    # 轉換成單詞向量,32個單詞構成的數組,如果此單詞在數組中,數組的項值置1
    testVec = words2Vec(allWordsVec, testWords)
    # 通過將單詞向量testVec代入,根據貝葉斯公式,比較各個類別的後驗概率,判斷當前數據的分類情況
    testClass = classifyNB(testVec, p0V, p1V, pClass1)
    # 打印出測試結果
    printClass(testWords, testClass)


if __name__ == '__main__':
    tNB()
    # x

    # print(len(a))
    # print(log(4,2))
    # print(words2Vec(['高', '差', '少', '中', '好', '近', '小', '遠', '多'],['少', '多', '少', '高', '差', '中', '中', '差']))
‘’‘輸出結果’‘’
['福利少', '加班多', '工資多', '壓力大', '發展前景差', '氛圍中', '位置中', '環境差'] 推測爲:壞公司
['福利少', '加班少', '工資中', '壓力小', '發展前景好', '氛圍好', '位置近', '環境好'] 推測爲:好公司
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章