机器学习课程笔记(Machine Learning)

随机森林(Random Forest)

Posted by 月月鸟 on December 22, 2021

本文介绍随机森林(Random Forest)算法在机器学习中的基本原理、特点、注意事项,以及如何从零实现随机森林模型的完整步骤。

1. 随机森林算法的基本原理

随机森林是一种集成学习算法,通过构建多个决策树并结合其预测结果来提高模型的准确性和稳健性。它适用于分类和回归任务。

1.1 集成学习

集成学习通过组合多个基模型的预测来提高整体模型性能。随机森林是一种基于装袋(Bagging)思想的集成方法。

1.2 随机森林的构建

随机森林通过以下步骤构建:

  • 样本选择:从原始数据集中有放回地随机抽取样本生成多个子集(即自举样本)。
  • 特征选择:在构建每个决策树时,对每个节点只随机选择部分特征进行划分。
  • 树的生长:在各个子集上分别训练决策树,直到达到最大深度或叶节点样本数小于预设阈值。

1.3 投票与平均

  • 分类任务:随机森林通过多数投票选择最终类别。
  • 回归任务:随机森林通过计算平均值输出最终预测结果。

1.4 特点和注意事项

  • 鲁棒性:能够有效降低过拟合风险。
  • 处理高维数据:适用于包含大量特征的数据集。
  • 易于并行化:各个决策树可以并行训练。

2. 实现步骤

我们使用 iris 数据集进行随机森林的实现。

2.1 数据准备

加载数据集,分割训练集和测试集。

import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report

# 加载iris数据集
data = load_iris()
X = data.data  # 特征数据
y = data.target  # 目标标签

# 将数据集分为训练集和测试集,80%用于训练,20%用于测试
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

2.2 模型训练

实现随机森林算法。

from collections import Counter

class DecisionTree:
    def __init__(self, max_depth=None):
        self.max_depth = max_depth
        self.tree = None

    def fit(self, X, y):
        self.tree = self._grow_tree(X, y)

    def _grow_tree(self, X, y, depth=0):
        num_samples_per_class = [np.sum(y == i) for i in np.unique(y)]
        predicted_class = np.argmax(num_samples_per_class)
        node = {
            'predicted_class': predicted_class
        }

        if depth < self.max_depth:
            idx, threshold = self._best_split(X, y)
            if idx is not None:
                indices_left = X[:, idx] < threshold
                X_left, y_left = X[indices_left], y[indices_left]
                X_right, y_right = X[~indices_left], y[~indices_left]
                node['feature_index'] = idx
                node['threshold'] = threshold
                node['left'] = self._grow_tree(X_left, y_left, depth + 1)
                node['right'] = self._grow_tree(X_right, y_right, depth + 1)
        return node

    def _best_split(self, X, y):
        m, n = X.shape
        if m <= 1:
            return None, None

        num_parent = [np.sum(y == c) for c in np.unique(y)]
        best_gini = 1.0 - sum((n / m) ** 2 for n in num_parent)
        best_idx, best_thr = None, None

        for idx in range(n):
            thresholds, classes = zip(*sorted(zip(X[:, idx], y)))
            num_left = [0] * len(num_parent)
            num_right = num_parent.copy()
            for i in range(1, m):
                c = classes[i - 1]
                num_left[c] += 1
                num_right[c] -= 1
                gini_left = 1.0 - sum(
                    (num_left[x] / i) ** 2 for x in range(len(num_parent))
                )
                gini_right = 1.0 - sum(
                    (num_right[x] / (m - i)) ** 2 for x in range(len(num_parent))
                )
                gini = (i * gini_left + (m - i) * gini_right) / m

                if thresholds[i] == thresholds[i - 1]:
                    continue

                if gini < best_gini:
                    best_gini = gini
                    best_idx = idx
                    best_thr = (thresholds[i] + thresholds[i - 1]) / 2
        return best_idx, best_thr

    def predict(self, X):
        return [self._predict(inputs) for inputs in X]

    def _predict(self, inputs):
        node = self.tree
        while 'threshold' in node:
            if inputs[node['feature_index']] < node['threshold']:
                node = node['left']
            else:
                node = node['right']
        return node['predicted_class']


class RandomForest:
    def __init__(self, n_trees=10, max_depth=None, max_features='sqrt'):
        self.n_trees = n_trees
        self.max_depth = max_depth
        self.max_features = max_features
        self.trees = []

    def fit(self, X, y):
        self.trees = []
        for _ in range(self.n_trees):
            tree = DecisionTree(max_depth=self.max_depth)
            X_sample, y_sample = self._bootstrap_sample(X, y)
            tree.fit(X_sample, y_sample)
            self.trees.append(tree)

    def _bootstrap_sample(self, X, y):
        n_samples = X.shape[0]
        indices = np.random.choice(n_samples, size=n_samples, replace=True)
        return X[indices], y[indices]

    def predict(self, X):
        tree_preds = np.array([tree.predict(X) for tree in self.trees])
        return [Counter(tree_preds[:, i]).most_common(1)[0][0] for i in range(X.shape[0])]

2.3 模型预测

对测试集进行预测。

# 创建随机森林模型
rf = RandomForest(n_trees=10, max_depth=3)

# 训练模型
rf.fit(X_train, y_train)

# 预测测试集
y_pred = rf.predict(X_test)

2.4 模型评估

使用测试数据集评估模型性能。

accuracy = accuracy_score(y_test, y_pred)
conf_matrix = confusion_matrix(y_test, y_pred)
class_report = classification_report(y_test, y_pred)

print("\nAccuracy:", accuracy)
print("Confusion Matrix:\n", conf_matrix)
print("Classification Report:\n", class_report)

3. 优缺点

3.1 优点

  1. 高准确性:通过集成多个决策树提高模型性能。
  2. 鲁棒性:对异常值和噪声具有较强的鲁棒性。
  3. 处理高维数据:适用于特征维度较高的数据集。

3.2 缺点

  1. 计算复杂度:随着树的数量增加,训练时间和预测时间可能较长。
  2. 可解释性差:模型的复杂性使得难以直观解释。

随机森林通过集成多棵决策树,提升了模型的准确性和稳健性,广泛应用于各类分类和回归任务。在模型训练和使用过程中,应注意选择适当的参数以获得最佳效果。