决策树的构建:
开始,构建根节点,将所有的训练数据都放在根节点,选择一个最优特征,按照这一特征将训练数据分割成子集,使得各个子集有一个在当前条件下最好的分类。
如果特征数量很多,可以在决策树学习开始的时候,对特征进行选择,只留下对训练数据有足够分类能力的特征。
import numpy as np from sklearn import datasets from sklearn import tree from sklearn.cross_validation import train_test_split iris = datasets.load_iris() train_X,test_X, train_y, test_y = train_test_split(iris.data, iris.target, test_size = 0.2, random_state = 0) model = tree.DecisionTreeClassifier() model = model.fit(train_X,train_y) predicted= model.predict(test_X) print "test accuracy %g" %np.mean(predicted == test_y)
决策树学习算法包含特征选择,决策树生成和决策树剪枝三个过程。由于决策树表示一个条件概率分布,所以深浅不同的决策树对应着不同复杂度的概率模型。
特征选择:如果利用一个特征进行分类的结果与随机分类的结果没有很大的差异,则称这个特征是没有分类能力的。通常特征选择的准测是信息增益或者信息增益比。
定义(信息增益):特征A对训练数据集D的信息增益g(D,A),定义为集合D的经验熵H(D)与特征A给定条件下D的经验条件熵H(D|A)之差,即g(D,A) = H(D) - H(D|A)。
信息增益算法:
输入:训练数据D和特征A;
输出:特征A对训练数据D的信息增益g(D,A)
(1)计算数据集D的经验熵H(D),其中|Ck|表示训练样本不同label的个数,|D|表示训练集大小。
(2)计算特征A对数据集D的经验条件熵H(D|A),其中Di是被特征A分成的小数据集,而Dik是小数聚集Di中各个不同label的样本集。
所以,信息增益的代码为:
import math import pandas as pd df = pd.DataFrame(columns = ['age','work','house','state','label']) df.age = [1,1,1,1,1,2,2,2,2,2,3,3,3,3,3] df.work = [0,0,1,1,0,0,0,1,0,0,0,0,1,1,0] df.house = [0,0,0,1,0,0,0,1,1,1,1,1,0,0,0] df.state = [1,2,2,1,1,1,2,2,3,3,3,2,2,3,1] df.label = [0,0,1,1,0,0,0,1,1,1,1,1,1,1,0] #建立数据集########################## ################################### def entoroy(D): sum = 0 for i in list(set(D.label)): p = float(len(D[D.label==i]))/len(D) sum = sum+p*math.log(p,2) return -sum def con_entory(D,A): sum = 0 for i in list(set(D[A])): lit_D = D[D[A] ==i] p = float(len(lit_D))/len(D) sum = sum+p*entoroy(lit_D) return sum def gain(D,A): return entoroy(D) - con_entory(D,A)
由于以信息增益作为划分训练数据集的特征,存在偏向于选择取值较多的特征的问题,所以利用信息增益比来校正这个问题。
定义(信息增益比):特征A对训练数据集D的信息熵增益比gR(D,A)定义为信息增益g(D,A)与训练数据集D关于特征A的值的熵HA(D)之比。
其中,对于HA(D)的理解可以这样理解,其是代表由于特征A的取值,将训练数据集D分成Di,与数据集中的label是没有关系的,注意此与H(D|A)的区别。
其计算代码:
def entoroy(D,A): sum = 0 for i in list(set(D[A])): p = float(len(D[D[A]==i]))/len(D) sum = sum+p*math.log(p,2) return -sum def con_entory(D,A): sum = 0 for i in list(set(D[A])): lit_D = D[D[A] ==i] p = float(len(lit_D))/len(D) sum = sum+p*entoroy(lit_D,'label') return sum def gain(D,A): return entoroy(D,'label') - con_entory(D,A) def grain(D,A): return gain(D,A)/entoroy(D,A)
ID3算法:
利用递归的方式建立决策树,从根节点开始,对节点计算所有可能特征的信息增益,选择信息增益最大的特征作为结点的特征。
C4.5算法:
是ID3算法的改进版本,用信息增益比作为评判标准,选择信息增益比最大的特征作为结点的特征。C4.5算法有如下优点:产生的分类规则易于理解,准确率较高。其缺点是:在构造树的过程中,需要对数据集进行多次的顺序扫描和排序,因而导致算法的低效。此外,C4.5只适合于能够驻留于内存的数据集,当训练集大得无法在内存容纳时程序无法运行。
下面是我自己写的简单模型的源码。
from collections import Counter def maxcount(listCount): m = 0 a = Counter(listCount) for i in set(listCount): if a[i]>m: m = a[i] n = i return n def find_maxgain(dataset , labels): a = 0 for i in labels: if gain(dataset,i)>a: a = gain(dataset,i) b = i return b def find_maxgrain(dataset , labels): a = 0 for i in labels: if grain(dataset,i)>a: a = grain(dataset,i) b = i return b ###################################################### #######################ID3########################### def ID3(dataset, labels): if len(set(dataset['label'])) ==1: return list(set(dataset['label']))[0] if len(labels) ==1: return maxcount(dataset['laber']) bestfea = find_maxgain(dataset , labels) dectree = {bestfea:{}} labels.remove(bestfea) for i in set(dataset[bestfea]): subdataset = dataset[dataset[bestfea] ==i] dectree[bestfea][i] = ID3(subdataset,labels) return dectree ######################################################### #######################C4.5############################### def C4_5(dataset, labels): if len(set(dataset['label'])) ==1: return list(set(dataset['label']))[0] if len(labels) ==1: return maxcount(dataset['laber']) bestfea = find_maxgrain(dataset , labels) dectree = {bestfea:{}} labels.remove(bestfea) for i in set(dataset[bestfea]): subdataset = dataset[dataset[bestfea] ==i] dectree[bestfea][i] = ID3(subdataset,labels) return dectree ########################################################## ###############决策树调用################################# def classify(tree, x): First_columns = tree.keys()[0] Second_cand = tree[First_columns].keys() for i in Second_cand: if x[First_columns] ==i: if type(tree[First_columns][i]).__name__ == 'dict': classlabel = classify(tree[First_columns][i],x) else : classlabel = tree[First_columns][i] return classlabel def classifyall(train, test): label = [] train_tree = ID3(train, Columns) for i in test: a = classify(train_tree,i) label.append(a) return label
在源码中,是用python字典的形式表示树,利用最简单的方式实现了决策树。
对于决策树的剪枝这个问题,在后续的博客中会给出自己写的源码。
决策树的优点
相对于其他数据挖掘算法,决策树在以下几个方面拥有优势:
• 决策树易于理解和实现. 人们在通过解释后都有能力去理解决策树所表达的意义。
• 对于决策树,数据的准备往往是简单或者是不必要的 . 其他的技术往往要求先把数据一般化,比如去掉多余的或者空白的属性。
• 能够同时处理数据型和常规型属性。其他的技术往往要求数据属性的单一。
• 在相对短的时间内能够对大型数据源做出可行且效果良好的结果。
• 对缺失值不敏感
• 可以处理不相关特征数据
• 效率高,决策树只需要一次构建,反复使用,每一次预测的最大计算次数不超过决策树的深度。
决策树的缺点
1)对连续性的字段比较难预测。
2)对有时间顺序的数据,需要很多预处理的工作。
3)当类别太多时,错误可能就会增加的比较快。
4)一般的算法分类的时候,只是根据一个字段来分类。
5)在处理特征关联性比较强的数据时表现得不是太好