决策树
决策树基于二分类思想,类似于编程语言中的if-else,决策树通常会有两个阶段:构造和剪枝。
-
构造:构造的过程就是选择什么属性作为节点的过程。构造过程中会存在三种节点:
- 根节点:最开始的节点
- 内部节点:树中间的节点
- 叶节点:决策结果
-
剪枝:目的是为了防止过拟合。可分为预剪枝和后剪枝。
- 预剪枝是指在决策树构造时进行剪枝。在构造过程中对节点进行评估,如果对某个节点进行划分,在验证集中不能带来准确性的提升,那么对这个节点进行划分就没有意义,这时就会把当前节点作为叶节点,不对其进行划分。
- 后剪枝是在生产决策树之后在进行剪枝。通常会从决策树的叶节点开始,逐层向上对每个节点进行评估。如果剪掉这个节点子树,与保留该节点子树在分类准确性上差别不大,或者剪掉该节点子树,能在验证集中带来准确性的提升,那么就可以把该节点子树进行剪枝。方法是:用这个节点子树的叶子节点来替代该节点,类标记为这个节点子树中最频繁的那个类。
选取根节点属性的指标:纯度和信息熵
ID3算法节点属性选择采用的指标是信息增益。(对噪声敏感)
- ID3算法的缺点:倾向于选择取值比较多的属性,有些属性可能对分类任务没有太大作用,但仍然可能被选为最优属性。
C4.5算法节点属性选择采用的指标是信息增益率。(解决了ID3对噪声敏感的问题,但算法效率低)
- 信息增益率=信息增益/属性熵,基于ID3缺点的改进。
- 采用悲观剪枝,提升决策树泛化能力。悲观剪枝通过递归估算每个内部节点的分类错误率,比较剪枝前后这个节点的分类错误率来决定是否对其进行剪枝。
- 离散化处理连续属性
- 能够处理缺失值
Cart算法节点属性选择采用的指标是基尼指数。基尼系数越小,说明样本之间的差异性小,不确定程度低。
- CART树既可作分类树,又可作回归树。
- 基于基尼系数对特征属性进行二元分裂
例CART分类树:
# encoding=utf-8
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_iris
# 准备数据集
iris=load_iris()
# 获取特征集和分类标识
x = iris.data
y = iris.target
# 随机抽取33%的数据作为测试集,其余为训练集
train_x, test_x, train_y, test_y = train_test_split(x, y, test_size=0.33, random_state=0)
# 创建CART分类树
clf = DecisionTreeClassifier(criterion='gini')
# 拟合构造CART分类树
clf = clf.fit(train_x, train_y)
# 用CART分类树做预测
test_predict = clf.predict(test_x)
# 预测结果与测试集结果作比对
score = accuracy_score(test_y, test_predict)
print("CART分类树准确率 %.3lf" % score)
CART回归树通常采用差值的绝对值或方差来作为属性选取指标,对于这两种节点划分标准分别对应着两种目标函数最优化的标准,即最小绝对偏差(LAD)或最小二乘偏差(LSD).
例CART回归树:
# encoding=utf-8
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_boston
from sklearn.metrics import r2_score,mean_absolute_error,mean_squared_error
from sklearn.tree import DecisionTreeRegressor
# 准备数据集
boston=load_boston()
# 探索数据
print(boston.feature_names)
# 获取特征集和房价
x = boston.data
y = boston.target
# 随机抽取33%的数据作为测试集,其余为训练集
train_x, test_x, train_y, test_y = train_test_split(x, y, test_size=0.33)
# 创建CART回归树
dtr=DecisionTreeRegressor()
# 拟合构造CART回归树
dtr.fit(train_x, train_y)
# 预测测试集中的房价
predict_price = dtr.predict(test_x)
# 测试集的结果评价
print('回归树二乘偏差均值:', mean_squared_error(test_y, predict_price))
print('回归树绝对值偏差均值:', mean_absolute_error(test_y, predict_price))
CART决策树的剪枝:
-
主要采用CCP方法,即代价复杂度。使用节点的表面误差率增益值作为剪枝前后误差的定义。
α=C(t)−C(Tt)∣Tt∣−1 \alpha=\frac{C(t)-C\left(T_{t}\right)}{\left|T_{t}\right|-1} α=∣Tt∣−1C(t)−C(Tt)
其中Tt代表以t为根节点的子树,C(Tt)表示节点t的子树没被裁剪时子树Tt的误差,C(t)表示节点t的子树被剪枝后节点t的误差,|Tt|代子树Tt的叶子数,剪枝后,T的叶子数减少了|Tt|-1。 -
节点的表面误差率增益值等于节点t的子树被剪枝后的误差变化除以剪掉的叶子数量
sklearn中的决策树分类器:
DecisionTreeClassifier(class_weight=None, criterion='entropy', max_depth=None,
max_features=None, max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=1, min_samples_split=2,
min_weight_fraction_leaf=0.0, presort=False, random_state=None,
splitter='best')
参数 | 作用 |
---|---|
criterion | 选择特征的标准,默认gini,可指定entropy |
splitter | 构造树时选择属性特征的原则,默认best代表在所有特征中选择最好的,可指定random代表在部分特征中选择最好 |
max_depth | 决策树最大深度 |
max_features | 划分数据集是考虑最大的特征值数量 |
min_samples_split | 当节点样本数少于这个值时,不再分裂。默认2 |
min_samples_leaf | 叶子节点需要的最少样本数 |
max_leaf_nodes | 最大叶子节点数 |
min_impurity_decrease | 节点划分最小不纯度 |
min_impurity_split | 信息增益的阈值 |
class_weight | 类别权重 |
presort | bool类型,默认false,表示拟合前,是否对数据进行排序来加快树的构建,当数据集较小时,使用presort=true可以加快分类器构造速度,当数据集较大时,会导致整个分类非常缓慢 |
函数 | 作用 |
---|---|
fit(features,labels) | 通过特征矩阵、分类标识,让分类器进行拟合 |
predict(features) | 返回预测结果 |
score(features, labels) | 返回准确率 |
案例:泰坦尼克号生存预测
- 准备:首先对训练集、测试集数据探索,分析数据质量进行数据清洗,然后通过特征选择进行数据降维。
- 分类:通过训练集的特征矩阵、分类结果得到决策树分类器,然后将分类器应用于测试集,最后对分类器准确性进行分析。
第一步:数据探索
-
使用info()了解数据表的基本情况:行数、列数、每列的数据类型、数据完整度;
-
使用describe()了解数据表的统计情况:总数、平均值、标准差、最小值、最大值等;
-
使用describe(include=[‘O’])查看字符串类型(非数字)的整体情况;
-
使用head查看前几行数据(默认是前5行);
-
使用tail查看后几行数据(默认是最后5行)
import pandas as pd train_data = pd.read_csv('./train.csv') test_data = pd.read_csv('./test.csv') print(train_data.info()) print('-'*30) print(train_data.describe()) print('-'*30) print(train_data.describe(include=['O'])) print('-'*30) print(train_data.head()) print('-'*30) print(train_data.tail())
第二步:数据清洗
-
age,fare,cabin有缺失值,age和fare为数字型用平均值补齐。
# 使用平均年龄来填充年龄中的nan值 train_data['Age'].fillna(train_data['Age'].mean(), inplace=True) test_data['Age'].fillna(test_data['Age'].mean(),inplace=True) # 使用票价的均值填充票价中的nan值 train_data['Fare'].fillna(train_data['Fare'].mean(), inplace=True) test_data['Fare'].fillna(test_data['Fare'].mean(),inplace=True)
-
cabin为船舱,有大量缺失值,无法补齐。embarked为登录港口,有少量缺失值,利用出现次数最多的港口补齐。
# 使用登录最多的港口来填充登录港口的nan值 train_data['Embarked'].fillna('S', inplace=True) test_data['Embarked'].fillna('S',inplace=True)
第三步:特征选择
-
PassengerId为乘客编号,Name为乘客姓名,对分类没有作用;Cabin字段缺失值太多,放弃;Ticket字段为船票号码,杂乱无章且无规律,放弃。
-
其余字段包括:Pclass、Sex、Age、SibSp、Parch和Fare,分别对应乘客的船票等级、性别、年龄、亲戚数量以及船票价格。
-
将Sex字段变成Sex=male和Sex=female两个字段,数值用0或1来表示。Embarked字段改成Embarked=S、Embarked=C和Embarked=Q三个字段,数值用0或1来表示。
from sklearn.feature_extraction import DictVectorizer dvec=DictVectorizer(sparse=False) train_features=dvec.fit_transform(train_features.to_dict(orient='record'))
-
特征选择后train_features为891行,10列的特征矩阵
第四步:决策树模型
from sklearn.tree import DecisionTreeClassifier
# 构造CART决策树
clf = DecisionTreeClassifier(criterion='entropy')
# 决策树训练
clf.fit(train_features, train_labels)
第五步:模型预测评估
test_features=dvec.transform(test_features.to_dict(orient='record'))
# 决策树预测
pred_labels = clf.predict(test_features)
import numpy as np
from sklearn.model_selection import cross_val_score
# 使用K折交叉验证 统计决策树准确率,这里是10折
print(u'cross_val_score准确率为 %.4lf' % np.mean(cross_val_score(clf, train_features, train_labels, cv=10)))
K折交叉验证原理:
- 将数据集平均分割成K个等份;
- 使用1份数据作为测试数据,其余作为训练数据;
- 计算测试准确率;
- 使用不同的测试集,重复2、3步骤。