资讯详情

资讯详情

随机森林原理详解:从Bagging到特征随机选择与调参实战

1. 随机森林到底解决了什么问题先聊点实际的。刚接触机器学习的时候我相信很多人跟我一样第一次跑通的模型就是决策树。这玩意好处是直观画出来跟流程图似的人人都能看懂。但用着用着问题就来了只要树的深度稍微大一点训练集上的准确率能逼近100%换到测试集上直接拉胯标准的过拟合。而且树本身特别不稳定数据稍微扰动一下整棵树的结构可能就完全变了。后来接触到随机森林相当于一下子把这两个头疼的问题都解决了大半。它属于集成学习里非常经典的一类核心思想说白了就一句话单个模型不行那就多搞几个模型一起投票。就像你面试拿不准一个人的水平找一个面试官可能看走眼那就找十个面试官最后按多数意见来出错的概率就低多了。随机森林这个名字很多人以为它就是随机加森林这么简单但其实里面的随机有两个层面的含义这两个随机恰好就是它对抗过拟合、提升泛化能力的关键武器。这篇文章不讲虚的我会把随机森林从原理到实战参数再到实际调参遇到的坑尽量一次讲透。适合刚学完决策树、准备接触集成学习的同学也适合那些已经用过随机森林但始终搞不清内部细节的从业者。2. 集成学习的两大流派以及随机森林站在哪边既然说随机森林是集成学习的代表那得先搞清楚集成学习到底在集成了个啥。集成学习的大方向分两块一类叫Boosting另一类叫Bagging。Boosting的思路是串行训练后一个模型重点关注前一个模型犯错的样本相当于一群人接力赛每个人都在前面的人跌倒的地方重新爬起来典型代表就是AdaBoost、GBDT、XGBoost。Bagging的思路是并行训练每个模型各练各的最后大家的意见汇总互相纠正错误典型代表就是随机森林。随机森林用的是Bagging的思路但它在传统Bagging的基础上又加了一层随机性这就是后面要详细展开的特征随机选择。2.1 Bagging里的自助采样为什么要有放回Bagging全称是Bootstrap Aggregating翻译过来就是自助聚合。这里最核心的操作是Bootstrap Sampling也就是有放回的随机抽样。假设训练集有1000条样本我要训练10棵决策树。按传统思路可能就是从1000条里随机选700条给每棵树但不放回这样每棵树拿到的数据都不同但数据总量受限而且某些边缘样本可能永远没被选到。随机森林的做法是有放回抽样每棵树训练时从原始训练集里随机抽N条样本其中N等于原始训练集大小但每抽一条就放回去所以同一批数据里某条样本可能被抽中多次也可能一次都没被抽中。算一下大概能懂每次抽样没被抽中的概率是(1 - 1/N)^N当N足够大的时候这个值趋近于0.368。也就是说每一棵树大概有36.8%的原始样本没参与训练。这部分没被用到的样本恰恰是最好的验证集因为模型完全没见过它们。这就是随机森林里OOB Score袋外分数的理论基础不需要单独划分验证集直接用它来评估模型效果特别省事。2.2 随机森林比纯Bagging多了哪一层随机传统Bagging只是在样本层面做文章每棵树用不同的子样本训练。但随机森林又额外加了一层随机性就是在每次节点分裂的时候不是从全部特征里选最优分裂特征而是先从全部特征里随机挑一部分出来比如总共20个特征每次分裂只随机抽K个常见取法是sqrt(20)然后只在这K个特征里挑最优的那个来分裂。这一步的意义非常深刻。如果不加这层随机集成里的每棵树虽然训练数据不同但大家选来选去总是会在那几个特别强的特征上做分裂结果就是树长得虽然不完全一样但本质上高度雷同相关性很强。一群高度相似的人投票跟一个人投票没什么本质区别。但强制随机制后每棵树只能在小范围特征里做决策这棵树没用到某个强特征可能就会被那棵用到这个特征的树的结果纠正过来。树之间的相关性下降了集成系统的多样性上来了最终的泛化能力才会真正提升。2.3 为什么树的多样性比单棵树的准确率更关键这个点我可以明确说在随机森林里单棵树的准确率低一点没关系甚至低一些反而从集成角度看是好事。因为集成的最终误差可以近似拆成两个部分一个是每棵树自己的偏差另一个是树与树之间的相关性。如果每棵树都又准又相似那误差的核心就卡在相关性上你加再多树也没用。如果每棵树各有各的毛病但大家的错误不怎么重叠投票的时候错误就会互相抵消。打个比方一个团队里全是同一个学校毕业、同一个导师带出来的学生思维方式完全一致那讨论问题的时候看着热火朝天实际上就是一个人在重复说话。但如果团队成员背景差异大互相谁也不能说服谁的时候经过充分讨论后的结论反而更靠谱。随机森林的随机特征选择不是在制造混乱而是在刻意制造这种观点多样性。3. 随机森林的构建流程以及每个步骤的隐藏细节这一节我们把随机森林从数据到成林的完整流程拆开一步一步看。整个过程其实不复杂复杂的是每一步里那些文档上不怎么会写明白的细节。3.1 单棵决策树的生长方式先说单棵树怎么长。随机森林里的决策树通常用的是CART树也就是基于基尼系数做分类、基于方差做回归的那种二叉树。它在每个节点要做的事很简单从当前可用的特征集合里遍历每个特征的每个可能取值或者阈值算一下分裂后两个子节点的纯度提升选择提升最大的那个特征和阈值进行切分。但这里注意随机森林里的树跟单独训练一棵决策树的树不太一样。单独训练一棵决策树时我们会考虑剪枝来控制复杂度但随机森林里的树通常不剪枝放任它一直长到叶子节点不能再分为止。为什么不剪因为随机森林本身已经通过样本随机和特征随机引入了足够的扰动单棵树过拟合一点没关系集成之后会把这部分过拟合给平均掉。如果早早剪枝反而可能让单棵树欠拟合削弱整个森林的表达能力。3.2 节点分裂时的特征随机选择这个步骤是整个随机森林算法里最核心的一环。假设原始特征总数是M每次节点分裂时先随机抽取K个特征然后只在这K个特征里找最优分裂。K的默认值分类问题通常取sqrt(M)回归问题通常取M/3左右。为什么分类和回归的K取值不同分类问题更依赖特征之间的组合效应特征少了会让单棵树多样性太大精度下降特征多了又会增加计算开销。sqrt(M)是一个在精度和计算效率之间平衡得很好的经验值。回归问题因为目标是连续值对特征的敏感度更高需要更多特征来捕捉平滑的函数关系所以K取大一些。另外提一个调参小技巧K越小森林里每棵树的差异性越大偏差越大但方差越小。K越大森林整体越接近Bagging树的差异性越小。实际调参时如果发现模型过拟合严重可以考虑适当调小max_features也就是K强制增加随机性反而能提高泛化效果。3.3 从样本抽样到决策树生成的全流程我用一个完整的流程把这部分串起来输入训练集D特征集F设定树的数量n_estimators和每棵树的最大特征数max_features。对每一棵树从D中有放回地抽样N条数据形成该树的训练子集D_i。从根节点开始递归构建从F中随机抽取max_features个特征构成候选特征集F_i。遍历F_i中的每个特征计算所有可能分裂点的基尼系数或方差增益。选择增益最大的特征和阈值进行分裂。递归重复直到叶子节点样本数小于min_samples_leaf或者树深度达到设定上限。所有树构建完成后对同一输入样本分类问题用多数投票决定输出类别回归问题用所有树预测结果的平均值作为输出。流程本身确实不复杂但正是这种简单规则的大量重复反而成就了它在各种数据集上稳定的表现。3.4 关于OOB Score一个免费的验证集前面提过每棵树大约有36.8%的样本没参与训练。随机森林算法在构建每棵树的过程中会记录哪些样本是OOB样本。整片森林构建完毕后把每条OOB样本送入它没参与训练的那些树里做预测汇总所有这样的预测结果得到的准确率或均方误差就是OOB Score。这个分数有什么用最大的意义在于不用单独划分验证集。很多新手做机器学习时习惯把数据分成训练集和测试集训练集跑完再上测试集。但如果你在测试集上调参调了很多次这个测试集就已经被污染了它反馈的信息其实已经进入了你的决策过程。OOB Score由于天然来自未训练样本且不需要单独留数据是一个更干净的泛化性能估计。实测下来OOB Score跟交叉验证的结果非常接近尤其在数据量中等的场景下完全可以直接拿它来指导调参。4. 随机森林为什么不容易过拟合以及它仍有尴尬的地方随机森林的抗过拟合能力不是玄学它有数学上的支撑但我尽量用直觉讲清楚。4.1 方差被压低的原理说明集成学习的误差分解里有一个重要结论如果若干相互独立的模型每个模型的方差是σ²那么它们平均后的方差是σ²/kk是模型数量。当然随机森林里的树并不完全独立因为数据采样自同一个源所以方差不可能降到σ²/k那么完美但至少是显著降低的。偏差方面呢每棵树都是对原始分布的无偏估计平均之后偏差不会变大。所以整体效果就是偏差基本保持不变方差大幅下降最终泛化误差降低。这就是为什么单棵决策树容易抖动、随机森林却相对稳定的核心原因。4.2 随机森林最常见的三个限制说完了优点也得说说它让人头疼的地方。第一是解释性变差。单棵决策树你还能画出来给老板看说因为年龄小于30所以推这个产品。随机森林几百棵树你根本不可能把它们画出来解释。虽然可以用特征重要性来侧面解释但失去了决策树那种天然的透明度。第二是训练效率问题。树的数量一旦上到几百甚至上千训练时间会显著上升。虽然每棵树可以并行训练但如果你处理的是高维稀疏数据随机森林的效率就会明显不如线性模型。第三是对极端值的处理不够细腻。随机森林的每棵树输出是离散区间或叶子节点的均值对于训练集里没有出现过的特征组合它只能靠泛化不能外推。回归任务中如果测试样本的特征值超出了训练范围随机森林的预测结果会显得比较保守甚至明显不准确。4.3 哪些场景适合随机森林哪些场景应该绕开适合的场景我总结为四类数据特征类型多样数值和类别混杂不用太做预处理也能跑。样本量中等特征维度中等几千到几万的级别。对模型解释性要求不高更看重预测准确率。需要评估特征重要性的场景。应该绕开的场景超高维稀疏数据比如文本分类的One-Hot向量特征动辄几十万维随机森林会很吃力。需要外推预测的时间序列场景随机森林在这块天然不擅长。极度要求可解释性的金融风控等场景直接用逻辑回归或决策树更合适。在线学习场景新数据实时到达需要频繁更新模型随机森林不适合这种动态迭代。5. 随机森林核心参数与调参路线图这一节是实操党最关心的部分。我用一个实际的项目场景来说明参数怎么调。假设某公司给出了一份用户行为数据特征包括用户的年龄、登录频次、浏览时长、点击率、设备类型、地区等共15个特征样本量2万条目标变量是用户是否完成付费转化。用随机森林来建模。5.1 核心参数逐个拆解随机森林在绝大多数框架里比如scikit-learn核心参数就那么几个n_estimators、max_features、max_depth、min_samples_split、min_samples_leaf。n_estimators森林里树的数量。很多人以为越大越好其实到了一定规模就趋于平稳。太小了肯定不行一般起步选100然后观察OOB Score或交叉验证分数如果200棵比100棵提升明显就继续加如果提升不到0.5个点那就保持100节省计算时间。max_features这个参数前面重点说过。分类默认sqrt(n_features)回归默认n_features/3。调参的时候它不是越大越好也不是越小越好需要通过搜索找到最优区间。我见过很多案例里max_features从默认值入手小范围调整效果往往比动其他参数更显著。max_depth树的深度。随机森林里树可以长得很深但实际调参的时候还是建议给个上限。特别在样本量不大的任务里太深的树会带来不必要的计算开销对泛化能力的提升几乎没有帮助。我一般从10开始向上试到20基本到头了。min_samples_split节点分裂所需的最小样本数。默认是2但这个值在数据有噪声时容易导致过拟合调到5到10之间通常效果更好。min_samples_leaf叶子节点的最小样本数。这个参数我强烈建议重点调。它直接限制了叶子节点的纯度值太小会导致单棵树过度拟合噪声值太大会让模型过于平滑失去捕捉细节的能力。分类问题里我通常先从5开始回归问题会根据数据波动情况适当调大比如10到20。为了看得清楚我整理了一个表格展示每个参数的影响方向参数调大方向的影响调小方向的影响n_estimators模型更稳定但计算成本上升收益递减模型波动变大可能欠拟合max_features单棵树更强树间相关性升高方差变大单棵树更弱树间差异变大偏差变大max_depth单棵树更复杂可能过拟合模型更简单可能欠拟合min_samples_leaf叶子更大模型更平滑防止过拟合叶子更小模型更细碎容易过拟合min_samples_split分裂更保守模型更简单分裂更频繁模型更复杂5.2 一份可以直接套用的调参路线我给出一套自己反复用过的调参顺序不一定最优但至少不会让你在参数空间里乱撞第一轮先固定n_estimators100max_featuressqrt(n_features)然后调max_depth。我习惯从5开始每次增加5观察OOB Score变化。如果深度到20还没收敛一般就不会再往深了调。第二轮在选好的max_depth基础上调min_samples_leaf。从2开始试逐步增加到10看OOB Score的峰值出现在哪里。第三轮调max_features。这一步往往能带来惊喜。范围可以设在sqrt(n_features)的50%到150%之间用网格搜索或者随机搜索都可以。第四轮回来后稍微调一下n_estimators。如果前面的操作让OOB Score还有上升趋势就继续增大树的数量否则保持原状。整个调参过程如果工具允许可以直接用框架里的RandomizedSearchCV随机搜索交叉验证来做时间预算充足的话再上GridSearchCV网格搜索交叉验证。但我觉得理解参数意义比盲目搜索更重要因为只有理解了你看到搜索结果才能判断哪些值是合理范围内的偶然哪些是真正的信号。5.3 回归任务里的参数调整差异上面聊的主要是分类回归任务里的随机森林参数调整有几个不太一样的地方。回归时max_features默认值是n_features/3比分类大很多。原因前面解释过连续目标需要更多特征来拟合平滑函数。min_samples_leaf在回归里要适当加大因为叶子节点样本太少时预测值是少数几个样本的均值波动非常大。我做过一个房屋价格预测项目特征21个min_samples_leaf从2调到10之后验证集上的均方误差下降了差不多15%效果极其明显。还有些时候可以考虑对目标值做变换。随机森林本身不关心目标值的分布但如果目标值严重偏态比如长尾严重可以先做log变换训练完再还原预测值误差指标通常会更好看。6. 特征重要性的计算原理和使用心得随机森林有一个很吸引人的附带功能输出特征重要性。这个功能在日常工作中非常常用但很多人不知道它背后的计算逻辑用起来难免出错。6.1 两种主要的特征重要性计算方式第一种是基于不纯度减少。在建树的过程中每个特征被选中做分裂时会带来基尼系数或均方误差的下降。把同一个特征在所有树上的下降量加起来再归一化就是特征重要性。这种计算方式速度很快是默认做法。第二种是基于OOB样本的置换重要性。思路是把某个特征在OOB样本上的值随机打乱然后观察模型预测误差的变化。如果打乱这个特征后误差显著上升说明这个特征很重要如果误差几乎没有变化说明这个特征可有可无。两种方式各有优劣。不纯度减少计算快但容易被高基数特征比如某个特征的取值特别多带偏导致重要性虚高。置换重要性更可靠但计算量大尤其特征多的时候要跑很多次。实际项目里我一般先看不纯度减少版本筛选出一批候选特征再用置换重要性复核这几个候选的高分特征这样兼顾效率和可靠度。6.2 使用特征重要性时的三个注意点第一特征重要性是相对的不是绝对的。它反映的是特征在模型中的相对贡献不代表特征单独和目标的因果强度。一个特征可能跟目标高度相关但如果另一个特征已经包含了它的信息它的重要性就会显得很低。第二类别特别多的特征容易被高估。比如一个城市字段取值有上百个树很容易在它上面找到分裂点得到很高的不纯度下降。这并不一定说明它真的比一个只有两个取值的数值特征更重要。遇到这种情况我习惯把高基数类别特征先做目标编码或者频次编码再去跑重要性。第三特征重要性不能帮你判断该不该删除某个特征。删除特征的正确做法是看删除后的模型效果变化而不是只看重要性排序。有时候删掉一个重要性很低的特征反而会导致另一个高重要特征失效因为它们之间存在强交互关系。7. 常见问题与排查技巧实录最后这部分是我自己踩过的坑以及帮别人排查项目时见过的典型问题整理成清单希望你能绕开。7.1 问题一OOB Score很高但测试集分数很低这个现象通常有两个原因一是数据划分出了问题OOB样本和训练样本来自同一个分布但测试集分布漂移即训练数据和测试数据分布不一致导致OOB评估失效二是特征里存在时间相关的泄漏比如用了未来信息做预测OOB评估时这种泄漏也存在所以分数高但真实场景里未来信息根本拿不到。排查思路很简单先检查特征里有没有与目标直接相关的字段比如目标变量本身或其编码衍生变量。其次对比OOB样本和测试样本的特征分布看是否一致。最后在时间序列问题上一定要用时间切分别用随机切分。7.2 问题二模型训练很慢怎么加速如果你已经确定了树的数量但训练时间还是太长优先检查max_features是不是设得太大。特征多的时候max_features过大会显著增加每棵树的节点分裂耗时。其次检查min_samples_split和min_samples_leaf把这两个值从默认往上调一点能大幅减少树的生长深度和节点数量。另外一个容易忽略的点如果数据里某个类别特征取值特别多比如几十万个唯一值决策树处理这类特征时会反复排序非常耗时。可以考虑先做类别编码压缩或者直接用直方图类算法替代。不过那就不是随机森林的范畴了属于XGBoost、LightGBM那边的思路。7.3 问题三分类效果偏科某类准确率特别低随机森林处理类别不平衡问题时效果不如专门的平衡方法那么强。最常见的做法是调整类别权重很多框架里直接支持class_weight参数。把它设为balanced会让少数类的错误在损失计算中获得更高的权重。但如果少数类占比实在太低比如不到5%单纯调权重帮助有限还需要考虑采样手段比如对少数类做SMOTE合成或者对多数类做下采样。我建议先跑一版不加权重的随机森林记录各类别的精确率和召回率再决定要不要上这套组合拳。别一上来就加所有花活先看基线。7.4 问题四随机森林的预测结果不可复现这是因为随机森林内部有多个随机源数据的自助采样、特征子集的随机选择。想要结果可复现需要在框架里固定随机种子。实际操作中我一般在三处设置随机种子算法初始化时的random_state、数据划分的随机种子、以及如果用了交叉验证或随机搜索它们内部的随机种子也要固定。这里说句题外话做竞赛或者写论文的时候固定种子没问题。但在工程上线时我反而建议不要过度依赖固定种子所带来的高分数因为那可能只是某个种子恰好运气好。应该多跑几个种子取效果的中位数作为模型真实水平的估计。8. 写在最后的个人经验随机森林这个模型我已经用了很多年每次项目里遇到表格型数据第一个上场的基准模型永远是它。不是因为它在所有数据集上都能拿第一而是因为它几乎不需要多少数据预处理就能给你一个不错的下限。有了这个下限你再去看其他模型到底值不值得换心里就有底了。最后分享一个小小的实用习惯每次跑随机森林之前我都会顺手打印出特征重要性排序哪怕后面不一定用得上。因为很多问题做久了你会发现建模真正的难点往往不在算法本身而在于你对自己手上数据理解得有多深。特征重要性就是一个很好的切入点它会告诉你原来这个不起眼的字段才是决定结果的关键。这种发现的过程比模型分数涨几个点有趣得多。希望这篇内容能让你对随机森林的内部工作机制、调参路线和坑点有一个相对完整的认识。拿着这些理解去跑你的数据一定能比我当年刚接触它的时候顺利得多。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →