一、决策树
1.定义
决策树是一种基于树形结构的分类模型,它通过将数据集划分为不同的分支和叶节点来进行决策。
其中每个节点代表一个特征或属性,每个分支代表一个可能的决策规则,而叶子节点代表最终的类别或结果。决策树通过一系列的问题和条件来分割数据集,以便最终能够对新的数据点进行分类或预测。
2.基本流程
决策树的生成是一个自根结点一直到叶结点的递归生成过程,其基本流程遵循简单而直观的 分而治之 策略,也可以将决策树看成是一个if-then规则的集合,根据规则,逐次判断条件,最终依据路径归于特定类别。如下图:
- 当前结点包含的样本全部属于同一个类别,无需划分;(第3行)
- 当前属性集为空,或是所有样本在所有属性上取值相同,无法划分。在这种情况下,把当前结点标记为叶结点,并且将其类别设定为该结点所含样本最多的类别;(第6行)
- 当前结点包含的样本集和为空,当前结点标记为叶节点,类别=该结点的父节点所含样本最多的类别 (第12行)
3. 最优划分属性
在决策树的构建过程中,选择最佳划分属性(特征)是非常重要的。一般而言我们希望选择一个属性之后,其分支节点所包含的样本尽可能属于同一类别,即结点的纯度 (Purity) 越来越高。
简单来说,当我们有了输入数据,并且输入的数据有自己的属性特征。那么决策要做的就是决定用那个特征来划分特征空间。并且我们希望节点内部样本的相似程度或同质性较高,即提高纯度。
经典的属性划分方法:
- 信息增益: ID 3
- 增益率:C 4.5
- 基尼指数:CART
3.1 信息熵
在介绍属性划分方法之前,需先引入一个概念:信息熵。
信息熵是度量样本集合纯度最常用的一种指标,假定当前样本集合D中第k类样本所占的比例为 pk (K=1, 2, ..., |y|) ,则D的信息熵定义为
Ent(D)的值越小,则D的纯度越高。Ent(D)的最小值为0,最大值为log2|y|
3.2 信息增益- ID 3
信息增益(information gain):离散属性a有V个可能的取值{a1, a2, ..., aV},用a来进行划分,则会产生V个分支结点,其中第v个分支结点包含了D中所有在属性a上取值为av的样本,记为Dv。则可计算出用属性a对样本集D进行划分所获得的“信息增益”:一般而言,信息增益越大则意味着用属性 a 来进行划分所获得的“纯度提升”越大(信息增益越大越好)。
3.3 增益率- C 4.5
信息增益比(information gain ratio),也称为增益率:信息增益的大小是相对训练数据集而言的,并没有绝对意义。在分类问题困难时,也就是说在训练数据即的经验熵大的时候,信息增益值会偏大,反之信息增益值会偏小。
称为属性a的“固有值” ,属性a的可能取值数目越多(即V越大),则IV(a)的值通常就越大 。
3.4 基尼指数-CART
分类问题中,假设D有K个类,样本点属于第k类的概率为𝑝𝑘,则p_k,则概率分布的基尼值定义为:
Gini(D)越小,数据集D的纯度越高;
随机森林使用“基尼指数”来选择划分属性。基尼指数越小,则数据集的纯度越高。
(随机森林是属于集成学习,其核心思想就是集成多个弱分类器以达到预期的效果。其以决策树为基本单元,通过集成大量的决策树,就构成了随机森林。)
4.剪枝处理
构建决策树的过程中,由于过分依赖训练数据和特征,决策树很容易出现过拟合的现象,即在训练数据上表现很好,但在未知数据上表现不佳。剪枝算法就是通过去除过于复杂的分支或子树,来减小模型的复杂度,从而提高了模型的泛化能力。
4.1 预剪枝
预剪枝的核心思想是在节点分裂之前,判断分裂是否会带来模型性能的提升,如果不会或者提升不大,就停止节点的分裂,将当前节点标记为叶子节点。
优点:
- 降低过拟合风险: 预剪枝可以防止决策树在训练数据上过度拟合,避免学习到训练数据中的噪声和细节。
- 减小树的规模: 预剪枝可以限制决策树的深度和宽度,生成相对简单的树结构,提高模型的解释性和可用性。
- 减少计算开销: 由于剪枝导致的树的规模减小,预剪枝可以减少计算和存储资源的需求,提高算法的效率。
缺点:
- 能会造成欠拟合,即模型过于简单,无法很好地捕捉数据的复杂关系。
4.2 后剪枝
后剪枝的核心思想是先构建一个完整的决策树,然后根据验证集或交叉验证的结果,决定哪些子树或叶子节点可以被剪枝,从而提高模型的泛化能力。
优点:
- 提高泛化能力: 后剪枝可以通过删除一些不必要的细节和噪声,提高决策树的泛化能力,使其更好地适应新的未见数据。
- 欠拟合风险小:可以从完整树中剪掉不必要的分支或叶子节点,减少了过拟合的风险。
缺点:
- 可能会增加计算开销,因为需要构建完整的决策树,然后再进行剪枝。
5.单变量决策树和多变量决策树
单变量决策树(univariate decision tree)是一种基于单个自变量的决策树模型,也叫做单特征决策树。在单变量决策树中,每个节点都只考虑一个自变量的取值作为判断条件,将数据集分成若干个子集,直到所有子集为同一类别或无法再分割。单变量决策树的优点在于模型解释性强,容易理解,但缺点是可能会忽略多个自变量之间的关系,导致模型过于简单且欠拟合。
多变量决策树(multivariate decision tree)是一种基于多个自变量的决策树模型,也叫做多特征决策树。在多变量决策树中,每个节点都考虑多个自变量的取值作为判断条件,将数据集分成若干个子集。与单变量决策树相比,多变量决策树可以更准确地捕捉自变量之间的关系,从而提高模型的预测精度。但其缺点是模型复杂度高,容易过拟合,同时解释性较弱。
二、决策树的代码实现
1.导入所需的库和模块,这里我使用的鸢尾花作为例子
import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
2. 定义一个节点类,表示决策树的节点
class Node:
def __init__(self, feature=None, threshold=None, left=None, right=None, value=None):
self.feature = feature # 用于分割的特征索引
self.threshold = threshold # 分割的阈值
self.left = left # 左子树
self.right = right # 右子树
self.value = value # 叶节点的类别值
3.定义一个决策树类和计算基尼不纯度
# 定义一个决策树类
class DecisionTree:
def __init__(self, max_depth=None):
self.max_depth = max_depth
# 计算基尼不纯度
def gini_impurity(self, y):
m = len(y)
if m == 0:
return 0
p = [np.sum(y == c) / m for c in np.unique(y)]
return 1 - np.sum(np.array(p) ** 2) # 使用numpy数组进行平方操作
4.计算信息增益并且递归构建决策树
# 计算信息增益
def information_gain(self, left_y, right_y, current_gini):
p = len(left_y) / (len(left_y) + len(right_y))
return current_gini - p * self.gini_impurity(left_y) - (1 - p) * self.gini_impurity(right_y)
# 递归构建决策树
def build_tree(self, X, y, depth=0):
m, n = X.shape
current_gini = self.gini_impurity(y)
if depth >= self.max_depth or current_gini == 0:
# 如果达到最大深度或基尼不纯度为0,返回叶节点
return Node(value=np.bincount(y).argmax())
best_feature = None
best_threshold = None
best_left_indices = None
best_right_indices = None
max_info_gain = 0
for feature in range(n):
# 尝试不同的特征和阈值来分割数据
thresholds = np.unique(X[:, feature])
for threshold in thresholds:
left_indices = X[:, feature] <= threshold
right_indices = X[:, feature] > threshold
if len(left_indices) == 0 or len(right_indices) == 0:
continue
info_gain = self.information_gain(y[left_indices], y[right_indices], current_gini)
if info_gain > max_info_gain:
best_feature = feature
best_threshold = threshold
best_left_indices = left_indices
best_right_indices = right_indices
max_info_gain = info_gain
if max_info_gain > 0:
# 如果信息增益大于0,继续递归构建树
left_subtree = self.build_tree(X[best_left_indices], y[best_left_indices], depth + 1)
right_subtree = self.build_tree(X[best_right_indices], y[best_right_indices], depth + 1)
return Node(feature=best_feature, threshold=best_threshold, left=left_subtree, right=right_subtree)
else:
# 如果信息增益小于等于0,返回叶节点
return Node(value=np.bincount(y).argmax())
首先,它检查当前节点的深度是否达到最大深度或者样本在该节点上的基尼不纯度是否为0。如果是,则返回一个叶节点,其类别为该节点上样本数最多的类别。否则,它会遍历每个特征和阈值,并计算将数据集分成左右两个子集后的信息增益。如果信息增益大于0,则选择当前信息增益最大的特征和阈值,递归地构建左右子树并返回当前节点。否则,也返回一个叶节点,类别与上文相同。
需要注意的是,这里使用了Node
类来表示决策树的节点,其中feature
表示该节点划分所使用的特征,threshold
表示划分所使用的阈值,left
和right
表示左右子树。当feature
和threshold
为空时,表明该节点为叶节点,并且value
表示该节点上样本数最多的类别。
5.训练决策树,进行预测
# 训练决策树
def fit(self, X, y):
self.root = self.build_tree(X, y)
# 预测
def predict(self, X):
return [self.predict_one(x, self.root) for x in X]
def predict_one(self, x, node):
if node.value is not None:
return node.value
if x[node.feature] <= node.threshold:
return self.predict_one(x, node.left)
else:
return self.predict_one(x, node.right)
6. 训练
# 加载数据集
data = load_iris()
X = data.data
y = data.target
# 划分数据集为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 创建并训练决策树模型
tree = DecisionTree(max_depth=3)
tree.fit(X_train, y_train)
# 预测训练集
y_train_pred = tree.predict(X_train)
# 计算训练集准确率
train_accuracy = np.mean(y_train_pred == y_train)
print(f'决策树模型在训练集的准确率: {train_accuracy}')
# 预测测试集
y_pred = tree.predict(X_test)
# 计算准确率
accuracy = np.mean(y_pred == y_test)
print(f'决策树模型的准确率: {accuracy}')
运行结果:
小结
决策树是一种简单而强大的机器学习算法,可以用于分类和回归任务。通过构建决策树模型,我们可以从数据集中学习到特征之间的关系,并用于预测新样本的类别。然而,决策树也存在一些限制,如容易过拟合和对数据中噪声和异常值敏感等。因此,在实验中需要谨慎选择数据和参数,以获得最佳性能和解释性,或者使用集成学习方法来提高模型的泛化能力。