题目
试编程实现AODE分类器,并以西瓜数据集3.0位训练集,对p.151的“测1”样本进行判别。
编号 | 色泽 | 根蒂 | 敲声 | 纹理 | 脐部 | 触感 | 密度 | 含糖率 | 好瓜 |
---|---|---|---|---|---|---|---|---|---|
测1 | 青绿 | 蜷缩 | 浊响 | 清晰 | 凹陷 | 硬滑 | 0.697 | 0.460 | ? |
这个数据的真实类别为好瓜,在上一题用朴素贝叶斯也测试过。
原理
这实际上是一个半朴素贝叶斯分类器。首先什么叫朴素贝叶斯分类器,即采用了“”属性条件独立性假设”。其含义为,每个属性之间是没有关系的,是独立的。基于上述假设,如果我们有样本 x x x,它的类为 c c c, 我们就有 P ( x ∣ c ) = P ( x 1 , x 2 ⋯ , x d ∣ c ) = ∏ i = 1 d P ( x i ∣ c ) P(x|c) = P(x1,x2\cdots,x_d|c)=\prod_{i=1}^dP(x_i|c) P(x∣c)=P(x1,x2⋯,xd∣c)=∏i=1dP(xi∣c)。 x i x_i xi表示第 i i i个属性的一个取值,比如可以是色泽属性的青绿,也可以是根蒂属性的蜷缩。所谓半朴素贝叶斯分类器的意思为不光简单的认为属性之间是独立的,而是考虑一个属性和另一个属性可能是相关的。AODE意思是只考虑一个属性一次只与一个属性相关的情况。即 P ( x ∣ c ) = ∏ i = 1 d P ( x i ∣ c , x j ) P(x|c) =\prod_{i=1}^dP(x_i|c,x_j) P(x∣c)=∏i=1dP(xi∣c,xj),即对于类c和第j个属性取某值的情况下,第i个属性取某值的概率。具体参考西瓜书。
代码
import numpy as np
import matplotlib.pyplot as plt
from pylab import *
import operator
# 特征字典,后面用到了好多次,干脆当全局变量了
featureDic = {
'色泽': ['浅白', '青绿', '乌黑'],
'根蒂': ['硬挺', '蜷缩', '稍蜷'],
'敲声': ['沉闷', '浊响', '清脆'],
'纹理': ['清晰', '模糊', '稍糊'],
'脐部': ['凹陷', '平坦', '稍凹'],
'触感': ['硬滑', '软粘']}
def getDataSet():
"""
get watermelon data set 3.0.
:return: 编码好的数据集以及特征的字典。
"""
dataSet = [
['青绿', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', 0.697, 0.460, 1],
['乌黑', '蜷缩', '沉闷', '清晰', '凹陷', '硬滑', 0.774, 0.376, 1],
['乌黑', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', 0.634, 0.264, 1],
['青绿', '蜷缩', '沉闷', '清晰', '凹陷', '硬滑', 0.608, 0.318, 1],
['浅白', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', 0.556, 0.215, 1],
['青绿', '稍蜷', '浊响', '清晰', '稍凹', '软粘', 0.403, 0.237, 1],
['乌黑', '稍蜷', '浊响', '稍糊', '稍凹', '软粘', 0.481, 0.149, 1],
['乌黑', '稍蜷', '浊响', '清晰', '稍凹', '硬滑', 0.437, 0.211, 1],
['乌黑', '稍蜷', '沉闷', '稍糊', '稍凹', '硬滑', 0.666, 0.091, 0],
['青绿', '硬挺', '清脆', '清晰', '平坦', '软粘', 0.243, 0.267, 0],
['浅白', '硬挺', '清脆', '模糊', '平坦', '硬滑', 0.245, 0.057, 0],
['浅白', '蜷缩', '浊响', '模糊', '平坦', '软粘', 0.343, 0.099, 0],
['青绿', '稍蜷', '浊响', '稍糊', '凹陷', '硬滑', 0.639, 0.161, 0],
['浅白', '稍蜷', '沉闷', '稍糊', '凹陷', '硬滑', 0.657, 0.198, 0],
['乌黑', '稍蜷', '浊响', '清晰', '稍凹', '软粘', 0.360, 0.370, 0],
['浅白', '蜷缩', '浊响', '模糊', '平坦', '硬滑', 0.593, 0.042, 0],
['青绿', '蜷缩', '沉闷', '稍糊', '稍凹', '硬滑', 0.719, 0.103, 0]
]
features = ['色泽', '根蒂', '敲声', '纹理', '脐部', '触感', '密度', '含糖量']
# features = ['color', 'root', 'knocks', 'texture', 'navel', 'touch', 'density', 'sugar']
# #得到特征值字典,本来用这个生成的特征字典,还是直接当全局变量方便
# featureDic = {}
# for i in range(len(features)):
# featureList = [example[i] for example in dataSet]
# uniqueFeature = list(set(featureList))
# featureDic[features[i]] = uniqueFeature
# 每种特征的属性个数
numList = [] # [3, 3, 3, 3, 3, 2]
for i in range(len(features) - 2):
numList.append(len(featureDic[features[i]]))
dataSet = np.array(dataSet)
return dataSet, features
def AODE(dataSet, data, features):
"""
AODE(Averaged One-Dependent Estimator)。意思为尝试将每个属性作为超父来构建SPODE。
:param dataSet:
:param data:
:param features:
:return:
"""
m, n = dataSet.shape
n = n - 3 # 特征不取连续值的属性,如密度和含糖量。
pDir = {} # 保存三个值。好瓜的可能性,坏瓜的可能性,和预测的值。
for classLabel in ["好瓜", "坏瓜"]:
P = 0.0
if classLabel == "好瓜":
sign = '1'
else:
sign = '0'
extrDataSet = dataSet[dataSet[:, -1] == sign] # 抽出类别为sign的数据
for i in range(n): # 对于第i个特征
xi = data[i]
# 计算classLabel类,第i个属性上取值为xi的样本对总数据集的占比
Dcxi = extrDataSet[extrDataSet[:, i] == xi] # 第i个属性上取值为xi的样本数
Ni = len(featureDic[features[i]]) # 第i个属性可能的取值数
Pcxi = (len(Dcxi) + 1) / float(m + 2 * Ni)
# 计算类别为c且在第i和第j个属性上分别为xi和xj的样本,对于类别为c属性为xi的样本的占比
mulPCond = 1
for j in range(n):
xj = data[j]
Dcxij = Dcxi[Dcxi[:, j] == xj]
Nj = len(featureDic[features[j]])
PCond = (len(Dcxij) + 1) / float(len(Dcxi) + Nj)
mulPCond *= PCond
P += Pcxi * mulPCond
pDir[classLabel] = P
if pDir["好瓜"] > pDir["坏瓜"]:
preClass = "好瓜"
else:
preClass = "坏瓜"
return pDir["好瓜"], pDir["坏瓜"], preClass
def calcAccRate(dataSet, features):
"""
计算准确率
:param dataSet:
:param features:
:return:
"""
cnt = 0
for data in dataSet:
_, _, pre = AODE(dataSet, data, features)
if (pre == '好瓜' and data[-1] == '1') \
or (pre == '坏瓜' and data[-1] == '0'):
cnt += 1
return cnt / float(len(dataSet))
def main():
dataSet, features = getDataSet()
pG, pB, pre = AODE(dataSet, dataSet[0], features)
print("pG = ", pG)
print("pB = ", pB)
print("pre = ", pre)
print("real class = ", dataSet[0][-1])
print(calcAccRate(dataSet, features))
if __name__ == '__main__':
main()
输出结果
可以发现,半朴素贝叶斯,在该数据集下,能取到比朴素贝叶斯更好的效果。