决策树挑出好西瓜
一、什么是决策树
决策树是一种机器学习的方法。决策树的生成算法有ID3, C4.5和C5.0等。决策树是一种树形结构,其中每个内部节点表示一个属性上的判断,每个分支代表一个判断结果的输出,最后每个叶节点代表一种分类结果。
对于复杂的预测问题,通过建立树模型产生分支节点,被划分成两个(二叉树)或多个(多叉树)较为简单的子集,从结构上划分为不同的子问题。将依规则分割数据集的过程不断递归下去。
随着树的深度不断增加,分支节点的子集越来越小,所需要提的问题数也逐渐简化。当分支节点的深度或者问题的简单程度满足一定的停止规则时, 该分支节点会停止分裂,此为自上而下的停止阈值法;有些决策树也使用自下而上的剪枝法。
二、ID3算法
1、ID3
ID3算法是决策树的一种,它是基于奥卡姆剃刀原理的,即用尽量用较少的东西做更多的事。ID3算法,即Iterative Dichotomiser 3,迭代二叉树3代,是Ross Quinlan发明的一种决策树算法,这个算法的基础就是上面提到的奥卡姆剃刀原理,越是小型的决策树越优于大的决策树,尽管如此,也不总是生成最小的树型结构,而是一个启发式算法。
2、信息熵
熵这个概念最早起源于物理学,在物理学中是用来度量一个热力学系统的无序程度,而在信息学里面,熵是对不确定性的度量。在1948年,香农引入了信息熵,将其定义为离散随机事件出现的概率,一个系统越是有序,信息熵就越低,反之一个系统越是混乱,它的信息熵就越高。所以信息熵可以被认为是系统有序化程度的一个度量。
假如一个随机变量X的取值为:
每一种取到的概率分别是:
那么X的熵定义为:
对于分类系统来说,类别C是变量,它的取值为:
而每一个类别出现的概率分别是:
而这里的n就是类别的总数,此时分类系统的熵就可以表示为:
3、信息增益、
信息增益是针对一个一个特征而言的,就是看一个特征T,系统有它和没有它时的信息量各是多少,两者的差值就是这个特征给系统带来的信息量,即信息增益。
三、挑选西瓜
在excel中构建如下的数据集
Python代码如下:
import pandas as pd
import numpy as np
from collections import Counter
from math import log2
#数据获取与处理
def getData(filePath):
data = pd.read_excel(filePath)
return data
def dataDeal(data):
dataList = np.array(data).tolist()
dataSet = [element[1:] for element in dataList]
return dataSet
#获取属性名称
def getLabels(data):
labels = list(data.columns)[1:-1]
return labels
#获取类别标记
def targetClass(dataSet):
classification = set([element[-1] for element in dataSet])
return classification
#将分支结点标记为叶结点,选择样本数最多的类作为类标记
def majorityRule(dataSet):
mostKind = Counter([element[-1] for element in dataSet]).most_common(1)
majorityKind = mostKind[0][0]
return majorityKind
#计算信息熵
def infoEntropy(dataSet):
classColumnCnt = Counter([element[-1] for element in dataSet])
Ent = 0
for symbol in classColumnCnt:
p_k = classColumnCnt[symbol]/len(dataSet)
Ent = Ent-p_k*log2(p_k)
return Ent
#子数据集构建
def makeAttributeData(dataSet,value,iColumn):
attributeData = []
for element in dataSet:
if element[iColumn]==value:
row = element[:iColumn]
row.extend(element[iColumn+1:])
attributeData.append(row)
return attributeData
#计算信息增益
def infoGain(dataSet,iColumn):
Ent = infoEntropy(dataSet)
tempGain = 0.0
attribute = set([element[iColumn] for element in dataSet])
for value in attribute:
attributeData = makeAttributeData(dataSet,value,iColumn)
tempGain = tempGain+len(attributeData)/len(dataSet)*infoEntropy(attributeData)
Gain = Ent-tempGain
return Gain
#选择最优属性
def selectOptimalAttribute(dataSet,labels):
bestGain = 0
sequence = 0
for iColumn in range(0,len(labels)):#不计最后的类别列
Gain = infoGain(dataSet,iColumn)
if Gain>bestGain:
bestGain = Gain
sequence = iColumn
print(labels[iColumn],Gain)
return sequence
#建立决策树
def createTree(dataSet,labels):
classification = targetClass(dataSet) #获取类别种类(集合去重)
if len(classification) == 1:
return list(classification)[0]
if len(labels) == 1:
return majorityRule(dataSet)#返回样本种类较多的类别
sequence = selectOptimalAttribute(dataSet,labels)
print(labels)
optimalAttribute = labels[sequence]
del(labels[sequence])
myTree = {optimalAttribute:{}}
attribute = set([element[sequence] for element in dataSet])
for value in attribute:
print(myTree)
print(value)
subLabels = labels[:]
myTree[optimalAttribute][value] = \
createTree(makeAttributeData(dataSet,value,sequence),subLabels)
return myTree
def main():
filePath = 'D:\watermelondata.xls'
data = getData(filePath)
dataSet = dataDeal(data)
labels = getLabels(data)
myTree = createTree(dataSet,labels)
return myTree
if __name__ == '__main__':
myTree = main()
结果如下:
色泽 0.10812516526536531
根蒂 0.14267495956679277
敲声 0.14078143361499584
纹理 0.3805918973682686
脐部 0.28915878284167895
触感 0.006046489176565584
['色泽', '根蒂', '敲声', '纹理', '脐部', '触感']
{'纹理': {}}
稍糊
色泽 0.3219280948873623
根蒂 0.07290559532005603
敲声 0.3219280948873623
脐部 0.17095059445466865
触感 0.7219280948873623
['色泽', '根蒂', '敲声', '脐部', '触感']
{'触感': {}}
硬滑
{'触感': {'硬滑': '否'}}
软粘
{'纹理': {'稍糊': {'触感': {'硬滑': '否', '软粘': '是'}}}}
清晰
色泽 0.04306839587828004
根蒂 0.45810589515712374
敲声 0.33085622540971754
脐部 0.45810589515712374
触感 0.45810589515712374
['色泽', '根蒂', '敲声', '脐部', '触感']
{'根蒂': {}}
硬挺
{'根蒂': {'硬挺': '否'}}
蜷缩
{'根蒂': {'硬挺': '否', '蜷缩': '是'}}
稍蜷
色泽 0.2516291673878229
敲声 0.0
脐部 0.0
触感 0.2516291673878229
['色泽', '敲声', '脐部', '触感']
{'色泽': {}}
青绿
{'色泽': {'青绿': '是'}}
乌黑
敲声 0.0
脐部 0.0
触感 1.0
['敲声', '脐部', '触感']
{'触感': {}}
硬滑
{'触感': {'硬滑': '是'}}
软粘
{'纹理': {'稍糊': {'触感': {'硬滑': '否', '软粘': '是'}}, '清晰': {'根蒂': {'硬挺': '否', '蜷缩': '是', '稍蜷': {'色泽': {'青绿': '是', '乌黑': {'触感': {'硬滑': '是', '软粘': '否'}}}}}}}}
模糊
四、用sk-learn库对西瓜数据集,分别进行ID3、C4.5和CART的算法代码实现
1.ID3
创建如图的数据集
import pandas as pd
import graphviz
from sklearn.model_selection import train_test_split
from sklearn import tree
f = open('D:\watermelondata.csv',encoding='utf-8')
data = pd.read_csv(f)
x = data[["色泽","根蒂","敲声","纹理","脐部","触感"]].copy()
y = data['好瓜'].copy()
print(data)
#将特征值数值化
x = x.copy()
for i in ["色泽","根蒂","敲声","纹理","脐部","触感"]:
for j in range(len(x)):
if(x[i][j] == "青绿" or x[i][j] == "蜷缩" or data[i][j] == "浊响" \
or x[i][j] == "清晰" or x[i][j] == "凹陷" or x[i][j] == "硬滑"):
x[i][j] = 1
elif(x[i][j] == "乌黑" or x[i][j] == "稍蜷" or data[i][j] == "沉闷" \
or x[i][j] == "稍糊" or x[i][j] == "稍凹" or x[i][j] == "软粘"):
x[i][j] = 2
else:
x[i][j] = 3
y = y.copy()
for i in range(len(y)):
if(y[i] == "是"):
y[i] = int(1)
else:
y[i] = int(-1)
#需要将数据x,y转化好格式,数据框dataframe,否则格式报错
x = pd.DataFrame(x).astype(int)
y = pd.DataFrame(y).astype(int)
print(x)
print(y)
x_train, x_test, y_train, y_test = train_test_split(x,y,test_size=0.2)
print(x_train)
#决策树学习
clf = tree.DecisionTreeClassifier(criterion="entropy") #实例化
clf = clf.fit(x_train, y_train)
score = clf.score(x_test, y_test)
print(score)
选择“根蒂",“敲声”,“纹理”,“脐部”,"触感”进行训练
2.C4.5
(1)ID3的缺陷
对于ID3算法,它存在以下问题:
- 取值多的属性,更容易使数据更纯,其信息增益更大;
- 训练得到的是一棵庞大且深度浅的树,这样的树是不合理的。
C4.5算法可以抑制ID3的上述缺点。
(2)C4.5算法
C4.5算法是由Ross Quinlan开发的用于产生决策树的算法。该算法是对Ross
Quinlan之前开发的ID3算法的一个扩展。C4.5算法产生的决策树可以被用作分类目的,因此该算法也可以用于统计分类。其通过信息增益比来进行特征选择。
(3)信息增益率
假如某个条件极其严格,比如某个同学提前知道了所有选题的答案,那么将选题的序号作为条件,不存在任何不确定性,所以可以得到最大的信息增益。但是这个条件是没有意义的,假设老师换一份考卷答案就全部作废了。
信息增益率在信息增益的基础上增加了惩罚项,惩罚项是特征的固有值,是避免上述情况而设计的。
写作 gr(X,Y)。定义为信息增益除以特征的固有值,如下
继续以单项选择题为例,通过分析选题的长短特征之后,信息增益g(X,Y)为2bit,惩罚项H(Y)=-0.1log0.1-0.1log0.1-0.8*log0.8=0.92
信息增益率为0.4/0.92=43%,其中,信息增益率为43%。
3.CART算符
(1)CART
CART是一棵二叉树,每一次分裂会产生两个子节点。CART树分为分类树和回归树。
分类树主要针对目标标量为分类变量,比如预测一个动物是否是哺乳动物。
回归树针对目标变量为连续值的情况,比如预测一个动物的年龄。
如果是分类树,将选择能够最小化分裂后节点GINI值的分裂属性;
如果是回归树,选择能够最小化两个节点样本方差的分裂属性。CART跟其他决策树算法一样,需要进行剪枝,才能防止算法过拟合从而保证算法的泛化性能。
(2)基尼指数
CART决策树算法使用基尼指数来选择划分属性,基尼指数定义为:
Gini(D) = ∑k=1 ∑k'≠1 pk·pk' = 1- ∑k=1 pk·pk
可以这样理解基尼指数:从数据集D中随机抽取两个样本,其类别标记不一致的概率。Gini(D)越小,纯度越高。
属性a的基尼指数定义:
Gain_index(D,a) = ∑v=1 |Dv|/|D|·Gini(Dv)
使用基尼指数选择最优划分属性,即选择使得划分后基尼指数最小的属性作为最优划分属性。
总结
本次实验的主要目的是了解以及熟悉分别在不使用sk-learn和使用sk-learn的情况下用ID3算法进行一个简单的机器学习。同时对C4.5算法和CART算法进行一个了解。
参考文章
https://blog.csdn.net/weixin_56102526/article/details/120987844?spm=1001.2014.3001.5501
https://blog.csdn.net/qq_45659777/article/details/120960616?spm=1001.2014.3001.5501