课后题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
该西瓜是好瓜