ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

GMM与DBSCAN实战:从K-means不足到聚类算法选型与调参

GMM与DBSCAN实战:从K-means不足到聚类算法选型与调参 “Lecture 5 GMM DBSCAN”——如果你是按顺序追机器学习课程这一讲多半排在K-means之后专门解决硬聚类解决不了的两类问题。我在真实项目里踩过同样的坑做用户分群时K-means 跑出来的几个簇看上去轮廓分明一落到业务人员手里就被质疑“分群边界为什么这么生硬”“中间这群人真的属于某个类别吗”。后来把 GMM 的概率输出引入再用 DBSCAN 处理那些形状不规则的密度区域才把结果做成可解释、可下钻的方案。这篇内容不是课程笔记的复读我会把 GMM 为什么要靠 EM 算法迭代、DBSCAN 的 eps 和 min_samples 到底怎么调结合我实际跑过的离线任务讲透还会把项目里掉过的坑直接摆出来。文章面向三类人正在啃聚类算法的学习者、要做用户画像或异常检测的数据分析同学、以及需要在特征工程里做分桶和标签生产的算法工程师。看完你能直接上手调参也能在下次被问到“为什么不直接K-means”的时候把选择逻辑讲得明明白白。1. 核心思路为什么 K-means 不够用才需要 GMM 和 DBSCAN1.1 我先讲一个翻车现场当时我接到的任务是对一批电商用户做分层。样本量不大大约 8 万用户特征选了客单价、近 30 天购买次数、平均浏览深度做标准化之后直接扔进 K-meansK 用肘部法定了 4。结果出来很尴尬其中一个大簇占掉了 68% 的用户剩下三个簇边缘又特别模糊。我检查过数据分布根本没有明显的球形结构用户的特征在二维投影里呈现的是长条形和月牙形K-means 用质心和等方差假设去切等于硬拿直尺量曲线量出来的边界当然不靠谱。这个场景其实是聚类算法选型里最常见的误区拿着最熟悉的算法去套所有问题。K-means 本质上是每个点硬分配到一个簇簇的形状天然偏向凸集对噪声点也完全没有缓冲能力。遇到非凸簇、密度不均匀、簇与簇之间有重叠的情况K-means 的误差就容易集中爆发。也正是这种挫败感让我把注意力转向 GMM 和 DBSCAN。1.2 两个算法的分工逻辑GMM 解决的是“软边界”问题。它假设数据由若干个高斯分布混合生成每个样本不是被硬性切分而是算出属于每个高斯成分的概率。比如用户 A 有 0.7 的概率属于高活跃簇、0.3 的概率属于中等活跃簇这个概率信息对营销策略的下钻非常有价值。GMM 还可以通过学习协方差矩阵来适应不同簇的形状也就是说它比 K-means 多了一点“变形”能力能拟合椭圆形的簇同时各个簇的尺度可以不同。DBSCAN 则换了一条完全不同的路不看质心只看密度。它把空间里靠得足够近、密度足够高的点连成一片剩下的零散点标记成噪声。这条思路对簇的形状没有任何预设——你给它一个月牙形它给你聚出一个月牙形给它一个环形它也能识别出来。更重要的是它天生带异常点检测能力你不用先清洗噪声再聚类DBSCAN 会在聚类过程中把噪声单拎出来。把两者放在同一讲里本质上是在讲聚类算法的两条延伸路线一条在概率生成模型上做文章把聚类变成密度估计另一条在几何密度上做文章把聚类变成连通区域搜索。两条路线解决问题的思路完全不同但恰好互补。2. GMM 的核心细节概率、协方差与 EM 迭代2.1 模型结构到底在表达什么GMM 的公式很简单p(x) Σ πₖ · N(x | μₖ, Σₖ)。其中 πₖ 是第 k 个高斯成分的混合权重满足所有 πₖ 之和为 1μₖ 是该成分的均值向量Σₖ 是该成分的协方差矩阵。直观地理解就是“总体数据是由 K 个高斯分布按不同比例混合而成的”我们要做的是反推每个分布的参数。我在项目里最看重的是协方差矩阵 Σₖ。sklearn 里 GMM 通过 covariance_type 参数提供了四种设置full 允许每个成分都有自己的完整协方差矩阵能拟合任意椭圆形状tied 让所有成分共享同一个协方差矩阵diag 要求每个成分的协方差矩阵是对角阵特征之间独立spherical 则假设每个成分是球形退化成类似 K-means 的分布。实际用下来用户行为数据的各个特征之间往往有相关性比如购买次数和客单价经常正相关所以我会优先试 full如果样本量不够担心过拟合再用 diag 降参数量。举个直观例子你对一批客户做分群如果只用 K-means你默认每个群是“圆形”群的边界是以质心为圆心的圆。但真实世界里高价值客户可能同时集中在“下单频次高但客单价低”的区间和“下单频次中等但客单价高”的区间两个区间形成两个方向不同的椭圆。GMM 用协方差矩阵正好能抓住这种特征间的相关结构而 K-means 会在两个椭圆交界处产生大量错误分配。2.2 EM 算法一次看懂GMM 的求解不能直接套最大似然估计闭式解因为每个样本来自哪个高斯成分是隐藏变量。EM 算法就是为了处理这种“有隐藏变量”的估计问题。EM 的核心思想是两步交替E 步和 M 步。E 步固定当前的参数对每个样本计算它属于每个成分的后验概率也就是责任度responsibility。假设样本 xᵢ当前成分 k 的参数为 μₖ 和 Σₖ那么责任度 rᵢₖ 正比于 πₖ · N(xᵢ | μₖ, Σₖ)再对所有成分做归一化。这一步相当于把每个样本按概率“软性分配”给各个成分。M 步用这份责任度作为加权系数重新估计每个成分的参数。πₖ 更新为所有样本对成分 k 的责任度平均值μₖ 更新为责任度加权的样本均值Σₖ 更新为责任度加权的样本协方差。这两步反复迭代对数似然函数会单调上升直到收敛。实际调包时你只需要设置 n_components 和 max_itersklearn 在后台自动完成 E-M 循环。但要注意一点EM 对初始化敏感不同的初始点可能收敛到不同的局部最优。这个问题我会在后面的常见错误里详细说。2.3 选多少个成分才合理GMM 里 n_components 就是事先指定的混合成分数可以把它理解为“簇数 K”。选 K 的方法和 K-means 类似但更推荐用信息准则比如 BIC 或 AIC。BIC -2·ln(L) k·ln(n)其中 L 是模型最大似然值k 是模型自由参数个数n 是样本数。BIC 在拟合优度和模型复杂度之间取得平衡值越小越好。我一般会画一条“不同成分数对应 BIC 值”的曲线找拐点或最小值。有过一次经历让我印象很深某数据真实有三个簇但 BIC 曲线在 K6 时才最低原因是数据里存在大量离群的小群体多出来的成分去拟合那些小群体导致 BIC 持续下降。当时我的处理办法是“BIC 差不多的区间里选小 K”比如 K4 和 K6 的 BIC 差距只有 1.5%那我会取 4避免模型过度拟合那些业务上无意义的细分群体。3. DBSCAN 的关键机制与调参逻辑3.1 密度连通是怎么一回事DBSCAN 的定义需要先理解三个角色核心点、边界点、噪声点。算法设定一个半径 eps以及最小邻居数 min_samples。核心点在以该点为中心、半径为 eps 的圆形区域内包含的样本数不少于 min_samples。边界点不是核心点但落在某个核心点的 eps 邻域内。噪声点既不是核心点也不落在任何核心点邻域内的点。在此基础上定义“密度可达”如果点 p 在核心点 q 的邻域内就说 p 从 q 直接密度可达如果存在一串点 p→...→q且相邻点之间直接密度可达那么 p 从 q 密度可达。DBSCAN 把所有相互密度可达的点归入同一个簇。这个机制聪明的地方在于它不关心簇的中心在哪只关心点与点之间的连接。所以能发现任意形状的簇同时在边界点处理上留了余地不像 K-means 那样强制每个点归属到最近中心。3.2 eps 和 min_samples 到底怎么定这两个参数磨掉了多少人的耐心我非常清楚。先说 min_samples一般经验做法是取特征维度的 2 倍或者更大的数。比如二维数据min_samples 取 410 比较常见如果你处理的数据有 20 个特征min_samples 至少取 40否则密度估计几乎没有统计意义。如果知道数据中噪声比例较高可以适当增大 min_samples让核心点定义更严格。eps 是更难的参数。一个经典方法是 k 距离图对每个样本计算它到第 k 个最近邻居的距离将所有距离从小到大排列然后画曲线。曲线斜率的拐点对应的距离就是候选 eps。实际做的时候我通常会设置 k min_samples然后对这个距离数组排序在上升最陡的那个位置找拐点。但我想提醒的是eps 的确定不应该完全依赖拐点还得结合业务语义回归一下。例如我聚的是地理围栏里的配送点距离单位是公里我直接可以知道业务方需要半径 2 公里内的点在一起那 eps 就直接设 2.0不需要依赖拐点。算法参数服务于业务目标这句话放在聚类任务里尤其重要。3.3 DBSCAN 的适用边界DBSCAN 不是万能药。第一它对密度变化大的数据非常吃力。如果数据里一部分簇很密集、另一部分簇很稀疏同一个 eps 无法同时满足两边需求稀疏簇可能被拆散或者密集簇被合并。第二高维空间里“邻域”这个概念越来越稀薄。维度一高点到点之间的距离都趋于相近eps 邻域要么包含全量点要么空无一物密度估计的判别力下降。我做过一个几十维特征的数据集DBSCAN 跑完几乎把所有点都标成噪声后来把维度降成 5 维再用它才正常。第三DBSCAN 不是确定性的。同一数据集样本顺序变化可能导致边界点归属不同簇因为算法对点的访问顺序有依赖。虽然内部点和簇结构相对稳定但如果你特别在意结果完全可复现记得固定样本顺序和随机种子。4. 实操过程拿同一份数据过一遍 GMM 和 DBSCAN4.1 数据和预处理准备为了讲清楚我构造一份简化版的电商用户行为数据。字段包括log_avg_amount平均客单价的对数、purchase_freq近30天购买次数、browse_depth平均浏览深度。对数和标准化处理后我用 PCA 把数据降到二维用于可视化但聚类算法本身在标准化后的原始特征上运行。有一件事必须提醒GMM 对量纲极其敏感不标准化的结果根本无法解释。我用 StandardScaler 把所有特征缩放成均值为 0、方差为 1。DBSCAN 对距离敏感同样需要标准化否则量纲大的特征会主导 eps 邻域判断。这是所有基于距离和概率的聚类算法共用的前置条件别偷懒跳过。4.2 GMM 实操与成分数选择我用 scikit-learn 的 GaussianMixture 按 BIC 曲线选成分数。代码大概长这样from sklearn.mixture import GaussianMixture from sklearn.preprocessing import StandardScaler import numpy as np X_scaled StandardScaler().fit_transform(X) bic_scores [] for k in range(1, 11): gmm GaussianMixture(n_componentsk, covariance_typefull, random_state42, max_iter200) gmm.fit(X_scaled) bic_scores.append(gmm.bic(X_scaled)) best_k np.argmin(bic_scores) 1我跑完的 BIC 曲线在 k3 之后下降趋缓取 k3 作为最终成分数。当年让我长记性的细节是GMM 的 n_init 参数默认只有 1意味着 EM 只跑一组随机初始化非常容易陷进局部最优。我在前面踩坑后养成了习惯n_init10让算法跑多组初始化选似然最大的结果计算量多一点但稳定得多。选定 k3 后看每个样本的后验概率。落在某个簇后验概率高于 0.8 的样本是“清晰分层”用户概率在 0.40.8 之间的属于“边缘用户”没有任何簇概率超过 0.4 的我直接视为“不确定群体”拉出来人工复核。这种处理方式在业务方那里特别好讲因为他们能看到“概率”而不是冷冰冰的标签。4.3 用 k 距离图定 DBSCAN 参数DBSCAN 这边我先取 min_samples 2 * n_features 2 * 3 6然后用 k 距离图找 epsfrom sklearn.neighbors import NearestNeighbors from sklearn.cluster import DBSCAN k 6 nn NearestNeighbors(n_neighborsk).fit(X_scaled) distances, _ nn.kneighbors(X_scaled) k_dist np.sort(distances[:, -1])画出来之后曲线在 1.2 附近有一个明显爬升拐点我设eps1.2, min_samples6跑 DBSCAN。跑完看簇标签分布大约 74% 的样本聚成 4 个簇剩余样本被标记为噪声。这里有一个我调整过的策略第一版 eps1.0 的时候噪声比例飙到 22%簇拆成 6 个明显过碎了。我把 eps 调到 1.4噪声降到 8%但两个小簇又合并在一起业务上没有区分度。最后折中取了 1.2保持 4 个簇和 11% 的噪声这 11% 的噪声点最终单独验证后判定为异常行为用户。这个过程我想强调DBSCAN 的参数不是一次性定死的而要在“噪声比例”和“簇数量”之间来回权衡。如果你发现噪声比例过高多半是 eps 设太小或 min_samples 设太大如果簇少得离谱、所有点连成一片多半是 eps 太大。4.4 两组结果怎么对照使用同一份数据GMM 给出三个概率簇DBSCAN 给出四个密度簇加噪声。很多初学者会纠结到底哪个“对”我的看法是两者回答的问题不同。GMM 擅长把数据切分成可解释的“分层”比如高、中、低活跃用户适合给运营做用户分群DBSCAN 擅长找出“聚集区域”和“离群点”适合做行为模式发现和异常识别。我在项目里的常规操作是先用 DBSCAN 过滤出噪声点把这些点单独标记为异常再把非噪声数据交给 GMM 做细粒度分层。这样既保留了密度聚类的异常识别能力又利用了 GMM 的概率分层能力。如果只跑 DBSCAN那 11% 噪声点可能被当成废弃数据如果只跑 GMM那些异常点会被强行塞进某个概率簇里干扰簇的参数估计。两套模型串联使用的效果明显优于单独任一方。5. 常见问题与排查技巧实录5.1 GMM 常见的三个坑第一个坑初始化不稳定。GMM 的目标函数非凸不同初始化经常收敛到不同结果。解决办法是调高 n_init用多次初始化取最佳结果。如果你发现每次都收敛到不同的标签数量分布大概率是 n_init 太低、数据重叠程度太高。第二个坑协方差矩阵奇异。样本量比特征维度少、或者数据存在完全冗余特征时协方差矩阵可能变成奇异矩阵算法直接报错或出现 NaN。解决办法是检查特征是否有多重共线性先做 PCA 降维或者把 covariance_type 调成 diag。我在项目里遇到过因为不小心把一个非数值 ID 列放进特征导致协方差爆炸的情况排查到最后一身冷汗。第三个坑对异常值敏感。GMM 本质是概率密度拟合极端离群点会把某个成分的均值拉偏、协方差撑大甚至为离群点专门分配一个成分。处理办法是聚类前先过滤或截断极端值也可以先跑一遍 DBSCAN 或孤立森林把噪声挑走。5.2 DBSCAN 常见的三个坑第一个坑eps 选得毫无依据。拍脑袋设 eps0.5 的结果通常是灾难有的数据点距离很近你以为是同一个簇实际因为 eps 太小被拆散了有的数据点密度均匀eps 稍微大一点全连成一片。我建议任何时候都先画 k 距离图不要跳过这一步。第二个坑高维密度失真。前面提过高维里最近邻距离分布非常扁平k 距离图拐点不明显DBSCAN 就失去了判别力。我的经验是特征超过 10 维就先用 PCA 或 UMAP 降到 5 维以内再跑 DBSCAN降维虽然会丢失一点点细节但换来的聚类稳定性非常值得。第三个坑把噪声点直接丢弃。这个坑更偏思维层面。DBSCAN 标记出的 noise不一定是“错误”反而可能是最值得业务关注的对象。我在反欺诈类项目里DBSCAN 分出来的噪声点里搞出了大量人工标注出来的风险样本。噪声不一定是垃圾只是密度上“不太合群”它们往往是异常检测的信号源。5.3 聚类效果评估与选型建议聚类没有唯一正确标签评估很头疼。轮廓系数是常用的内部指标它可以度量簇内紧密度和簇间分离度但要注意它假设簇是凸的对 DBSCAN 找出的复杂形状不一定公平。我常用的做法是同时看轮廓系数、DBIDavies-Bouldin Index和业务留存率。如果业务方明确说“这个分群可以被策略使用”比任何数值指标都有效。选型建议方面我总结一条简单的判断流如果你想给每个样本一个概率归属或者需要协方差结构来拟合不同形状的簇用 GMM如果你的数据簇形状不规则、有大量噪声或者你想顺手做异常检测用 DBSCAN如果你既想概率分层又想识别异常像我前面说的把两者串联起来是最稳的方案。前提永远是一样的理解你的数据、理解你的业务问题而不是拿着工具书按图索骥。最后再分享一个小技巧无论跑 GMM 还是 DBSCAN我都会把聚类结果输出成一张“特征分布 vs 簇标签”的箱线图直接看每个簇在原始特征上的差异。有一次就是靠这张图发现某个簇在所有特征上几乎完全相同只是规模比其他簇小后来查出来是数据采集端的重复记录。算法越自动化人工验证这步就越不能省。
返回列表