资讯详情

资讯详情

随机森林与集成学习:偏差方差调参与客户流失预测实战

你有没有遇到过这种情况单棵决策树在训练集上能拿九十八分一上验证集就掉到八十以下换成线性模型倒也稳定可稳定得让人着急训练验证都卡在八十出头。这种“模型分数上不去”的困局十有八九是偏差和方差在作怪。我当时做客户流失预警项目时就是被这个折磨了好几周最后真正把我从泥潭里拉出来的就是集成学习Ensemble Learning和随机森林Random Forests。这是机器学习实战系列的第七篇我会把随机森林为什么有效、怎么调参、有哪些坑以及一个完整的客户流失预测案例都拆开讲一遍。内容不绕弯子有基础概念也有实操细节适合那种已经跑通几个算法、但觉得模型效果始终差口气的读者。1. 单个模型为什么走投无路偏差与方差一对永远在打架的冤家1.1 用打靶游戏理解偏差与方差如果只用一句话概括模型效果不好我会说要么打偏了要么打散了要么两样都占。打靶时弹着点如果整体偏向靶心一侧这是准心出了问题对应机器学习里的高偏差弹着点如果散布得乱七八糟这是手法不稳定对应高方差。高偏差的模型通常意味着欠拟合训练集和测试集的分数都低典型代表是很浅的线性模型高方差的模型则表现为过拟合训练集分数高得离谱测试集立刻露馅典型代表就是不剪枝的深度决策树。我在实际项目中判断模型问题出在哪头从来不看单次结果而是直接对比训练集和验证集的指标差距。如果两者差得很大比如训练集AUC有0.95、验证集只有0.80这就是方差把精度吃掉了如果两者都很低折腾各种特征都上不去那就得怀疑偏差去加特征、上更复杂的模型而不是继续做正则化。大多数刚入门的人会习惯性先把单个模型的参数调到极致这条路往往走不通。因为决策树这类模型的超参数本身就是偏差和方差的旋钮把树加深偏差降了方差立刻抬头把树剪短方差稳了欠拟合又出现了。调参过程就像跷跷板压下一头必然翘起另一头。集成学习之所以能破局就是因为它不跟这个跷跷板硬刚而是换了一条路——用多个模型的结果做组合在尽量不增加偏差的前提下把方差压下去或者反过来用串行纠错把偏差吃掉。1.2 为什么“平均一下”在统计上真的有用你可能要问多个结果平均一下效果凭什么比单个模型好这里有个特别朴素的统计学直觉。假设我们训练了n个独立同分布的模型每个模型的误差是随机变量误差的期望方面各模型差不多但方差项在经过平均之后会缩小到单模型的大约1/n。极端一点说一个模型预测值在正负10之间乱跳一百个这样的模型平均下来预测值可能就稳定在很小范围内了。这就是“集体智慧”的根基——单看谁都不完美但它们的随机波动相互抵消了。当然这里面的关键在于“独立”。如果一百个模型全都长得一模一样平均结果和单个模型没有任何区别。随机森林的整个设计围绕的核心就是一件事如何构造出一群“彼此尽量不相似”的树让它们的误差波动能真正互相抵消。这件事不是靠随机拍脑袋而是靠样本采样、特征采样和树生长策略三条路同时做文章。2. Bagging、Boosting、投票法集成学习的三种底层思路2.1 Bagging并行训练投票消方差Bagging的全称是Bootstrap Aggregating直译就是“自助采样再聚合”。它的做法很直观对原始训练集做有放回抽样生成若干个大小与原训练集相同的子集在每个子集上独立训练一个模型最后做投票分类或取平均回归。这里有一个特别容易被忽视的细节有放回抽样会让每个子集里出现重复样本同时也有一些样本没被抽到。某个样本没被抽到的概率大约是0.368也就是说每个子集大概只有三分之二不到的原始样本出现。这些“没被抽中”的样本正好可以拿来当天然的验证集这就是后面要说的OOB误差的由来。Bagging主要解决的是方差问题。如果底层的单个模型本身方差大比如不剪枝的决策树Bagging几乎是一剂特效药。我见过很多项目单棵决策树的验证集AUC低到0.60上下套上Bagging之后没调整任何其他东西直接蹦到0.80左右。代价也很明显你要训练几十甚至几百棵树计算开销上来了可这是值得的。2.2 Boosting串行纠错步步为营Boosting和Bagging的哲学完全不同。Bagging是各干各的最后汇总Boosting是“接力赛”后一个模型专门盯着前面模型犯过的错继续优化。具体来说每一轮训练都会调整样本的权重上一轮判断错误的样本权重变大下一轮的模型就会更关注这些难啃的样本。典型代表是自适应增强AdaBoost和梯度提升树GBDT后者在工业界的各种大数据比赛里出镜率非常高。Boosting主要解决的是偏差问题。它擅长把一堆效果仅仅比瞎猜好一点的弱学习器一步一步拎成强学习器。我给你打个比方一个人做卷子每次只对一小部分题但只要他每次只修正上一次错过的题多轮下来正确率就会显著上升。这种串行结构的问题也很明显——因为每个新模型都依赖前面的结果没法并行训练而且如果数据里有噪声Boosting会把噪声也当成重点“纠错”对象反而容易被带偏。2.3 投票法与Stacking让不同类型的模型互补Bagging和Boosting都是“同一品种”的模型互相配合投票法和Stacking的思路更野既然每个模型都有自己的盲区那就把不同类型的模型拼在一起。常见做法是训练逻辑回归、决策树、支持向量机等几个差异明显的模型直接对它们的预测结果做硬投票少数服从多数或软投票概率取平均。再进阶一点把各个模型的预测结果作为新特征交给一个元模型学习如何组合这就是Stacking。我在项目中用投票法比用Stacking更多因为Stacking有个天生的风险——元模型学到的是各模型预测值之间的关系而这种关系在线上环境一旦发生偏移整套方案都容易崩。投票法虽然粗一点但胜在稳定、可解释、不容易过拟合。2.4 三种思路怎么选套用一句比较实用的话如果模型输在方差上优先考虑Bagging如果模型输在偏差上优先考虑Boosting如果手里已经有一批差异很大的模型且不确定谁靠谱投票和Stacking做兜底。这三个方向不是只能选一个工业级的方案里经常出现“先用随机森林做基准线再上梯度提升追精度最后加权融合”的组合拳。但把一种思路吃透远比什么都浅尝辄止重要这篇后面重点展开的就是Bagging框架下的王者——随机森林。3. 随机森林到底“随机”在哪里三条腿缺一不可3.1 样本随机自助采样和那37%的“局外人”随机森林的第一步继承自Bagging也就是对训练集做有放回抽样。每次抽出一个和原训练集等量的子集用来训练一棵树。因为是有放回抽样同一个样本在子集里可能出现两次、三次甚至更多次而另外一些样本从头到尾没露过脸。这里直接给一个实用结论用n个样本做有放回抽样n较大时某个样本从来不被抽中的概率约为0.368通常说每个子集约包含原数据集63.2%左右的互不相同的样本。那些没被抽中的样本就是这棵树的OOBOut-of-Bag袋外样本。每一棵树的袋外样本集合起来可以用来计算整个随机森林的无偏预测误差这也就是OOB误差。这个机制太重要了因为你在不额外切验证集的情况下就能一边训练一边评估模型简直是白捡的免费验证集。3.2 特征随机随机子空间与节点分裂如果只用Bagging重采样样本树们还是会陷入一个麻烦当数据里有几个特别强的特征时几乎所有树都倾向于优先用它做分裂结果就是树长得挺像独立性不够集成效果会打折扣。随机森林的核心创新就在这一步——每次分裂时不让树从全部特征中挑最优而是先随机抽出一个特征候选集再从这个候选集里挑最优分裂。这个候选集的规模通常用参数“max_features”控制。分类任务的经验值是特征总数的平方根附近回归任务的经验值是总数除以3或者取对数。我在实践中的感受是这个参数比树的数量对模型效果影响更明显。设得太小每棵树都严重“偏科”单棵树能力下降设得太大树之间的相关性又上去了。后面调参部分会演示怎么用数据说话。3.3 树的生长策略把复杂度控制权交给随机化随机森林里的每棵树普遍采用CART分类回归树的思路并且在充分生长的情况下不剪枝。你是不是觉得不剪枝必然过拟合单棵树确实会但放在森林里就不一样了。随机森林的容错机制是每棵树故意让它长得足够深来挖掘局部模式然后用样本随机和特征随机带来的多样性去抵消单棵树的过度自信。这就是“算法工程化”的奇妙之处——它不求每棵树都对只求整体对得稳。当然“不剪枝”不是绝对的永远正确数据量小、噪声大的场景下强行限制树的深度反而效果更好。我只是说不要在随机森林这里照搬单棵决策树的剪枝习惯先让它长再看验证集表现决定要不要限制。3.4 随机森林关键参数速查表参数作用常见取值思路n_estimators树的数量先跑OOB曲线找拐点通常100到500max_features每次分裂的候选特征数分类用特征数开根号回归用三分之一附近max_depth最大深度默认不限样本少噪声大时限制在10到20min_samples_leaf叶子节点最少样本数5到20能显著抑制过拟合min_samples_split内部节点分裂最少样本数2是默认数据量小时可以调到5以上class_weight类别权重正负样本不平衡且不想用采样时设置这张表里面的取值不是死的后面调参那一节会告诉你一个稳妥的先后顺序。4. 动手实战一个客户流失预测案例的完整调参闭环4.1 案例背景与基线模型为了不让讨论悬在半空我拿一个模拟的零售公司客户流失预警项目来演示。数据是某电商平台的脱敏用户行为记录一共两万条样本、二十多个特征包括消费频次、客单价、最近一次消费间隔天数、客服投诉次数、优惠券使用数量、注册渠道等。目标变量是用户是否在未来一个月流失正样本大概占百分之十五。拿到数据第一步永远不是跑模型而是先划分数据集。我先按时间顺序切出70%训练集、20%验证集、10%测试集。这里特别提醒一句凡是涉及任何统计信息的东西比如均值填充、标准化参数都只能从训练集里算验证集和测试集不能提前碰否则就是数据泄漏。我见过有同学先对整个数据集做标准化再切分结果线上评估被高估了一截这是最容易犯的隐藏错误。先训练两个基线模型一个逻辑回归一个不剪枝的深度决策树。为什么先上这两个因为它们分别代表了偏差主导和高方差主导的极端情况。结果逻辑回归在训练集和验证集上的AUC分别是0.74和0.72典型的偏差瓶颈学习能力不够单棵决策树在训练集AUC飙到0.97验证集只有0.71方差问题严重到肉眼可见。这一步告诉我两件事一是这类高维表格数据里线性模型的天花板就在那儿想靠它提高精度希望不大二是单树模型潜力很高但极度不稳定正适合用随机森林的Bagging机制去压方差。4.2 随机森林初跑与OOB误差曲线基线跑完之后我用随机森林默认参数先跑了一版树的数量设为200max_features用默认的开根号策略不限制深度min_samples_leaf保持默认的1。结果训练集AUC降到0.92验证集AUC升到了0.84左右。训练集和验证集的差距从0.26缩小到0.08过拟合被明显压住了但还有继续优化的空间。这里我建议大家一定要养成画OOB误差曲线的习惯。做法并不复杂训练过程中收集不同树数量下的袋外误差然后画出来。你会看到一个迅速下降再慢慢变平的曲线。如果曲线在五十棵树左右就开始平了那n_estimators200已经浪费了一半算力如果到三百棵树还在明显下降那说明你该给的树还不够。我自己的项目里通常取曲线基本变平位置再稍微加一点余量很少无脑拉到几千棵树。4.3 参数调优顺序与结果随机森林调参有个不错的顺序先定树的数量再调max_features然后调叶子节点约束最后考虑max_depth。顺序反过来的话你很容易陷入先调深度把模型弄复杂又不得不用其他参数往回找补的循环里。我当时的具体做法是这样的。第一步用默认max_features画OOB曲线发现两百棵左右已经足够平于是把n_estimators定在200。第二步网格搜索max_features的几个候选值——根号、log2、全部特征、以及根号的一半。用验证集对比结果很有意思全部特征那组训练集很高、验证集反而差树之间相关性太强了log2那组每棵树能力弱了整体也一般最优落在默认根号附近。第三步把min_samples_leaf从1逐步调到2、5、10、20每调一档都看训练验证差距和验证集AUC最后min_samples_leaf5的位置得分最好既稳又不损失精度。第四步max_depth限制到10到20之间基本没带来提升说明在特征随机和叶子样本约束下树的深度已经不是这个数据集的主要矛盾了。最终随机森林在验证集上的AUC是0.87左右测试集0.86F1值比基线提升了将近零点一个点。表格对比更直观模型训练AUC验证AUC测试AUC推理耗时万条样本逻辑回归0.740.720.71极快单棵决策树0.970.710.70极快随机森林默认参数0.920.840.83中等随机森林调优后0.880.870.86中等注意调优后的随机森林训练AUC反而比默认参数低了但验证集涨了。很多人看到训练分数降了就慌其实这是压制过拟合的正常信号模型是从“假装学得很好”变成了“真的有泛化能力”。4.4 特征重要性的业务落地除了提分还要能解释随机森林不比逻辑回归它不能直接给出系数但能输出特征重要性。我拿到这个项目的最重要特征是“最近一次消费间隔天数”和“消费频次”接下来是“投诉次数”。从业务逻辑上看完全说得通太久没来、互动频率低、带着不满情绪的用户流失风险自然高。这里有个特别实用的经验特征重要性不但用于解释模型还可以用于做特征筛选。我先按重要性排序然后把排名靠后的若干特征删掉重新训练验证集AUC反而几乎没有下降说明那些特征大部分是噪声。删掉之后模型文件变小了推理也更快。对线上推理速度有要求的场景这一招性价比很高。要注意的是特征重要性只在训练集内部算完用来解释它本身不需要验证集参与。但如果你想利用重要性做特征筛选再在同一个验证集上做评判那这验证集某种意义上已经“看过了”筛选结果有轻微乐观偏置最终效果要在测试集上确认一次才作数。5. 特征重要性、OOB误差里的常见坑与绕行方案5.1 高基数类别特征如何“欺骗”特征重要性随机森林默认计算特征重要性的方式是基于不纯度下降比如Gini系数下降量的累积。它的毛病是偏爱取值特别分散的特征。比如一个类别特征“用户所在城市”如果这一列有几百个互不重复的值随机森林可以在它上面切出非常纯的节点来于是它的重要性会被抬得非常高哪怕这个特征从业务上看没什么预测力。我遇到过不止一次这种情况模型跑出来排名第一的特征是个高基数的文本类编码字段替换掉这个特征之后再训练结果几乎不受影响。所以看重要性别只看单一指标。比较稳的做法是结合排列重要性来看将某个特征的所有取值随机打乱观察模型效果下降多少。这个指标不受分裂偏好影响更接近该特征的“真实存在感”。代价是计算量偏大需要把每个特征轮流打乱再预测一轮但对重要特征不多的小项目完全可行。5.2 OOB与验证集不可混用的数据泄漏风险OOB误差很好用但它是一个“训练过程附带产出的评估值”它的统计性质和一次性留出验证集是不太一样的。我见过不少同学拿着OOB误差做调参调完之后再用OOB误差汇报最终效果甚至用OOB选完特征后再拿OOB做最终结论。这样做会高估模型因为你的调参和特征选择已经干扰了袋外样本的独立性。我的习惯是OOB误差用来做前期快速迭代比如看树数量拐点、做第一轮大范围探路一旦进入精细对比阶段必须以一次性留出的验证集为准全部定稿之后再拿从没碰过的测试集做最终确认。三条数据通道各司其职才能避免自欺欺人的结果。5.3 回归场景的外推短板与树数量收益递减随机森林的回归预测本质上是训练样本目标值的加权平均这就导致一个边界限制它很难预测出超出训练集目标值范围的结果。如果你手里的任务是房价、销量这类需要跟着趋势继续往上走的场景直接用随机森林很容易出现预测值“封顶”的现象。我遇到过一次销量预测测试月份销量明显高于训练期历史峰值随机森林的回判结果全部压在训练最大值附近而梯度提升类模型在加入时间趋势后表现明显更好。树的数量同样存在收益递减。我从实践中得到的结论是大多数对精度有帮助的改进发生在树数从几十涨到两百的过程中两百到五百收益已经很小五百以后基本是边际收益趋近于零。所以别再追求用夸张的n_estimators暗示自己模型更强了树多了模型文件大推理慢收益却很微薄。先用OOB曲线选一个够用值再省下算力去调别的参数才是划算的。5.4 随机森林与梯度提升怎么取舍最后聊一个选型问题。随机森林和梯度提升树看起来都是“一次集成一大堆决策树”很多新手容易混。它们的主攻方向不同随机森林压方差梯度提升追精度。在实际项目里如果数据量不大、噪声高、特征维度多我通常先用随机森林打底它的鲁棒性好对异常值和噪声的处理更宽容几乎不需要前期做大规模特征预处理如果随机森林的精度到不了业务目标再上梯度提升配合细致的调参并做好过拟合监控。梯度提升的潜力通常更高但这不代表它一定适合你。它超参数更多、训练顺序串行调参路线复杂得多而且数据集一旦有标注噪声很容易死磕那些被错误标记的样本反而伤害泛化。我现在的习惯是两支并行都跑随机森林出结果快梯度提升慢慢磨最后哪个在同一个验证集上表现好就用哪个或者按比例融合。我在实际项目里用到现在的体会是随机森林从来不是误差下限最低的那个算法但它几乎是最容易帮你在任何一张表格数据上快速站稳脚根的算法。它自带防过拟合的“护栏”不需要繁琐的特征预处理还同时给你特征解释性和训练过程中的免费验证。把这一套搞明白了再往梯度提升那些更锋利的工具走路会顺很多。
觉得有用,分享给同行:

为您的企业打造数字门面

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

立即咨询 →