1. 项目概述与问题背景1.1 核心需求解析说到分类算法很多刚接触机器学习的朋友都有过这样的困惑同样的一个多分类数据集有人用决策树有人用随机森林还有人直接上多层感知机或者支持向量机结果五花八门各有各的说法。到底哪个算法更靠谱哪个更适合自己的数据我这次做的这个对比项目就是冲着这个问题去的。项目名称叫决策树、随机森林、多层感知机、支持向量机在多分类问题中的对比核心任务很明确在同一个多分类数据集上把四类经典算法拉到同一起跑线上经过预处理、训练、调参、评估这一整套流程用统一的评价指标横向比较它们的表现。项目并不刻意追求某个算法刷到最高分而是想搞清楚每类算法在不同条件下的行为差异以及这种差异背后的原因。这个对比适合谁来参考我觉得分三类人。第一类是刚入门机器学习的同学可以通过这个项目把四类算法的原理和适用场景串起来比单纯看教材直观得多。第二类是做实际业务建模的工程师比如要做用户分群、文本分类、图像识别这类多分类任务需要一个选型参考。第三类是准备面试或者做课程项目的学生这种对比实验本身就是很完整的实战素材从数据处理到结果分析整个链路都有。1.2 数据与实验环境准备项目使用了一个公开的多分类数据集样本量在数万级别特征维度中等类别数量设置为10个。这里有几个细节需要提前说明。数据中既包含数值型特征也包含类别型特征存在少量缺失值同时部分特征的数值范围差异很大。这种数据形态其实非常贴近真实业务场景相比那种收拾得干干净净的玩具数据集这个对比结果更有说服力。实验环境方面我做了以下配置操作系统使用 LinuxPython 版本为 3.9。核心依赖库包括 scikit-learn 1.1.2、pandas 1.5.1、numpy 1.23.4。多层感知机部分使用了 PyTorch 2.0.1 构建了一个小规模网络。评估指标统一采用准确率、宏平均精确率、宏平均召回率、宏平均 F1 值训练时间也纳入对比。数据集采用分层采样划分训练集与测试集比例为 8:2。提示分层采样在多分类对比中很重要。如果直接用随机划分某些小类别在训练集或测试集中可能出现样本过少甚至缺失的情况导致评估结果失真。scikit-learn 中 train_test_split 的 stratify 参数就是干这个用的。2. 四类算法的原理差异与选型逻辑2.1 决策树分段划分的天然解释器决策树的核心机制是基于特征对样本空间进行递归划分。每次划分时算法会选择一个特征和对应的阈值把当前节点中的数据分成两个子集使得划分后的纯度提升最大。多分类场景中纯度通常用基尼指数或者信息熵来衡量。以基尼指数为例某个节点 D 的基尼值定义为Gini(D) 1 - Σ p_k^2其中 p_k 表示第 k 类样本在节点 D 中占的比例。选择划分特征时计算每个候选划分的基尼指数加权和取最小值对应的划分。这个计算过程类似于在整理一堆混色积木时每次挑一种最能把颜色分得更开的分类方式。我在项目中使用的决策树参数如下criteriongini即基尼指数max_depth8控制树的深度min_samples_split20节点分裂所需最小样本数min_samples_leaf10叶节点最少样本数class_weightbalanced缓解类别不平衡实际运行中决策树在训练集上的准确率大约 0.72测试集上约 0.68。这个成绩在四类算法中并不突出但它的价值在于可解释性极强。你可以直接把树结构导出成规则比如当特征 A 小于 5.2 且特征 B 大于 3.1 时属于类别 7。这种特性在金融风控、医疗诊断等对可解释性有硬性要求的场景里无可替代。2.2 随机森林用集成换稳定随机森林的本质是构建多棵决策树让它们投票决定最终结果。它的随机性体现在两个层面一是每棵树训练时使用有放回抽样得到的自助样本集二是在每次节点分裂时只随机挑选部分特征进行最优划分。这两重随机使得每棵树之间存在明显差异从而降低模型整体的方差。项目中随机森林配置为n_estimators200树的数量max_depth16单棵树最大深度max_featuressqrt(n_features)分裂时随机选特征数oob_scoreTrue启用袋外样本评分n_jobs-1使用全部 CPU 核心对比项目中随机森林在测试集上的准确率约 0.81明显高于单棵决策树的 0.68。这种提升在分类问题上体现得很稳定基本不需要太多精细调参就可以获得不错水位。如果说决策树是一个爱钻牛角尖的实习生那随机森林就是一群背景各异的实习生组成的评审团每个人都有盲区但综合判断不容易被单个人的偏见带偏。不过我提一个细节随机森林对特征重要性给出的是平均贡献它并不能回答这个特征对某个具体样本为什么重要。特征重要性排序适合用来做粗筛真要逐样本解释还是回退到单棵树。2.3 多层感知机非线性拟合的老牌选手多层感知机属于神经网络范畴由输入层、若干隐藏层和输出层组成。每一层执行线性变换加非线性激活正是这些非线性激活函数让网络能逼近非常复杂的决策边界。多分类输出层通常接 Softmax把网络输出转化成各类别的概率分布。我使用的网络结构如下输入层维度等于特征数隐藏层为两层第 1 层 128 个神经元第 2 层 64 个神经元激活函数为 ReLU输出层 10 个神经元Softmax 归一化优化器 Adam学习率 1e-3批量大小 128训练轮数 100早停机制验证集损失连续 10 轮无下降则停止训练用 PyTorch 实现的三层网络在测试集上准确率约 0.79训练过程大约 4 分钟收敛。它的优势在于不需要人工设计特征组合模型自己会从数据里学习高阶交互关系。但劣势也很明显对样本量和超参数敏感在小数据集上容易过拟合学习率设置不当直接导致不收敛。在对比项目中我把它的表现定为潜力股。数据量再大一些、特征再复杂一些MLP 的优势会更突出。但在当前这个中等规模数据集上它和随机森林的差距并不大稳定性反而略逊一筹。2.4 支持向量机边界思维的几何派支持向量机的出发点完全不同它试图找到一个超平面使不同类别样本之间的间隔最大化。多分类问题通常通过两种策略扩展一对多和一对一。scikit-learn 中 SVC 默认采用一对一策略也就是在每两个类别之间训练一个分类器最终通过投票确定结果。项目中我分别测试了线性核和 RBF 核线性核 SVC约 4 分钟完成训练测试集准确率约 0.74RBF 核 SVCC10gammascale测试集准确率约 0.69由于样本量在数万级别RBF 核训练耗时超过 40 分钟且内存占用较大这个结果和我们通常的印象有些出入。很多人认为 SVM 在多分类问题上效果一定很好但实际要看数据规模。SVM 适合样本量中等、特征维度不过高的场景超过几万样本之后核矩阵的计算成本攀升非常快。线性 SVM 在小规模数据集上往往表现稳健而 RBF 核在参数没调好的时候很容易出现欠拟合或过拟合两个极端。注意SVM 这类基于距离的模型特征缩放直接影响结果。项目中所有数值特征都做了标准化这个步骤在 SVM 上是必选项而非可选项。如果不做数值范围大的特征会主导距离计算分类边界完全被带偏。3. 数据预处理与实验流程设计3.1 清洗与特征工程对比实验要保证公平性四类算法的数据入口完全一致。我沿用了一套固定流程先处理缺失值再对类别型特征做编码最后统一做特征缩放。具体操作步骤用 SimpleImputer 策略填充缺失值数值列用中位数填充类别列用众数填充。类别特征采用独热编码生成哑变量列。数值特征做 StandardScaler 标准化使得均值为 0、方差为 1。检查类别分布确认不存在极端不平衡。划分训练集与测试集比例 8:2stratifyy。这里有一个容易踩的坑独热编码做完之后特征维度会膨胀。如果原始类别特征基数很大这一步可能产生几百列显著拖慢 SVM 训练。项目里有一个类别特征基数达到 40 多做完独热之后特征从 20 个变成 70 多个。幸好整体量级还能接受如果再大就得考虑用数值编码或者目标编码来压缩维度。标准化这个操作对决策树和随机森林没有影响因为树模型对特征单调变换不敏感。但 MLP 和 SVM 对特征尺度非常敏感所以标准化必须在同一份数据上完成而且要先用训练集拟合 scaler再同时变换训练集和测试集不能拿整个数据集拟合 scaler否则会造成数据泄露评估结果虚高。3.2 统一的评估框架多分类的评估比二分类复杂一些不能只看准确率。我采用了以下四个指标准确率全部样本中预测正确的比例直观但容易被多数类主导。宏平均精确率每个类别的精确率先单独计算再取算术平均对每个类别一视同仁。宏平均召回率同理按类别计算召回率后取平均。宏平均 F1精确率和召回率的调和平均兼顾二者。这四个指标放在一起可以更全面地判断模型在各个类别上的表现是否存在明显偏科。说得更直白一点假设某测试集里有 90 个 A 类样本和 10 个 B 类样本一个模型把所有样本都预测为 A 类准确率是 0.9看起来很高。但 B 类的召回率是 0宏平均 F1 会非常低。所以说只看准确率做算法选型对比容易被表面的数字骗到。3.3 实验顺序与资源调度多分类对比还有一个实际工程问题是资源调度。SVM 最耗时我率先启动它让它训练的同时并行跑决策树和随机森林。MLP 放中间等 SVM 出结果后开始调参。这个安排节省了不少时间。实验顺序具体如下先跑决策树确定基线水平。因为决策树速度快结果出来之后可以给后续调参提供一个参考下限。再跑随机森林树的数量和深度调整时对照决策树基线判断增益是否真实。启动 SVM 训练同时准备 MLP 的训练脚本。最后调 MLP重点是学习率和网络宽度。统一汇总各模型的损失曲线、预测结果和运行时间。跑完之后我把所有结果存在同一份结果表中方便后续画图和分析。4. 核心实操过程与关键实现细节4.1 决策树训练与剪枝策略决策树训练代码核心部分如下from sklearn.tree import DecisionTreeClassifier clf_tree DecisionTreeClassifier( criteriongini, max_depth8, min_samples_split20, min_samples_leaf10, class_weightbalanced, random_state42 ) clf_tree.fit(X_train, y_train)训练本身很快几秒就完成。真正有价值的是调参过程。我先后试了 max_depth 从 2 到 20 的递增序列发现深度超过 10 之后训练集准确率还在涨但测试集准确率开始下滑这是典型的过拟合信号。最终选择 8是因为它在训练集和测试集之间的差距最小。min_samples_leaf 也是一个很实用的防过拟合手段。它强制叶节点最少包含 10 个样本这样模型不会为了迎合个别样本而把边界切得过于碎片化。在噪声较大的多分类数据中这个参数的作用甚至比限制深度更明显。class_weightbalanced 是我特别加上的。原始数据集中最大类别的样本数约为最小类别的 5 倍。如果不处理决策树会倾向于把边界模糊的样本划分到大类小类召回率会被压低。加上平衡权重之后小类的召回率提升了约 8 个百分点虽然总体准确率小幅度下降但宏平均 F1 明显更好这在我做多分类项目时是更符合业务目标的方向。4.2 随机森林的训练与特征重要性分析随机森林代码核心部分如下from sklearn.ensemble import RandomForestClassifier clf_rf RandomForestClassifier( n_estimators200, max_depth16, max_featuressqrt, min_samples_leaf2, oob_scoreTrue, n_jobs-1, random_state42 ) clf_rf.fit(X_train, y_train) print(clf_rf.oob_score_)n_estimators 从 50 到 500 我都测过超过 200 之后准确率的提升幅度变得非常小大约在 0.1 到 0.2 个百分点之间但训练时间线性增加。实际项目里没必要一味追求更多的树200 棵在这个数据集上已经足够稳定。oob_score 是一个很划算的内置评估机制。训练过程中每棵树大约有 1/3 的样本没有被抽中这些样本可以直接用来评估树的性能。oob_score 相当于免费的交叉验证不用额外划分验证集。我在项目中用 oob_score 帮助选择树的深度效果和手动切验证集基本一致。特征重要性方面随机森林输出的是基于基尼重要性的排序。排名前三的特征合计重要性占比超过 50%。这说明这个数据集的信息分布比较集中部分特征对分类的贡献占据主导地位。通过排序结果后续可以做特征筛选实验把不重要的特征剔除后重新训练模型准确率几乎没有变化但训练时间下降约 20%。4.3 多层感知机的网络构建与训练MLP 的训练我用了 PyTorch 实现核心代码结构如下import torch import torch.nn as nn class MLP(nn.Module): def __init__(self, n_features, n_classes): super().__init__() self.net nn.Sequential( nn.Linear(n_features, 128), nn.ReLU(), nn.Linear(128, 64), nn.ReLU(), nn.Linear(64, n_classes) ) def forward(self, x): return self.net(x) model MLP(X_train.shape[1], 10) optimizer torch.optim.Adam(model.parameters(), lr1e-3) criterion nn.CrossEntropyLoss()训练循环中我加入了两层保障机制。其一是早停机制。我预留了 10% 的训练数据作为验证集每个 epoch 结束后计算验证集损失。如果连续 10 轮验证损失没有下降就中止训练并恢复最优权重。实际训练中大约在第 32 个 epoch 触发早停。如果没有这个机制模型会在 100 轮完整跑完的过程中逐渐过拟合测试集准确率反而会下降 2 到 3 个百分点。其二是学习率调度。我用的是 ReduceLROnPlateau当验证损失停滞时学习率乘以 0.5。Adam 优化器虽然适应性较强但在训练后期还是容易出现震荡。降低学习率之后损失曲线下沉得更平滑最终收敛值也更低。MLP 在这个数据集上的表现是测试集准确率 0.79。我尝试过把隐藏层扩到 256-128 结构准确率提升大约 0.01但训练时间从 4 分钟增加到 7 分钟性价比其实一般。在中等规模数据集上两层 128-64 的宽度已经足够盲目加大网络结构对性能增益有限却大幅增加训练开销。4.4 支持向量机的核函数对比与调参SVM 部分我做了两组对照实验from sklearn.svm import SVC svm_linear SVC(kernellinear, C1.0, class_weightbalanced) svm_linear.fit(X_train, y_train) svm_rbf SVC(kernelrbf, C10.0, gammascale, class_weightbalanced) svm_rbf.fit(X_train, y_train)线性核 SVC 的测试集准确率 0.74RBF 核 SVC 为 0.69。这个结果值得细说。线性核的本质是在原始特征空间中找一个线性超平面。当特征是数值型并且经过标准化后线性核的表现往往不差因为它把问题简化到了一个合适的假设空间里不需要面对高维核映射带来的复杂度。RBF 核在这个数据集上反而没有占便宜。原因主要是样本量上来了。RBF 核需要计算两两样本之间的相似度复杂度接近样本数的平方在几万样本规模下训练耗时超过 40 分钟。它的优势通常体现在特征维度较高、但样本量不大的场景比如文本分类中的词袋特征这种情况下利用核技巧在隐含高维空间刻画复杂的样本分布。参数调优方面C 控制对误分类的惩罚力度。C 值过小容易欠拟合C 过大则容易过拟合。我将 C 从 0.01 到 100 按对数尺度扫描RBF 核在 C10 附近表现最好继续增大到 100 时测试集准确率反而下滑。gamma 控制 RBF 核的径向影响范围本次固定为 scale 模式即 gamma 1 / (特征维度 * 方差)。手动调高 gamma 会让决策边界更弯曲但也更容易在样本稀疏区域产生过拟合的小孤岛。提示SVM 在多分类项目中如果类别之间的特征分布重叠比较严重RBF 核的调参难度会明显增加。建议先用线性核跑一个参考结果再决定是否值得尝试核方法。如果线性核已经达到 0.74而 RBF 核需要 40 分钟才能跑到 0.69这个时间成本就要慎重考虑了。5. 结果对比与深入分析5.1 整体指标一览四类算法的最终评估结果汇总如下模型准确率宏平均精确率宏平均召回率宏平均F1训练时间决策树0.680.670.660.66约3秒随机森林0.810.800.790.79约90秒多层感知机0.790.780.770.77约4分钟线性核SVM0.740.730.720.72约4分钟RBF核SVM0.690.680.670.67约40分钟从这张表能读出几个关键信息。随机森林在准确率和宏平均 F1 上都是最高。这说明在中等规模、含噪声、特征类型混合的数据集上集成方法确实拥有明显的综合优势。它不需要像 SVM 那样精细调参对缺失值、异常值和特征尺度都不敏感几乎是一个开箱即用的选择。MLP 的 0.79 紧随其后。考虑到它只用了 100 轮训练和早停机制这个表现说明只要数据量足够神经网络不需要特别复杂的结构也能和集成学习方法掰手腕。如果数据集规模再翻几倍MLP 的排位完全可能反超。线性核 SVM 的 0.74 处在中游。在标准化之后的特征空间里线性模型的表现没有掉队太多并且它的训练时间只比随机森林稍长。决策树的 0.68 垫底。单棵树的方差太大稳定性不够换成多棵树的随机森林之后直接提升 13 个百分点这个差距本身就是对集成方法降低方差这一理论最直观的验证。5.2 类别维度上的差异分析全局指标之外我还逐类统计了每个算法的精确率和召回率。这里有几个有意思的发现。第一个发现是所有算法在样本量最大的类别上表现都最好召回率普遍超过 0.9。但在小类别上算法之间的分化非常明显。随机森林和 MLP 对小类别的召回率维持在 0.7 左右而决策树只有 0.55。这个现象说明树模型单棵在面对小类别时容易把边界区域划给大类而随机森林的投票机制可以在多棵树之间稀释这种偏差MLP 则通过连续可导的决策函数天然地处理类别间的过渡区域。第二个发现是部分类别之间存在系统性混淆。我查看了随机森林的混淆矩阵发现类别 3 和类别 5 之间相互误判的频率较高。进一步回看数据后发现这两个类别在多个重要特征上的均值分布几乎重叠。这说明当前特征空间本身对这两个类别的区分度不足任何算法都难以做到完美区分。这个信息对业务侧是有价值的它表明可能需要补充新特征而不是继续调参。第三个发现是类别不平衡的影响在决策树上最明显在随机森林和 MLP 中则得到一定抑制。决策树做类别划分时本身就是基于样本占比来优化纯度小类别样本少天然容易被忽略。虽然我为它加了 class_weightbalanced但效果有限。随机森林由于每棵树只用了部分样本小类别被淹没的概率降低MLP 的损失函数中类别权重也起到了调节作用。5.3 复杂度与资源消耗对比从工程角度衡量算法选型不能只看准确率还要看训练复杂度、调参成本和推理效率。训练时间上决策树最快随机森林次之MLP 和线性 SVM 居中RBF SVM 最慢。RBF SVM 在 4 万样本量下训练耗时超过 40 分钟是随机森林的 20 倍以上。如果在更大规模的数据上做实验RBF SVM 的计算量还会急剧上升基本不具备扩展性。推理速度上决策树和随机森林最快单样本预测是亚毫秒级。MLP 在 CPU 上推理速度略慢但也可以接受。SVM 的推理复杂度取决于支持向量的数量在样本量大、边界复杂时支持向量数量可能很多推理速度会明显下降。调参成本上决策树和随机森林的超参数不多默认配置下随机森林已经可以拿到 0.79 左右的分数。MLP 对网络结构和学习率比较敏感需要做多组实验。SVM 的调参空间更大C 和 gamma 组合扫描耗时较长而且不同核函数之间的差异不够直观容易让人陷入调参迷宫。综合考虑如果业务场景对推理延迟不敏感、预估数据规模较大随机森林是一个比较稳的实际选择。如果数据量非常庞大且需要自动学习复杂特征交互MLP 值得投入调参精力。如果类别数和样本量都适中、特征维度较高线性 SVM 也能撑起一片天。6. 常见问题与排查实录6.1 问题速查表现象可能原因解决方案决策树准确率远低于预期树过深导致过拟合或类别不平衡主导划分限制 max_depth调大 min_samples_leaf开启 class_weight随机森林提升不明显树的数量太少或特征采样策略不合适增大 n_estimators换 max_features 为 log2MLP 训练不收敛学习率过大或数据未标准化调低学习率确认特征做过标准化MLP 过拟合模型容量过大缺少早停或正则化减宽度、加 Dropout、使用早停RBF SVM 训练极慢样本量大导致核矩阵计算量爆炸换线性核或改用随机森林RBF SVM 准确率反而不如线性核gamma 设置不当或数据线性可分性强调低 gamma或直接用线性核各类别召回率差异悬殊类别不平衡或特征对部分类别区分度不足使用类别权重检查混淆矩阵定位问题类别准确率高但小类别 F1 低多数类主导评估方向把宏平均 F1 作为主要优化目标6.2 训练中遇到的典型问题第一个问题是 MLP 在首次训练时出现损失震荡。原因是我把初始学习率设成了 1e-2偏大。Adam 优化器在 1e-2 的学习率下步子迈得太大损失函数在最小值附近来回横跳。我把学习率调到 1e-3 之后曲线明显平滑。这个教训说明遇到不收敛先别怀疑网络结构优先检查学习率和数据标准化。第二个问题是随机森林的 oob_score 和测试集准确率差距偏大。后来检查发现数据经过独热编码后特征数量从 20 变到了 70 多max_features 的 sqrt 策略选出的特征数也相应变化影响了单棵树的多样性。改成 log2 策略之后oob_score 和测试集表现的差值缩小了。这个细节提醒我特征工程会联动影响树模型的超参数选择换特征集之后必须重新验证参数。第三个问题是 SVM 训练的内存告警。RBF 核的训练过程中SMO 算法需要维护一些中间矩阵当样本量达到数万时内存占用迅速攀升。我处理的办法是把数据降采样到约 2 万样本做了快速实验先确定参数区间再全量跑最终版本。实际项目中如果 SVM 跑不动降采样或者换线性核是更务实的方案。6.3 几条独家避坑心得第一对比实验必须保持数据管线完全一致。我一开始在决策树和随机森林上用了原始数据在 SVM 和 MLP 上用了标准化后的数据结果跑出来的差距包含了预处理的影响根本没法公平对比。后来统一成同一套管线结论才站得住脚。第二多分类对比不要只保存最终的准确率。我强烈建议保存每个类别的精确率、召回率和混淆矩阵。这些信息能告诉你哪些类别容易被混淆哪些类别是模型的短板。光看一个平均值等于只看到了森林看不到单棵树的病。第三调参时把随机种子固定。树模型和 MLP 的训练都带有随机性随机种子不同最终准确率可能有 1 到 2 个百分点的波动。如果不固定种子两次实验的差异可能并不是参数变化带来的而是随机波动造成的那调参判断就失真了。第四RBF SVM 在数据集超过 3 万样本时训练时间会变得难以接受。我在排查过程中实测样本量从 2 万增加到 4 万训练时间增长了近 4 倍。如果有大规模多分类需求建议优先考虑随机森林或线性模型。第五MLP 的验证集划分要独立于测试集。有些朋友图省事直接用测试集来看早停时机这等于把测试集当验证集用模型的超参数已经污染了最终的评估结果。正确做法是在训练集内再切一部分做验证测试集保持完全陌生状态直到最后才碰。7. 实验结论与个人经验总结四类算法在同一个多分类任务上跑完之后我可以给出一个比较明确的结论随机森林是当前这个数据集上的首选MLP 是最有潜力的备选线性 SVM 是简单稳健的轻量方案决策树则是可解释性最强的基线模型RBF 核 SVM 在这个数据规模下性价比并不理想。但我想强调的是这个结论不能无脑迁移到所有多分类问题。如果你手里的数据集只有五千样本RBF 核 SVM 的表现可能完全反过来。如果特征维度非常高并且稀疏线性 SVM 可能比随机森林更合适。如果业务对可解释性要求极高那决策树或者浅层树模型又成了不可替代的选择。算法选型从来没有绝对的王者只有和具体数据形态、业务约束匹配的答案。我个人做这类对比实验的经验是先把数据跑通把基线和固定评估框架立好然后再按简单到复杂的顺序逐个引入模型。你会发现真正让你对某个算法建立信心的不是教科书上的理论推导而是你在自己的数据上看到的真实差异和模式。那 40 分钟 RBF SVM 等待时间里观察到的换轮波动比任何文章都更能教会你应该怎么评估一个模型。如果你后续要做类似的对比项目最后再分享一个小技巧为每个模型单独建一个结果档案记录参数、训练时间、精度变化曲线和混淆矩阵。实验做完之后这些档案就是一手资料不管是写报告、做展示还是后续复盘都比重新跑一遍数据靠谱得多。
阅读完成 · 觉得有帮助?