课后题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 mathdef 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,attrNumdef getClassPrior(classname,classvalue,dataset,attrNum): # 得到类先验概率,经过拉普拉斯平滑count = 0for i in range(len(dataset)):if dataset[i][classname] == classvalue : count += 1return (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 = 0count_ = 0for i in range(len(dataset)):if dataset[i][classname]==classvalue and dataset[i][classCondname]==classCondvalue:count += 1if dataset[i][classCondname]==classCondvalue : count_ += 1return (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 pddef 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,attrNumdef getClassPrior(classname1,classvalue1,classname2,classvalue2,dataset,attrNum): # 得到类先验概率,经过拉普拉斯平滑count = 0for i in range(len(dataset)):if dataset[i][classname1] == classvalue1 and dataset[i][classname2] == classvalue2 : count += 1return (count+1)/(len(dataset) + attrNum[classname1]*attrNum[classname2])def getClassCondition(classname1,classvalue1,classname2,classvalue2,classname,classvalue,dataset,attrNum): # 得到类条件概率count = 0count_ = 0for i in range(len(dataset)):if dataset[i][classname1]==classvalue1 and dataset[i][classname2] == classvalue2 and dataset[i][classname]==classvalue:count += 1if dataset[i][classname1]==classvalue1 and dataset[i][classname2] == classvalue2 : count_ += 1return (count+1)/(count_+attrNum[classname])def main():test1 = {'色泽':'青绿','根蒂':'蜷缩','敲声':'浊响','纹理':'清晰','脐部':'凹陷','触感':'硬滑'}dataset,attrNum = readData()good = 0bad = 0for 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 += Pgoodbad += Pbadprint(good,bad)print('该西瓜是%s'%('好瓜' if good>bad else '坏瓜'))if __name__ == '__main__':main()
输出结果如下:
0.01867093430879439 0.00040009144947416545
该西瓜是好瓜