集体智慧编程——决策树建模(上)
本文主要介绍一种非常流行的分类算法:决策树分类算法。
例子:一个网站上有多少用户会愿意为某些高级会员功能而支付费用。
步骤一:导入数据;
新建一个treepredict.py的问价,将下面数据写入。数据的具体信息分别是:来源网站,位置,是否读过FAQ,浏览网页数,选择服务类型。
my_data=[['slashdot','USA','yes',18,'None'], ['google','France','yes',23,'Premium'], ['digg','USA','yes',24,'Basic'], ['kiwitobes','France','yes',23,'Basic'], ['google','UK','no',21,'Premium'], ['(direct)','New Zealand','no',12,'None'], ['(direct)','UK','no',21,'Basic'], ['google','USA','no',24,'Premium'], ['slashdot','France','yes',19,'None'], ['digg','USA','no',18,'None'], ['google','UK','no',18,'None'], ['kiwitobes','UK','no',19,'None'], ['digg','New Zealand','yes',12,'Basic'], ['slashdot','UK','no',21,'None'], ['google','UK','yes',18,'Basic'], ['kiwitobes','France','yes',19,'Basic']]
二:引入决策树:
决策树是一种比较简单的机器学习方法。它是对被观测对象数据进行分类的一种相当直观的方法,当决策树经过训练后,看上去就像以树叶形状排序的if-then语句。
一旦我们有了决策树,根据此进行决策的过程就会变得很简单了,只需要沿着树的路径一直向下,直到叶子节点就可以了。
请新建一个类,取名为decisionnode,他代表树上的每一个节点:
class decisionnode: def __init__(self,col=-1,value=None,results=None,tb=None,fb=None): self.col=col self.value=value self.results=results self.tb=tb self.fb=fb
每一个节点都有5个实例变量,这5个实例变量在初始化的时候被设置。
1.col是待检验的判断条件所对应的列索引值
2.value对应于为了使结果为true,当前列必须匹配的值
3。tb,fb也是decisionnode,他们分别对应于结果为true或者false时,树上相对于当前节点的子树上的节点
4.results保存的是针对当前分支的结果,它是一个字典。除了叶子节点以外,在其他节点上该值都为None
三:对数进行训练
我们使用一种叫CART(分类回归树)的算法。为了构造决策树,算法首先创建一个根节点。然后通过评价表中的所有的观测变量,从中选择一个最合适的变量对数据进行拆分。
函数divideset的作用是根据列表中的某一栏的数据将列表进行拆分成两个数据集。该函数接受一个列表,一个指示表中列所在位置的数字,和一个用以对列进行拆分的参考值作为参数。算法会返回两个列表:第一个列表是满足条件的数据,第二个是不满足的。
def divideset(rows,column,value): split_function=None if isinstance(value,int) or isinstance(value,float): split_function=lambda row:row[column]>=value else: split_function=lambda row:row[column]==value set1=[row for row in rows if split_function(row)] set2=[row for row in rows if not split_function(row)] return (set1,set2)
注意上述代码中lambda表达式的作用,不明白的可以查看:http://www.cnblogs.com/itdyb/p/5014052.html
>>> import treepredict >>> treepredict.divideset(treepredict.my_data,2,'yes') ([['slashdot', 'USA', 'yes', 18, 'None'], ['google', 'France', 'yes', 23, 'Premium'], ['digg', 'USA', 'yes', 24, 'Basic'], ['kiwitobes', 'France', 'yes', 23, 'Basic'], ['slashdot', 'France', 'yes', 19, 'None'], ['digg', 'New Zealand', 'yes', 12, 'Basic'], ['google', 'UK', 'yes', 18, 'Basic'], ['kiwitobes', 'France', 'yes', 19, 'Basic']], [['google', 'UK', 'no', 21, 'Premium'], ['(direct)', 'New Zealand', 'no', 12, 'None'], ['(direct)', 'UK', 'no', 21, 'Basic'], ['google', 'USA', 'no', 24, 'Premium'], ['digg', 'USA', 'no', 18, 'None'], ['google', 'UK', 'no', 18, 'None'], ['kiwitobes', 'UK', 'no', 19, 'None'], ['slashdot', 'UK', 'no', 21, 'None']])
四:选择最合适的拆分方案
首先,我们需要对数据集合中的每一项结果进行计数,代码如下:
def uniquecounts(rows): results={} for row in rows: r=row[len(row)-1] #last row result #print r if r not in results:results[r]=0 results[r]+=1 return results
上面函数的作用是找出不同的可能结果,并返回一个字典,其中包含了每一项结果的出现次数。
接下来我们考察基尼不纯度和熵。
对于基尼不纯度和熵值不了解的请参考《数据挖掘——概念与技术》中,或者百度(嘿嘿,公式比较麻烦,小弟就不写了)
1.基尼不纯度:是指将来自集合中的某些结果随机应用于集合中某一数据项的预期误差率。计算函数如下:
def giniimpurity(rows): total=len(rows) counts=uniquecounts(rows) #print tatal,counts imp=0 for k1 in counts: p1=float(counts[k1])/total #print p1 for k2 in counts: if k1==k2:continue p2=float(counts[k2])/total #print p2 imp+=p1*p2 #print imp return imp
该函数利用集合中每一个项结果出现的次数除以集合的总行数来计算相应的概率,然后将这些概率的值的乘积累加起来。这样就会得到某一行数据被随机分配到错误结果的总概率。该值越小越好
2.熵:代表集合的无序程度——基本上就相当于集合的混杂程度。函数如下:
def entropy(rows): from math import log log2=lambda x:log(x)/log(2) results=uniquecounts(rows) ent=0.0 for r in results.keys(): p=float(results[r])/len(rows) ent=ent-p*log2(p) return ent
计算熵的公式为H(x)=E[I(xi)]=E[log2 1/p(xi)]=-ξp(xi)log2 p(xi)(i=1,2,..n)
>>> treepredict.giniimpurity(treepredict.my_data) 0.6328125 >>> treepredict.entropy(treepredict.my_data) 1.5052408149441479
五:递归的方式构造树
信息增益:是指当前熵与两个新群组经过加权平均后的熵之间的差值。
def buildtree(rows,scoref=entropy): if len(rows)==0: return decisionnode() current_score=scoref(rows) # Set up some variables to track the best criteria best_gain=0.0 best_criteria=None best_sets=None column_count=len(rows[0])-1 for col in range(0,column_count): # Generate the list of different values in # this column column_values={} for row in rows: column_values[row[col]]=1 # Now try dividing the rows up for each value # in this column for value in column_values.keys(): (set1,set2)=divideset(rows,col,value) # Information gain p=float(len(set1))/len(rows) gain=current_score-p*scoref(set1)-(1-p)*scoref(set2) if gain>best_gain and len(set1)>0 and len(set2)>0: best_gain=gain best_criteria=(col,value) best_sets=(set1,set2) # Create the sub branches if best_gain>0: trueBranch=buildtree(best_sets[0]) falseBranch=buildtree(best_sets[1]) return decisionnode(col=best_criteria[0],value=best_criteria[1], tb=trueBranch,fb=falseBranch) else: return decisionnode(results=uniquecounts(rows))
关于决策树的显示,预测,修剪在(下)中。
参考自《集体智慧编程》