决策树 CART 算法实战:Python 手写分类器,5步实现鸢尾花数据集预测
决策树 CART 算法实战Python 手写分类器5步实现鸢尾花数据集预测决策树算法作为机器学习领域的经典方法其直观性和可解释性一直备受开发者青睐。而在众多决策树算法中CARTClassification and Regression Trees因其出色的泛化能力和灵活性成为工业界应用最广泛的算法之一。本文将带您从零开始实现CART算法并通过经典的鸢尾花数据集验证其分类效果。1. CART算法核心原理剖析CART算法的独特之处在于它采用二分递归分割技术每次都将当前样本集划分为两个子集使得生成的每个非叶节点都有两个分支。这种设计让CART树始终保持二叉树结构大大提升了模型的简洁性和运算效率。1.1 基尼指数CART的核心指标与ID3、C4.5算法使用信息增益不同CART使用基尼指数作为特征选择标准。基尼指数反映了从数据集中随机抽取两个样本其类别标记不一致的概率。基尼指数越小数据集的纯度越高。基尼指数的计算公式为def gini_index(y): classes np.unique(y) gini 1.0 for cls in classes: p len(y[y cls]) / len(y) gini - p**2 return gini注意当所有样本属于同一类别时基尼指数为0达到最小值当样本均匀分布 across 所有类别时基尼指数最大。1.2 特征选择与节点分裂对于每个候选特征和对应的分割点CART会计算划分后的加权基尼指数def weighted_gini(left_y, right_y): n len(left_y) len(right_y) return (len(left_y)/n)*gini_index(left_y) (len(right_y)/n)*gini_index(right_y)算法会遍历所有可能的特征和分割点选择使加权基尼指数最小的特征和分割点作为最优分裂条件。2. 构建CART决策树类下面我们实现一个完整的CART决策树分类器类包含训练和预测的核心方法。2.1 树节点结构设计首先定义决策树的节点类每个节点需要存储以下信息class TreeNode: def __init__(self, feature_idxNone, thresholdNone, leftNone, rightNone, valueNone): self.feature_idx feature_idx # 分裂特征索引 self.threshold threshold # 分裂阈值 self.left left # 左子树 self.right right # 右子树 self.value value # 叶节点的预测值2.2 核心训练逻辑实现CART树的构建采用递归方式主要包含以下步骤计算当前节点的基尼指数寻找最佳分裂特征和分割点递归构建左右子树class CARTClassifier: def __init__(self, max_depthNone, min_samples_split2): self.max_depth max_depth self.min_samples_split min_samples_split self.tree None def _best_split(self, X, y): best_gini float(inf) best_feature, best_thresh None, None for feature_idx in range(X.shape[1]): thresholds np.unique(X[:, feature_idx]) for threshold in thresholds: left_indices X[:, feature_idx] threshold left_y y[left_indices] right_y y[~left_indices] if len(left_y) 0 or len(right_y) 0: continue current_gini weighted_gini(left_y, right_y) if current_gini best_gini: best_gini current_gini best_feature feature_idx best_thresh threshold return best_feature, best_thresh def _build_tree(self, X, y, depth0): # 终止条件判断 if (len(np.unique(y)) 1 or len(y) self.min_samples_split or (self.max_depth is not None and depth self.max_depth)): return TreeNode(valueself._most_common_label(y)) # 寻找最佳分裂 feature_idx, threshold self._best_split(X, y) if feature_idx is None: return TreeNode(valueself._most_common_label(y)) # 递归构建子树 left_indices X[:, feature_idx] threshold left self._build_tree(X[left_indices], y[left_indices], depth1) right self._build_tree(X[~left_indices], y[~left_indices], depth1) return TreeNode(feature_idx, threshold, left, right) def fit(self, X, y): self.tree self._build_tree(np.array(X), np.array(y)) return self2.3 预测方法实现预测时从根节点开始根据特征值决定路径直到到达叶节点def predict(self, X): return np.array([self._predict_single(x, self.tree) for x in X]) def _predict_single(self, x, node): if node.value is not None: return node.value if x[node.feature_idx] node.threshold: return self._predict_single(x, node.left) else: return self._predict_single(x, node.right)3. 鸢尾花数据集实战现在我们将实现的CART分类器应用于经典的鸢尾花数据集验证其分类效果。3.1 数据准备与探索首先加载并分析数据集from sklearn.datasets import load_iris import pandas as pd iris load_iris() X iris.data y iris.target feature_names iris.feature_names target_names iris.target_names df pd.DataFrame(X, columnsfeature_names) df[species] y print(df.head())输出示例sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) species 0 5.1 3.5 1.4 0.2 0 1 4.9 3.0 1.4 0.2 0 2 4.7 3.2 1.3 0.2 0 3 4.6 3.1 1.5 0.2 0 4 5.0 3.6 1.4 0.2 03.2 数据分割与模型训练将数据集分为训练集和测试集并训练我们的CART模型from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42) model CARTClassifier(max_depth3) model.fit(X_train, y_train)3.3 模型评估与可视化评估模型在测试集上的表现from sklearn.metrics import accuracy_score y_pred model.predict(X_test) accuracy accuracy_score(y_test, y_pred) print(fTest Accuracy: {accuracy:.2f})为了更好理解模型的决策过程我们可以可视化决策树。虽然我们实现了自己的CART分类器但为了可视化可以借助scikit-learn的plot_treefrom sklearn.tree import DecisionTreeClassifier, plot_tree import matplotlib.pyplot as plt # 使用sklearn的决策树与我们实现的CART参数一致 dt DecisionTreeClassifier(max_depth3, criteriongini) dt.fit(X_train, y_train) plt.figure(figsize(12,8)) plot_tree(dt, feature_namesfeature_names, class_namestarget_names, filledTrue) plt.show()4. 关键参数调优指南CART算法的性能很大程度上依赖于参数设置以下是关键参数及其影响参数默认值作用设置建议max_depthNone树的最大深度通常3-10通过交叉验证选择min_samples_split2节点分裂所需最小样本数防止过拟合可设为数据集大小的1-5%min_samples_leaf1叶节点所需最小样本数确保叶节点有足够统计支持max_featuresNone考虑的特征数量可设为sqrt(n_features)或log2(n_features)交叉验证寻找最优参数组合的示例from sklearn.model_selection import GridSearchCV param_grid { max_depth: [3, 5, 7, None], min_samples_split: [2, 5, 10], min_samples_leaf: [1, 2, 4] } grid_search GridSearchCV(DecisionTreeClassifier(), param_grid, cv5) grid_search.fit(X_train, y_train) print(Best parameters:, grid_search.best_params_)5. CART算法的优势与局限5.1 独特优势处理混合类型数据能同时处理数值型和类别型特征无需特征缩放对特征的量纲不敏感自动特征选择通过基尼指数自动选择重要特征可解释性强决策规则清晰可见5.2 潜在局限容易过拟合需要通过剪枝和参数调优控制不稳定数据微小变化可能导致完全不同的树偏向于多值特征具有更多取值的特征可能被优先选择5.3 实际应用建议对于高维稀疏数据建议先进行特征选择在金融风控等需要解释性的场景特别适用可以与其他模型集成如随机森林提升性能