前言
决策树(Decision Tree)是一种类似于流程图的树形结构,每个内部节点表示在一个属性上的测试,每个分支代表一个属性输出,而每个叶节点代表类或类分布。决策树通过树状结构,基于数据特征与目标变量之间的关系,将数据集划分为不同的子集,以逐步构建决策规则。其工作原理是从根节点开始,根据输入特征的取值,沿着相应的分支向下移动,直到到达一个叶节点,叶节点的值即为最终分类或预测结果,递归地选择最优特征进行分裂,构建出完整的决策树。
一、决策树原理及构建
原理
决策树的概念来源于统计学和决策论,能够直观地模拟人类的决策过程。它通过递归地将数据集划分为多个子集,形成树状结构,以表示数据的分布和分类规则。每个内部节点代表一个特征的决策,每个叶子节点则是决策结果(分类或回归值)。决策树的构建主要基于“信息增益”(Information Gain)或“基尼不纯度”(Gini Impurity)等指标。其核心思想是逐步选择最佳的特征来划分数据集,从而构建出能够最好地分类或回归的树。
信息增益:表示某个特征对分类结果的不确定性的减少量。信息增益越大,表示该特征越能有效减少不确定性,从而更好地划分数据。
基尼不纯度:用于衡量一个数据集在某特征下的不纯度。基尼不纯度越低,数据集越“纯”。
构建步骤
决策树的构建过程是一个递归选择最优特征,并根据特征的不同取值对数据进行划分的过程,直到满足停止条件为止。以下是构建步骤:
①准备数据:将数据集划分成训练集和测试集,进行缺失值处理和特征选择。
②选择最优特征:对于数据集中的每个特征,计算其对数据集的信息增益或基尼不纯度,选择信息增益最大或基尼不纯度最小的特征作为本次分类的特征。
③划分数据集:根据选择的特征,将数据集划分成若干子集,每个子集包含特征一致的一部分数据。
④递归构建决策树:对于每个子集,重复上述选择最优特征和划分数据集的过程,直到子集中的数据属于同一类别或者到达一定的停止条件为止。常见的停止条件包括:所有样本属于同一类别、特征集为空、或者样本数量小于预设的阈值。
⑤测试模型:将测试集中的数据输入到构建好的模型中,计算模型的分类准确度和召回率等评估指标。
⑥优化模型:对于分类错误的样本,可以通过调整参数(如决策树的最大深度、最小样本数等)和增加样本来优化模型。此外,为了防止决策树过拟合,还可以进行剪枝操作,即去除不必要的分支,保留重要的决策节点,从而提高模型的泛化能力。
二、决策树的典型算法
ID3算法
概念:ID3(Iterative Dichotomiser 3)算法是决策树学习算法的一种经典方法,它根据信息增益来选择每个节点的分裂属性。
- 步骤:选择最佳特征:构建根节点,将所有训练数据都放在根节点,根据信息增益选择最能区分数据的特征作为当前节点的分割标准。
- 数据分割:使用选定的最佳分裂属性将当前节点的数据集划分成多个子集,每个子集对应于分裂属性的不同取值。
- 递归构建树:对于每个子集,递归地应用上述步骤,直到达到停止条件。停止条件可以是节点中样本的类别完全相同,或者节点中的样本数小于某个阈值。
- 生成树:重复上述步骤,直到所有叶子节点都是纯的(即只包含同一类别的样本)或者达到停止条件。
- 优点:简单易懂,生成的决策树易于解释。
- 缺点:对缺失值敏感,不能处理连续型属性,容易过拟合。
C4.5算法
- 概念:C4.5是ID3的改进版本,引入了能够处理连续属性和缺失值的机制,以及剪枝操作,能够更好地应对实际问题。
- 改进:1. 处理连续属性:通过将连续属性离散化,C4.5能够处理连续属性。2. 处理缺失值:C4.5提供了处理缺失值的方法,使得算法在存在缺失值的情况下也能正常工作。3. 剪枝操作:C4.5引入了剪枝操作,通过剪除不必要的分支,提高了决策树的泛化能力。
- 应用:C4.5算法既适合于分类问题,又适合于回归问题。
CART算法
- 概念:CART(Classification and Regression Trees)算法是一种广泛使用的决策树算法,它可以用于分类问题和回归问题。
- 特点:1. 二叉树结构:CART算法生成的决策树是二叉树,即每个节点最多有两个子节点。2. 基尼系数:CART算法使用基尼系数来衡量数据集的不纯度,并选择能够最大程度减少不纯度的特征作为分裂属性。3. 剪枝操作:CART算法也包含了剪枝操作,以提高决策树的泛化能力。
- 应用:CART算法在分类和回归问题中都有广泛的应用。
ID3、C4.5和CART是决策树的典型算法,它们各有特点,适用于不同的应用场景。在实际应用中,可以根据具体问题的需求和数据的特性选择合适的算法。
三.决策树的算法实现示例
ID3算法代码示例
import numpy as np
from collections import Counter
class DecisionTree:
class Node:
def __init__(self):
self.value = None # 内部叶节点属性
self.feature_index = None
self.children = {}
def __str__(self):
if self.children:
s = '内部节点<%s>:\n' % self.feature_index
for fv, node in self.children.items():
ss = '[%s]-> %s' % (fv, node)
s += '\t' + ss.replace('\n', '\n\t') + '\n'
s = s[:-1]
else:
s = '叶节点(%s)' % self.value
return s
def __init__(self, gain_threshold=1e-2): # 信息增益阈值
self.gain_threshold = gain_threshold
def _entropy(self, y): # 熵: -sum(pi*log(pi))
c = np.bincount(y)
p = c[np.nonzero(c)] / y.size
return -np.sum(p * np.log2(p))
def _conditional_entropy(self, feature, y): # 条件熵
feature_values = np.unique(feature)
h = 0.
for v in feature_values:
y_sub = y[feature == v]
p = y_sub.size / y.size
h += p * self._entropy(y_sub)
return h
def _information_gain(self, feature, y): # 信息增益 = 经验熵 - 经验条件熵
return self._entropy(y) - self._conditional_entropy(feature, y)
def _select_feature(self, X, y, features_list): # 选择信息增益最大特征
if features_list:
gains = np.apply_along_axis(self._information_gain, 0, X[:, features_list], y)
index = np.argmax(gains)
if gains[index] > self.gain_threshold:
return index
return None
def _create_tree(self, X, y, features_list): # 创建节点
node = DecisionTree.Node()
labels_count = np.bincount(y)
node.value = np.argmax(labels_count) # 节点值总等于数据集中样本最多的类标记
if np.count_nonzero(labels_count) != 1: # 判断类标记是否全部一致
index = self._select_feature(X, y, features_list)
if index is not None:
node.feature_index = features_list.pop(index)
feature_values = np.unique(X[:, node.feature_index])
for v in feature_values:
idx = X[:, node.feature_index] == v
X_sub, y_sub = X[idx], y[idx]
node.children[v] = self._create_tree(X_sub, y_sub, features_list.copy())
return node
def train(self, X_train, y_train): # 训练决策树
_, n = X_train.shape
self.tree_ = self._create_tree(X_train, y_train, list(range(n)))
def predict(self, X_test): # 对每一个测试样本, 调用_predict_one, 将收集到的结果数组返回
return np.apply_along_axis(self._predict_one, axis=1, arr=X_test)
def _predict_one(self, x_test): # 搜索决策树, 对单个样本进行预测
node = self.tree_
while node.children:
child = node.children.get(x_test[node.feature_index])
if not child:
break
node = child
return node.value
def __str__(self):
if hasattr(self, 'tree_'):
return str(self.tree_)
return ''
# 使用示例数据集进行分类
# 数据集加载和数据预处理部分略...
# 假设X_train和y_train是已经准备好的训练数据集
# 假设X_test是待预测的测试数据集
tree = DecisionTree()
tree.train(X_train, y_train)
predictions = tree.predict(X_test)
print(predictions)
C4.5算法代码示例
import numpy as np
from collections import Counter
def calc_entropy(labels):
counter = Counter(labels)
probs = [counter[c] / len(labels) for c in counter]
entropy = -np.sum(probs * np.log2(probs))
return entropy
def calc_info_gain(data, labels, feature_idx):
feature_values = set(data[:, feature_idx])
entropy_before = calc_entropy(labels)
gain = entropy_before
for value in feature_values:
subset = data[data[:, feature_idx] == value]
subset_labels = labels[data[:, feature_idx] == value]
weight = len(subset_labels) / len(labels)
gain -= weight * calc_entropy(subset_labels)
return gain
def choose_best_feature(data, labels):
num_features = data.shape[1]
best_feature = None
best_gain = 0
for i in range(num_features):
info_gain = calc_info_gain(data, labels, i)
if info_gain > best_gain:
best_gain = info_gain
best_feature = i
return best_feature
def create_decision_tree(data, labels):
if len(set(labels)) == 1:
return labels[0]
if data.shape[1] == 0:
return Counter(labels).most_common(1)[0][0]
best_feature = choose_best_feature(data, labels)
tree = {best_feature: {}}
feature_values = set(data[:, best_feature])
for value in feature_values:
subset = data[data[:, best_feature] == value]
subset_labels = labels[data[:, best_feature] == value]
tree[best_feature][value] = create_decision_tree(subset, subset_labels)
return tree
# 使用示例数据集进行分类
data = np.array([[1, 1, 1], [1, 1, 0], [0, 1, 1], [0, 0, 1]])
labels = np.array([1, 1, 0, 0])
decision_tree = create_decision_tree(data, labels)
print(decision_tree)
CART算法代码示例(回归树)
import numpy as np
import pickle
import matplotlib.pyplot as plt
from sklearn.datasets import load_boston
def loadDataSet(fileName):
dataMat = []
fr = open(fileName)
for line in fr.readlines():
curLine = line.strip().split('\t')
fltLine = list(map(float, curLine))
dataMat.append(fltLine)
return dataMat
def binSplitDataSet(dataSet, feature, value):
mat0 = dataSet[dataSet[:, feature] > value]
mat1 = dataSet[dataSet[:, feature] <= value]
return mat0, mat1
def regLeaf(dataSet):
return np.mean(dataSet[:, -1])
def regErr(dataSet):
return np.var(dataSet[:, -1]) * len(dataSet)
def chooseBestSplit(dataSet, leafType=regLeaf, errType=regErr, ops=(1, 4)):
tolS = ops[0]; tolN = ops[1]
if len(set(dataSet[:, -1].T.tolist()[0])) == 1:
return None, leafType(dataSet)
m, n = dataSet.shape
S = errType(dataSet)
bestS = float('inf'); bestIndex = 0; bestValue = 0
for featIndex in range(n - 1):
for splitVal in set(dataSet[:, featIndex].T.A.tolist()[0]):
mat0, mat1 = binSplitDataSet(dataSet,
总结
大数据决策树是一种基于树形结构的机器学习算法,特别适用于处理和分析大规模数据集。它通过递归地将数据集分割成更小的子集,以构建出一个类似于树状的预测模型。在树的每个节点上,算法会根据某个特征属性的值来判断数据应该流向哪个分支,直到到达叶子节点,给出最终的预测结果或分类标签。
大数据决策树的优势在于其直观易懂、易于实现,并且能够有效地处理高维数据和具有缺失值的数据集。此外,它还能够自动进行特征选择和交互效应的识别,为决策过程提供有价值的洞察。然而,随着数据量的增加,决策树可能会变得过于复杂,导致过拟合问题。因此,在实际应用中,常需要结合剪枝、集成学习等技术来优化模型性能。
总之,大数据决策树是一种强大且灵活的工具,能够帮助我们从海量数据中挖掘出有价值的规律和模式,为决策提供有力支持。
版权归原作者 什么又不懂了? 所有, 如有侵权,请联系我们删除。