一、90%数据人都在瞎调决策树,你可能也是
从事做机器学习的那些人, 基本上没有谁未曾编写过这样一段代码, 代码内容是, 进行导入er的操作, 接着设置等于5, 还设置等于42, 再之后开展fit训练。
人人都在抄这段代码, 可是却没人去问一下: 为啥是等于5? 而不是3也不是15? 等于42究竟有啥作用? 其余9个默认参数, 你难道从来都没认真看过吗?
更为扎心的是, 好多人调整参数完全依靠猜测, 训练集的准确率达到了百分之九十九, 然而一到测试集就直接遭遇失败, 明明是一模一样的数据集, 其他人调整参数能够达到百分之八十五的准确率, 可你却被困在百分之六十, 迟迟无法提升。
实际上并非是你自身的能力欠缺不足, 而是并没有人向你传达告知, 决策树的每一个超参数, 都具备着明确清晰的作用, 有着特定的适用场景情形以及相关的调优规则法则。那些为你所忽略没能重视的默认值因素, 恰恰正是能够拉开你与高手之间差距距离的关键所在要点。
今日, 我们将中er的11个超参数一次性详尽讲解清楚, 附上实战场景, 给出调参代码, 还提供避坑指南, 看完即可直接着手操作, 再也无需盲目调整参数而白白浪费时间。
关键技术补充: er核心信息
我们所运用到的决策树工具, 源自那个最为常用的机器学习库-learn(此为简称), 它属于那种完全开源且免费的机器学习工具集合, 其星数高达50.8万(数据为准2026年4月的), 是全球数据从业者必定需要具备的工具。
那当中的er, 它属于是达成决策树分类的核心函数, 上手起来较为简单且带有很强的解释性, 同时它还是诸如随机森林等多种高级模型的基础性要素, 若想把握它的超参数调试, 那就等同于开启学习机器的“任督二脉”。
二、核心拆解:11个超参数,逐个讲透(附代码)
在对参数进行拆解以前, 我们要先弄明白一个核心的逻辑, 决策树的本质, 是借助持续去询问“是或者否”这样的二元问题, 将数据划分成越发纯粹的小组, 而超参数, 其本质是掌控“怎样去拆分、拆分到何时停止”的规则。
超参数全部都能够被归结为三种类型, 这三种类型分别是: 用于控制树生长的复杂度约束, 用于判断拆分优劣的质量规则, 以及用于控制随机性的随机规则。接下来会逐一对其进行拆解, 并且针对每一个参数都会附上实战场景以及可直接运行的代码。
1. :判断拆分好坏的“标尺”
它所具备的作用, 是用于衡量一个节点的“纯度”, 拆分之后的数据, 越是呈现出集中的态势(例如全部都是正例或者全部都是负例), 那么拆分所产生的效果, 就会越发良好。
默认的数值是gini, 也就是基尼系数, 另外存在着两个可供选择的值, 分别是熵, 再者是对数损失。
基尼系数, 其计算具备简单的特性, 而且速度较快, 这种情况适合用于平衡数据集; (熵)呢, 它对于类别分布表现得更为敏感, 适宜用来处理不平衡数据集; 在需要呈现概率输出的场景当中(诸如预测用户购买概率这类情况)它是适合的。
现实应用场景里, 在医疗诊断范畴内, 存在着这样的情况, 即百分之九十属于正常患者, 而仅有百分之十是高危患者, 此乃不平衡数据, 运用特定方式能够促使模型更尽力地去捕获高危患者;在电商客户流失的预知方面, 所涉及的数据是平衡的, 使用基尼系数会更为高效。
调整参数的规则是, 若要平衡数据并且追求速度就要使用gini, 要是面对不平衡数据且关注少数类的情况就进行相应操作, 如果需要概率输出这一情形也行进行对应举措。
2. :拆分时的“选择策略”
控制在每个节点进行拆分之际, 有没有必要去遍历全部特征从而找寻到最优的拆分点呢。其默认的数值是best(将所有特征都遍历一番, 进而找出最优的拆分情况), 拥有的可选数值是(随机挑选出特征的子集, 再从中找寻最优的拆分状态)。
best所具备的优势在于精准、稳定, 适宜应用于小数据集;其优势还在于速度快, 能够发现并非特定的规律, 对于高维数据(好似具备 200 多个特征的电商点击数据这种情况)是适用的, 并且这也是随机森林的核心逻辑当中的一个部分。
实战情形是这样的、针对五十万行以及两百个特征的电商点击类数据来看、要是采用best的话、那么所耗费的时间会极其漫长、并且还特别容易过度去依赖那些强势特征、要是选用那种能够进行快速训练的、并且还能够挖掘出弱特征所具备的价值的。
调参规则是, 针对小数据集, 要是追求准确率那就用best, 对于高维数据, 要是追求速度那就用特定方式, 当使用单个决策树做成最终模型的时候要用best, 而作为集成模型的基础的时候要用特定方式。
3. :决策树的“最大高度”(最关键参数)
对决策树的最大层数予以控制, 其默认值为None, 也就是树会持续生长, 直至所有叶子都达到纯净状态, 而这恰恰是极易致使过拟合现象出现的参数。
当处于等于无的情况时, 树木会以机械记忆的方式对待训练数据, 训练所达成的准确率有可能达到百分之九十九, 然而测试时的准确率却会急剧下降, 像是从百分之九十九降至百分之六十一这般, 根本没办法推广至全新的数据。
那种实际进行操作的场景是这样的, 存在一个用于贷款审批的模型, 其具有相关的特征, 这些特征涵盖了年龄、收入以及征信分数等方面。当设定为4的时候, 这个模型会提出4个关键的问题, 分别是征信分数是不是大于680, 月收入是不是大于50000元, 贷款金额是否为2年。该模型经过训练后准确率达到84%, 对于测试来说准确率是81%, 并且其泛化效果是最好的。
可直接运行代码(贷款审批模型调参):
from sklearn.tree import DecisionTreeClassifier from sklearn.model_selection import cross_val_score import numpy as np # 初始化模型,遍历不同max_depth值 for depth in range(1, 15): model = DecisionTreeClassifier(max_depth=depth, random_state=42) scores = cross_val_score(model, X, y, cv=5, scoring='accuracy') print(f"深度{depth}: 平均准确率 {scores.mean():.3f} ± {scores.std():.3f}")如果出现模型过拟合的状况, 此状况表现为训练准确率远远高于测试准确率, 那么就要减小;要是模型出现欠拟合的情形, 也就是训练成绩同测试成绩都很低, 那就得增大;若噪音较多, 并且特征显得杂乱, 这时就要控制在小于等于5;当数据整洁干净, 且特征强烈的时候, 可调到变为10至到15;要是有需要解释性, 比如说给业务人员查看的情况, 那就得小于等于3。
4. :节点拆分的“最低门槛”
一个节点倘若想要进行拆分, 那就必须得至少拥有这么多的样本方才可行, 其默认的值是 2。这即表明, 哪怕仅仅存在 2 个样本, 树依旧会试着展开拆分, 这样极易捕捉到噪音!
情形为实战场景, 存在100万笔交易的fraud检测情况, 其中仅0.1%属于fraud, 当默认等于2的时候, 树此时会为了能够捕捉个别fraud案例, 进而创建出极其细分的分支, 其本质是“死记硬背”, 并非是学习规律;要是设为50, 也就是0.01, 即总样本的1%, 那么拆分就一定要有50个样本进行支撑, 如此规律会更可靠。
可直接运行代码(fraud检测配置):
model = DecisionTreeClassifier( max_depth=6, min_samples_split=0.01, # 1%的总样本,数据扩容时自动适配 random_state=42 )调参的规则是, 对于小数据集, 也就是10万行的那种, 采用0.005到0.02的范围;要是遇到噪音多, 并且标签异常的情况, 那就增大到20到100;平衡数据有所增大, 目的是避免拆分少数类的小节点。
5. :叶子节点的“最小规模”
拆分后所形成的叶子节点, 其样本数量必定要至少达到这样的数量, 该数量的默认值为1。而两者之间有所区别, 具体表现为: 前者把控着“拆分之后是否符合要求”这一情况, 后者则对“是否着手进行拆分”起到控制作用。
实战的场景是, 针对8000名患者进行再入院预测, 当默认等于1的时候, 就会出现这样的情况, 即年龄67岁、血压142、吃3种药这样的情况就会导致会再入院, 这属于极端规则, 这种极端规则是记住了单个患者的信息, 并非是医疗规律;若设为15, 那么每个规则都有15名及以上的患者来支撑, 此时泛化能力会得到大幅度的提升。
可直接运行代码(患者再入院预测):
model = DecisionTreeClassifier( max_depth=7, min_samples_split=30, min_samples_leaf=15, # 每个叶子至少15个样本 random_state=42 )改变参数的规则是这样的: 倘若出现过拟合的情况, 那就增大参数(按照5、10、20这样逐步去尝试);要是噪音量多、标签存在错误, 也增大参数;对于回归树(这种用于预测连续值的情况), 增大参数以此来让结果稳定下来;要是有需要精准边界的需求, 那就设置为1 - 5。
6. :拆分时的“特征范围”
在进行拆分节点这个操作的时候, 最多会去考虑多少个特征呢, 其默认的值是None也就是考虑所有的特征, 而可选的值有sqrt也就是根号下总特征数, 还有log2也就是log2总特征数, 另外还包括整数, 以及浮点数。
实战的场景是这样的, 针对有着10000个基因特征、500个样本的疾病预测, 倘若启用默认值, 就会出现过度拟合的状况, 大家也知道, 这10000个特征总是能寻找到那种可以将500个样本完美拆分的组合!而要是采用sqrt, 也就是大约100个特征, 那就能够强迫模型去探寻稳健的规律, 进而避免受到噪音的干扰。
可直接运行代码(高维基因数据配置):
model = DecisionTreeClassifier( max_depth=5, max_features="sqrt", # 每次拆分仅考虑√10000≈100个特征 random_state=42 )调参的规则是这样的, 对于低维数据, 具体是100个的那种, 要采用sqrt或者log2来进行操作;在集成模型当中呢, 要运用sqrt, 这里的sqrt是随机森林默认那种值;要是有需要进行特征选择的情况, 那就得使用处于0.5到0.8这个范围的浮点数。
7. :决策树的“最大叶子数”
对树的总计叶子节点数个进行控制, 其默认的值为None(也就是无限制), 这属于控制复杂度的另外一种方式, 即不限制深度, 仅仅限制最终的决策端点与终点。
它具备这样的优势, 即所谓“最优分配”, 具体表现为, 树会优先去拆分那纯度提升最大的节点, 将用来表示叶子数量的部分, 用在最具价值方面的拆分之上, 这种情况适合于存在可解释性需求的场景, 像是面向业务人员所展示的规则引擎这类场景。
实战情形是这样的, 零售类型的商品进行有关推荐, 这里面需要十个决策方面的规则, 它们是对应着十个商品的类别的, 设定等于十, 此时模型就会生成十个叶子节点, 并且每个节点都是对应着一个推荐规则的, 业务方面的相关人员能够直接去进行审核。
可直接运行代码(商品推荐规则引擎):
model = DecisionTreeClassifier( max_leaf_nodes=10, # 仅生成10个决策规则 criterion="gini", random_state=42 )调整参数的规则是, 要是需要具备可解释性, 那就设定为8至20;要对不同的树的复杂度进行对比, 得用它来替换;要是清楚了预期结果的数量, 像5个类别这种情况, 那就把它设定为相应的数量。
8. e:拆分的“最小价值”
仅当拆分之后, 节点的纯度产生的提升量大于或等于此数值时, 才会实施拆分, 其默认的数值是0.0(即只要存在些许提升便进行拆分) , 它能够将“表意欠缺重要性”的拆分予以过滤, 防止树发生过度的生长。
处于实战的场景之中, 是关于天气预报的预测降雨方面的情况, 其默认状况使得大树在“时间”这个特殊的特征之上进行无数次的细分, 细分的情况是像是14点、14.5点、再到15点这样, 每一种被拆分下去的内容所含有的纯度提升程度是非常小的, 而且这些提升的状况通通都属于噪音范畴;经过设置变为0.005之后, 仅仅保留纯度提升程度大于或者等于0.5%的那种拆分情况, 从而将重点放在真正具备价值意义的特征之上, 而这些特征例如湿度、风速等。
可直接运行代码(天气预报配置):
model = DecisionTreeClassifier( max_depth=8, min_impurity_decrease=0.005, # 仅保留有意义的拆分 random_state=42 )调整参数的规则如下, 树木要是过度进行拆分, 那么噪声就会增大到零点零零一至零点零一的范围;要是连续特征方面。噪声比较多, 那么就需要增大;要是数据干净, 而且特征优质, 那么就保留零点零。
9. :类别不平衡的“平衡器”
针对不同的类别, 去进行权重的分配, 其默认的值, 是None这种情况, 也就是所有的类别权重都是一致相同的, 而其可选的值存在于这样两种情况之中, 一种是通过一定方式自动依据类别数量来分配权重, 另一种是自定义字典, 像那种形如{0:1, 1:50}的模样。
它作为处理类别不平衡的核心参数, 并非事后进行调整, 而是在训练的时候, 就要使模型重视少数类, 像fraud、高危患者这类。
实际作战场景如下, 存在20万笔信用卡交易, 其中99.5%属于正常交易类型, 0.5%属于欺诈类型, 当默认值设定为无的时候, 该模型会做出所有交易均为正常的预测, 其准确率为99.5%, 然而却会完全遗漏欺诈交易;倘若将其设定为特定值, 那么欺诈交易的权重会自动转变为正常交易权重的100倍, 此时该模型便会主动去捕捉欺诈模式。
可直接运行代码(信用卡fraud检测):
# 自动平衡权重 model = DecisionTreeClassifier( max_depth=6, class_weight="balanced", min_samples_leaf=10, random_state=42 ) # 自定义权重(漏判fraud的成本是误判正常的50倍) model = DecisionTreeClassifier( max_depth=6, class_weight={0: 1, 1: 50}, random_state=42 )调参规则是这样的, 平衡数据的时候要用None, 不平衡数据且没有成本信息的时候这样处理, 有明确业务成本, 像 漏判fraud损失大这种情况, 要用自定义字典, 使用SMOTE等重采样方法之后要用None。
10. :决策树的“修剪刀”(后剪枝)
它有着这样的功能, 即“剪枝”, 先是让树生长到最为繁茂的状态, 接着再去除掉那些“性价比比较低”的分支, 这些分支所带来的纯度提升, 无法抵偿它所增添的复杂度, 其默认的值是0.0, 也就是不进行剪枝, 它属于最严谨的正则化方式。
实战情形是: 针对要预测学生成绩, 首先要让树生长至最为饱满的状态(也就是等于None的情况), 接着运用方法剪掉那些没有用处的分支, 如此一来, 既能够留存核心所遵循的规律, 另一方面又能够防止出现过拟合的状况。
可直接运行代码(学生成绩预测,自动找最优剪枝参数):
from sklearn.tree import DecisionTreeClassifier import matplotlib.pyplot as plt # 1. 训练完整决策树 full_tree = DecisionTreeClassifier(random_state=42) full_tree.fit(X_train, y_train) # 2. 获取剪枝路径,得到所有可能的ccp_alpha值 path = full_tree.cost_complexity_pruning_path(X_train, y_train) ccp_alphas = path.ccp_alphas # 3. 遍历所有alpha,找到最优值 train_scores, test_scores = [], [] for alpha in ccp_alphas: model = DecisionTreeClassifier(ccp_alpha=alpha, random_state=42) model.fit(X_train, y_train) train_scores.append(model.score(X_train, y_train)) test_scores.append(model.score(X_test, y_test)) # 4. 选择测试准确率最高的alpha optimal_alpha = ccp_alphas[test_scores.index(max(test_scores))] print(f"最优ccp_alpha: {optimal_alpha:.4f}") # 5. 训练最终模型 final_model = DecisionTreeClassifier(ccp_alpha=optimal_alpha, random_state=42) final_model.fit(X_train, y_train)调参的规则是, 假若要是追求着特别严谨的正则化操作之时那便使用它, 还有剪枝的相关路径;要是已经设置好了特别严格的情况那就不需要再用了(属于冗余范畴);当模型处于有轻微过拟合现象的时候要采用0.001至0.01之间的值;要是模型出现了严重过拟合的状况则优先去进行调整。
11. :结果可复现的“钥匙”
对于控制拆分之中所具有的随机性, 当处于比如等于、不等于None这样的情况时, 其默认的值是None, 而这会导致每次运行所产生的结果存在差异, 若将其设定为任意的整数, 诸如42、0、99之类, 那么每次运行的时候便都会得到相同的树。
它的核心价值在于“可复现”, 在生产环境里, 你于今天所训练的模型, 与下周所训练的模型, 必定得保持一致 , 只有如此, 在进行实验时, 才能够对比不同参数所产生的效果 , 不然的话, 随机性将会对判断造成干扰。
调参规则是, 不管处于何种场景, 都得进行设置。对于生产模型、实验代码以及共享代码, 这些都必须维持一致。在稳定性测试的时候, 可以更换各类不同的整数, 也就是从0到9之间的整数, 以此来查看模型准确率的波动情况。倘若波动幅度较大, 那就意味着该模型对于数据顺序是比较敏感的。
补充:超参数交互规则(避免踩坑)
好多人在进行调参的时候遭遇失败, 并非是参数设置出现错误, 而是忽视了参数之间存在的相互影响, 常见的交互方面的 是像下面这样。
1. +: 要是等于3, 而设定等于500(一共有1000行数据), 那几乎没办法进行拆分, 树就会转变成为一个根节点, 这差不多就相当于没有进行训练。
2. 加号表示, 前者必然要大于或等于二乘以后者这种情况, 诸如等于十、还有等于八这般, 拆分后, 叶子节点之中会有一个的数据量是少于八的, 如此便会导致异常剪掉枝叶这种状况出现。
3. 加: 设置了之后, 纯度的计算方式会被改变, 之前调好的e需要再次进行校准。
4. , +: = += None, 这个是“所有特征随机阈值”, = sqrt += best, 此为“随机特征子集找最优阈值”, 二者的随机性全然不同。
实战案例:电信客户流失预测(完整代码)
数据集包含达到70000数量的客户群体, 具备18种特征, 存在14%的流失率情况属于不平衡数据, 完整的调参代码如下, 其可直接进行复制以实现运行:
from sklearn.tree import DecisionTreeClassifier from sklearn.model_selection import cross_val_score, StratifiedKFold from sklearn.preprocessing import LabelEncoder import numpy as np # 数据预处理(标签编码,适配分类任务) le = LabelEncoder() y = le.fit_transform(y) # 模型配置(结合以上所有规则) model = DecisionTreeClassifier( criterion="entropy", # 不平衡数据,用entropy更敏感 max_depth=6, # 电信流失规律,6层足够 min_samples_split=0.005, # 70000行×0.005=350个样本,保证统计意义 min_samples_leaf=0.002, # 每个叶子至少140个样本 max_features="sqrt", # 18个特征,每次考虑4个左右 class_weight="balanced", # 自动平衡流失/非流失客户权重 random_state=42 # 结果可复现 ) # 5折交叉验证(避免过拟合,更可靠) cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) scores = cross_val_score(model, X, y, cv=cv, scoring="f1") print(f"F1分数: {scores.mean():.3f} ± {scores.std():.3f}")阐述: 选用F1分数而非准确率, 原因在于存在14%的流失率, 若预测“不流失”便能够获取86%的准确率, F1分数可以平衡精准度与召回率, 它更能够体现出模型对于流失客户的捕捉能力。
三、辩证分析:没有最优参数,只有最适配的参数
当看完上面所呈现的参数拆解之后, 好多人会陷入到这样一个误区之中, 即认为每一个参数都存在着“标准答案”, 只要依照着去抄写便能够得到好的模型。然而实际上, 决策树进行调参并不存在绝对的最优解, 仅仅存在着适配场景的选择, 这几点进行辩证思考, 是能够帮助你避开认知陷阱的。
首先, 参数调优的核心在于“平衡”, 即平衡准确率与泛化能力, 平衡训练速度跟模型复杂度, 平衡可解释性和性能。比如说, 越大, 模型准确率或许越高, 不过过拟合风险同样越大 ;=速度快, 然而稳定性比不上best, 不存在绝对的好坏, 仅取决于你更看重什么。
其次, 并不存在“万能参数”, 有的只是“适配数据的参数”, 同样是等于5, 在干净的小数据集上, 有可能情况是欠拟合, 在噪音多的大数据集上, 有可能情况是过拟合, 同样是等于, 在轻微不平衡即8比2的数据上, 效果呈现良好态势, 在极度不平衡也就是说99比1的数据上, 或许还需要结合重采样方法。
再者, 调参并非是靠单独进行, 而是相互结合来实现优化。许多人只去调特定参数, 却忽视了参数之间的配合, 最终致使模型出现情况要么是过拟合, 要么是欠拟合。比如说, 当调大某一参数的时候, 适度地增大相关参数, 这样一来既能够留存下更为丰富的规律, 又能够防止出现过度拆分这种状况。
最终, 切勿盲目去追求“高的准确率”。训练集所呈现的准确率为99%, 这不见得就是件好事, 极有可能仅仅是模型记住了那些噪音;测试集的准确率是80%, 然而它能够稳定地复现, 且适配业务场景, 反倒要比90%那种“看似虚高”的准确率更具价值。毕竟而言, 机器学习的关键核心在于解决实际存在的问题, 并非单纯追求数字看起来漂亮。
对于一个问题, 我们能够去思考: 为何是同样的参数, 别人使用时效果呈现良好态势, 可以达到预期, 而你来使用却出现了失败的状况,不尽如人意? 其关键的要点并非是参数出现了错误的情况, 而是在于你并未能够结合自身所具备的数据特点以及业务方面的需求, 只是一味地盲目去照搬别人应用的配置, 毫无自己的判断与思考。
四、现实意义:学会调参,到底能解决什么问题?
不少人认为“调参是无关紧要之事”, 然而事实上, 针对决策树进行超参数调优, 能够径直化解工作里的关键痛点, 带去切切实实的价值, 这同样是它成为数据人必须具备的技能的缘由。
它对于数据从业者而言, 能够解决“因调参靠猜测, 而致使效果不稳定”的痛点, 大量节省试错所需的时间, 往昔调参或许得耗费一整天, 然而在掌握这些规则之后, 半小时便能够寻觅到最优配置, 准确率居然还可以提升百分之十至百分之二十, 能够轻而易举地拉开与普通数据分析师的差距。
对于企业而言, 它具有一种功用, 可使得模型在可靠性这一方面更具保障, 在实用性这一块儿更为突出。比如说, fraud检测模型经过调优之后, 具备了能够多捕捉百分之三十对应与fraud的案例这样的能力, 进而减少企业所遭受的损失;客户流失预测模型在调优之后, 则能够精确地定位那些有着高流失风险的客户,借此提升留存率;贷款审批模型在调优之后, 能够降低坏账率, 以此减少信贷风险。
需知, 决策树乃是所有树模型的根基所在, 更为关键的是, 学会其调参的逻辑, 再去学习随机森林等模型时, 将会有事半功倍之效——要晓得, 这些高级模型的诸多参数, 本质意义上皆是决策树参数的延伸拓展, 就好比随机森林的某些参数, 和决策树的逻辑全然是一致的。
除此以外, 它还能够培育“数据思维”, 并非只是“运行代码、查看结果”, 而是要能够明白模型的底层逻辑, 知晓“为何如此调整”以及“为何这个参数具有成效”, 从“工具操作者”跻身成为“模型设计者”, 这同样是数据工作者长期持续发展的核心竞争能力。
五、互动话题:你调决策树时,最常踩哪个坑?
有过这样经历的人相信不少: 一下午都在调参数, 然而准确率却反倒下降了;参数与别人的全然相同, 可结果却有着极大差别;甚至连该从哪一个参数着手去调都不清楚, 越调越显得杂乱无序。
有人讲, 自身最为经常踩到的坑乃是“忘记设”, 每一回运行得出的结果均不相同, 压根没办法去对比参数所呈现的效果;有人讲, 老是把设弄得太大, 致使模型出现过拟合情形, 测试集的准确率急剧下跌;另外还有人讲, 忽视了, 在面对不平衡数据时, 模型全然毫无用处。
说一下在评论区, 说你调具体的决策树的时候, 最经常踩到哪一个参数所存在的坑, 是这个还是那个, 还有, 有没有什么属于自身总结产生的调相关参数的小技巧?
另外, 要是你正着手于具体的项目, 像是fraud检测, 或是客户流失预测,那就讲讲你的数据集特点, 大家一块儿交流最优调参方案~。