朴素貝葉斯python代碼實現(西瓜書)


朴素貝葉斯python代碼實現(西瓜書)

摘要:

朴素貝葉斯也是機器學習中一種非常常見的分類方法,對於二分類問題,並且數據集特征為離散型屬性的時候,
使用起來非常的方便。原理簡單,訓練效率高,擬合效果好。

朴素貝葉斯

貝葉斯公式:

朴素貝葉斯之所以稱這為朴素,是因為假設了各個特征是相互獨立的,因此假定下公式成立:

則朴素貝葉斯算法的計算公式如下:

在實際計算中,上面的公式會做如下略微改動:

  1. 由於某些特征屬性的值P(Xi|Ci)可能很小,多個特征的p值連乘后可能被約等於0。可以公式兩邊取log然后變乘法為加法,避免類乘問題。
  2. P(Ci) 和P(Xi|Ci) 一般不直接使用樣本的頻率計算出來,一般會使用拉普拉斯平滑。

上面公式中,Dc為該類別的頻數,N表示所有類別的可能數。

上面公式中,Dc,xi為該特征對應屬性的頻數,Dc為該類別的頻數,Ni表示該特征的可能的屬性數。

對應的西瓜書數據集為

色澤	根蒂	敲聲	紋理	臍部	觸感	好瓜
青綠	蜷縮	濁響	清晰	凹陷	硬滑	是
烏黑	蜷縮	沉悶	清晰	凹陷	硬滑	是
烏黑	蜷縮	濁響	清晰	凹陷	硬滑	是
青綠	蜷縮	沉悶	清晰	凹陷	硬滑	是
淺白	蜷縮	濁響	清晰	凹陷	硬滑	是
青綠	稍蜷	濁響	清晰	稍凹	軟粘	是
烏黑	稍蜷	濁響	稍糊	稍凹	軟粘	是
烏黑	稍蜷	濁響	清晰	稍凹	硬滑	是
烏黑	稍蜷	沉悶	稍糊	稍凹	硬滑	否
青綠	硬挺	清脆	清晰	平坦	軟粘	否
淺白	硬挺	清脆	模糊	平坦	硬滑	否
淺白	蜷縮	濁響	模糊	平坦	軟粘	否
青綠	稍蜷	濁響	稍糊	凹陷	硬滑	否
淺白	稍蜷	沉悶	稍糊	凹陷	硬滑	否
烏黑	稍蜷	濁響	清晰	稍凹	軟粘	否
淺白	蜷縮	濁響	模糊	平坦	硬滑	否
青綠	蜷縮	沉悶	稍糊	稍凹	硬滑	否

python實現

#encoding:utf-8

import pandas as pd
import numpy  as np

class NaiveBayes:
    def __init__(self):
        self.model = {}#key 為類別名 val 為字典PClass表示該類的該類,PFeature:{}對應對於各個特征的概率
    def calEntropy(self, y): # 計算熵
        valRate = y.value_counts().apply(lambda x : x / y.size) # 頻次匯總 得到各個特征對應的概率
        valEntropy = np.inner(valRate, np.log2(valRate)) * -1
        return valEntropy

    def fit(self, xTrain, yTrain = pd.Series()):
        if not yTrain.empty:#如果不傳,自動選擇最后一列作為分類標簽
            xTrain = pd.concat([xTrain, yTrain], axis=1)
        self.model = self.buildNaiveBayes(xTrain) 
        return self.model
    def buildNaiveBayes(self, xTrain):
        yTrain = xTrain.iloc[:,-1]
        
        yTrainCounts = yTrain.value_counts()# 頻次匯總 得到各個特征對應的概率

        yTrainCounts = yTrainCounts.apply(lambda x : (x + 1) / (yTrain.size + yTrainCounts.size)) #使用了拉普拉斯平滑
        retModel = {}
        for nameClass, val in yTrainCounts.items():
            retModel[nameClass] = {'PClass': val, 'PFeature':{}}

        propNamesAll = xTrain.columns[:-1]
        allPropByFeature = {}
        for nameFeature in propNamesAll:
            allPropByFeature[nameFeature] = list(xTrain[nameFeature].value_counts().index)
        #print(allPropByFeature)
        for nameClass, group in xTrain.groupby(xTrain.columns[-1]):
            for nameFeature in propNamesAll:
                eachClassPFeature = {}
                propDatas = group[nameFeature]
                propClassSummary = propDatas.value_counts()# 頻次匯總 得到各個特征對應的概率
                for propName in allPropByFeature[nameFeature]:
                    if not propClassSummary.get(propName):
                        propClassSummary[propName] = 0#如果有屬性滅有,那么自動補0
                Ni = len(allPropByFeature[nameFeature])
                propClassSummary = propClassSummary.apply(lambda x : (x + 1) / (propDatas.size + Ni))#使用了拉普拉斯平滑
                for nameFeatureProp, valP in propClassSummary.items():
                    eachClassPFeature[nameFeatureProp] = valP
                retModel[nameClass]['PFeature'][nameFeature] = eachClassPFeature

        return retModel
    def predictBySeries(self, data):
        curMaxRate = None
        curClassSelect = None
        for nameClass, infoModel in self.model.items():
            rate = 0
            rate += np.log(infoModel['PClass'])
            PFeature = infoModel['PFeature']
            
            for nameFeature, val in data.items():
                propsRate = PFeature.get(nameFeature)
                if not propsRate:
                    continue
                rate += np.log(propsRate.get(val, 0))#使用log加法避免很小的小數連續乘,接近零
                #print(nameFeature, val, propsRate.get(val, 0))
            #print(nameClass, rate)
            if curMaxRate == None or rate > curMaxRate:
                curMaxRate = rate
                curClassSelect = nameClass
            
        return curClassSelect
    def predict(self, data):
        if isinstance(data, pd.Series):
            return self.predictBySeries(data)
        return data.apply(lambda d: self.predictBySeries(d), axis=1)

dataTrain = pd.read_csv("xiguadata.csv", encoding = "gbk")

naiveBayes = NaiveBayes()
treeData = naiveBayes.fit(dataTrain)

import json
print(json.dumps(treeData, ensure_ascii=False))

pd = pd.DataFrame({'預測值':naiveBayes.predict(dataTrain), '正取值':dataTrain.iloc[:,-1]})
print(pd)
print('正確率:%f%%'%(pd[pd['預測值'] == pd['正取值']].shape[0] * 100.0 / pd.shape[0]))

輸出

{"否": {"PClass": 0.5263157894736842, "PFeature": {"色澤": {"淺白": 0.4166666666666667, "青綠": 0.3333333333333333, "烏 黑": 0.25}, "根蒂": {"稍蜷": 0.4166666666666667, "蜷縮": 0.3333333333333333, "硬挺": 0.25}, "敲聲": {"濁響": 0.4166666666666667, "沉悶": 0.3333333333333333, "清脆": 0.25}, "紋理": {"稍糊": 0.4166666666666667, "模糊": 0.3333333333333333, "清晰": 0.25}, "臍部": {"平坦": 0.4166666666666667, "稍凹": 0.3333333333333333, "凹陷": 0.25}, "觸感": {"硬滑": 0.6363636363636364, "軟粘": 0.36363636363636365}}}, "是": {"PClass": 0.47368421052631576, "PFeature": {"色澤": {"烏黑": 0.45454545454545453, "青綠": 0.36363636363636365, "淺白": 0.18181818181818182}, "根蒂": {"蜷縮": 0.5454545454545454, "稍蜷": 0.36363636363636365, "硬挺": 0.09090909090909091}, "敲聲": {"濁響": 0.6363636363636364, "沉悶": 0.2727272727272727, "清脆": 0.09090909090909091}, "紋理": {"清晰": 0.7272727272727273, "稍糊": 0.18181818181818182, "模糊": 0.09090909090909091}, "臍 部": {"凹陷": 0.5454545454545454, "稍凹": 0.36363636363636365, "平坦": 0.09090909090909091}, "觸感": {"硬滑": 0.7, "軟粘": 0.3}}}}
   預測值 正取值
0    是   是
1    是   是
2    是   是
3    是   是
4    是   是
5    是   是
6    否   是
7    是   是
8    否   否
9    否   否
10   否   否
11   否   否
12   是   否
13   否   否
14   是   否
15   否   否
16   否   否
正確率:82.352941%

總結:

  • 貝葉斯分類器是一種生成式模型,不是直接擬合分類結果,而是擬合出后驗概率公式計算對應分類的概率。
  • 本文只介紹了二分類,也可以用來處理多分類問題。
  • 對於小規模數據集,表現良好。
  • 建立在特征相互獨立的假設上。
  • 這是我的github主頁https://github.com/fanchy,有些有意思的分享。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM