ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

决策树、集成学习与聚类:从原理到实战的机器学习主线

决策树、集成学习与聚类:从原理到实战的机器学习主线 我把标题拆开说一句决策树、集成学习、聚类这三个词放在一起基本就是一份机器学习入门到进阶的主线菜单。单看任何一个都是大部头但把它们放一块儿学反而能理出一条特别清晰的脉络——先会用一棵树做判断再发现单棵树太弱于是用集成方法把一堆树凑起来投票最后手里没有标签数据时又靠聚类把样本自然分组。这个顺序我当年走了不少弯路现在回头看其实每一步都踩在前一步的肩膀上。这篇就按这个思路把这三大块从原理到落地串一遍中间穿插一些我实际调参、跑实验时攒下来的细节和教训。1. 先搞清楚决策树在干什么它其实是套人类判断习惯的翻版很多人第一次接触决策树被一堆信息增益、基尼系数的名词吓住。我换个说法你就懂了决策树模拟的就是人类做决定时“先问哪个问题最管用”的思维方式。比如判断一个人年收入是否超过5万你会先问什么大概率不会先问“他平时喝美式还是拿铁”而是会先问“学历是什么”“从事什么行业”。因为学历和行业对收入的区分度更大。决策树做的事就是在每个节点上自动找出“当前最能把数据分开的那个特征和阈值”然后一直分下去直到叶子节点里的样本足够纯净。1.1 三个分裂指标信息增益、增益率、基尼系数到底怎么选教科书写得正经我给你捋成人话信息增益ID3衡量“分裂之后不确定性减少了多少”。不确定性用信息熵表示熵降得越多说明这个特征越能切中要害。缺点是它天然偏向取值多的特征比如“身份证号”这种每个样本一个值的特征信息增益一定最大但毫无泛化意义。增益率C4.5就是为了治ID3这个偏科病。它在信息增益基础上除以一个“特征自身取值数带来的惩罚项”取值越多的特征被惩罚得越狠。基尼系数CARTCART树用的是基尼指数它衡量的是“从数据集里随便抽两个样本它们的类别不一致的概率”。这个值越小数据集越纯。相比熵要算对数基尼指数只做平方计算速度快这也是sklearn里DecisionTreeClassifier默认用基尼的原因之一。拿一组简单数据算一遍你就通了。假设有10个样本5个高收入、5个低收入。现在有一个特征“是否硕士学历”把数据切成两堆硕士组4个高收入、1个低收入非硕士组1个高收入、4个低收入先算分裂前的基尼类别五五开基尼 1 - (0.5² 0.5²) 0.5。再算分裂后的加权基尼(5/10) × [1 - (4/5)² - (1/5)²] (5/10) × [1 - (1/5)² - (4/5)²] 0.5 × 0.32 0.5 × 0.32 0.32。基尼从0.5降到0.32说明这个特征带来的纯度提升很明显值得作为分裂点。如果是连续特征比如年龄sklearn会先排序然后在每个相邻值中点尝试切分选基尼下降最大的那个阈值。1.2 一棵树完整长出来的过程从根到叶子的每一步都在干什么决策树的生长是个贪心过程——每一步只考虑当前节点的最优分裂不做全局最优的回头调整。流程是这样的从根节点开始包含全部训练样本。遍历每个特征的每个可能取值按分裂指标算一遍分裂质量。选分裂后“纯度提升最大”的特征和阈值把样本分到左右两个孩子节点。对每个孩子节点递归重复第2、3步。触发停止条件就停节点里样本全属于同一类、没有可用特征了、样本数小于预设的min_samples_leaf或者树深达到max_depth。这个流程听起来简单但有个关键点容易被忽略决策树对特征数值的绝对值不敏感对特征的量纲也不敏感因为它本质上是按阈值比较大小不做距离运算。所以决策树不需要像SVM那样做标准化。这也让它成了“拿到数据先跑一把看效果”的首选模型。2. 从零手写一棵决策树的真正价值搞懂它你才能驾驭sklearn热搜词里有“决策树原理与实现-python版”“决策树原理与实现头歌”这一类说明很多人正处于需要手写实现的阶段。我特别支持这个动作——不是让你在生产环境重复造轮子而是手写一遍能逼你把分裂逻辑彻底想明白。sklearn封装得太好fit一下全出来反而容易让你把它当黑盒。2.1 手写版的核心骨架一个能算基尼、能找最佳分裂点的类下面这版我精简过保留最核心的逻辑对新手友好同时又能跑通小型数据集。它包含三部分计算基尼、搜索最佳分裂点、递归建树。import numpy as np from collections import Counter class DecisionTreeClassifier: def __init__(self, max_depthNone, min_samples_leaf1): self.max_depth max_depth self.min_samples_leaf min_samples_leaf self.tree None def _gini(self, y): # 计算一个节点里的基尼系数 counter Counter(y) total len(y) gini 1.0 for count in counter.values(): p count / total gini - p ** 2 return gini def _best_split(self, X, y): best_gain -float(inf) best_idx, best_thr None, None parent_gini self._gini(y) n_total len(y) n_features X.shape[1] for idx in range(n_features): values np.unique(X[:, idx]) # 连续特征在相邻取值中点尝试切分 thresholds (values[:-1] values[1:]) / 2 for thr in thresholds: left_mask X[:, idx] thr right_mask ~left_mask if left_mask.sum() self.min_samples_leaf or right_mask.sum() self.min_samples_leaf: continue gini_left self._gini(y[left_mask]) gini_right self._gini(y[right_mask]) weighted_gini (left_mask.sum() / n_total) * gini_left (right_mask.sum() / n_total) * gini_right gain parent_gini - weighted_gini if gain best_gain: best_gain gain best_idx, best_thr idx, thr return best_idx, best_thr, best_gain def _build(self, X, y, depth): # 停止条件全同类 / 深度到顶 / 没法再切 if len(np.unique(y)) 1: return Counter(y).most_common(1)[0][0] if self.max_depth is not None and depth self.max_depth: return Counter(y).most_common(1)[0][0] if len(X) 2 * self.min_samples_leaf: return Counter(y).most_common(1)[0][0] idx, thr, gain self._best_split(X, y) if gain 0: return Counter(y).most_common(1)[0][0] left_mask X[:, idx] thr tree_node { feature: idx, threshold: thr, left: self._build(X[left_mask], y[left_mask], depth 1), right: self._build(X[~left_mask], y[~left_mask], depth 1) } return tree_node def fit(self, X, y): self.tree self._build(np.asarray(X), np.asarray(y), 0) return self def _predict_one(self, x, node): if not isinstance(node, dict): return node if x[node[feature]] node[threshold]: return self._predict_one(x, node[left]) return self._predict_one(x, node[right]) def predict(self, X): return np.array([self._predict_one(x, self.tree) for x in np.asarray(X)])这个版本有几个设计值得说阈值取相邻值中点这是处理连续特征的标准做法其实就是sklearn里采用的近似思路。min_samples_leaf提前拦截当样本量不足两倍叶子下限时直接停止防止分出极端小的节点。gain 0就停白话讲就是“切了跟没切一样”强行切反而引入噪声。2.2 手写版和sklearn版跑同一份收入预测数据差距在哪用这个手写版和sklearn的DecisionTreeClassifier同时跑收入预测这类二分类任务我做过对比。数据规模不大几千条手写版的精度大概比sklearn低3到5个百分点。差异主要来自几个地方sklearn对连续特征的阈值搜索做了直方图优化分裂点找得更细。sklearn支持对类别特征直接按类别分组搜索不会因为one-hot把特征空间撑爆。sklearn内置了剪枝策略手写版如果不加max_depth限制更容易过拟合。但有意思的是如果你给手写版也加上合理的深度限制比如max_depth5差距会缩小到1到2个百分点。这说明决策树的核心能力在于“分裂逻辑本身”工程优化属于锦上添花。理解到这一层你就不再怕面试官问“决策树原理”了——你脑子里有从熵到分裂再到递归建树的完整链路。3. 决策树最容易翻车的三个地方剪枝、数据泄漏、可解释性的真相3.1 剪枝为什么一定要做任何不设上限的树都是过拟合机器决策树不剪枝的话理论上可以把每个训练样本都分到一个专属叶子里。听起来拟合得很好但换个数据集立刻原形毕露。这是决策树和线性模型最大的区别线性模型天生欠拟合倾向大决策树天生过拟合倾向大。剪枝分两种思路预剪枝在建树过程中提前停止。限制max_depth、min_samples_split、min_samples_leaf本质都是预剪枝。好处是省时间坏处是“目光短浅”——当前分裂收益不大但后续分裂可能收益显著预剪枝容易欠拟合。后剪枝先把树建完整再自底向上把对验证集提升不明显的子树替换成叶子节点。sklearn里对应的就是ccp_alphacost complexity pruning代价复杂度剪枝。这相当于让树先充分表达再回头看哪些分支是噪音效果通常比预剪枝更稳。面试题里高频的是“预剪枝和后剪枝的区别”答题要点就两条预剪枝边建边停效率高但可能欠拟合后剪枝建完再裁效果更稳但计算量大。实际项目中我一般两个都用先把max_depth限制到一个合理范围再用ccp_alpha筛选最优子树。3.2 数据泄漏才是树模型最隐蔽的坑你以为的“高精度”其实是假象决策树的特征选择完全靠数据驱动这意味着它会把“和标签有相关性但实际不可用”的特征当成宝。我见过一个真实案例有人用决策树预测用户是否流失特征里包含了“用户最后一次投诉距今天数”。模型精度高得离谱上线后一查才发现只有已流失用户才会被系统打上投诉标签未流失用户这个字段全是空值。模型学到的其实是“这列是不是空值”。这就是典型的数据泄漏——特征里包含了未来才发生或标签衍生出来的信息。检查方法也很简单训完模型看特征重要性排序如果排名靠前的特征你觉得“业务上不该这么强”就要警惕了。对抗方式一是做特征工程时就想清楚每个特征在预测时刻是否真实可得二是做时间序列类任务时严格按时间切训练集和验证集绝不做随机打乱因为随机打乱会把未来的信息混进训练集。3.3 可解释性不是“能画出一棵树”就算数很多人说决策树可解释性强我一度也这么认为直到我拿一棵深度20的树去给业务同事讲。树画出来好几屏根本没人看得完。决策树的可解释性只在小规模深度小于等于5时成立一旦树变大它的解释能力迅速衰减最终还不如线性模型的系数来得直接。如果真要向别人解释一棵复杂树实际管用的做法是输出决策路径对某一个具体样本把从根节点到叶子节点的判定条件逐条打印出来。这等于告诉对方“模型对这个样本做判断时问了哪几个问题、每个问题的答案是什么”。这比甩出一整棵树图实用得多。sklearn里可以用decision_path方法来取这条路径。4. 单棵决策树不够看集成学习才让树模型真正封神如果说单棵决策树是个容易钻牛角尖的新手集成学习就是拉了一群各有偏执的专家来开会少数服从多数。集成学习两大流派——Bagging和Boosting——解决问题的思路相反效果却都出奇地好。4.1 Bagging和Boosting的本质差异并行纠错还是串行补差Bagging以随机森林为代表并行训练多棵相互独立的树每棵树用不同的自助采样数据有放回抽样最后投票或取平均。它解决的问题是“降低方差”——单棵树波动大但100棵树的平均波动就小得多。随机森林在Bagging之上又加了一层随机性每棵树分裂时不是看全部特征而是随机抽一部分特征再找最佳分裂点。这一招让树与树之间的相关性进一步降低集成的泛化能力肉眼可见地提升。Boosting以AdaBoost、GBDT、XGBoost为代表串行训练后一棵树重点学前一棵树预测错的样本。它解决问题的方向是“降低偏差”——单棵树太简单学不到位就用一堆树接力补差。GBDT每一轮用负梯度近似残差当伪标签让新树去拟合残差XGBoost在GBDT基础上加了二阶导、正则项和列采样速度更快、精度更高。用一个例子说透两者差异假设一个班成绩整体中等但每个学生都有偏科。Bagging是组几个小组各自独立做题最后按多数答案定结果——适合水平参差但各有所长的情况。Boosting是第一个人做完第二个人盯着他做错的题重点复习第三个继续盯前两个人共同的薄弱点——适合大家水平都一般但可以逐步查漏补缺的情况。4.2 随机森林实战收入预测案例的完整调参链路用sklearn跑一个收入预测收入是否超过50K完整链路大概是from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split, GridSearchCV from sklearn.metrics import classification_report, roc_auc_score # 假设 X 是已经处理好编码、缺失值填充的特征矩阵y 是二分类标签 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) # 先跑一个基线模型观察默认参数下的表现 rf_base RandomForestClassifier(n_estimators100, random_state42) rf_base.fit(X_train, y_train) y_pred_base rf_base.predict(X_test) print(classification_report(y_test, y_pred_base)) # 观察过拟合程度比较训练集和测试集精度 train_acc rf_base.score(X_train, y_train) test_acc rf_base.score(X_test, y_test) print(fTrain acc: {train_acc:.4f}, Test acc: {test_acc:.4f})如果train_acc约等于0.99而test_acc只有0.86说明严重过拟合优先调三个参数max_depth限制单棵树深度。对收入预测这种有大量类别特征的数据推荐从5开始往上试。min_samples_leaf叶子节点最小样本数。从3调到5甚至10往往能明显压低过拟合。max_features每次分裂看的特征数。默认是sqrt(n_features)可以试着调小到0.3~0.5倍让树之间更“独立”。调参时别用网格搜索一把梭把所有参数都丢进去会非常慢。我的习惯是先固定n_estimators500粗搜max_depth和min_samples_leaf确定这两个之后再微调max_features最后再看n_estimators对验证集误差的收敛曲线。4.3 从GBDT到XGBoost集成学习为什么不满足于单棵树GBDT这类Boosting模型的核心公式看着抽象理解它有个很实用的切入点每一轮在拟合的是前面所有树的残差。当你有100个样本第一棵树预测完发现10号样本和67号样本误差最大第二棵树就重点学这两个样本的规律。第三棵树看前两棵联合作业后谁还在错继续往前补。这就是“串行纠错”的数学含义。XGBoost在工程上做了几个关键升级我挑面试里最爱问的三点目标函数加了正则项树的叶子节点数和叶子权重平方和都会被惩罚。这相当于给每棵树戴了紧箍咒防止某一棵树在局部样本上太放肆。引入二阶导数传统GBDT只用一阶梯度信息XGBoost把损失函数的二阶泰勒展开也用了收敛路径更准。列采样与并行化虽然不是逐样本并行但在特征粒度上做了并行排序加速训练速度比GBDT原生实现快一个量级。现在LightGBM和XGBoost在实际项目中几乎成了默认选择但你要搞清楚一点这些库在底层把“决策树分裂”做了大量近似工程优化比如LightGBM的直方图算法会把连续特征分桶牺牲一点精度换取数量级的提速。在数据量几万条级别的场景里这种优化带来的收益其实不明显经典随机森林完全够用上了百万级数据LightGBM的优势才真正拉开。5. 聚类不是分类的穷亲戚无监督学习怎么在没有标签时挖出结构标题里把聚类和决策树、集成学习并列我估计很多人不太理解。我换个角度说聚类和决策树解决的是不同的问题。决策树解决的是“有标准答案时怎么学”聚类解决的是“没有标准答案时怎么发现规律”。电商做用户分群、风控做异常账户挖掘、文本做主题聚合都是聚类的典型战场。5.1 K-Means的适用边界为什么它最快也最容易用错K-Means的思想朴素到惊人先随机挑K个中心点把每个样本分到最近的中心然后算每个簇的新中心重复直到中心不再移动。它的优点是快、简单、可解释缺点是它隐含假设簇是凸的、大小差不多的球形分布而且对初始中心敏感。from sklearn.cluster import KMeans from sklearn.preprocessing import StandardScaler import numpy as np # 数据标准化几乎是必须的否则量纲大的特征直接主导距离 scaler StandardScaler() X_scaled scaler.fit_transform(X) # K值的选择肘部法则 inertias [] K_range range(2, 11) for k in K_range: km KMeans(n_clustersk, random_state42, n_init10) km.fit(X_scaled) inertias.append(km.inertia_) # 画折线图看拐点在哪 import matplotlib.pyplot as plt plt.plot(list(K_range), inertias, markero) plt.xlabel(K) plt.ylabel(Inertia) plt.show()inertia是每个样本到它所属簇中心的距离平方和K越大这个值必然越小但下降速度会在某个K处骤减这个拐点就是“再多分一个簇也带不来明显收益”的位置。实操里我还会结合业务需求选K——比如给用户分群K5意味着运营可以针对5类人分别出策略K14虽然统计上更精细但运营根本顾不过来这时候业务约束比统计指标更重要。K-Means另一个隐藏坑是初始中心选择。sklearn默认用K-Means策略初始化它会故意让初始中心彼此分散大幅降低落到局部最优的概率。老版本里n_init默认是10sklearn 1.4以后把默认值改成了10就不再显式警告了所以你写代码时最好还是显式设置n_init10保证可复现。5.2 DBSCAN什么时候比K-Means强当你的数据不是“一团一团的圆”DBSCAN和K-Means最大的不同是你不需要提前告诉它有几类。它的核心是两个参数eps邻域半径和min_samples成为核心点需要的最少邻居数。算法逻辑可以一句话概括把距离在eps内且邻居数超过min_samples的点连成一片密度够高的区域长成簇密度不够的孤立点标为噪声。k-means处理不了“环形簇”“月牙形簇”因为它的距离度量天然假设了凸形分布。DBSCAN靠密度连通来定义簇对任意形状都有效。用sklearn里经典的make_moons数据集测一下K-Means会把两个月牙拦腰切断DBSCAN却能沿着密度方向把两个月牙完整分开。from sklearn.cluster import DBSCAN from sklearn.datasets import make_moons X_moons, _ make_moons(n_samples300, noise0.05, random_state42) db DBSCAN(eps0.2, min_samples5) labels db.fit_predict(X_moons) # labels 中 -1 代表噪声点选参数时有个实用办法先算样本间距离矩阵排序后取每个样本到第min_samples个近邻的距离画出k-distance曲线曲线的拐点差不多就是eps的合理值。当然这是手艺活更多时候你还是得结合业务调几轮。5.3 层次聚类为什么适合“需要解释”的场景树状图就是说明书层次聚类分两类自底向上的凝聚法AGNES和自顶向下的分裂法DIANA实际用得最多的是凝聚法。初始每个样本独立成簇然后每次合并距离最近的两簇直到只剩一个簇。它最独特的产品是树状图dendrogram——一张图展示所有样本从个体到整体逐级合并的全过程。sklearn里用scipy.cluster.hierarchy来画树状图和做聚类from scipy.cluster.hierarchy import dendrogram, linkage, fcluster import matplotlib.pyplot as plt # linkage 的 method 参数控制簇间距离怎么算 Z linkage(X_scaled, methodward) plt.figure(figsize(10, 6)) dendrogram(Z, truncate_modelevel, p5) plt.xlabel(样本编号) plt.ylabel(距离) plt.show() # 从树状图上按某个距离阈值切出聚类结果 labels fcluster(Z, t3, criteriondistance)树状图的阅读方式纵轴是合并时两簇之间的距离。你截一条水平线线穿过的竖线数量就是簇的个数。如果某条合并线的高度特别高说明这次合并在拿两个差异很大的簇硬凑到一起往往意味着再往上合并就不合理了。层次聚类的优点是结果稳定、能提供全局视角的层级关系、不需要预设簇数量。代价是复杂度高对几千个样本没问题到几万样本就已经明显吃力几十万样本基本只能靠采样算近似解。6. 决策树、集成学习、聚类之间的关系它们如何在同一个项目里配合很多人学完三大块之后会有个疑问这些算法到底怎么在同一个项目里用我拿一个实际做过的场景来说一个电商平台要做用户精细化运营手里有几万用户的消费记录、浏览行为、客单价、活跃时长等特征但没有任何用户标签。第一步是聚类先用K-Means或层次聚类把用户分成几类。这个分出来的簇可能对应“高客单价低频次”“低客单价高频次”“深夜活跃的冲动型买家”等等。聚类出来的簇标签就成了后续模型的y。第二步是把簇标签当监督信号训练决策树拿原始特征和聚类得到的簇标签训练一棵决策树特征重要性靠前的字段就是“区分用户类型的关键因素”。这一步的技术含量在于它间接完成了聚类结果的业务解释——否则聚类输出一堆编号不知道每类人到底长什么样。第三步是集成学习做精细预测如果你还想预测“某类用户未来会不会流失”可以在对应簇内单独训练随机森林或XGBoost。簇内样本相对同质化模型可以学到更细微的规律。这样做的好处是每个簇一个小模型避免“一刀切”的全局模型在行为差异巨大的用户群上顾此失彼。这个流程之所以成立是因为它精准匹配了三个算法各自的优势聚类擅长发现结构决策树擅长给出解释集成学习擅长榨干预测精度。算法之间不是相互替代的关系而是各管一段、互相补位。7. 期末复习和面试高频点从热搜词看大家真正卡住的地方热搜里出现了大量“机器学习期末复习”“决策树剪枝面试题”“头歌机器学习”这类词说明很多人正处在刷题备考或做实验作业的阶段。我把这些高频点集中做一次梳理按面试官实际会问的逻辑顺序走一遍。7.1 决策树面试题的回答框架面试里最常问的三连是决策树怎么选特征怎么防止过拟合和线性模型比优缺点是什么选特征的回答按分裂指标展开ID3用信息增益、C4.5用增益率、CART用基尼系数并说明CART是sklearn和众多集成学习库的默认底座因为它既支持分类也支持回归且分裂速度快。防过拟合从三方面答预剪枝限制树结构、后剪枝用ccp_alpha的代价复杂度做全局裁减、集成学习配合随机采样降低单棵树的影响。对比线性模型的答案有一个要命的点很多人会漏决策树不需要对特征做标准化或归一化因为它是按阈值比较而非距离计算。线性模型则需要。另外决策树天然支持特征交互——比如“年龄大于30且收入大于5万”这种组合条件线性模型需要手动构造交互项才能表达。决策树的短板是外推能力差训练集里没有的取值区间它预测不了线性模型则可以顺趋势外推。7.2 聚类面试和作业里最常见的计算陷阱期末题里爱考K-Means的手动迭代过程给几个点让你算两轮之后的簇中心变化。这里最容易错的是距离度量没统一——比如算欧氏距离时有一个特征量级特别大计算会被它主导。虽然作业题一般用二维坐标考试不会在这儿埋坑但你要有这个意识。DBSCAN的考点则集中在eps和min_samples的语义上eps太小会把一个完整簇切成碎片eps太大会把明显两团数据合并成一片min_samples越大形成簇的门槛越高噪声点越多。还有一个高频对比K-Means和层次聚类的区别。答题框架K-Means需要预设K、对初始中心敏感、假设簇为凸形、适合大规模数据层次聚类不需要预设K、通过树状图可交互选择切分位置、本质是贪心合并导致复杂度高、适合小规模数据和需要可解释性的场景。7.3 关于“头歌”这类实训平台上作业的实操建议热搜里反复出现“头歌机器学习”说明很多学校的课程作业是在这类实训平台完成的。我直接给几点经验这类平台的判定标准通常是“特定指标达到阈值”比如准确率或F1超过某个数。它看的是结果不是你调参过程所以不要沉迷手写轮子平台允许用sklearn就用sklearn。决策树收入预测这类题数据里如果含年龄、教育年限、职业等字段先做缺失值处理和类别编码这两步做完模型精度往往就能过线。卡住的大部分原因是编码出了问题比如把字符串类别直接传给了fit。如果平台判定“内存超限”或“超时”优先把数据转成float32并用RandomForestClassifier(n_jobs-1)开多核。遇到“决策树原理与实现”这类需要补全代码的题目核心考察点几乎总是结点的分裂函数基尼或信息增益计算和递归终止条件。上面2.1节的手写版代码骨架可以直接套进去改。8. 一条完整的上手路径和实际调参心得看到这里理论框架你应该已经有了。最后聊一条可以照着走的上手路径以及那些不跑几轮实验很难获得的实际手感。如果让我一个人从零开始接触这三个主题我会按下面这个顺序推进先拿一棵树、一个二分类数据集iris或收入预测手动算一次根节点的特征选择把熵或基尼的公式手推一遍。这个步骤不能跳它决定了你后面看集成学习源码能不能坐得住。用sklearn的DecisionTreeClassifier跑通预测流程然后故意不设max_depth跑一次再看训练集和测试集的精度差。亲眼看一次过拟合比背十遍定义都管用。调参时先用GridSearchCV粗搜max_depth和min_samples_leaf再细看ccp_alpha路径画出“验证集精度随ccp_alpha变化”的曲线选精度最高的点。切到随机森林和XGBoost对比同样数据下和单棵树的差距顺便把特征重要性和单棵树对比一下——你会发现集成模型给的特征排名往往比单棵树稳定得多。把标签丢掉做主成分降维可视化再跑一遍K-Means和DBSCAN观察簇结构是否符合直觉。最后一关把聚类的簇标签当y重新训练一棵决策树看特征重要性——这一步能帮你把无监督和有监督串成一条线。最后分享一个我踩过好几次的坑聚类前忘记做标准化。K-Means和DBSCAN都用距离度量数值范围大的特征天然权重更高。比如收入字段是万级活跃时长是个位数如果不标准化聚类结果几乎完全被收入主导画出来像是一刀切出来的分段而不是真正意义上的“人群画像”。sklearn里一句话就能解决scaler StandardScaler()之后fit_transform再做聚类。就这一步我当年漏掉之后折腾了整整两个晚上才怀疑到问题上。这类细节书本上不会写只能靠实战踩出来。
返回列表