决策树
分类决策树模型是一 种描述对实例进行分类的树形结构。决策树由结点和有向边组成。结点有两种类型:内部结点和叶结点。内部结点表示一个特征或属性,叶结点表示一个类。
用决策树分类,从根结点开始,对实例的某一特征进行测试,根据测试结果,将实例分配到其子结点;这时,每一个结点对应着该特征的一个取值。如此递归地对实例进行测试并分配,直至达到叶结点。最后将实例分到叶结点的类中。 决策树学习常用的算法有ID3,C4.5,CART。
特征选择
特征选择在于选取对训练数据具有分类能力的特征。这样可以提高决策树学习的效率。
信息增益
在信息论与概率统计中,熵(entropy)表示随机变量不确定性的度量。设X是一个取有限个值的离散随机变量,其概率分布为:
$ P(X=x{i}) = p{i}, i=1,2, \cdot \cdot \cdot, n $ 则随机变量X的熵定义为
$ H(X)=-\sum{i=1}^{n}p{i} \log p_{i} $ ,通常式中的对数以2为底或以e为底。
熵越大,随机变量的不确定性就越大。 (信息增益:定义) 特征A对训练数据集D的信息增益g(D,A),定义为集合D的经验熵H(D)与特征A给定条件下D的经验条件熵H(D|A)之差,即
$ g(D,A)= H(D)-H(D|A) $
熵H(Y)与条件熵H(Y|X)之差称为互信息。
from scipy.stats import entropy
from collections import Counter
# 计算熵
def calcEnt(dataSet):
num = len(dataSet)
labelCount = {}
for featVec in dataSet:
label = featVec[-1]
if label not in labelCount:
labelCount[label] = 0
labelCount[label] += 1
Ent = 0.0
for k in labelCount:
prob = float(labelCount[k])/num
Ent -= prob * log(prob, 2)
return Ent
def calcEnt2(dataSet):
dataSet = np.array(dataSet)
num = len(dataSet)
labelCount = Counter(dataSet[:,-1]) #使用Counter简化代码
prod = [float(labelCount[k])/num for k in labelCount]
Ent = entropy(prod, base=2) # scipy中提供计算熵的函数
return Ent
# 对数据集进行切割
def splitData(dataSet, axis, value):
retDataSet = []
for featVec in dataSet:
if featVec[axis] == value:
reducedFeatVec = featVec[:axis]
reducedFeatVec.extend(featVec[axis+1:])
retDataSet.append(reducedFeatVec)
return retDataSet
# 选择信息增益最大的特征
def chooseBestFeature(dataSet):
numFeatures = len(dataSet[0]) - 1 # 特征的数量
hd = calcEnt2(dataSet)
bestInfoGain, bestFeature = 0.0, -1
for i in range(numFeatures):
featList = [elt[i] for elt in dataSet] #获取特征列表
uniqueVals = set(featList) #对特征值进行去重
newEntropy = 0.0
for v in uniqueVals:
# 切割数据计算熵
subDataSet = splitData(dataSet, i, v)
prob = len(subDataSet)/float(len(dataSet))
newEntropy += prob * calcEnt2(subDataSet)
infoGain = hd - newEntropy # 计算信息增益
print(infoGain)
if infoGain > bestInfoGain:
bestInfoGain = infoGain
bestFeature = i
return bestFeature
# 返回出现次数最多的那项
def majorityCnt(classList):
classCount = {}
for vote in classList:
if vote not in classCount:
classCount[vote] = 0
classCount[vote] += 1
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
# 使用字典构造决策树
def createTree(dataSet, labels):
classList = [elt[-1] for elt in dataSet] #获取分类
# 如果只有一分类,则只有一个根节点
if classList.count(classList[0]) == len(classList):
return classList[0]
# 当数据为一维时,返回出现次数最多的项
if len(dataSet[0]) == 1:
print('dataSet', dataSet)
return majorityCnt(classList)
bestFeat = chooseBestFeature(dataSet)
bestFeatLabel = labels[bestFeat]
myTree = {bestFeatLabel: {}}
del(labels[bestFeat])
featValues = [elt[bestFeat] for elt in dataSet]
uniqueVals = set(featValues)
print('labels=', labels)
for v in uniqueVals:
subLabels = labels[:]
print('subLabels',subLabels)
# 使用递归法构建决策树
myTree[bestFeatLabel][v] = createTree(splitData(dataSet, bestFeat, v), subLabels)
return myTree
# 使用构建的决策树进行分类
def classify(inputTree, featLabels, testVec):
firstStr = list(inputTree.keys())[0]
secondDict = inputTree[firstStr]
featIndex = featLabels.index(firstStr)
print('featIndex', featIndex)
print('secondDict', secondDict)
for k in secondDict:
if testVec[featIndex] == k:
if isinstance(secondDict[k], dict):
classLabel = classify(secondDict[k], featLabels, testVec)
else:
classLabel = secondDict[k]
return classLabel
Ref:
1.机器学习实战
2.统计学习方法—李航