本文约1900字,给出了决策树模型在分类、回归两方面的具体实现。具体包括对鸢尾花的分类、泰坦尼克号乘客生还情况预测两个具体实例。建议文中所有代码读者在自己机器上运行。
决策树(DTs)是一种用于分类和回归的非参数监督学习方法。目标是通过创建一个模型,学习隐藏在数据中的简单决策规则来预测目标变量的数值和用于分类。
例如,在下面的示例中,决策树利用分支结构(if-then-else)的决策规则从数据中学习以逼近正弦曲线。决策树的深度越大,决策规则越复杂,拟合的模型也更好。

决策树的优点:
⑴模型易于理解、结果易于解释;树结构能够可视化。
⑵只需少量的数据准备。别的方法经常要求数据标准化,创建虚拟变量,移除空白值。
⑶训练决策树的成本随着树的高度呈指数增加。
⑷能够处理数值和分类数据,其它方法通常只擅长处理只包含一种类型数据的数据集。
⑸能够处理多输出问题。
⑹是一个白盒子模型,易于理解和解释,不像人工神经网络模型(黑盒子),难以解释。
⑺可以使用统计测试验证模型,增加模型的可靠性。
⑻即使模型采取的假设与真实数据有些违背,其最终性能也比较好。
决策树的缺点:
⑴不宜创建过度复杂的树,以免造成过拟合。需要诸如修剪(当前不支持),设置叶节点所需的最小样本数或设置树的最大深度的机制来避免这个问题。
⑵决策树可能不稳定,小数据的变化可能导致生成完全不同的树。这个问题可以通过在集成模型中使用决策树来缓解。
⑶有些概念很难学习,因为决策树不能轻易表达它们。例如XOR,奇偶校验或多路复用器问题。
⑷学习一棵最优决策树相当难的,即使对于相当简单概念。因此,在实际的决策树学习算法中,往往是基于启发式算法(如贪婪算法)来实现。但是贪婪算法并不能得到全局最优结果。这可以通过在集成模型中使用多棵树的方法来缓解面临的问题。
解决实际问题——分类
DecisionTreeClassifier是Scikit-learn模块中的一个类,能够实现数据集的多分类问题。
与其它分类器一样,DecisionTreeClassifier采用两个输入参数,参数类型为两个数组(array),一个数组X(可以为)作为训练样本,当然你可以看成一个二维矩阵,行数就是数据集的样本数量n_samples,列数就是特征数n_features;另一个数组Y是以整数为数值的列向量,行数为n_samples。
DecisionTreeClassifier实现多分类问题(如K类,则labels为[0, ..., K-1]):
我们使用(鸢尾花)Iris数据集,构建如下决策树。
#从sklearn.datasets中导入数据集load_iris,导入tree模块
from sklearn.datasets import load_iris
from sklearn import tree
#读取数据存入变量iris
iris = load_iris()
#初始化决策树模型
clf = tree.DecisionTreeClassifier()
#调用模型中fit函数/模块训练模型
clf = clf.fit(iris.data, iris.target)
此时,我们已经完成了模型训练。我们可以使用export_graphviz导出器将训练的决策树以Graphviz格式导出。使用如下代码:
with open("iris.dot", 'w') as f:
f = tree.export_graphviz(clf, out_file=f)
然后,我们使用Graphviz的dot工具创建pdf格式的输出文件(或者其它被支持的格式)。
import os
os.unlink('iris.dot')
如果我们安装好了python的pydotplus模块,也可以用如下方法创建pdf格式的文件。代码如下:
import pydotplus
dot_data = tree.export_graphviz(clf, out_file=None)
graph = pydotplus.graph_from_dot_data(dot_data)
graph.write_pdf("iris.pdf")
export_graphviz导出器也支持许多其他选项,这包括为不同的结点(nodes)着色,使用显示变量和类名等。IPython notebooks也可以使用Image()函数。
#从IPython.display模块中导入Image()函数
from IPython.display import Image
dot_data = tree.export_graphviz(clf, out_file=None,
feature_names=iris.feature_names,
class_names=iris.target_names,
filled=True, rounded=True,
special_characters=True)
graph = pydotplus.graph_from_dot_data(dot_data)
Image(graph.create_png())
运行以上代码则以pdf的格式输出你所构建的决策树。

在拟合模型后,使用predict()函数对样本做分类预测。使用如下代码:
clf.predict(iris.data[:1, :])
预测每个类的概率:
clf.predict_proba(iris.data[:1, :])
解决实际问题——泰坦尼克号乘客的生还情况预测
分析:首先红互联网上导入泰坦尼克号乘客数据,使用pandas中的read_csv()函数;观察数据发现数据缺失严重,对缺时的某些数据补全;使用决策树模型进行预测。
# 导入pandas模块,使用read_csv()函数直接从互联网收集泰坦尼克号乘客数据并转化成dataframe格式给titanic。
import pandas as pd
titanic = pd.read_csv('http://biostat.mc.vanderbilt.edu/wiki/pub/Main/DataSets/titanic.txt')
#观察前几行数据,了解数据集大致信息;使用info()函数查看变量信息
titanic.head()
titanic.info()
#特征的选择,本模型选择了sex,age,pclass三个特征。
X = titanic[['pclass', 'age', 'sex']]
y = titanic['survived']
# 补充age的缺失值。
X['age'].fillna(X['age'].mean(), inplace=True)
#训练集比例占整个数据集的75%,从sklearn.cross_validation导入train_test_split分割数据
from sklearn.cross_validation import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state = 33)
# 使用scikit-learn.feature_extraction中的特征转换器DictVectorizer
from sklearn.feature_extraction import DictVectorizer
vec = DictVectorizer(sparse=False)
# 转换特征后,我们发现凡是类别型的特征都单独剥离出来,独成一列特征,数值型的则保持不变。
X_train = vec.fit_transform(X_train.to_dict(orient='record'))
print vec.feature_names_
#对测试数据的特征进行转换。
X_test = vec.transform(X_test.to_dict(orient='record'))
# 从sklearn.tree中导入决策树分类器,并初始化。
from sklearn.tree import DecisionTreeClassifier
dtc = DecisionTreeClassifier()
# 将预处理的数据训练模型。
dtc.fit(X_train, y_train)
# 将训练好的模型对样本测试集做预测。
y_predict = dtc.predict(X_test)
# 从sklearn.metrics导入classification_report并输出详细分类信息。
from sklearn.metrics import classification_report
print classification_report(y_predict, y_test, target_names = ['died', 'survived'])
最终输出结果:

完整代码如下:
iris集完整代码:
from sklearn.datasets import load_iris
from sklearn import tree
iris = load_iris()
clf = tree.DecisionTreeClassifier()
clf = clf.fit(iris.data, iris.target)
with open("iris.dot", 'w') as f:
f = tree.export_graphviz(clf, out_file=f)
import os
os.unlink('iris.dot')
import pydotplus
dot_data = tree.export_graphviz(clf, out_file=None)
graph = pydotplus.graph_from_dot_data(dot_data)
graph.write_pdf("iris.pdf")
from IPython.display import Image
dot_data = tree.export_graphviz(clf, out_file=None,
feature_names=iris.feature_names,
class_names=iris.target_names,
filled=True, rounded=True,
special_characters=True)
graph = pydotplus.graph_from_dot_data(dot_data)
Image(graph.create_png())
clf.predict(iris.data[:1, :])
clf.predict_proba(iris.data[:1, :])
titanic据集完整代码:
import pandas as pd
titanic = pd.read_csv('http://biostat.mc.vanderbilt.edu/wiki/pub/Main/DataSets/titanic.txt')
titanic.head()
titanic.info()
X = titanic[['pclass', 'age', 'sex']]
y = titanic['survived']
X['age'].fillna(X['age'].mean(), inplace=True)
from sklearn.cross_validation import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state = 33)
from sklearn.feature_extraction import DictVectorizer
vec = DictVectorizer(sparse=False)
X_train = vec.fit_transform(X_train.to_dict(orient='record'))
print vec.feature_names_
X_test = vec.transform(X_test.to_dict(orient='record'))
from sklearn.tree import DecisionTreeClassifier
dtc = DecisionTreeClassifier()
dtc.fit(X_train, y_train)
y_predict = dtc.predict(X_test)
from sklearn.metrics import classification_report
print classification_report(y_predict, y_test, target_names = ['died', 'survived'])