本文介绍机器学习中决策树算法的python实现过程
共介绍两类方法:
(1)亲手实习Python ID3决策树经典算法
(2)利用sklearn库实现决策树算法
关于决策树的原理,指路:机器学习 第四章决策树
ID3决策树算法采用“最大化信息增益准则”。在树的构建过程中,采用了递归的思想。下面我们来一步步解析ID3算法的实现过程。
首先是导入数据集:这里我们使用pandas库导入csv文件。csv文件内容结构应如下图所示,共有三个因变量(‘no sufacing’, ‘flippers’, ‘oxygen resistant’),一个因变量(‘label’)
对应函数createDataSet,输入dataframe(pandas导入csv后的默认格式),输出自变量的列表,变量名。
# 创建数据集
def createDataSet(dataframe):
# 提取属性名
colsname = list(dataframe.columns)
# 构造数据集
dataSet = np.array(dataframe)
dataSet = dataSet.tolist()
return dataSet, colsname
然后编写计算数据集信息熵 E n t Ent Ent的函数,代码如下:
# 计算信息熵
def InfoEnt(dataSet):
DataNum = len(dataSet)
# 为分类创建字典,统计各类别数据的数量
LabelCounts = {}
for data in dataSet:
data_label = data[-1]
LabelCounts[data_label] = LabelCounts.get(data_label, 0) + 1
# 计算信息墒
info_ent = 0.0
for label in LabelCounts:
p_label = float(LabelCounts[label]) / DataNum
info_ent += p_label * math.log(1 / p_label, 2)
return info_ent
根据最大信息增益的准则,选取最优属性 a a a,函数代码如下:
# 返回第number个属性值为value的数据集
def SplitDataSet(dataSet, number, value):
NewDataSet = []
for data in dataSet:
if data[number] == value:
newdata = data[:number]
newdata.extend(data[number + 1:])
NewDataSet.append(newdata)
return NewDataSet
# 按照max信息增益的准则,选取最优属性
def ChooseBestFeature(dataSet, colsname):
# 统计属性个数
FeatureNum = len(colsname)
# 初始情况的信息熵,即未经划分时,所有数据的信息熵
BaseEnt = InfoEnt(dataSet)
# 记录最大信息增益以及其对应的属性
BestInfoGain = 0
BestFeature = -1
# 遍历各个属性,计算信息增益, 找到最大值
for i in range(FeatureNum):
# 得到该属性的所有值
Values = [data[i] for data in dataSet]
# 获得该属性的所有可能取值
UniqualValues = set(Values)
NewEnt = 0
for value in UniqualValues:
NewDataSet = SplitDataSet(dataSet, i, value)
p_value = len(NewDataSet) / float(len(dataSet))
NewEnt += p_value * InfoEnt(NewDataSet)
# 计算信息增益
InfoGain = BaseEnt - NewEnt
# 更新最大信息增益
if InfoGain > BestInfoGain:
BestInfoGain = InfoGain
BestFeature = colsname[i]
# 返回最大信息增益,最优属性
return BestInfoGain, BestFeature
然后我们创建决策树,从根节点开始选取最优属性,开辟新结点,直到当前结点的样本都属于同个类别或样本在所有属性上取值相同无法划分,根据样本中label最多的一类作为当前结点的结果,并设置为叶结点。
这里有个很重要的地方要注意,就是CreateTree中,我们会逐步剔除已经选取过的属性,为了不改变函数体外部的colsname(变量名列表),这里我们采用浅拷贝copy(),避免改变原colsname。
# 选择label中最多的一类作为叶结点决策结果
def MaxNumLabel(LabelList):
LabelCount = {}
for label in LabelList:
LabelCount[label] = LabelCount.get(label, 0) + 1
# 根据(key, value)的value排序
SortedLabelCount = sorted(LabelCount.items(), key=operator.itemgetter(1), reverse=True)
# 返回出现次数最多的key
return SortedLabelCount[0][0]
def CreateTree(dataSet, colsname):
LabelList = [data[-1] for data in dataSet]
colsname = colsname.copy()
# 当LabelList中均为同一个label
if LabelList.count(LabelList[0]) == len(LabelList):
return LabelList[0]
# 遍历完所有特征值时,返回数量最多的
if (len(dataSet[0]) == 1):
return MaxNumLabel(LabelList)
# 获取最佳划分属性和最大信息增益
BestInfoGain, BestFeature = ChooseBestFeature(dataSet, colsname)
# 定位最佳划分属性的索引BestFeature_id
BestFeature_id = 0
for i in range(len(colsname)):
if colsname[i] == BestFeature:
BestFeature_id = i
break
# 构造决策树
myTree = {BestFeature: {}}
# 将最佳划分属性BestFeature从属性集中剔除
del (colsname[BestFeature_id])
# 最优划分属性的值
Values = [data[BestFeature_id] for data in dataSet]
# 最优划分属性的所有可能取值
UniqueValues = set(Values)
for value in UniqueValues:
NewLabels = colsname[:]
# 递归调用创建决策树
myTree[BestFeature][value] = CreateTree(SplitDataSet(dataSet, BestFeature_id, value), NewLabels)
return myTree
最后,这棵树还要能够预测新样本的label,代码如下:
def Predict_DecisionTree(data_pre, colsname, Tree):
colsname = colsname[0:-1].copy()
for i in range(len(data_pre)):
if colsname[i] in Tree.keys():
result = Tree[colsname[i]][data_pre[i]]
if isinstance(result, int):
return result
else:
next_result = Predict_DecisionTree(data_pre, colsname, result)
if isinstance(next_result, int):
return next_result
最终,编写主函数,导入数据,完成决策树的构建和预测。
if __name__ == "__main__":
# 导入数据
data = pd.read_csv('data/train.csv')
dataSet, colsname = CreateDataSet(data)
tree = CreateTree(dataSet, colsname)
print('决策树:', tree)
a = [1, 1, 1]
print('预测' + str(a) + '的分类结果:', Predict_DecisionTree(a, colsname, tree))
最终结果如下:
sklearn库中集成了决策树Tree类,操作起来比较方便,数据集采用iris数据集,代码如下:
from sklearn.datasets import load_iris
from sklearn import tree
# 导入数据集
iris = load_iris()
# 训练决策树
clf = tree.DecisionTreeClassifier()
clf = clf.fit(iris.data, iris.target)
# 预测分类结果
# 预测属于哪个类
print(clf.predict([[6.4, 0.7, 3.4, 5.1]]))
# 预测属于每个类的概率
print(clf.predict_proba([[6.4, 0.7, 3.4, 5.1]]))