尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

亲和传播算法实战:无需预设K值的聚类新思路

发布时间:2026/9/28 22:46:30

资讯中心
01
ARTICLE

亲和传播算法实战:无需预设K值的聚类新思路

亲和传播算法实战:无需预设K值的聚类新思路
1. 聚类的老问题K值从哪来以及亲和传播给出的另一种答案我做机器学习项目这几年聚类算法用过不少但每次落到具体场景都挺头疼。最典型的就是 K-means你得先告诉它“我要分几类”然后它才肯干活。你说 5 类它给你分 5 类你说 8 类它也能给你分 8 类。模型本身没有任何判断能力它只是忠实执行你的指令。问题就出在这——在实际业务里我经常不知道数据到底应该分几类或者说分类数量本来就是业务分析的一部分而不是预先给定的条件。我第一次接触亲和传播算法Affinity Propagation是在处理一批用户行为数据的时候。当时做用户分群业务方给的诉求很含糊“你看看这帮用户大概有几种典型的行为模式。”这个“大概”就是最难办的部分。我用肘部法则看过 K-means 的 SSE 曲线拐点不明显用轮廓系数试过得分都差不多。你甚至可以为了报告凑一个“合理”的 K 值但心里其实没底。后来换用亲和传播算法情况就不一样了。这个算法的特点在于聚类中心是通过数据点之间的“消息传递”自动涌现出来的不需要预先指定类别数。它给你输出的是一组实际存在的、有代表性的数据点作为聚类中心剩下的样本各自归属到离它最近的聚类中心。这跟我之前用过的所有划分式聚类都不同更像是数据自己在“投票”选代表。而且它的好处不止是省去了定 K 的烦恼。因为聚类中心是从原始数据里挑出来的真实样本而不是像 K-means 那样计算出来的“虚拟质心”所以结果的可解释性天然更强。你在给业务方解释的时候可以直接说“这批用户里有这么几个典型样本其他用户的行为模式跟它们最像。”做推荐系统冷启动、做用户画像、做异常模式识别这个特性都非常好用。当然世上没有免费的午餐。亲和传播算法的计算复杂度比 K-means 高不少而且有两个关键参数需要仔细调处理不好容易得到反直觉的结果。不过这些问题都有规律可循这篇文章我就把这两年实际使用亲和传播算法的经验完整梳理一遍从原理到代码从参数调到避坑一次性讲透。2. 消息传递机制拆解Responsibility 和 Availability 到底在传什么要理解亲和传播最关键的是理解它内部那两套“消息”Responsibility责任度和 Availability可用度。我当时是看了原论文才真正搞明白这两个概念只看博客的话很容易被绕晕。这里我用一个尽量不那么学术的方式讲清楚。2.1 用“选举代表”的比喻理解两个消息变量假设你所在的小组要选一个组长候选人出来小组里每个人都可以自荐也可以推荐别人。每个人会同时做两件事一是告诉另一个人“我觉得你挺适合当组长的”这就是 Responsibility 的雏形二是倾听别人给自己的评价看看有多大事。在算法里Responsibility ( r(i,k) ) 表示的是数据点 ( i ) 认为数据点 ( k ) 适合当自己聚类中心的程度。它的计算方式是这样的取 ( i ) 和 ( k ) 之间的相似度通常是负的欧氏距离再减去 ( i ) 和其他潜在候选者之间相似度里最大的那个竞争值。说白了就是“在所有可能的中心里你 ( k ) 对我的吸引力比其他人强多少”如果这个值是正的说明 ( k ) 对 ( i ) 来说确实是个不错的代表。Availability ( a(i,k) ) 表示的是数据点 ( k ) 作为聚类中心这个“意愿”有多大程度是被其他点支持出来的。它取决于两个因素一是 ( k ) 自己给自己积累的 Responsibility 有多少表达“我自己想不想当中心”二是别的点给 ( k ) 传来的正向 Responsibility 累加值表达“其他人都觉得你行不行”。当一个点的可用度值大起来它就变成了一个更有说服力的中心候选。这两个量是相互迭代、相互修正的。每个点既在发送自己的“推荐意愿”又在汇总别人的“支持信号”这很像是一个分布式协商的过程。每次迭代所有的 Responsibility 和 Availability 都会更新一遍直到它们不再大幅变化或者到达最大迭代次数算法停止。提示如果你之前学过 PageRank 或者做过图神经网络里的消息传递会感觉这套机制很亲切。本质上亲和传播就是一种在完整图上做信息汇聚的方法只不过它的“图”是全连接图——每个点都和其他所有点相连。2.2 相似度矩阵是这一切的地基不管是 Responsibility 还是 Availability所有计算都建立在相似度矩阵之上。这个矩阵的第 ( i ) 行第 ( j ) 列就是数据点 ( i ) 和数据点 ( j ) 之间的相似度 ( s(i,j) )。在默认的 sklearn 实现里相似度取的是负欧氏距离即 ( s(i,j) -|x_i - x_j|^2 )。所以点之间距离越近相似度越高越接近 0距离越远相似度越低负得越大。有一个非常关键又容易忽略的地方矩阵对角线上的值 ( s(k,k) ) 代表数据点 ( k ) 自己作为自己聚类中心的“先验倾向”。这个值在算法里叫作 preference偏好度它直接决定了聚类结果里类别数量的倾向。我在实践里发现很多人调亲和传播算法不怎么会调 preference就给它一个默认参数。默认值一般是相似度矩阵的中位数这样模型会自动选择一个“不偏不倚”的类别数。但这个数值往往不是最优解因为它没有考虑业务上你对聚类粒度的需求。后面我会在参数调优那节详细展开。2.3 数据归一化在聚类之前的重要性因为相似度用的是距离平方而距离对量纲非常敏感所以我在实际项目里第一步永远是做标准化。假如你有两个特征一个是年龄范围 0~80一个是月消费金额范围 0~30000那么距离计算时消费金额的数值会完全淹没年龄的差异。这不是亲和传播独有的问题但在它身上尤其严重因为它用负平方距离一个量纲大的特征会让小量纲特征形同虚设。我常用的做法是先做 Z-score 标准化或者用 Min-Max 归一化把数据压到 [0,1] 区间。具体用哪种取决于数据分布一般默认用标准化。如果数据里有明显的长尾分布我会先用对数变换压缩一下再标准化。这些预处理看起来很简单但对最终聚类效果的提升是最直接、最廉价的。3. Preference 与 Damping两个参数决定了聚类结果的成色刚开始用亲和传播的时候我犯过不少错最常踩的就是给了 sklearn 的 AffinityPropagation 默认参数就直接跑。结果出来的类非常多散布得很碎跟业务方期望的“几大类”完全对不上。后来才慢慢摸清楚两个参数是真正的“命门”preference 和 damping。3.1 preference把“倾向度”从中位数往高处调会发生什么preference 是所有数据点自己推荐自己当聚类中心的先验值。它的数值越大每个点越倾向于成为聚类中心最终输出的类别数就越多数值越小负得越多点在当中心这件事上就越“谦虚”最终类别数就越少。我看到很多文章说“preference 越大类越多越小类越少”但没解释一个细节这个大小是相对的不是绝对的。你拿到的相似度矩阵是负值所以 preference 通常也在负数区间。具体效果取决于它和相似度矩阵的整体分布的关系。如果相似度中位数是 -20你设 preference -1那等于告诉每个点“你可以大胆当中心”聚类数量会激增如果你设 preference -1000那等于说“你们都很平庸别老想着当代表”聚类数量会明显收缩。那怎么设一个合理的初值呢我的实践是想得到较少的粗粒度类别可以取相似度矩阵的极小值或极小值的 1.5~2 倍负值更小这样类别数会显著减少。想得到较多的细粒度类别取相似度矩阵的最大值附近或者直接取中位数的 1.1~1.5 倍作为起点。完全没有业务约束时先跑一次默认中位数看基线再根据颗粒度微调。我在做一个商品分层项目的时候业务方希望从几千个 SKU 里分出 5~8 个商品档次。我一开始设 preference 为中位数结果分出来 30 多个类根本没法解释。后来把 preference 压到相似度最小值的两倍附近类数稳定落到了 7 个业务方看了直接拍板。这就是 preference 调优最直接的收益——用参数控制粒度而不是事后拿刀硬切分类结果。3.2 damping振荡问题的“减震器”damping阻尼系数的官方解释是在每次更新消息时让新值的一部分与旧值融合避免迭代过程发生震荡而不收敛。它本质上就是一个梯度下降里的动量系数。sklearn 默认值是 0.5这在很多简单数据集上没问题但如果数据点之间相似度差异不大或者你设置的 preference 过于极端消息更新就可能出现震荡——也就是一次迭代结果全变、下一次又变回来迟迟达不到收敛条件。这时候把 damping 提升到 0.6~0.9 区间消息更新的幅度被压缓迭代过程逐渐平稳下来。我自己的经验是damping0.9 通常能解决绝大多数不收敛的问题但代价是迭代次数明显增加收敛速度变慢。所以别一上来就调到 0.9先试 0.6 或 0.7不行再升。注意damping 并不改变最终聚类结构它只是保证算法能跑完。如果调高之后收敛了、结果也合理那就说明之前确实是在振荡如果调高之后还是不收敛那大概率是 preference 取值范围有问题另外一个就是要检查样本量是否太大——样本超过几千以后亲和传播的计算量会变得很大迭代速度也会非常慢。3.3 收敛条件的判断与最大迭代次数设置sklearn 的 AffinityPropagation 默认最大迭代次数是 200收敛条件是连续 15 次迭代的聚类结果没有变化。这个默认值在面对几千个点时其实不太给力——我测试过在 5000 个样本、全连接距离矩阵上不调参数经常出现“达到最大迭代次数但没收敛”的警告。解决办法就是同时增大 convergence_iter 和 max_iter。我的经验是样本数在 1000 以内max_iter300、convergence_iter20 比较稳样本数到 5000max_iter 建议开到 500 以上。不过最好先通过降采样验证效果不要一上来就全量跑否则计算时间可能会让你怀疑人生。亲和传播需要对 ( N \times N ) 的相似度矩阵做多次乘法运算空间和时间复杂度都在 ( O(N^2) ) 级别这是它的先天瓶颈。4. 用 Python 跑通一个完整案例从随机数据到聚类中心可视化理论讲再多不如直接跑代码。这里我用 sklearn 自带的 make_blobs 生成三团高斯分布的数据然后跑一遍亲和传播把结果可视化出来。你可以在自己机器上直接复现。4.1 生成模拟数据与初始化模型在跑算法之前准备好数据和模型。下面这段代码生成三个中心的样本点然后用负欧氏距离构造相似度矩阵。注意 sklearn 的 AffinityPropagation 允许直接传原始特征矩阵内部会自动算距离矩阵但如果你想用自定义的相似度度量比如余弦相似度就需要自己先算好再传进去。import numpy as np import matplotlib.pyplot as plt from sklearn.cluster import AffinityPropagation from sklearn.datasets import make_blobs from sklearn.preprocessing import StandardScaler # 生成三团数据标准差调小一点让簇的边界清晰 X, y_true make_blobs(n_samples500, centers3, cluster_std0.8, random_state42) X StandardScaler().fit_transform(X) # 构造相似度矩阵负的欧氏距离平方 sim_matrix -((X[:, None, :] - X[None, :, :]) ** 2).sum(axis2) # 设置 preference 为相似度中位数作为基线 preference np.median(sim_matrix) # 初始化模型 af AffinityPropagation( affinityprecomputed, preferencepreference, damping0.7, max_iter500, convergence_iter20, random_state42 )这里我把 affinity 设成 “precomputed”意思是我自己把相似度矩阵算好传进去这样对 preference 的控制更精确。如果你直接用affinityeuclideansklearn 内部会帮你算负距离但不同版本对负距离的实现细节有些微差异我自己还是习惯手动算一遍。4.2 拟合与结果解读拟合之后模型会给出三个关键属性cluster_centers_indices_聚类中心的原始数据点索引、labels_每个样本的簇标签、n_iter_实际迭代次数。af.fit(sim_matrix) cluster_centers af.cluster_centers_indices_ labels af.labels_ n_clusters len(cluster_centers) print(f聚类数量: {n_clusters}) print(f聚类中心对应的原始样本索引: {cluster_centers}) print(f实际迭代次数: {af.n_iter_}) # 画出样本点并用五角星标出聚类中心 plt.figure(figsize(8, 6)) colors plt.cm.tab10(np.unique(labels)) for k, color in zip(np.unique(labels), colors): mask labels k plt.scatter(X[mask, 0], X[mask, 1], colorcolor, s12, labelf簇 {k}) for idx in cluster_centers: plt.scatter(X[idx, 0], X[idx, 1], marker*, cred, s220) plt.title(Affinity Propagation 聚类结果) plt.legend() plt.show()跑完之后你会发现它找出来的 3 个中心点都落在每团数据比较密集的核心区域。这不是巧合而是算法特性的体现作为聚类中心的点既要被足够多的邻居“认可”又要在相似度上对周边点保持较高的吸引力所以集中区域里的点天然占有优势。4.3 不同 preference 下的结果对比看完你就懂怎么调了很多人只跑一次默认参数就下了结论我觉得这样容易错过亲和传播最大的优势。下面这段代码遍历三个不同的 preference 值分别跑模型打印聚类数量让你直观感受参数变化对结果的影响preference_values [ np.min(sim_matrix), np.median(sim_matrix), np.max(sim_matrix) ] for pref in preference_values: af_temp AffinityPropagation( affinityprecomputed, preferencepref, damping0.8, max_iter500, convergence_iter20, random_state42 ) af_temp.fit(sim_matrix) print(fpreference{pref:.2f}, 聚类数量{len(af_temp.cluster_centers_indices_)})我实际跑出来的结果大概是这样的preference 取最小值负得最厉害时聚成 1 类或者 2 类取中位数时稳定在 3 类取最大值附近接近 0时可能分出 10 类以上而且很多类里只有一两个样本。这就是 preference 对粒度的控制力。如果你在工作里遇到“聚类结果太碎了”的情况不用怀疑算法先把 preference 往小调一两个数量级看看。反过来如果几万个样本只聚成 1 类那就是 preference 设得太低了往大抬一点就行。5. 实战中的坑与对策从振荡、类碎片化到海量数据的工程折中算法本身讲完了但真正有价值的往往是那些“跑起来之后”才会遇到的问题。我把这两年用亲和传播碰到的坑统一整理出来每个都有对应的解决办法你可以当成一份排错清单来用。5.1 类碎片化聚类结果里冒出大量只有一两个样本的微型簇这是我把 preference 调得太高后的经典翻车现场。出现微型簇本质上是多个数据点同时竞争成为聚类中心而它们各自又都获得了足够的“支持”。算法认为这些点处在某些局部密度峰值上每一个“领军点”都可以自立门户。对策有两个。第一降低 preference让更多点“没有底气”自己当中心。第二如果降低 preference 后还是有很多小簇检查相似度是不是有异常值个别样本与其他所有样本的距离都很远它们天然会成为独苗中心。这时候可以考虑做一次离群点剔除或者用 DBSCAN 先把噪声滤掉再对剩余数据跑亲和传播。我在一个图像特征聚类的项目里就遇到过后一种情况几千张图有 100 多个聚类簇里面三分之一是只有 1 张图的“孤簇”。后来排查发现那些图在提取特征时因为分辨率差异产生了大量全零特征向量特征空间里它们离谁都远自然就各自为政了。解决方法是先删除重复/空特征再跑聚类簇数量一下就合理了。5.2 迭代不收敛调了 damping 还不行问题通常出在这两点不收敛是亲和传播绕不开的话题。从日志和迭代过程来看你会看到消息矩阵一直在规律性跳变聚类中心在两组集合之间反复横跳。这时候我第一反应是调高 damping但如果你已经调到 0.9 还是不行那就要从另外两个方向排查相似度矩阵是否退化某个点与其他所有点的相似度几乎一样会导致消息更新没有明确方向。样本量是不是太大上万样本的亲和传播迭代过程中消息收敛本身就非常困难因为每个中心候选要汇总成万上下的信息量信号被平均得没什么区分度了。对于第二种情况工程上最实用的办法是分层处理先在小批量样本上比如随机抽样 2000 个点跑出聚类中心然后把剩余样本按相似度分配到最近的中心。这个思路跟 K-means 的“可扩展性优化”如出一辙牺牲一点点局部精度换来能用的全局结果。5.3 计算资源消耗N 的平方不是开玩笑亲和传播的时间复杂度是 ( O(N^2 \times T) )N 是样本数T 是迭代次数。1000 个样本还好5000 个样本的相似度矩阵就是 2500 万个浮点数大约 200MB 内存迭代几十次之后的时间消耗已经肉眼可见。到了 10000 个样本内存直奔 800MB 以上计算时间更是到分钟级别。在工业项目里我通常把亲和传播用在两类场景一类是样本量在 2000 以内的精细聚类另一类是先对海量样本做 Mini-Batch K-means 粗聚类把每个粗簇的质心作为代表点再对这些代表点跑亲和传播做二次聚类。后者是很多图谱类项目常用的“两阶段聚类”方案速度和效果都不错。提示如果你的数据特征维度很高比如文本 TF-IDF 向量可以先做 PCA 降维或者 UMAP 降维到 20~50 维再跑亲和传播。这样不仅计算压力小很多距离计算的语义也更稳定——高维空间里欧氏距离的区分度非常差这是所有基于距离的聚类算法共通的弱点。5.4 与 K-means / DBSCAN 的选型对比什么时候用它什么时候用别的我在文章开头就说过亲和传播最大的卖点是不用预设 K 值且聚类中心是真实样本。但它也不是万能的。我给自己定了一个选型标准你可以参考想要全局粒度的聚类并且样本量在 5000 以内 → 亲和传播很合适。样本量超过 1 万 → 优先 K-means / Mini-Batch K-means或者两阶段方案。数据簇形状不规则例如环形簇、凹形簇→ 亲和传播和 K-means 都搞不定用 DBSCAN / 谱聚类。需要确定性的簇边界 → 亲和传播因为它可以看作一个非参数模型类别数由数据和参数共同决定。5.5 手动把负欧氏距离换成更适合业务的相似度度量亲和传播最灵活的地方在于相似度矩阵可以自定义。比如在推荐系统里可以把相似度定义为1/(1欧氏距离)或者用余弦相似度再取负。你只需要保证数值越大代表越相似对角线上的 preference 按业务语义设置。我做过一个电商文本聚类的小项目直接把 BERT 句向量之间的余弦相似度作为相似度矩阵跑亲和传播效果相当不错。因为文本向量经过归一化之后余弦相似度比欧氏距离更贴合语义空间。这种“自定义相似度”的灵活性是 K-means 做不到的。6. 人脸聚类之外的落地心得从参数调优到业务解释亲和传播最有名的落地案例是人脸图像聚类——人脸特征空间里每个被选中的聚类中心就代表一个身份。除此之外我在金融风控、用户行为分群和文本主题归纳里也都用过它效果都不错但每次都要根据业务重新校准 preference。6.1 给业务方解释聚类结果时的表达策略亲和传播结果里的“中心样本”是很宝贵的解释素材。给业务方汇报时我会特意把每个簇的中心样本拿出来做一次人工标注看看它到底长什么样。比如做用户分群时把一个簇的代表用户拿出来看他的消费记录、活跃时段、偏好品类基本上就能给这个簇起一个业务名。这种“典型样本驱动”的解释逻辑比拿一堆统计数值去硬说“这是蓝海用户”要可信得多。我每次都会跟业务方强调“这个结果不是模型凭空捏造的类而是数据里真实存在的一批典型样本只是我们把它们找出来了。”这句话在汇报时特别好用。6.2 调参的完整流程一个可以直接照抄的套路如果你不想再一个个试参数可以按下面的流程走一遍我个人觉得这个流程能覆盖 80% 的场景数据清洗 标准化必须。先用默认 preference中位数跑一遍记录聚类数量和收敛情况。如果聚类太碎preference 下调到最小值附近再看。如果聚类太少preference 上调到最大值附近再看。如果出现不收敛或者振荡damping 从 0.5 往上加一次加 0.1直到稳定。找到聚类数量符合预期的区间按业务粒度的敏感度做精调。检查是否有“独苗簇”有就降 preference 或者先剔除离群点。最后做可视化验证——聚类结果是不是符合你对数据的直觉判断。这套流程我自己实践了不下十次虽然没有一次是完全一样的但大的方向从来没有变过。如果你对具体的数据集拿不准可以把 preference 从最小值到最大值按 10 个档位扫一遍画出“preference vs 聚类数”的曲线然后选一个业务上最舒服的颗粒度。6.3 关于结果的稳定性多次运行会得到一样的答案吗亲和传播在 sklearn 里是确定性算法给定相同相似度矩阵和相同参数每次跑出来的结果是一致的。这点跟 K-means 不一样——K-means 依赖初始质心多次运行可能得到不同结果。不过要注意我前面都用random_state固定随机种子这不是算法本身需要的而是为了在调参对比时保证一致性。实际部署时不用太纠结随机种子因为亲和传播没有随机初始化步骤。真正需要担心的反而是另一个点当数据更新、新增样本后聚类中心几乎一定会发生漂移。所以它在生产环境里更适合做“阶段性离线聚类”不适合做那种实时在线预测的模型。你要想对新增样本做预测正确方式是找到它离哪个历史聚类中心最近然后直接归属。每次全量重跑不仅慢还会让类别定义不稳定分析上很难延续。6.4 一段关于“算法并不高级但你的解释必须高级”的题外话最后说点技术之外的东西。亲和传播这个概念听起来很学术但放到实际业务里它的价值取决于你怎么定义相似度、怎么校准 preference以及怎么把结果翻译成业务语言。很多人以为调参只调的是模型的“性能”其实你调的是“一个问题的答案粒度”。我后来做博主分享项目最常被问到的就是“你这个聚类结果跟 K-means 比准不准”。坦白讲“准不准”在无监督场景里并不是一个核心衡量维度核心是你得到的每个簇是否能被讲成故事、是否能指导下一步的动作。从这一点来说亲和传播给我的帮助相当大因为它给了我可解释的“代表样本”让我能把无监督学习的黑盒开出一个口子来。如果你正准备在自己的项目里尝试亲和传播我给的建议很简单从一份 500~2000 条样本的数据集开始认真做好标准化把 preference 拉几个档位看看聚类数怎么变再对比一下选出的代表样本是不是符合业务直觉。用不了一个上午你就能体会到这个算法和 K-means 之间的本质差异——然后你大概就会跟我一样在需要“既不知道分几类、又想要可解释结果”的时候第一时间想起它。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。