决策树算法是机器学习中的经典算法
1.决策树(decision tree)
决策树是一种树形结构,其中每个内部节点表示一个属性上的测试,每个分支代表一个测试输出,每个叶节点代表一种类别。
假设小明去看电影,影响看电影的外部因素有 时间 电影类型 评分 三个情况,目前已知的样本数据如下
根据以上样本数据,整理成tree形结构如下
2.决策树算法中熵的概念
1948年香农提出了“信息熵(entropy)”的概念
一条信息的信息量大小和它的不确定性有直接,信息量的度量就等于不确定性的多少,我们用bit(比特)来衡量信息量的多少
信息熵的计算公式如下,以log2为底,取对数,然后把每一种情况相加,当每种情况下概率相等时,取最大值,为n/2 -1 ,即变量的不确定性越大,则信息熵也越大
3.决策树归纳算法
策树归纳算法是J.Ross.Quinian在19世纪70年代提出的的ID3算法.
上面小明看电影实例中,总的信息量(单位为bit)为
同理,我们可以计算不同电影类型的信息熵,结果为0.65bits
此处解释下为什么根节点从电影类型开始划分,判断应该用哪个类型划分节点,可以依据如下公式判定
Gain(A) = info(D) - Info_A(D)
即总的信息量减除以特定节点的信息获取量,如果此值越大,说明获取的信息量越多,据此可以作为根节点
以type为节点的信息获取量:
Gain(type) = 0.991 - 0.65 = 0.341 (bits)
依次类推,也可以计算出以time 和 grade获取的信息量,在此不一一计算了。因为此处以type为节点获取的信息量最大,所以根节点以type区分
其它算法
c4.5: (Quinlan)
cart: Classification and Regression Trees (L.Breiman, J.Friedman, R.Olshen, C.Stone)
以上两个算法c4.5和cart以及前面介绍的entropy都是贪心算法,主要区别在于属性的度量方法不同.
tips
决策树算法,直观,便于理解,试用于小规模的数据,对连续型变量处理不好,如果要处理,需要做到离散化。如果类型分得太细,可能会造成train较好,但是predict不好,为避免此种情况的overfitting,一般采取减枝
代码实现
本文以python为例,讲解代码的实现,本文会用到机器学习中常用的python库sklearn
下面直接看代码
其中用到了sklearn库中的DictVectorizer(转换成sklearn所能接受的类型用), csv(处理csv格式文件用), preprocessing(预处理数据,只能是数值类型),tree(决策树), StringIO(sklearn中的IO处理)
from sklearn.feature_extraction import DictVectorizer
import csv
from sklearn import preprocessing
from sklearn import tree
from sklearn.externals.six import StringIO
接下来,首先读取本读的csv数据,数据样本如第一张图片
allFilmsData = open(r'/Users/max/Desktop/seeFilm.csv', 'rb')
reader = csv.reader(allFilmsData)
headers = reader.next()
接着,我们对数据进行处理
# 特征数组
featureList = []
# 标签数组
labelList = []
for row in reader:
labelList.append(row[len(row) - 1])
rowDict = {}
for i in range(1, len(row) - 1):
rowDict[headers[i]] = row[i]
featureList.append(rowDict)
# vetoarize feature
vec = DictVectorizer()
#DictVectorizer实例化
dummyX = vec.fit_transform(featureList).toarray()
#转化成dummy viable格式的
通过以上转化,得到的数据结构如下
dumyX:[
[ 1. 0. 0. 0. 1. 0. 0. 1. 0.]
[ 0. 0. 1. 1. 0. 0. 0. 0. 1.]
[ 0. 0. 1. 1. 0. 0. 1. 0. 0.]
[ 1. 0. 0. 1. 0. 1. 0. 0. 0.]
[ 1. 0. 0. 0. 1. 0. 0. 0. 1.]
[ 0. 1. 0. 1. 0. 0. 1. 0. 0.]
[ 0. 1. 0. 0. 1. 0. 0. 0. 1.]
[ 1. 0. 0. 0. 1. 0. 1. 0. 0.]
[ 0. 0. 1. 1. 0. 0. 0. 1. 0.]
]
同时,我们可以查看feature_names和labelList
feature_names格式如下:
['grade=high', 'grade=low', 'grade=middle', 'time=weekend', 'time=workday_night', 'type=art', 'type=crime', 'type=love', 'type=science_fiction']
labelList格式如下:
labelList:['see', 'no', 'see', 'see', 'no', 'no', 'no', 'see', 'see']
把labelList转化,代码如下
# vectorize class labels
lb = preprocessing.LabelBinarizer()
dummyY = lb.fit_transform(labelList)
print("dummy:" + str(dummyY))
接下来,我们可以查看树结构
clf = tree.DecisionTreeClassifier(criterion='entropy')
'''
上述采用的信息熵的差作为度量标准,即ID3
如果此处不传,默认采用的是gini,即是cart算法
'''
clf = clf.fit(dummyX, dummyY)
print("clf:" + str(clf))
with open("/Users/max/Desktop/allFilmInfoGainOri.dot", 'w') as f:
f = tree.export_graphviz(clf, feature_names=vec.get_feature_names(), out_file=f)
我们把上面结果存储为allFilmInfoGainOri.dot的文件,可以看到文档信息如下,打开本地文件,可以看到文件结构如下
当然为了更加直观的查看以上部分数据结构,我们可以用Graphviz工具转换成树形结构的形式便于阅读,转换后的属性结构如下
最后,我们用代码预测
例如:我们修改第一行的数据,预测代码如下
oneRowX = dummyX[0, :]
newRowX = oneRowX
newRowX[0] = 0
newRowX[7] = 1
['grade=high', 'grade=low', 'grade=middle', 'time=weekend', 'time=workday_night', 'type=art', 'type=crime', 'type=love', 'type=science_fiction']
labelList:['see', 'no', 'see', 'see', 'no', 'no', 'no', 'see', 'see']
newRowX[0] = 0, 表示评分高为0
newRowX[7] = 1, 表示是love类型电影
predictedY = clf.predict([newRowX])
以上代码执行后,我们会得到predictedY为[1], 即说明此中情况下,小明会去看电影