西瓜书课后题——第七章(贝叶斯分类器)

课后题7.1:极大似然估计西瓜数据集3.0前3个属性的类条件概率。

其实就是概率论中常见的极大似然估计问题。

参见这篇博客:https://blog.csdn.net/icefire_tyh/article/details/52167273

课后题7.3:编程实现拉普拉斯修正的朴素贝叶斯分类器。

原理和公式书上均有详细的说明,这里直接给出代码:

import numpy as np
import pandas as pd
import math

def readData():          # 读取数据
    dataset = pd.read_excel('./WaterMelon_3.0.xlsx',encoding = 'gbk')  # 读取数据
    Attributes = dataset.columns[1:]    # 属性名称列表
    dataset = np.array(dataset)
    dataset = dataset[:,1:]
    m,n = np.shape(dataset)
    dataList = []
    for i in range(m):      # 生成数据列表,列表元素是集合类型
        curset = {}
        for j in range(n):
            curset[Attributes[j]] = dataset[i,j]
        dataList.append(curset)

    attrNum = {}            # 统计每个属性的可取值个数
    for i in range(n):
        curSet = set()      # 使用集合是利用了集合里面元素不可重复的特性,从而提取出了每个属性的取值
        for j in range(m):
            curSet.add(dataset[j,i])
        attrNum[Attributes[i]] = len(curSet)
    return dataList,attrNum


def getClassPrior(classname,classvalue,dataset,attrNum):     # 得到类先验概率,经过拉普拉斯平滑
    count = 0
    for i in range(len(dataset)):
        if dataset[i][classname] == classvalue : count += 1
    return (count+1)/(len(dataset) + attrNum[classname])

def getClassCondition(classname,classvalue,classCondname,classCondvalue,dataset,attrNum):   # 得到类条件概率
    if classname=='密度'or classname=='含糖率':      # 若是连续属性,则用概率密度进行计算
        value = []
        for i in range(len(dataset)):
            if dataset[i][classCondname]==classCondvalue:
                value.append(dataset[i][classname])
        mean = np.mean(value)
        delt = np.std(value)
        return (1/(math.sqrt(2*math.pi)*delt))*math.exp(-(classvalue-mean)**2/(2*delt**2))
    else:                                             # 离散属性用频率代替概率,并进行拉普拉斯平滑
        count = 0
        count_ = 0
        for i in range(len(dataset)):
            if dataset[i][classname]==classvalue and dataset[i][classCondname]==classCondvalue:
                count += 1
            if dataset[i][classCondname]==classCondvalue : count_ += 1
        return (count+1)/(count_+attrNum[classname])

def main():
    test1 = {'色泽':'青绿','根蒂':'蜷缩','敲声':'浊响','纹理':'清晰','脐部':'凹陷','触感':'硬滑',\
         '密度':0.697,'含糖率':0.460}
    dataset,attrNum = readData()
    Pgood = getClassPrior('好瓜','是',dataset,attrNum)
    Pbad = getClassPrior('好瓜','否',dataset,attrNum)
    for i in test1:
        Pgood *= getClassCondition(i,test1[i],'好瓜','是',dataset,attrNum)
        Pbad *= getClassCondition(i,test1[i],'好瓜','否',dataset,attrNum)
    print(Pgood,Pbad)
    print('该西瓜是%s'%('好瓜' if Pgood>Pbad else '坏瓜'))

if __name__ == '__main__':
    main()

最终结果如下:

0.0218012464059     4.91583402142e-05
该西瓜是好瓜

课后题7.4:连乘时属性值过多可能造成下溢,试述防止下溢的方案。

方案一:对所有概率均取e指数,即是用 exp(P) 代替 P,此时就可以保证所有值均大于1,不会产生下溢问题。

方案二:取对数。当然,考虑到正负号的问题,不能直接取对数,可以使用 ln(1+P) 来代替 P。

方案三:还可以在连乘的过程中,当数据值小于预先设定的界限后,人为地对每一类的概率同时乘上一个相同的数值,防止下                    溢。

方案四:也可以直接取对数后,将连乘运算变成累加运算进行。

课后题7.6:编程实现AODE分类器,并以西瓜数据集3.0为训练集,对‘测1‘进行判别

这个题其实和课后题7.3差不多,只是在进行计数时要再加一个条件。这里直接在7.3的代码基础上进行了一些改动,为了方便起见,这里仅考虑了离散属性。(因为连续属性要计算联合概率密度……呃…………)

代码如下所示:

import numpy as np
import pandas as pd

def readData():          # 读取数据,只取离散属性
    dataset = pd.read_excel('./WaterMelon_3.0.xlsx',encoding = 'gbk')  # 读取数据
    Attributes = np.hstack((np.array(dataset.columns[1:-3]),np.array(dataset.columns[-1])))   # 属性名称列表
    dataset = np.array(dataset)
    dataset = np.hstack((dataset[:,1:-3],np.reshape(dataset[:,-1],newshape=(len(dataset[:,-1]),1))))
    m,n = np.shape(dataset)
    dataList = []
    for i in range(m):      # 生成数据列表,列表元素是集合类型
        curset = {}
        for j in range(n):
            curset[Attributes[j]] = dataset[i,j]
        dataList.append(curset)

    attrNum = {}            # 统计每个属性的可取值个数
    for i in range(n):
        curSet = set()      # 使用集合是利用了集合里面元素不可重复的特性,从而提取出了每个属性的取值
        for j in range(m):
            curSet.add(dataset[j,i])
        attrNum[Attributes[i]] = len(curSet)
    return dataList,attrNum


def getClassPrior(classname1,classvalue1,classname2,classvalue2,dataset,attrNum):     # 得到类先验概率,经过拉普拉斯平滑
    count = 0
    for i in range(len(dataset)):
        if dataset[i][classname1] == classvalue1 and dataset[i][classname2] == classvalue2 : count += 1
    return (count+1)/(len(dataset) + attrNum[classname1]*attrNum[classname2])


def getClassCondition(classname1,classvalue1,classname2,classvalue2,classname,classvalue,dataset,attrNum):   # 得到类条件概率
    count = 0
    count_ = 0
    for i in range(len(dataset)):
        if dataset[i][classname1]==classvalue1 and dataset[i][classname2] == classvalue2 and dataset[i][classname]==classvalue:
            count += 1
        if dataset[i][classname1]==classvalue1 and dataset[i][classname2] == classvalue2 : count_ += 1
    return (count+1)/(count_+attrNum[classname])


def main():
    test1 = {'色泽':'青绿','根蒂':'蜷缩','敲声':'浊响','纹理':'清晰','脐部':'凹陷','触感':'硬滑'}
    dataset,attrNum = readData()
    good = 0
    bad = 0
    for j in test1:
        Pgood = getClassPrior('好瓜','是',j,test1[j],dataset,attrNum)
        Pbad = getClassPrior('好瓜','否',j,test1[j],dataset,attrNum)
        for i in test1:
            Pgood *= getClassCondition(j,test1[j],'好瓜','是',i,test1[i],dataset,attrNum)
            Pbad *= getClassCondition(j,test1[j],'好瓜','否',i,test1[i],dataset,attrNum)
    good += Pgood
    bad += Pbad
    print(good,bad)
    print('该西瓜是%s'%('好瓜' if good>bad else '坏瓜'))

if __name__ == '__main__':
    main()

输出结果如下:

0.01867093430879439     0.00040009144947416545
该西瓜是好瓜

猜你喜欢

转载自blog.csdn.net/qq_37691909/article/details/86036396