机器学习——决策树

1 基本流程

决策树(decision tree)是一类常见的机器学习方法.

一般的,一棵决策树包含一个根结点、若干个内部结点和若干个叶结点;叶结点对应于决策树结果,其他每个结点则对应于一个属性测试;每个结点包含的样本集合根据属性测试的结果被划分到子结点中;根结点包含样本全集。从根结点到每个叶结点的路径对应了一个判定测试序列。

决策树的学习目的是为了产生一棵泛化能力强,即处理未见示例能力强的决策树,其基本流程遵循简单且直观的“分而治之”(divide-and-conquer)策略。

决策树的生成是一个递归过程。.在决策树基本算法中,有三种情形会
导致递归返回:
(1) 当前结点包含的样本全属于同一类别,无需划分;
(2) 当前属性集为空,或是所有样本在所有属性上取值相同,无法划分;
把当前结点标记为叶结点,井将其类别设定为该结
点所含样本最多的类别;
(3) 当前结点包含的样本集合为空,不能划分.
把当前结点标记为叶结点,但将其类别设定为其父结点所含样本最多的类别.

在这里插入图片描述

构建决策树

在这里插入图片描述

决策树算法流程

在这里插入图片描述

决策树参数

在这里插入图片描述
min_samples_leaf : 决定了能否继续分隔的最少分隔样本
当设置的很小时,可能会导致过拟合。
在这里插入图片描述

决策树的准确率

import sys
from class_vis import prettyPicture
from prep_terrain_data import makeTerrainData

import matplotlib.pyplot as plt
import numpy as np
import pylab as pl

features_train, labels_train, features_test, labels_test = makeTerrainData()



########################## DECISION TREE #################################


### your code goes here--now create 2 decision tree classifiers,
### one with min_samples_split=2 and one with min_samples_split=50
### compute the accuracies on the testing data and store
### the accuracy numbers to acc_min_samples_split_2 and
### acc_min_samples_split_50, respectively

from sklearn import tree
clf = tree.DecisionTreeClassifier(min_samples_split=2)
clf.fit(features_train, labels_train)
pred = clf.predict(features_test)

from sklearn.metrics import accuracy_score
acc_min_samples_split_2 = accuracy_score(pred, labels_test)
print(acc_min_samples_split_2)


############min_samples_split=50#####################
clf50 = tree.DecisionTreeClassifier(min_samples_split=50)
clf50.fit(features_train, labels_train)
pred50 = clf50.predict(features_test)

acc_min_samples_split_50 = accuracy_score(pred50, labels_test)
print acc_min_samples_split_50

def submitAccuracies():
  return {"acc_min_samples_split_2":round(acc_min_samples_split_2,3),
          "acc_min_samples_split_50":round(acc_min_samples_split_50,3)}
0.908
0.912
{"message": "{'acc_min_samples_split_50': 0.912, 'acc_min_samples_split_2': 0.908}"}

当min_samples_split=2 时,准确率为90.8%
当min_samples_split=50时,准确率为91.2%

熵主要控制决策树在何处分隔数据。
它是一系列样本中的不纯度的测量值。
在这里插入图片描述

在这里插入图片描述

熵的计算

在这里插入图片描述
在这里插入图片描述

import math
ss = -0.5 * math.log(0.5,2 ) + (-0.5 *math.log(0.5,2 ) )
 = 1.0

熵为1.0,这是单一性最差的状态。
我们所能得到的熵值最大为1,所以这是我们所能得到的单一性最差的样本。
如果我们有两个类别的标签,那么我们所能得到的单一性最差的情况就是各样本被平均分配到两类中。

2 划分选择

决策树学习的关键是如何选择最优划分属性。一般而言,随着划分过程不断进行,我们希望决策树的分支结点所包含的样本尽可能属于同一类别,即结点的“纯度”(purity)越来越高。

2.1 信息增益

“信息熵”(information entropy)是度量样本集合最常用的一种指标。

信息增益定义为:父节点的熵减去子节点的熵的加权平均。这些子节点是划分父节点后生成的。

决策树算法会最大化信息增益。
在这里插入图片描述
考虑到不同的分支结点所包含的样本数不同,样本数越多的分支结点的影响越大,于是可计算出用属性a对样本D进行划分所获得的“信息增益”(information gain)

一般而言,信息增益越大,则意味着使用属性a来划分所获得的“纯度提升”越大。

在这里插入图片描述

信息增益的计算(坡度)

在这里插入图片描述
右边节点f,由于只有一种,所以它的熵为0
左边节点的P_slow = 2/3
左边节点的P_fast = 1/3
左边节点的熵为 -2/3 *log(2/3,2) -1/3 *log(1/3,2) = 0.9184
在这里插入图片描述
根据坡度划分的信息增益为 0.3112

信息增益的计算(地形的颠簸和平坦)

在这里插入图片描述

在这里插入图片描述

根据地形的颠簸和平坦划分的信息增益为0;所以我们构建决策树时不能选择地形的颠簸和平坦来划分样本。

信息增益的计算(限速)

在这里插入图片描述
根据地形的颠簸和平坦划分的信息增益为1.0

2.2 增益率

实际上,信息增益准则对可取数目较多的属性有所偏好,为减少这种偏好可能带来的不利影响,著名的 C4.5 决策树不直接使用信息增益,而是使用“增益率”(gain ratio)来选择最优划分属性。
增益率定义为:在这里插入图片描述
其中
在这里插入图片描述
称为属性a的“固有值”(intrinsic value)。
增益率准则对可取数目较少的属性有所偏好,因此,C4.5算法并不是直接选择增益率最大的候选划分属性,而是使用了一个启发式:先从候选划分属性中找出信息增益高于平均水平的属性,再从中选择增益率最高的。

2.3 基尼指数

CART 决策树[Breiman et al., 1984] 使用"基尼指数" (Gini index)来选择划分属性.
在这里插入图片描述

3 剪枝处理

剪枝(pruning)是决策树学习算法对付“过拟合”的主要手段。
决策树剪枝的基本策略有“预剪枝”(prepruning)和“后剪枝”(postpruning)。

3.1 预剪枝

预剪枝是指在决策树生成过程中,对每个结点在划分前先进行估计,若当前结点的划分不能带来决策树泛化性能提升,则停止划分并将当前结点标记为叶结点。

预剪枝优缺点:

预剪枝使得决策树的很多分支都没有“展开”,这不仅降低了过拟合的风险,还显著减少了决策树的训练时间开销和测试时间开销,但另一方面,有些分支的当前划分虽不能提升泛化性能、甚至可能导致泛化性能暂时下降,但在其基础上进行的后续划分却有可能导致性能显著提高;预剪枝基于“贪心”本质禁止这些分支展开,给预剪枝决策树带来了欠拟合的风险。

3.2 后剪枝

后剪枝则是先从训练集生成一棵完整的决策树,然后自底向上地对非叶结点进行考察,若将该结点对应的子树替换为叶结点能带来决策树泛化性能提升,则将该子树替换为叶结点。

后剪枝优缺点:

后剪枝决策树通常比预剪枝决策树保留了更多的分支。一般情形下,后剪枝决策树的欠拟合风险很小,泛化性能往往优于预剪枝决策树。但后剪枝过程是在生成完全决策树之后进行的,并且要自底向上地对数中的所有非叶结点进行逐一考察,因此其训练时间开销比未剪枝决策树和预剪枝决策树要大得多。

4 连续与缺失值

4.1 连续值处理

对于连续属性可以使用连续属性离散化技术,最简单的策略是采用二分法(bi-partition)对连续属性进行处理,这是 C4.5 决策树中采用的机制。
对连续属性a,我们可考察包含 n-1 个元素的候选划分点集合在这里插入图片描述
在这里插入图片描述

4.2 缺失值处理

现实任务中常会遇到某些属性值缺失的不完整样本,此时需要解决两个问题:(1)如何在属性值缺失的情况下进行划分属性选择?(2)给定划分属性,若样本在该属性上的值缺失,如何对样本进行划分?

对于问题(1)可以根据训练集中在属性a上没有缺失值的样本子集来判断属性a的优劣,从而划分属性选择;对于问题(2)若样本 x 在划分属性 a 上的取值已知,则将 x 划入与其取值对应的子结点。若样本 x 在划分属性 a 上的取值未知,就让同一个样本以不同的概率划入到不同的子结点中去。

5 多变量决策树

决策树所形成的分类边界有一个明显的特点:轴平行(axis-parallel),即它的分类边界由若干个与坐标轴平行的分段组成。“多变量决策树”(multivariate decision tree)能实现“斜划分”甚至更复杂划分的决策树。
与传统的“单变量决策树”(univariate decision tree)不同,在多变量决策树的学习过程中,不是为每个非叶结点寻找一个最优划分属性,而是试图建立一个合适的线性分类器。

6 决策树代码实现

// Structure to represent node of kd tree
struct Node
{
	std::vector<float> point;
	int id;
	Node* left;
	Node* right;

	Node(std::vector<float> arr, int setId)
	:	point(arr), id(setId), left(NULL), right(NULL)
	{}
};

struct KdTree
{
	Node* root;

	KdTree()
	: root(NULL)
	{}

	void insertHelper(Node** node, uint depth, std::vector<float>point, int id)
	{
		if (*node == NULL)
		{
			*node = new Node(point, id);
		}
		else
		{
			uint cd = depth % 3;
			if (point[cd] < ((*node)->point[cd]))
				insertHelper(&((*node)->left), depth + 1, point, id);
			else
				insertHelper(&((*node)->right), depth + 1, point, id);
		}

	}

	void insert(std::vector<float> point, int id)
	{
		// TODO: Fill in this function to insert a new point into the tree
		// the function should create a new node and place correctly with in the root 
		insertHelper(&root, 0, point, id);

	}

	void searchHelper(std::vector<float> target, Node* node, int depth, float distanceTol, std::vector<int>& ids)
	{
		if (node != NULL)
		{
			if ((node->point[0] >= (target[0]- distanceTol) && node->point[0] <= (target[0]+ distanceTol) &&
				node-point[1] >= (target[1] - distanceTol) && node->point[1] <= (target[1] + distanceTol)))
			{
				float distance = sqrt((node->point[0] - target[0]) * (node->point[0] - target[0]) +
					(node->point[1] - target[1]) * (node->point[1] - target[1]));
				if (distance <= distanceTol)
					ids.push_back(node->id);
			}


			//check across boundary
			if ((target[depth % 2] - distanceTol) < node->point[depth % 2])
				searchHelper(target, node->left, depth + 1, distanceTol, ids);
			if((target[depth % 2] - distanceTol) > node->point[depth % 2])
				searchHelper(target, node->right, depth + 1, distanceTol, ids);
		}

	}

	// return a list of point ids in the tree that are within distance of target
	std::vector<int> search(std::vector<float> target, float distanceTol)
	{
		std::vector<int> ids;
		searchHelper(target, root, 0, distanceTol, ids);
		return ids;
	}
	

};



7.决策树编码sklearn

classifyDT.py

def classify(features_train, labels_train):
    
    ### your code goes here--should return a trained decision tree classifer
    
    from sklearn import tree

    clf = tree.DecisionTreeClassifier()

    clf = clf.fit(features_train, labels_train)

	pred = clf.predict(features_test)

   from sklearn.metrics import accuracy_score 


     acc = accuracy_score(pred, labels_test)
	 ### you fill this in!
	print acc
    
    
    return clf

main.py

#!/usr/bin/python

""" lecture and example code for decision tree unit """

import sys
from class_vis import prettyPicture, output_image
from prep_terrain_data import makeTerrainData

import matplotlib.pyplot as plt
import numpy as np
import pylab as pl
from classifyDT import classify

features_train, labels_train, features_test, labels_test = makeTerrainData()



### the classify() function in classifyDT is where the magic
### happens--fill in this function in the file 'classifyDT.py'!
clf = classify(features_train, labels_train)

#### grader code, do not modify below this line

prettyPicture(clf, features_test, labels_test)
output_image("test.png", "png", open("test.png", "rb").read())

准确率为0.908
在这里插入图片描述

8.决策树优缺点

优点
1.非常易用,能以图形化方式很好地剖析数据
2.能通过集成的方法,从决策树出发构件更大规模的分类器

缺点:
容易过拟合,尤其是对于包含大量特征的数据时,复杂的决策树会导致过拟合。
测量决策树的准确性非常重要,你需要在合适的时候提前停止决策树的生长。

9. 偏差和方差

高偏差机器学习算法实际上会忽略训练数据,它几乎没有能力学习任何数据,这被称为偏差,相当于欠拟合

高方差算法会对数据高度敏感,它只能复现曾经见过的东西,它的问题在于,对于之前未见过的情况,它的反应很差,因为没有适当的偏差,让它泛化新的东西,相当于过拟合。

决策树小项目——识别作者

几年前,J.K. 罗琳(凭借《哈利波特》出名)试着做了件有趣的事。她以 Robert Galbraith 的化名写了本名叫《The Cuckoo’s Calling》的书。尽管该书得到一些不错的评论,但是大家都不太重视它,直到 Twitter 上一个匿名的知情人士说那是 J.K. Rowling 写的。《伦敦周日泰晤士报》找来两名专家对《杜鹃在呼唤》和 Rowling 的《偶发空缺》以及其他几名作者的书进行了比较。分析结果强有力地指出罗琳就是作者,《泰晤士报》直接询问出版商情况是否属实,而出版商也证实了这一说法,该书在此后一夜成名。

我们也将在此项目中做类似的事。我们有一组邮件,分别由同一家公司的两个人撰写其中半数的邮件。我们的目标是仅根据邮件正文区分每个人写的邮件。在这个迷你项目一开始,我们将使用朴素贝叶斯,并在之后的项目中扩展至其他算法。

我们会先给你一个字符串列表。每个字符串代表一封经过预处理的邮件的正文;然后,我们会提供代码,用来将数据集分解为训练集和测试集。

准备好决策树并将它作为分类器运行起来,设置 min_samples_split=40。可能需要等一段时间才能开始训练。

1) 准确率是多少?

import sys
from time import time
sys.path.append("../tools/")
from email_preprocess import preprocess


### features_train and features_test are the features for the training
### and testing datasets, respectively
### labels_train and labels_test are the corresponding item labels
features_train, features_test, labels_train, labels_test = preprocess()




#########################################################
### your code goes here ###
from sklearn import tree

clf = tree.DecisionTreeClassifier(min_samples_split=40)

t0 = time()
clf.fit(features_train, labels_train)
print "training time:", round(time()-t0, 3), "s"
t1 = time()

pred = clf.predict(features_test)
print "testing time:", round(time()-t1, 3), "s"

from sklearn.metrics import accuracy_score

acc = accuracy_score(pred, labels_test)
print acc


training time: 64.302 s
tsting time: 0.222 s
0.975540386803

2) 特征数量

你从 SVM 迷你项目中了解到,参数调整可以显著加快机器学习算法的训练时间。一般情况下,参数可以调整算法的复杂度,越复杂的算法通常运行起来越慢。

控制算法复杂度的另一种方法是通过你在训练/测试时用到的特征数量。算法可用的特征数越多,越有可能发生复杂拟合。我们将在“特征选择”这节课中详细探讨,但你现在可以提前有所了解。

你数据中的特征数是多少?

print len(features_train[0])

共计3785个特征

3)更改特征数量

在其他所有方面都相等的情况下,特征数量越多会使决策树的复杂性更高

当你仅使用 1% 的可用特征(即百分位数 = 1)时,决策树的准确率是 0.9664

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值