"线性模型提供稳固的基准,但许多关系并非线性。基于树的模型提供了一种不同方式,通过将特征空间划分为矩形区域来进行预测。它们是许多先进机器学习技术的重要组成部分。本节讲解两种主要的基于树的算法:决策树及其强大的集成扩展——随机森林。"决策树想象一下通过基于特征提出一系列是/否问题来进行预测。这就是决策树背后的想法。它学习一种基于特征的层级划分,最终得出预测结果(分类中的类别标签或回归中的连续值)。工作原理:划分: 算法从根节点处的整个数据集开始。它寻找最佳特征和阈值将数据划分为两个子节点。“最佳”指该划分能最大化节点的纯度(例如,有效分离类别)或最小化预测误差。常用准则有:基尼不纯度 (分类): 衡量集合中任何元素根据子集标签分布随机标记时被错误标记的频率。基尼不纯度为0表示节点中所有元素属于同一类别。熵 (分类): 衡量节点中的无序度或不确定性。熵值越低表示纯度越高。分裂实现的熵减少,即信息增益,常被使用。方差减少 / 均方误差 (回归): 衡量节点内目标变量的同质性。分裂旨在与父节点相比,最小化子节点中的方差(或MSE)。递归: 此划分过程递归应用于每个子节点。停止: 当节点纯净(所有样本属于一个类别或值非常接近)、达到预设的最大深度(max_depth)、包含的样本少于最小阈值(min_samples_split或min_samples_leaf),或者无法通过分裂进一步提高纯度/减少误差时,递归停止。预测: 对新数据点进行预测时,它从根节点向下遍历树,根据其特征值遵循划分,直到到达叶节点。预测结果是该叶节点中训练样本的多数类别(分类)或平均目标值(回归)。使用scikit-learn实现:让我们在一个简单数据集上训练一个DecisionTreeClassifier。import pandas as pd from sklearn.tree import DecisionTreeClassifier, plot_tree from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score import matplotlib.pyplot as plt import graphviz # 可选,用于可视化 # 样本数据(请替换为您的实际数据) data = { 'Feature1': [2.5, 0.5, 2.2, 1.9, 3.1, 2.3, 2.0, 1.0, 1.5, 1.1], 'Feature2': [1.7, 0.8, 1.5, 1.0, 2.5, 0.9, 2.0, 1.1, 0.5, 1.3], 'Target': [1, 0, 1, 0, 1, 0, 1, 0, 0, 0] } df = pd.DataFrame(data) X = df[['Feature1', 'Feature2']] y = df['Target'] # 划分数据 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42) # 初始化并训练决策树分类器 # 限制深度以防止过拟合并便于可视化 dt_clf = DecisionTreeClassifier(criterion='gini', max_depth=3, random_state=42) dt_clf.fit(X_train, y_train) # 进行预测 y_pred = dt_clf.predict(X_test) # 评估 accuracy = accuracy_score(y_test, y_pred) print(f"决策树准确率: {accuracy:.4f}") # 可视化树(需要matplotlib,可选graphviz) plt.figure(figsize=(12, 8)) plot_tree(dt_clf, filled=True, feature_names=X.columns.tolist(), class_names=['类别 0', '类别 1'], rounded=True) plt.title("简单决策树结构(最大深度=3)") plt.show() # 使用graphviz的替代可视化(如果已安装) # dot_data = export_graphviz(dt_clf, out_file=None, # feature_names=X.columns.tolist(), # class_names=['类别 0', '类别 1'], # filled=True, rounded=True, # special_characters=True) # graph = graphviz.Source(dot_data) # graph.render("decision_tree") # 将树保存到decision_tree.pdf # print("决策树已保存到decision_tree.pdf")对于回归任务,您将使用DecisionTreeRegressor并通过均方误差(MSE)等回归指标进行评估。优点与缺点:优点:可解释性: 简单的决策树易于理解和可视化。您可以追踪决策路径。非线性: 自然地捕捉特征与目标之间的非线性关系。无需特征缩放: 划分过程基于阈值而非距离,使得缩放通常没有必要。处理数值和分类特征(尽管scikit-learn要求分类特征首先进行数值编码)。缺点:过拟合: 决策树很容易变得过于复杂并记住训练数据,导致在新数据上的泛化能力差。设置诸如max_depth或min_samples_leaf之类的约束会有帮助,但找到合适的平衡点可能比较困难。不稳定性: 训练数据中的微小变化可能导致树结构显著不同。偏向具有更多水平的特征: 不纯度测量有时可能偏好在具有许多不同值的特征上进行划分。过拟合的倾向是最大的缺点,这引导我们了解像随机森林这样的集成方法。随机森林与其依赖单个可能不稳定且过拟合的树,为什么不构建许多不同的树并结合它们的预测呢?这是随机森林背后的主要思想。它是一种集成方法,利用自助法(bagging)和特征随机性来生成一组决策树。工作原理:自助采样 (Bagging): 通过有放回抽样创建原始训练数据的多个子集。每个子集与原始数据集大小相同,但包含重复实例并省略其他实例。在每个自助样本上训练一个独立的决策树。特征随机性: 构建每棵树时,在每个划分点,只考虑可用特征的一个随机子集来寻找最佳划分(max_features参数)。这使得树之间去相关。如果某个特征预测能力很强,它也不会在所有树的划分中占据主导地位。聚合:分类: 对于新数据点,森林中的每棵树预测一个类别。最终预测是获得最多票数的类别(多数投票)。回归: 每棵树预测一个值。最终预测是所有树的预测值的平均。自助法和特征随机性的这种组合使得树之间各不相同。平均它们的预测可以减少方差,并提高模型相对于单个决策树的泛化能力。使用scikit-learn实现:训练RandomForestClassifier或RandomForestRegressor非常直接。from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor import numpy as np # 针对分类任务,使用之前相同的数据划分 rf_clf = RandomForestClassifier(n_estimators=100, # 森林中树的数量 max_depth=5, # 单个树的最大深度 max_features='sqrt', # 每个划分点要考虑的特征数量 random_state=42, n_jobs=-1) # 使用所有可用CPU核心 rf_clf.fit(X_train, y_train) # 进行预测 y_pred_rf = rf_clf.predict(X_test) # 评估 accuracy_rf = accuracy_score(y_test, y_pred_rf) print(f"随机森林准确率: {accuracy_rf:.4f}") # 特征重要性(有助于理解特征贡献) importances = rf_clf.feature_importances_ feature_names = X.columns indices = np.argsort(importances)[::-1] print("\n特征重要性:") for i in indices: print(f"{feature_names[i]}: {importances[i]:.4f}") # 回归示例(需要不同的目标'y') # Generate some sample regression data # np.random.seed(42) # X_reg = np.random.rand(100, 2) * 10 # y_reg = 2 * X_reg[:, 0] - 3 * X_reg[:, 1] + np.random.randn(100) * 2 + 5 # X_train_reg, X_test_reg, y_train_reg, y_test_reg = train_test_split(X_reg, y_reg, test_size=0.3, random_state=42) # rf_reg = RandomForestRegressor(n_estimators=100, random_state=42, n_jobs=-1) # rf_reg.fit(X_train_reg, y_train_reg) # y_pred_reg = rf_reg.predict(X_test_reg) # mse = mean_squared_error(y_test_reg, y_pred_reg) # print(f"\n随机森林回归器MSE: {mse:.4f}")我们可以可视化特征重要性,它通常来源于每个特征在森林中所有树中对减少不纯度的贡献大小。{"layout": {"title": "随机森林的特征重要性", "xaxis": {"title": "重要性"}, "yaxis": {"title": "特征", "categoryorder": "total ascending"}, "height": 300, "margin": {"l": 100, "r": 20, "t": 40, "b": 40}}, "data": [{"type": "bar", "y": ["Feature1", "Feature2"], "x": [0.6, 0.4], "orientation": "h", "marker": {"color": ["#4dabf7", "#74c0fc"]}}]}这些特征重要性源自随机森林分类器示例。数值越高表示对模型预测的贡献越大。注意:具体数值取决于训练数据和模型参数。优点与缺点:优点:高准确率: 通常在各种任务上表现出色,常能达到领先水平。抗过拟合能力强: 集成性质大幅降低了相对于单个决策树的过拟合风险。处理高维度数据: 即使特征数量很多也能良好运作。特征重要性: 提供一个有用的特征相关性度量。隐式特征交互: 可以捕捉特征之间复杂的交互。缺点:可解释性较低: 模型变成了一个“黑盒”。很难追溯特定预测是通过数百棵树形成的。计算密集型: 训练多棵树可能计算成本高昂并需要更多内存,尤其是在大型数据集或树数量很多(n_estimators)的情况下。参数调整: 仍需要调整诸如n_estimators、max_depth和max_features等超参数以获得最佳性能。基于树的模型,特别是随机森林,是监督学习中强大且广泛使用的工具。虽然单个决策树提供可解释性,但它们过拟合的倾向常使随机森林成为获得更高预测准确度的更实用选择。了解它们的工作原理以及如何使用scikit-learn等库实现它们是应用数据科学的一项基本技能。接下来的章节将介绍梯度提升等其他强大的集成方法,并探讨系统地调整模型超参数的方式。