1.背景介绍
决策树(Decision Tree)是一种基本的分类与回归方法,它能够将复杂的非线性数据划分成简单的分类或回归问题。其特点在于简单、易于理解和实现。因此,决策树可以广泛用于各种各样的机器学习任务中,如决策分析、预测、排序等。
决策树的基本原理是,从根节点开始,按照一定的顺序将原始数据集划分为多个子集,并据此建立起分类规则。每个节点代表一个条件,若满足该条件则选择该子集作为输出;否则选择另一个子集作为输出。通过组合条件,最终将所有的数据都划分到某一类别或某一属性上。
而在实际应用中,决策树的构建往往需要多次迭代训练才能得到最优的分类结果。这种多次迭代过程称为“监督学习”(Supervised Learning),其中目标变量的取值被标注为训练数据的一部分,通过算法学习出模型。因此,决策树是一个非常经典的监督学习算法。
本文试图通过案例介绍如何利用决策树解决实际问题。首先,介绍决策树的定义及其与随机森林的区别;然后通过一个具体的案例介绍如何利用决策树进行预测和分类;最后,提出几个关键词,指引读者阅读更多相关资料,进一步探讨决策树的应用。
2.核心概念与联系
(1)决策树的定义
决策树(Decision Tree)是一种基本的分类与回归方法,它能够将复杂的非线性数据划分成简单的分类或回归问题。其特点在于简单、易于理解和实现。因此,决策树可以广泛用于各种各样的机器学习任务中,如决策分析、预测、排序等。
决策树的基本原理是,从根节点开始,按照一定的顺序将原始数据集划分为多个子集,并据此建立起分类规则。每个节点代表一个条件,若满足该条件则选择该子集作为输出;否则选择另一个子集作为输出。通过组合条件,最终将所有的数据都划分到某一类别或某一属性上。
(2)决策树与随机森林的区别
随机森林(Random Forest)是一种基于决策树的集成学习算法,由多棵决策树组成。它对每个决策树做了如下改进:
- 每棵树的生成采用袋外样本的方式,即每次在生成之前,先随机从袋中抽取一些样本作为训练集。
- 在每棵树生长结束时,用袋外样本的表现来估计整体树的性能,不再像其他决策树那样用子树的数量作为性能标准。
- 对测试数据进行预测时,用多棵树投票的方式来决定最终类别,而不是像决策树那样只选取概率最大的那棵树作为最终结果。
决策树与随机森林的关系如图所示:
以上三个主要区别就是决策树是单颗树,随机森林是多棵树。而且,随机森林采用袋外样本的方式,使得决策树泛化能力更强。
(3)案例:预测疾病症状
假设有一个新病人的病历,病史等信息,需要根据这些信息来判断其可能得的不同诊断。我们可以使用决策树来解决这个问题。假定病人的病情表现被记录下来,包括:
- 年龄:病人年龄
- 是否患有流行性腺炎:是否患有流感
- 是否服用抗肿瘤药物:是否服用抗生素
- 患病时间:病人感染后经过的时间
- 是否病房隔离观察:病人是否住在医院的病房
这些数据中的“患有流感”等信息都是可选的,表示对该病的认识程度。因此,我们可以利用决策树对这些信息进行分类。
(3.1)准备数据
为了让案例更加贴近实际情况,我们随机生成了100个病人的病历信息,包括病史、病情表现和是否诊断为流感。病史有流感、心脏病、糖尿病、血友病等。病情表现包括发热、咳嗽、头晕、乏力、恶心等。
import random
class Data:
def __init__(self):
self._data = []
@property
def data(self):
return self._data
def generate_data(self):
for i in range(100):
age = random.randint(18, 70)
has_fever = bool(random.getrandbits(1))
smokes = bool(random.getrandbits(1))
time_of_symptoms = random.choice(['early','mid', 'late'])
isolation = bool(random.getrandbits(1))
if not has_fever and not smokes and (time_of_symptoms == 'early' or time_of_symptoms =='mid'):
label = 'Healthy'
elif has_fever and not smokes and time_of_symptoms!= 'late':
label = 'Fever'
else:
label = 'Severe fever'
self._data.append([age, has_fever, smokes, time_of_symptoms, isolation])
data = Data()
data.generate_data()
(3.2)特征工程
要进行决策树分类,首先需要对数据的特征进行清洗和处理。比如,“年龄”这一特征可能比较难以给出一个准确的数值,因为一个人的年龄是变化的,所以我们可以使用年龄段来代替。另外,“是否服用抗肿瘤药物”这一特征的取值为True或者False,但是决策树的输入必须是数字,因此需要把它转换为一个整数。
def feature_engineering():
# convert the boolean features into numbers
feats = [i[1:] for i in data.data] + [[int(j=='Fever')*1 for j in i[-2:]] for i in data.data]
labels = ['Healthy'] * len(data.data) + ['Fever'] * sum([(j=='Fever')*1 for j in i[-1]] for i in data.data)
return zip(*feats), labels
features, labels = feature_engineering()
print('Features:', features[:5], '\nLabels:', labels[:5])
(3.3)训练模型
决策树是一个分类算法,它的训练方式就是从训练数据中找到分类规则。由于决策树是一种递归的算法,所以我们需要遍历所有的特征空间,找出使分类效果最好的条件。对于每一个条件,我们就要计算出其对应的分类效率,然后在该条件下将数据集切分为左右两个子集,继续选择剩余的特征进行划分。直到无法继续划分,或者划分后的子集不能再进一步划分为止。
from sklearn import tree
clf = tree.DecisionTreeClassifier()
clf = clf.fit(features[:-10], labels[:-10])
(3.4)预测和评估模型
模型训练好之后,就可以用来预测新的病人的病情表现了。我们将病人的病历信息输入到模型中,模型会给出一个诊断,然后我们可以对比这个诊断是否符合病人的实际病情。
new_data = [[40, False, True, 'early', True]]
prediction = clf.predict(new_data)[0]
actual_label = new_data[0][-2]
if prediction == actual_label:
print("The model correctly diagnosed the patient with a", prediction)
else:
print("The model incorrectly diagnosed the patient as having a", prediction)
from sklearn.metrics import accuracy_score
accuracy = accuracy_score(labels[-10:], predictions)
print('Accuracy on test set:', accuracy)
3.核心算法原理和具体操作步骤以及数学模型公式详细讲解
(1)决策树的构建过程
在决策树的构建过程中,通常有以下四个步骤:
- 数据准备阶段:进行数据清洗和处理,准备数据成为适合建模的形式。
- 特征选择阶段:选择特征,即将原始数据中较无用或相关性较低的信息过滤掉,以便提高建模效率。
- 决策树生成阶段:根据特征和标签构建决策树,递归地将数据集划分为子集,直到子集不能再继续划分为止。
- 决策树评估阶段:使用测试数据对已生成的决策树进行评估,确定其正确率。
(2)决策树的预剪枝过程
预剪枝(Prepruning)是一种在生成决策树过程中优化的策略,目的是减少树的大小,使其在训练数据上的错误率降低。其基本思想是从底层向上检查,如果一个结点的划分不能带来任何益处,则将其去掉。例如,如果某个叶结点的样本全属于同一类,那么这个叶结点的父结点就可以去掉,因为它没有足够的能力改变其子结点的标签。
预剪枝在实际应用中有着重要作用,它可以有效地减少决策树的大小,避免过拟合,同时也能提升训练速度。
(3)决策树的后剪枝过程
后剪枝(Postpruning)是指在完成决策树的构造之后,对已经生成的树进行裁剪,消除掉一些不影响决策结果的叶结点,从而简化决策树的结构。
后剪枝的主要思路是在训练集上使用一些指标来衡量模型的性能,例如准确率、AUC、损失函数等。当某个结点的子结点都不纯,并且与父结点的划分毫无关联时,就把该结点及其所有子结点删去,这样就得到了一个更小的、不纯度更低的子树。
(4)决策树的剪枝算法流程
剪枝算法的一般流程为:
- 使用全局基尼系数选择最佳的分割特征及其阈值,计算当前划分下的基尼指数。
- 如果当前划分的基尼指数小于设定的阈值,停止划分,形成叶结点。
- 如果当前划分的基尼指数大于阈值,则进行局部剪枝。
- 以当前结点为根的子树进行剪枝,找到其所有非叶结点的集合。
- 对于集合中的每个结点,计算其所有非叶结点的集合。
- 对于该结点的所有非叶结点的集合,计算所有结点分支的基尼指数。
- 对于当前结点的非叶结点的集合,如果存在一个结点的分支的基尼指数大于阈值,则删除该结点及其所有子结点,否则,对剩下的结点集合重复步骤1和2。
(5)决策树的缺陷
决策树算法在一些情况下存在一些缺陷。比如:
- 偏斜问题:如果数据集中正负样本的分布不平衡,比如正负样本数量差距很大,可能会导致决策树的偏向,造成欠拟合。
- 类别不平衡:决策树算法对类别不平衡的问题不敏感,容易产生过拟合。
- 连续变量缺失值处理:决策树的计算结果依赖于每个变量的取值,对于缺失值或者异常值会产生问题。
4.具体代码实例和详细解释说明
以上是决策树的基础知识介绍,接下来通过案例来详细介绍如何使用Python实现决策树算法。
(1)案例:预测疾病症状(详细版)
本案例中,我们使用决策树算法来预测患有流感的人群是否会得肺炎或其它传染病。
(1.1)数据集介绍
疾病数据集共518条,包含年龄、患有流感、是否患有冠心病、是否患有高血压、是否患有动脉硬化等诸多因素。希望通过建立决策树模型,对未知患者的诊断进行准确预测。
(1.2)安装必要的库
!pip install pandas numpy scikit-learn matplotlib
(1.3)准备数据
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.tree import DecisionTreeClassifier, plot_tree
from sklearn.metrics import classification_report, confusion_matrix, accuracy_score
import seaborn as sns
# load dataset
df = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/00383/Data_for_UCI_named.csv')
# split dataset into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(df.iloc[:, :-1].values, df['diagnosis'].values, test_size=0.3, random_state=1)
# scale features
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
(1.4)特征工程
# encode categorical variables
cat_cols = ['diagnosis', 'bp', 'hypertension', 'heart_disease', 'gender']
encoder = {}
for col in cat_cols:
encoder[col] = dict(enumerate(set(df[col])))
for col in cat_cols:
df[col] = df[col].map(encoder[col]).fillna(-1).astype(int)
# separate numerical and categorical features
num_cols = list(set(df.columns) - set(cat_cols))
X_train_num = X_train[:, :len(num_cols)]
X_train_cat = X_train[:, len(num_cols):]
X_test_num = X_test[:, :len(num_cols)]
X_test_cat = X_test[:, len(num_cols):]
# create one-hot encoded matrix of categories
X_train_onehot = np.concatenate((np.array(pd.get_dummies(df.iloc[:, len(num_cols):])), X_train_num), axis=1)
X_test_onehot = np.concatenate((np.array(pd.get_dummies(df.iloc[:, len(num_cols):])), X_test_num), axis=1)
(1.5)训练模型
# fit decision tree classifier to training data
dtree = DecisionTreeClassifier(criterion='gini', max_depth=None, min_samples_split=2,
class_weight={0:0.2, 1:0.8}, random_state=1)
dtree.fit(X_train_onehot, y_train)
(1.6)模型评估
# make predictions on test set
y_pred = dtree.predict(X_test_onehot)
# evaluate performance metrics
print("Classification Report:\n", classification_report(y_test, y_pred))
cm = confusion_matrix(y_test, y_pred)
ax = sns.heatmap(cm, annot=True, fmt="d")
bottom, top = ax.get_ylim()
ax.set_ylim(bottom + 0.5, top - 0.5)
plt.title("Confusion Matrix")
plt.xlabel("Predicted Label")
plt.ylabel("True Label");
print("\nAccuracy:", accuracy_score(y_test, y_pred))
(1.7)模型可视化
plot_tree(dtree, filled=True, rounded=True, proportion=True,
fontsize=10);
(1.8)总结
本案例通过引入决策树算法,对患者的诊断结果进行预测。本文结合案例实践,梳理了决策树算法的基本原理和构建流程,还给出了相应的代码实现。决策树模型是一个十分灵活且精巧的机器学习模型,具有很高的普遍性和实用价值。