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
接下来,我们计算其他特征的信息增益,选择最大的一个作为最优特征进行分割。最终,我们得到以下决策树:

二、决策树代码实现
决策树的代码实现包括以下几个步骤:
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 数据集,将其拆分为训练集和测试集,构建决策树,并输出准确率。