文章目录
1、决策树
1.1什么是决策树
有监督机器学习算法中的一类经典算法,是最经常使用的数据挖掘算法
它是一种典型的分类方法,首先对数据进行处理,利用归纳算法生成可读的规则和决策树,然后使用决策对新数据进行分析。本质上决策树是通过一系列规则对数据进行分类的过程。 典型算法有ID3,C4.5,CART等>
1.2和knn算法的区别
k-近邻算法的缺点是无法给出数据的内在含义
决策树:
- 优点:计算复杂度不高,输出结果易于理解,对中间值的确实不敏感,可以处理并不相关特征数据
- 缺点:可能会产生过度匹配问题
- 使用数据类型:数值型,标称型
1.3伪代码
检测数据集中每个子项是否属于同一分类
IF so return 类标签
Else
寻找划分数据集的最好特征
划分数据集
创建分支节点
for 每个划分的子集
调用函数createBranch并增加返回结果到分支节点中
return 分支节点
2 决策树的构造流程
- 收集数据
- 准备数据
- 分析数据
- 训练数据
- 测试算法
- 使用算法
决策树学习的目的是为了产生一棵泛化能力强,
即处理未见示例能力强的决策树
3 属性划分方法
3.1 信息增益(ID3)
信息增益: 划分数据集之前之后信息发生的变化
划分数据集的原则:将无序的数据变得更加有序
ID3(Iterative Dichotomiser,迭代二分器)决策树学习算法[Quinlan, 1986]以信息增益为准则来选择划分属性。
3.1.1 信息熵
“信息熵”是度量样本集合纯度最常用的一种指标
假定,当前样本集合D中第k类样本所占的比例为 pk (K=1, 2, …, |y|);则D的信息熵增益为:
- Ent(D)的值越小,则D的纯度越高
- 计算信息熵时约定:若p = 0,则plog2p=0
- Ent(D)的最小值为0,最大值为log2|y|
python代码计算给定数据集的信息熵
def createDataSet():
dataSet = [[1, 1, 'yes'],
[1, 1, 'yes'],
[1, 0, 'no'],
[0, 1, 'no'],
[0, 1, 'no']]
labels = ['no surfacing','flippers']
return dataSet,labels
def calcShannonEnt(dataSet):
numEntris = len(dataSet)
labelCounts = {}
for featVec in dataSet:
currentLabel = featVec[-1]
if currentLabel not in labelCounts.keys():
labelCounts[currentLabel] = 0
labelCounts[currentLabel] +=1
shannonEnt = 0.0
for key in labelCounts:
prob = float(labelCounts[key])/numEntris
shannonEnt -= prob * log(prob,2)
return shannonEnt
测试代码:
>>> d,l=trees.createDataSet()
>d
[[1, 1, 'yes'], [1, 1, 'yes'],
[1, 0, 'no'], [0, 1, 'no'],
[0, 1, 'no']]
>>> trees.calcShannonEnt(d)
0.9709505944546686
3.1.2 信息增益
样本数越多的分支结点的影响越大
信息增益越大,则意味着使用属性a来进行划分所获得的“纯度提升”越大
信息增益对可取值数目较多的属性有所偏好
代码实现:
在这里插入代码片
3.2 信息增益率(C4.5)
缺点: 对取值数目较少的属性有所偏好
代码实现:
def create_tree_C45(self,dataset,feat_labels):
#print("本节点特征",labels)
classList=list(dataset['label'])#获得类别列表
#若是所有样本属于同一类别,则返回这一类别做节点标记,停止划分
if classList.count(classList[0])==len(classList):
#print("该节点上所有样本为同一类")
return classList[0]
#若特征集为空,则返回数据集中样本数最多的类作为节点标记,停止划分
if len(dataset.iloc[0])==1:
#print("特征集为空")
return self.majorityCnt(classList)
bestFeatLabel,bestFeatIndex=self.choose_best_feature_C45(dataset,feat_labels) #选择最优特征
print("最佳划分特征(createtree)",bestFeatLabel,bestFeatIndex)
myTree={bestFeatLabel:{}} #分类结果以字典形式保存
#如果最佳划分特征不为空则继续划分
if(bestFeatLabel!=''):
del(feat_labels[bestFeatIndex])
featValues=dataset[bestFeatLabel] #最好划分特征的所有取值
#print(featValues)
uniqueVals=set(featValues)
for value in uniqueVals:
subLabels=feat_labels[:]
#print(bestFeatLabel,"取值为:",value)
values_head=subLabels
newdataset=self.split_dataset(dataset,bestFeatLabel,bestFeatIndex,value,values_head)
myTree[bestFeatLabel][value]=self.create_tree_C45(newdataset,subLabels)
return myTree
3.3 基尼指数(CART)
3.3.1 求解基尼指数
import numpy as np
def calcGini(data_y): #根据基尼指数的定义,根据当前数据集中不同标签类出现次数,获取当前数据集D的基尼指数
m = data_y.size #获取全部数据数量
labels = np.unique(data_y) #获取所有标签值类别(去重后)
gini = 1.0 #初始基尼系数
for i in labels: #遍历每一个标签值种类
y_cnt = data_y[np.where(data_y==i)].size / m #出现概率
gini -= y_cnt**2 #基尼指数
return gini
3.3.2 实现数据集切分
def splitDataSet(data_X,data_Y,fea_axis,fea_val): #根据特征、和该特征下的特征值种类,实现切分数据集和标签
#根据伪算法可以知道,我们要将数据集划分为2部分:特征值=a和特征值不等于a
eqIdx = np.where(data_X[:,fea_axis]==fea_val)
neqIdx = np.where(data_X[:,fea_axis]!=fea_val)
return data_X[eqIdx],data_Y[eqIdx],data_X[neqIdx],data_Y[neqIdx]
3.3.3 选取最优特征和特征值划分
def chooseBestFeature(data_X,data_Y): #遍历所有特征和特征值,选取最优划分
m,n = data_X.shape
bestFeature = -1
bestFeaVal = -1
minFeaGini = np.inf
for i in range(n): #遍历所有特征
fea_cls = np.unique(data_X[:,i]) #获取该特征下的所有特征值
# print("{}---".format(fea_cls))
for j in fea_cls: #遍历所有特征值
newEqDataX,newEqDataY,newNeqDataX,newNeqDataY=splitDataSet(data_X,data_Y,i,j) #进行数据集切分
feaGini = 0 #计算基尼指数
feaGini += newEqDataY.size/m*calcGini(newEqDataY) + newNeqDataY.size/m*calcGini(newNeqDataY)
if feaGini < minFeaGini:
bestFeature = i
bestFeaVal = j
minFeaGini = feaGini
return bestFeature,bestFeaVal #返回最优划分方式
3.3.4 创建树
代码实现:
def createTree(data_X,data_Y,fea_idx): #创建决策树
y_labels = np.unique(data_Y)
#1.如果数据集中,所有实例都属于同一类,则返回
if y_labels.size == 1:
return data_Y[0]
#2.如果特征集为空,表示遍历了所有特征,使用多数投票进行决定
if data_X.shape[1] == 0:
bestFea,bestCnt = 0,0
for i in y_labels:
cnt = data_Y[np.where(data_Y==i)].size
if cnt > bestCnt:
bestFea = i
bestCnt = cnt
return bestFea
#按照基尼指数,选择特征,进行继续递归创建树
bestFeature, bestFeaVal = chooseBestFeature(data_X,data_Y)
# print(bestFeature,bestFeaVal)
feaBestIdx = fea_idx[bestFeature]
my_tree = {feaBestIdx:{}}
#获取划分结果
newEqDataX,newEqDataY,newNeqDataX,newNeqDataY = splitDataSet(data_X,data_Y,bestFeature,bestFeaVal)
#删除我们选择的最优特征
newEqDataX = np.delete(newEqDataX,bestFeature,1)
newNeqDataX = np.delete(newNeqDataX,bestFeature,1)
fea_idx = np.delete(fea_idx,bestFeature,0)
my_tree[feaBestIdx]["{}_{}".format(1,bestFeaVal)] = createTree(newEqDataX,newEqDataY,fea_idx)
my_tree[feaBestIdx]["{}_{}".format(0,bestFeaVal)] = createTree(newNeqDataX,newNeqDataY,fea_idx)
return my_tree
3.4决策树
3.4.1 创建树的代码
def createTree(dataSet,labels):
classList = [example[-1] for example in dataSet]
if classList.count(classList[0]) == len(classList):
return classList[0]
if len(dataSet[0]) == 1:
return majorityCnt(classList)
bestFeat = chooseBestFeatureToSplit(dataSet)
bestFeatLabel = labels[bestFeat]
myTree = {bestFeatLabel:{}}
del(labels[bestFeat])
featValues = [example[bestFeat] for example in dataSet]
uniqueVals = set(featValues)
for value in uniqueVals:
subLabels = labels[:]
myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)
return myTree
3.4.2 使用决策树的分类函数
def classify(inputTree,featLabels,testVec):
firstStr = inputTree.keys()[0]
secondDict = inputTree[firstStr]
featIndex = featLabels.index(firstStr)
key = testVec[featIndex]
valueOfFeat = secondDict[key]
if isinstance(valueOfFeat, dict):
classLabel = classify(valueOfFeat, featLabels, testVec)
else: classLabel = valueOfFeat
return classLabel
3.4.4 存储决策树
def storeTree(inputTree,filename):
import pickle
fw = open(filename,'w')
pickle.dump(inputTree,fw)
fw.close()
def grabTree(filename):
import pickle
fr = open(filename)
return pickle.load(fr)
4. 实验部分
4.1 预测隐形眼镜类型
>>> fr = open('lenses.txt')
>>> lensens = [inst.strip().split('\t') for inst in fr.readlines()]
>>> lensensLabels = ['age', 'prescript', 'astigmatic', 'tearRate']
>>> lensesTree = trees.createTree(lensens,lensensLabels)
>>> lensensTree
结果:
{'tearRate': {'normal': {'astigmatic': {'yes': {'prescript': {'hyper': {'age': {'pre': 'no lenses', 'presbyopic': 'no lenses', 'young': 'hard'}}, 'myope': 'hard'}}, 'no': {'age': {'pre': 'soft', 'presbyopic': {'prescript': {'hyper': 'soft', 'myope': 'no lenses'}}, 'young': 'soft'}}}}, 'reduced': 'no lenses'}}