机器学习基础篇更新到第十篇,这次聊聚类。聚类是无监督学习的代表,不管你有没有标签,它都能从数据里找到天然的结构分组。我最早接触聚类是在做用户分群的项目里,那时候对它的理解就是“把相似的人放一起”,但真正上手后发现,选算法、定K值、做评估,每一步都有大量细节坑。这篇文章我尽量把这几年的实操经验浓缩成一套完整的方法论,从算法原理讲到工程落地,适合正在学习机器学习基础、或者想在业务里第一次尝试聚类的小伙伴。
1. 聚类到底在解决什么问题
1.1 无监督学习的核心思想
先理清一个概念:有监督学习和无监督学习的本质区别在哪。分类问题有标准答案,比如判断一封邮件是否垃圾邮件,训练集里每封邮件都标好了“垃圾”或“正常”,模型学的是特征和答案之间的映射。聚类完全不同,它面对的数据没有任何标签,我们的任务是在一堆看似杂乱的样本中,发现潜在的“抱团”结构。
我常拿整理衣柜来打比方。分类是你妈告诉你“这条裤子属于休闲裤,那条属于正装裤”,按规则归类。聚类是你面对一柜子衣服,没人告诉你该分类几类,但你自然会发现T恤会放一起、牛仔裤会放一起、外套会放一起——因为它们长得像、材质接近、穿着的场合相似。这个“发现”的过程,就是聚类。
聚类在机器学习基础中的地位不低,因为它几乎是所有探索性数据分析的起点。你拿到一批新数据,不知道从何下手时,先跑一遍聚类看看天然的分群,往往能快速建立对数据的直觉。而且聚类不仅是独立的算法,它还是很多复杂系统的前置组件——推荐系统的用户分群、风控里的异常检测、图像分割里的颜色聚类,底层都是这一套东西。
1.2 从数学视角看聚类问题
把直觉翻译成数学语言,聚类问题的输入通常是一个数据集 (X={x_1,x_2,...,x_n}),每个样本是 (d) 维向量。聚类的目标是把 (n) 个样本划分成 (k) 个簇 (C_1,C_2,...,C_k),要求簇内的样本之间高度相似、不同簇的样本之间差异明显。
这里有一个关键点常常被初学者忽略:相似度的定义决定了聚类的上限。几乎所有聚类算法都需要一个距离度量,而距离度量的选择本身就是一门学问。最常见的欧氏距离 (d_{ij}=\sqrt{\sum_{m=1}^d(x_{im}-x_{jm})^2}) 适合连续数值型特征、数值范围相近的情况;曼哈顿距离 (d_{ij}=\sum_{m=1}^d|x_{im}-x_{jm}|) 对离群值不太敏感;余弦相似度则适合文本向量、稀疏高维数据,它只关心方向不关心长度。
我踩过一个大坑:当年第一次给用户做分群,特征里有“登录次数”(取值范围0到几百)和“付费金额”(取值范围几千),直接把这两个特征丢进K-Means,算出来的分群几乎完全被金额主导,登录次数约等于没有参与聚类。后来把特征做了标准化再跑,分群才变得合理。这件事让我记住了:任何基于距离的聚类算法,特征量纲的一致性都是前提。
2. 四大主流聚类算法:原理、选型与适用边界
2.1 K-Means:最快上手也最容易用错的算法
K-Means是绝大多数人入坑聚类的第一个算法,简单、快、容易理解。它的目标函数是簇内平方误差和(SSE,Sum of Squared Errors):
[ SSE=\sum_{i=1}^{k}\sum_{x\in C_i}|x-\mu_i|^2 ]
其中 (\mu_i) 是第 (i) 个簇的质心。算法流程是:第一步随机选择 (k) 个点作为初始质心;第二步把每个样本分配到离它最近的质心所在簇;第三步重新计算每个簇的均值作为新质心;第四步重复第二、三步,直到质心几乎不再移动或达到最大迭代次数。
理论上很干净,但实际用起来有至少三个限制。
第一,K-Means假设簇是凸的、近似球形的。如果数据是两个嵌套环或月牙形,K-Means会硬生生把一条弧形切成两半,结果非常不符合直觉。处理这种场景要换密度聚类,我会在2.3节细说。
第二,K-Means对初始质心非常敏感。运气不好时,质心初始在两个密集簇之间,最终会收敛到很差的局部最优。标准做法是用K-Means++初始化,它的思路是让初始质心尽量彼此远离,我在实操中明显感觉到K-Means++把结果稳定性拉高了很多。另一个笨但有效的办法是重复跑10到20次,选SSE最小的结果。
第三,K-Means对离群值极其敏感。因为质心的计算是取均值,一个远离群体的异常点会把质心拽歪。分析前最好做离群值处理,或者换用K-Medoids这种以中位数代替均值的变体。
2.2 层次聚类:自带层级关系的聚类方法
如果说K-Means给你一张扁平的分类表,层次聚类给你的是一棵完整的家族树。层次聚类分两种方向:自底向上的凝聚式(AGNES)和自顶向下的分裂式(DIANA)。凝聚式更常用,初始时每个样本都是一个独立的簇,每一步合并距离最近的两个簇,直到达到预设的簇数量。
“距离最近”怎么定义,这里学问很大。单链接(single linkage)取两个簇中最近的一对样本距离,容易产生链状效应;全链接(complete linkage)取最远的一对样本,偏向紧凑的簇;平均链接(average linkage)取所有配对距离的平均;Ward法最小化合并后簇内方差的增量,在实操中效果通常最好,我遇到的大多场景选Ward不会出错。
层次聚类最大的好处是树状图(dendrogram)可解释性极强。你可以从树状图上直观看到哪些样本在哪个层次聚到一起,然后根据自己的业务需求选择切割的高度——相当于事后决定K值,不用像K-Means那样提前拍脑袋定K。代价是复杂度高,朴素实现是 (O(n^3)),优化后也有 (O(n^2)) 级别,样本几万时就开始吃力,所以我一般只在两万以内的数据集上用它。
2.3 DBSCAN:处理任意形状和噪声的利器
DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是“密度聚类”的代表,它的核心思想很简单:簇是样本密度高的区域,被密度低的区域隔开。它把样本分成三类:核心点(半径 (\varepsilon) 内的邻居数不小于 min_samples)、边界点(在某个核心点的 (\varepsilon) 邻域内,但自己的邻居数不够)、噪声点(既不是核心也不是边界)。
参数只有两个,(\varepsilon)(邻域半径)和 min_samples(判定核心点的最少邻居数),调参逻辑很直观。
DBSCAN不需要预指定簇数,能发现任意形状的簇,还能自动识别噪声点,这些都是对K-Means的重要补充。它也明显不适合两个场景:一是数据密度差异极大时,固定的 (\varepsilon) 没法兼顾稠密区和稀疏区,容易出现“这头全是噪声、那头全糊在一起”的窘境;二是高维数据中距离度量会失真,(\varepsilon) 几乎无从选起。另外,如果业务里每个簇都要有一个“中心”去解释(比如每个用户簇的钱花在哪),DBSCAN给出的形状不一定能满足这种解释需求。
2.4 高斯混合模型:软聚类的代表
K-Means给每个样本一个硬标签,非A即B。但现实中很多数据是模棱两可的——一个人既像高活跃用户又像沉睡用户,你很难一刀切。高斯混合模型(GMM,Gaussian Mixture Model)用概率来解决这个问题。
GMM假设整个数据集是由 (k) 个高斯分布混合生成的,每个高斯分布对应一个簇。它要估计的参数是每个高斯分布的均值 (\mu)、协方差矩阵 (\Sigma) 和混合权重 (\pi),目标函数是最大化所有样本的似然:
[ L(\theta)=\sum_{i=1}^{n}\log\left(\sum_{j=1}^{k}\pi_j \mathcal{N}(x_i|\mu_j,\Sigma_j)\right) ]
这个式子直接求导困难,用EM算法(期望最大化)迭代求解。E步根据当前参数计算每个样本属于每个高斯的后验概率,M步用这些概率加权更新参数,如此循环直到收敛。最终输出的每个样本不是“属于簇A”而是“属于簇A的概率是0.7,属于簇B的概率是0.3”。
GMM的软聚类能力在业务中特别有用。我曾经做过一个物流调度项目,车辆装载任务分配时,某些订单模糊地匹配多个区域的车辆。用GMM输出概率后,系统就可以结合车辆剩余运力做多目标分配,效果比硬切分好得多。
四大算法选型时我一般这样判断:
| 算法 | 适合场景 | 优点 | 缺点 |
|---|---|---|---|
| K-Means | 样本量大、簇近似球形、需要快速出结果 | 快、简单、可解释 | 需预先定K、对异常值敏感、只能找球形簇 |
| 层次聚类 | 样本量小、想要层级结构、需要可视化决策 | 树状图直观、不用预先定K | 计算复杂度高、不适合大数据 |
| DBSCAN | 簇形状不规则、噪声明显、无需预知簇数 | 自动识别噪声、任意形状 | (\varepsilon) 难调、高维效果差 |
| GMM | 簇有重叠、需要软概率输出 | 软聚类、可输出概率 | 计算量较大、可能收敛到局部最优 |
3. 完整跑通一个聚类项目
3.1 标准化与清洗:决定聚类成败的第一步
很多教程的Demo把聚类写得像三五行代码的事,sklearn里fit一下就算完。真实项目远没那么简单,数据预处理的质量直接决定聚类结果的上限。
先做清洗。缺失值要处理,一般用中位数填充分类变量、均值填充连续变量,也可以用预测模型填补。离群值要排查,简单的方法是画箱线图看特征分布,或者用IQR(四分位距)准则把超过 (Q_3+1.5 \times IQR) 的点单独拎出来判断。别的项目离群值可能是要剔除的垃圾,聚类项目里离群值有时候反而是最值得关注的群体——它们也许是异常交易、极端高价值用户,先别急着删,做一次单独分析更稳妥。
再谈标准化。这一步是硬性的,尤其对K-Means、GMM这类基于距离的算法。我常用两种:Z-score标准化 (x'=(x-\mu)/\sigma),适合数据近似正态时;Min-Max归一化 (x'=(x-\min)/(\max-\min)),适合知道上下界的特征。如果你后续要做聚类可视化,Min-Max能保持相对空间位置更直观。选择标准化的参考是:簇的划分更多依赖特征的相对差异,而不是绝对大小,所以把每个特征放到相近的尺度上是必须的。
有个容易犯的错:先划分训练集和预测集再标准化,还是先整体标准化再划分?聚类通常是探索性分析,我会在全部数据上做标准化,但如果你想评估聚类在新数据上的稳定性,就必须在训练集上计算 (\mu) 和 (\sigma),再用同一组参数去转换新数据,避免信息泄漏。
3.2 特征探索与降维:用可视化建立直觉
聚类模型本身不会告诉你“哪些特征重要”,它只会盲目地把所有特征纳入距离计算。盲目塞入大量无关特征,会让真实的分群信号被淹没,这被称为“维度灾难”。我建议先做两件事。
第一,特征相关性分析。两两计算皮尔逊相关系数,相关系数超过0.9的特征对,留下一半就行。实际业务中“消费金额”和“累计订单金额”往往是近乎共线的,留着它们相当于同一个特征在距离公式里被加了两次权重。
第二,用降维可视化来摸数据底细。PCA降维到2维或3维后画散点图,先肉眼看有没有抱团趋势。有时你会发现数据根本没有明显的簇结构——比如都是均匀分布的一团,这时聚类大概率没有意义,趁早调整特征比硬调参数更明智。t-SNE和UMAP也适合做可视化,但要注意它们会扭曲距离关系,只能当探索工具,不能直接把t-SNE的结果当成聚类依据。
3.3 确定簇数量:肘部法、轮廓系数与业务判断
K-Means和GMM都需要预先定K,这个K怎么定?教科书会教你肘部法:对不同K值跑K-Means,记录SSE,画成折线图,找曲线下降速度变缓的“肘点”。我实测下来,真实数据的SSE曲线往往平滑得像个斜坡,肘点并不明显,这时候需要结合其他指标交叉验证。
轮廓系数(Silhouette Coefficient)是更可靠的参考。对每个样本,计算它到同簇其他样本的平均距离 (a) 和到最近其他簇样本的平均距离 (b),轮廓系数 (s=(b-a)/\max(a,b)),取值范围在-1到1之间。接近1表示簇内距离小、簇间分离好。把K从2试到10,画出每个K对应的平均轮廓系数,选峰值对应的K。实际操作里,我还会看每个簇的样本量是否均衡,如果跑出某个簇只有两三个样本,即使整体轮廓系数很高也不能用——业务上没法解释一个两三个人的群组。
最后一步是业务判断,这在机器学习基础篇里容易被忽略,但恰恰是最关键的。你做用户分群,K=5时运营能记住每个群的特征,K=7时运营开始分不清楚,那K=5就是最终答案。技术指标只能缩小范围,拍板的人必须是懂业务的人。
3.4 结果评估与簇画像
聚类没有标签,怎么知道结果好不好?三类评估手段配合使用。
第一类是无监督内部指标,除了轮廓系数,Calinski-Harabasz指数(簇间离散度与簇内离散度之比,越大越好)和Davies-Bouldin指数(簇内平均距离之和与簇中心距离之比,越小越好)也常用。不过内部指标带有各自假设,不能完全信任。
第二类是稳定性检验。把数据随机切两半,分别做聚类,比较两次聚类结果的分群一致性。如果换个子集结果就大变样,说明模型学到的是噪声,不是结构。也可以用Bootstrap抽样反复跑几十次,看每一对样本被分到同一个簇的频率,频率高说明这个分群是稳的。
第三类是业务验证,也是我最看重的。聚类算完必须做簇画像分析:对每个簇统计各特征的均值、中位数、占比,写成一段“画像描述”。比如用户分群里“簇0:高活跃低消费,集中在20-30岁,偏好海外购”,这个描述如果能回答业务方的问题,聚类才算真正落地。有时候业务方会拿画像去和已有的运营经验对照,大部分吻合、少部分有启发,这基本就是一次成功的聚类了。
4. 排雷实录:聚类项目里的典型坑
4.1 初始化漂移:为什么每次跑出来结果都不一样
刚接触K-Means的人都会遇到一个迷惑:代码没改,数据没变,今天跑和昨天跑的聚类结果差很多。原因是K-Means基于随机初始化,而SSE的非凸性质决定了它可能收敛到不同的局部最优解。
解决办法很直接。第一,用K-Means++初始化,它极大地降低了随机初始化的影响。第二,固定随机种子,在sklearn里设置random_state=42,保证复现。第三,多重启动法,跑20次取SSE最小的结果,这是最稳妥的做法。我还见过有人直接把K-Means的n_init参数调大到50,实测在中等规模数据上耗时还可以接受,结果稳定性丝滑。
4.2 标准化失效:数据的分布形态超出算法假设
标准化做了、离群值清了、K也精心选了,聚类结果依然一塌糊涂,这时要想想是不是算法本身的假设不合适。K-Means假设簇是球形的,当你把长条形的簇硬切成两半时,怎么调参都没用。
我做过一个城市交通数据项目,车辆轨迹点呈狭长的带状分布,K-Means跑出的簇是一段一段的切块,没有实际意义。换成DBSCAN之后,带状的密集区域自然连成一条簇,结果立刻合理了。所以,跑聚类之前先做两类检查:一是用PCA降维可视化观察簇形态,二是在小样本上快速试跑DBSCAN和K-Means对比一下结果。聚类这种探索性任务多试试不同算法,比在单一算法上死磕有效率得多。
4.3 DBSCAN的ε参数怎么选
DBSCAN的min_samples通常设成特征维度的两倍左右,比如2维数据设4,高维数据要相应加大。真正难的是 (\varepsilon)。(\varepsilon) 太小会导致大量点被标成噪声,(\varepsilon) 太大会把所有点合并成一个簇。
实操里的标准方法是画k-distance图。步骤是:把min_samples先定下来,计算出每个样本到第k个最近邻的距离,对这些距离从小到大排序画折线。理想情况下,曲线的拐点处对应的纵坐标就是合适的 (\varepsilon)——这个拐点之前是簇内密集区,之后是簇间稀疏区。这个方法不是万能的,数据密度梯度平缓时拐点不明显,这时候我会先选一个让绝大多数样本(比如90%以上)不是噪声的最小值,再做微调。
4.4 无标签世界里的验证难题
没有标签意味着没有准确率,这是聚类评估最尴尬的地方。内部指标只是数学度量,无法保证业务含义。我见过一个极端案例:某个全球分布的用户数据集,轮廓系数最优的那个K值跑出来的簇,基本是按国家地理划分的,技术上堪称完美,但业务方完全用不上。
我给读者的建议是三层防线:第一层用内部指标粗筛K值;第二层用稳定性检验筛掉那些对数据扰动敏感的配置;第三层也是最重要的一层,找业务方一起设计一个“决策指标”——比如分群后在某个营销活动上的响应率差异是否显著,或者分群后客服人力是否分配更合理。聚类结果是业务动作的输入,业务反馈才是终极裁判。
以下把典型问题整理成速查表:
| 现象 | 可能原因 | 排查思路 |
|---|---|---|
| 每次结果不一样 | K-Means初始化随机 | 用K-Means++、固定random_state、多重启动 |
| 簇里有一个是明显的杂混 | 离群值干扰质心 | 检查离群值、考虑K-Medoids |
| 两个特征主导了聚类 | 特征量纲差异大 | Z-score或Min-Max标准化 |
| 数据呈弯曲带状但K-Means切碎 | 算法假设与数据不符 | 换DBSCAN或GMM |
| DBSCAN基本全是噪声 | (\varepsilon) 过小 | 画k-distance图选拐点 |
| 高维数据全糊成一团 | 维度灾难 | 降维、换余弦距离、增加样本 |
| 指标很好但业务不认 | 聚类簇不具业务含义 | 做簇画像、换特征、调整K |
5. 聚类在真实业务中的落地经验
5.1 用户分群:从算法到运营动作
用户分群可能是聚类最常见的落地场景。标准做法是构建用户特征矩阵,经典框架是RFM(最近一次消费时间Recency、消费频次Frequency、消费金额Monetary),扩展后还可以加入浏览量、收藏数、优惠券使用率等特征。
特征构建阶段有个细节常被忽视:RFM三个特征的分布通常是长尾的,少数用户贡献了大部分金额。此时直接用原始值聚类,少数高价值用户会独成一簇,普通用户全挤在一簇。建议对特征做对数变换 (\log(1+x)),把长尾压一压,聚类效果会好很多。标准化之后跑K-Means,K值用轮廓系数加业务方讨论来定。
曾经有一个电商项目,聚类把用户分成六群,其中有一个“沉默高潜”群——近30天未消费、但历史客单价高、浏览活跃。运营组针对这个群设计了一条唤醒短信策略,次月这批用户的回流率比大盘高出不少。聚类的好结果往往不是“算出新东西”,而是把业务方隐隐感觉但说不清的用户群体,用数据明确地定义出来了。
5.2 图像分割与文本主题
图像分割是聚类的经典应用,最直白的做法是把像素颜色作为特征向量(RGB三个值),对全图像素做聚类。K-Means跑完后,每个像素的簇标签就代表它属于哪一类颜色区域,把同一个簇的像素映射成同一颜色,就实现了分割。
我做过一个简单的街拍图分割实验,K设3到5时可以把天空、建筑、地面大致分开,效果出乎意料地好。这个思路在医学切片分析、卫星遥感分类中都有应用。一张大图几百万像素,K-Means的线性复杂度能扛住,但要注意RGB空间感知不均匀,改用Lab颜色空间效果会更好,官方文档没提过这些,是我试出来的。
文本主题聚类用TF-IDF或词向量把文档变成向量,再做聚类。K-Means适合短文本,LDA这类主题模型适合长文本。聚类结束后,主题词怎么提取?可以看每个簇的中心向量,取分量最大的几个词,或者对簇内文档做词频统计,去掉停用词后选出top词。主题发现类的聚类,业务方最关心的往往不是“分得准”,而是“主题词能不能一句话解释清楚”。如果一个簇的中心词既有“耳机”又有“运费险”,说明这个簇混入了两种意图,需要回到特征细化。
5.3 给新手的几点个人心得
写到这里,分享几个我在无数个项目里反复验证的心得。
第一,聚类永远是探索工具,不是最终答案。它输出一个分组假设,你必须用业务反馈来验证。不要试图靠一个聚类模型走通所有环节,更不建议一上来就用复杂的深度聚类模型。先跑K-Means和DBSCAN,基本能覆盖80%的问题。
第二,先可视化,再谈调参。把数据降维画图,肉眼观察到的簇结构比任何指标都直观。哪怕数据是100维,用PCA看前两个主成分的散点图都很值得。如果肉眼都看不到分离趋势,模型大概率也分不出来,这时要回到特征工程而不是调参。
第三,记录每一次实验的随机种子和参数组合。聚类实验反复调整太频繁,不记录的话,三天后你自己都说不清当前结果是从哪组参数来的。我用一个简单的表格记录K值、算法参数、标准化方式、内部指标分数、业务结论,复盘时效率翻倍。
最后说说调参的底线思维。聚类算法都有假设,K-Means假设球形簇,DBSCAN假设密度均匀,GMM假设高斯分布。数据违反假设时,再精妙的调参也补不回来。我的习惯是拿到数据先试两三种算法,看哪个方向和业务直觉最接近,然后把精力集中在一个方向深耕。这个系列写到聚类,机器学习基础的无监督篇章算是开了个头,后文细聊主题模型和降维的话,很多聚类里积累的经验都还用得上。
在实际工作中,我最喜欢聚类的地方在于它总能在数据里发现一些意想不到的东西。那批分析电商物流数据时,聚类把某个区域的订单聚集成了一个独立簇,后来一查是新建的仓储中心覆盖了新区域,系统里还没来得及更新分区规则。聚类就是这样,它不追求精确,但常常给你一个意想不到的视角,让你注意到那些原本被平均掩盖的细节。