Python实现机器学习算法:决策树原理与代码实现 在机器学习中,决策树是一种非常重要的算法。它可以用于分类和回归问题,常用于数据挖掘和统计分析。本文将介绍决策树的原理和代码实现。 一、决策树原理 决策树是一种树形结构,其中每个节点代表一个特征,每个分支代表一个特征的取值,每个叶子节点代表一个类别。其基本原理是将训练集中的数据依据特征进行分割,使得同一类别的数据尽可能聚集在一起,不同类别的数据尽可能分开。我们通过计算每个特征的信息熵的变化,来选择最优特征进行分割。 决策树的构建遵循以下过程: 1. 根据训练集选择最优特征进行分割 2. 将不同分支的样本分别划分到不同节点 3. 对每个节点重复步骤 1 和 2,直到所有分支都是同一类别或无法继续分割为止 例如,我们有以下训练集: | 编号 | 年龄 | 收入 | 是否有房 | 是否结婚 | 类别 | | --- | --- | --- | --- | --- | --- | | 1 | 青年 | 高 | 否 | 否 | 否 | | 2 | 青年 | 高 | 否 | 否 | 否 | | 3 | 中年 | 高 | 否 | 是 | 否 | | 4 | 老年 | 中 | 否 | 是 | 是 | | 5 | 老年 | 低 | 是 | 是 | 是 | | 6 | 中年 | 低 | 是 | 是 | 是 | | 7 | 青年 | 中 | 否 | 否 | 否 | | 8 | 青年 | 低 | 是 | 是 | 是 | | 9 | 老年 | 低 | 是 | 是 | 是 | | 10 | 青年 | 中 | 是 | 否 | 是 | 我们希望通过这个训练集来构建一个决策树。首先,我们需要选择一个最优特征进行分割。我们可以通过计算信息增益来选择最优特征。信息增益表示的是分割前后样本熵的变化。 计算公式如下: 信息增益 = 父节点熵 - 权重和 * 子节点熵 其中,父节点熵表示整个训练集的熵,可以通过以下公式计算: 熵 = -p1 * log2 p1 - p2 * log2 p2 - ... - pn * log2 pn 其中,p1~pn 表示不同类别的样本占比。 子节点熵表示将样本按照某个特征值分割后,每个子节点中的样本熵加权平均值。计算公式如下: 子节点熵 = 权重1 * 熵1 + 权重2 * 熵2 + ... + 权重n * 熵n 其中,权重表示每个子节点所占的比例,熵表示子节点中不同类别的样本熵加权平均值。 对于我们的训练集,我们可以通过计算信息增益来选择最优特征进行分割。首先,我们计算整个训练集的熵。根据上面的公式,我们可以计算出: 熵 = -6/10 * log2 6/10 - 4/10 * log2 4/10 = 0.971 接下来,我们依次计算每个特征的信息增益。例如,我们可以计算年龄特征的信息增益。首先,我们将年龄分为青年、中年和老年三个取值。对于青年这个取值,我们可以将数据分为以下两组: | 编号 | 年龄 | 收入 | 是否有房 | 是否结婚 | 类别 | | --- | --- | --- | --- | --- | --- | | 1 | 青年 | 高 | 否 | 否 | 否 | | 2 | 青年 | 高 | 否 | 否 | 否 | | 7 | 青年 | 中 | 否 | 否 | 否 | | 8 | 青年 | 低 | 是 | 是 | 是 | | 10 | 青年 | 中 | 是 | 否 | 是 | 我们可以计算出该分支的熵为: 熵 = -2/5 * log2 2/5 - 3/5 * log2 3/5 = 0.971 对于中年和老年这两个取值,我们可以类似地计算出它们的熵。然后,我们可以计算出年龄这个特征的信息增益: 信息增益 = 0.971 - 5/10 * 0.971 - 4/10 * 0.811 = 0.124 接下来,我们计算其他特征的信息增益,选择最大的一个作为最优特征进行分割。最终,我们得到以下决策树: ![decision tree](https://i.imgur.com/VOsH9wW.png) 二、决策树代码实现 决策树的代码实现包括以下几个步骤: 1. 定义节点类 2. 定义树类 3. 定义递归函数构建决策树 4. 定义预测函数 以下是 Python 实现决策树的代码: ```python import math from collections import Counter class Node: def __init__(self, feature, split, left, right, label): self.feature = feature self.split = split self.left = left self.right = right self.label = label class Tree: def __init__(self): self.root = None def build(self, X, y): self.root = self._build(X, y) def _build(self, X, y): if len(set(y)) == 1: return Node(None, None, None, None, y[0]) n_samples, n_features = X.shape best_feature = None best_split = None current_score = self.score(y) for i in range(n_features): splits = set(X[:, i]) for split in splits: y_left = y[X[:, i] < split] y_right = y[X[:, i] >= split] if len(y_left) == 0 or len(y_right) == 0: continue score = current_score - len(y_left) / n_samples * self.score(y_left) - len(y_right) / n_samples * self.score(y_right) if score > current_score: current_score = score best_feature = i best_split = split if best_feature is None: return Node(None, None, None, None, Counter(y).most_common(1)[0][0]) X_left = X[X[:, best_feature] < best_split] y_left = y[X[:, best_feature] < best_split] left = self._build(X_left, y_left) X_right = X[X[:, best_feature] >= best_split] y_right = y[X[:, best_feature] >= best_split] right = self._build(X_right, y_right) return Node(best_feature, best_split, left, right, None) def score(self, y): n_samples = len(y) if n_samples == 0: return 0 counts = Counter(y) p = [count / n_samples for count in counts.values()] return -sum([pi * math.log(pi, 2) for pi in p]) def predict(self, X): return [self.predict_one(x, self.root) for x in X] def predict_one(self, x, node): if node.label is not None: return node.label if x[node.feature] < node.split: return self.predict_one(x, node.left) else: return self.predict_one(x, node.right) ``` 以上代码定义了一个节点类和一个树类,其中节点类保存了该节点代表的特征、分割点、左子树、右子树和分类标签,树类保存了根节点。`build` 方法递归地构建决策树,`score` 方法计算熵,`predict` 方法预测多个样本的分类,`predict_one` 方法预测单个样本的分类。 我们可以使用以下代码进行测试: ```python from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score data = load_iris() X_train, X_test, y_train, y_test = train_test_split(data.data, data.target, test_size=0.2, random_state=42) tree = Tree() tree.build(X_train, y_train) y_pred = tree.predict(X_test) accuracy = accuracy_score(y_test, y_pred) print('Accuracy:', accuracy) ``` 以上代码从 sklearn 中加载 Iris 数据集,将其拆分为训练集和测试集,构建决策树,并输出准确率。