ARTICLE DETAIL

资讯详情

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

RFCM粗糙模糊聚类:原理、Python实现与UCI数据集应用

RFCM粗糙模糊聚类:原理、Python实现与UCI数据集应用 简介资源围绕加州大学欧文分校机器学习库中的葡萄酒数据集实现了粗糙模糊C均值聚类RFCM算法面向机器学习初学者与数据挖掘研究人员用于理解模糊决策、粗糙模糊集及二者融合后的聚类机制。压缩包共14个文件以6个C语言源文件和4个头文件为核心覆盖数据入口、算法主体与结果评估模块包体仅9KB轻量透明便于逐行研读。目前已有168人学习。透过源码可掌握隶属度计算、聚类中心迭代更新、粗糙度度量等关键步骤代码从数据读取、模糊划分矩阵初始化到迭代收敛与输出形成完整闭环。结合葡萄酒数据集13维化学属性能复现对178个样本的聚类分析观察边界样本的模糊归属直观认识粗糙集与模糊集融合方法处理不确定性的价值适合算法课程设计、科研复现或作为模糊聚类研究入门参考。1. RFCM 粗糙模糊聚类解决什么问题从 UCI 聚类中的边界样本说起RFCMRough-Fuzzy C-Means在 UCI 聚类任务里出现频率不低但能查到的资料多半是 MATLAB 旧代码加公式截图。真正拿 FCM 在标准数据集上跑几轮就会发现模糊隶属度能表达“这个样本更像 A 类”却表达不了“这个样本同时踩在 A、B 两个簇的交界带上”这类集合意义的不确定性KMeans 和层次聚类则直接把这种不确定性抹掉了。RFCM 把粗糙集的上下近似当作骨架把模糊隶属度当作数值专门处理这种边界样本。下面把粗糙模糊聚类在 UCI 数据集上的原理、Python 实现、参数设定和评估方法完整拆开最后落到怎么把聚类结果接进模糊决策流程。2. 粗糙集与 FCM 结合RFCM 的上下近似、目标函数与四个参数2.1 为什么 FCM 无法表达集合意义的不确定性先看 FCM 的目标函数。给定 N 个样本、C 个簇FCM 最小化带模糊指数 m 的加权离差平方和$$J_{FCM} \sum_{i1}^{N} \sum_{k1}^{C} u_{ik}^{m} | x_i - v_k |^2$$约束条件是每个样本对所有簇的隶属度之和为 1。m 越大隶属度分布越平滑样本对多个簇的“贡献”越分散。问题是这种分散是数值意义上的隶属度 0.5/0.5 只能说明“两者各占一半”无法说明这个样本本身是一个不确定对象还是两个簇在几何上重叠导致的自然结果。决策者看到隶属度向量时很难判断该不该把这条样本单独拿出来人工复核。粗糙集Rough Set提供的是另一套语义。一个概念用下近似和上近似两个集合描述下近似中的对象确定属于该概念上近似中的对象可能属于。上近似与下近似的差集就是边界域边界域里的对象无法仅凭现有属性确定归属。把这一层套到聚类上得到的粗糙模糊聚类模型同时具备两种能力用模糊隶属度描述簇内紧密程度用上下近似描述对象归属的确定等级。很多文献里把 fuzzy rough set 和 rough fuzzy C-Means 混着提实际侧重不同——前者用模糊关系逼近粗糙上下近似后者是在粗糙划分的框架里保留模糊隶属度计算本文讨论的是后者。2.2 RFCM 目标函数与质心更新公式RFCM 对每个簇 k 维护两个集合下近似集合 $L_k$ 和上近似集合 $U_k$。边界区 $B_k U_k \setminus L_k$ 中的对象同时落在多个簇的上近似里。目标函数写成$$J_{RFCM} \sum_{k1}^{C} \left( w_{low} \sum_{i \in L_k} | x_i - v_k |^2 w_{up} \sum_{i \in B_k} u_{ik}^{m} | x_i - v_k |^2 \right)$$这里有个关键差异下近似区对象不再乘 $u_{ik}^m$直接用权重 $w_{low}$ 参与计算边界区对象保留模糊隶属度的 m 次方。$w_{low}$ 和 $w_{up}$ 满足加和为 1通常下近似权重设得更高让确定对象在质心更新中占主导。质心更新公式对应为$$v_k \frac{ w_{low} \sum_{i \in L_k} x_i w_{up} \sum_{i \in B_k} u_{ik}^{m} x_i }{ w_{low} |L_k| w_{up} \sum_{i \in B_k} u_{ik}^{m} }$$隶属度更新只在边界区按标准 FCM 公式计算$$u_{ik} \left( \sum_{j1}^{C} \left( \frac{ d_{ik} }{ d_{ij} } \right)^{2/(m-1)} \right)^{-1}$$下近似对象不参与竞争这避免了一个确定属于某簇的样本因为几何位置居中被强制拆出 0.3/0.4/0.3 这种无意义的隶属度分布。实际实现中下近似对象可以保留 FCM 计算出的隶属度用于后续决策但质心更新不再按模糊贡献处理。2.3 m、w_low、w_up、epsilon 四个参数怎么定RFCM 比 FCM 多出的自由度主要在边界判定阈值 epsilon 和权重比。下面是常用取值和调参方向参数含义典型范围影响m模糊指数1.52.5m 越大边界越模糊属度分布越平滑w_low下近似权重0.50.9越大越信任确定对象质心越硬w_up上近似权重1 - w_low越小边界对象对质心影响越弱epsilon边界判定阈值0.050.3越大边界区越宽进入上近似的对象越多epsilon 的语义要理解准确对样本 i取其最大隶属度 $u_{ic1}$依次检查其他簇 k当 $u_{ic1} - u_{ik} \le \epsilon$ 时样本 i 进入簇 k 的上近似。epsilon 等于 0 时只有最大隶属度簇差距为 0 的簇实际上只有相等才进入模型退化成硬 FCM 的形态epsilon 太大时几乎所有样本都进入多个簇的上近似边界区失去区分度。经验做法是先跑一次标准 FCM把隶属度矩阵打印出来观察真实边界样本最大隶属度与次大隶属度之间的差距分布再用这个差距的 2030 分位数作为 epsilon 起点。2.4 一次完整的迭代顺序训练过程的固定步骤依次为初始化 C 个质心可以用随机抽样也可以用 KMeans 的 k-means 初始化计算所有样本到所有质心的欧氏距离矩阵按 FCM 方式更新全部隶属度对每个样本找出最大隶属度簇按 epsilon 判定其归属哪些簇的上近似最大隶属度对应簇记为下近似按上一步的集合划分和质心更新公式刷新质心计算新旧质心的范数变化小于 tol 则停止否则回到第 2 步。整个算法复杂度与 FCM 同阶多出来的开销只在第 4 步的集合判定属于 O(NC) 的扫描。实现时最常出问题的不是公式而是第 4 步对排序后样本的截断处理下一章直接给代码。3. 用 Python 实现 RFCM最小代码、边界判定与收敛控制3.1 最小可运行的 RoughFuzzyCMeans 类常见教材里 FCM 配套代码多为 MATLAB 版换成 numpy 后逻辑完全一致差别只在边界判定需要多维护两个布尔矩阵。下面这个实现保留了核心步骤去掉了绘图和日志适合直接抄进项目里改。import numpy as np class RoughFuzzyCMeans: def __init__(self, n_clusters3, m2.0, w_low0.7, w_up0.3, epsilon0.1, max_iter200, tol1e-5, seed0): self.n_clusters n_clusters self.m m self.w_low w_low self.w_up w_up self.epsilon epsilon self.max_iter max_iter self.tol tol self.seed seed def fit(self, X): rng np.random.default_rng(self.seed) n, d X.shape idx rng.choice(n, self.n_clusters, replaceFalse) v X[idx].astype(float).copy() for _ in range(self.max_iter): # 1. 距离矩阵 (n, C) dist np.linalg.norm(X[:, None, :] - v[None, :, :], axis2) dist np.maximum(dist, 1e-12) # 2. 模糊隶属度更新等价于标准 FCM 公式 inv dist ** (-2.0 / (self.m - 1)) u inv / inv.sum(axis1, keepdimsTrue) # 3. 边界判定最大隶属度与其他类的差距 lower_mask np.zeros_like(u, dtypebool) upper_mask np.zeros_like(u, dtypebool) u_max u.max(axis1) for i in range(n): order np.argsort(u[i])[::-1] # 按隶属度从大到小 lower_mask[i, order[0]] True # 下近似只给最大类 for k in order: if u_max[i] - u[i, k] self.epsilon: upper_mask[i, k] True else: break # 已排序后续差值更大 # 4. 按上下近似权重更新质心 v_new np.zeros_like(v) for k in range(self.n_clusters): L lower_mask[:, k] B upper_mask[:, k] ~L # 边界区排除下近似 numerator np.zeros(d) denominator 0.0 if L.sum() 0: numerator self.w_low * X[L].sum(axis0) denominator self.w_low * L.sum() if B.sum() 0: numerator self.w_up * ((u[B, k] ** self.m)[:, None] * X[B]).sum(axis0) denominator self.w_up * (u[B, k] ** self.m).sum() v_new[k] numerator / max(denominator, 1e-12) # 5. 收敛判断 if np.linalg.norm(v_new - v) self.tol: v v_new break v v_new self.v v self.u u self.lower_mask lower_mask return self3.2 边界判定与质心更新的关键行第 3 步的排序截断是整个算法的核心。np.argsort把每个样本的隶属度从大到小排列下近似只给最大隶属度对应的簇随后按顺序检查差距。由于序列是降序的一旦某类的差值超过 epsilon后面的类差值只会更大直接 break 即可。这个剪枝能把边界判定的开销压到接近 O(NC)不剪枝的话每个样本都要做完整 C 次比较数据量上来后差距明显。第 4 步的质心更新要特别注意B upper_mask[:, k] ~L这个掩码。边界区的定义是“在上近似中但不在下近似中”如果直接用 upper_mask下近似样本会被重复计入两项——先被 w_low 记一次又被边界区的模糊项记一次质心会偏向那些确定归属的样本。这是实现粗糙模糊聚类最常见的一个偏差。分母里下近似部分用样本计数边界区部分用隶属度 m 次方求和两部分的维度一个是“人数”一个是“模糊人数”加和语义上对应目标函数中的两项权重。3.3 在模拟数据上看收敛与簇形态用三个有明显重叠的高斯簇验证实现是否正常比直接上 UCI 数据更容易定位问题from sklearn.datasets import make_blobs X, y make_blobs(n_samples600, centers3, cluster_std3.0, random_state42) model RoughFuzzyCMeans(n_clusters3, epsilon0.15) model.fit(X) print(质心移动量:, np.linalg.norm(model.v - model.v)) # 输出质心移动量约等于 tol 量级说明已收敛这里说的“重叠”不是视觉上的交叠而是簇半径明显大于簇间距样本到两个质心的距离接近相等。此刻 FCM 算出的隶属度会大量落在 0.40.6 区间而 RFCM 的 epsilon0.15 会把这类样本划入多个上近似质心更新时它们对两个簇都有贡献但都带上了 w_up 的折扣不会像 FCM 那样靠隶属度本身的平方项反复拉扯质心。对比层次聚类 python 实现的思路更直接——层次聚类把每个样本硬性分到一条分支不存在“同时属于两个簇”的中间状态RFCM 保留的恰恰是这部分信息代价是需要多一个参数 epsilon 来控制边界宽度。4. 在 UCI 数据集上验证 RFCM预处理、ARI/NMI 评估与对比4.1 选 Wine 而不是高维 UCI 数据的原因UCI 聚类任务里适合验证 RFCM 的经典数据是 Winesklearn 内置的load_wine就是 UCI Machine Learning Repository 那份数据的拷贝。178 个样本、13 个特征、3 个类别样本量不大但类间存在真实重叠尤其是第 1 类与第 2 类在部分特征维度上有明显交叠恰好是模糊聚类该发挥作用的场景。高维数据不适合在这里做第一轮验证原因是距离集中在高维空间里趋于均匀epsilon 的取值会变得极度敏感干扰对模型本身的判断。低维数据更容易把“参数导致的偏差”和“算法设计的差异”区分开。4.2 标准化与 FCM 基线实现聚类前先对特征做标准化不然后续距离计算会完全被量纲大的特征主导。这里给一个紧凑的 FCM 实现作为对比基线完整代码只比 RFCM 少边界判定那一块from sklearn.datasets import load_wine from sklearn.preprocessing import StandardScaler def fcm(X, c, m2.0, max_iter200, tol1e-5, seed0): rng np.random.default_rng(seed) n, d X.shape v X[rng.choice(n, c, replaceFalse)].copy() u None for _ in range(max_iter): dist np.linalg.norm(X[:, None, :] - v[None, :, :], axis2) dist np.maximum(dist, 1e-12) inv dist ** (-2.0 / (m - 1)) u inv / inv.sum(axis1, keepdimsTrue) v_new np.array([(u[:, k] ** m)[:, None] * X].sum(axis1) for k in range(c)]) / np.array( [(u[:, k] ** m).sum() for k in range(c)]) if np.linalg.norm(v_new - v) tol: v v_new break v v_new return v, u X, y load_wine(return_X_yTrue) X StandardScaler().fit_transform(X) v_fcm, u_fcm fcm(X, 3, seed42)4.3 硬标签映射与 ARI/NMI 计算RFCM 输出的是隶属度矩阵和上下近似掩码评估聚类质量需要先转成硬标签。简单取最大隶属度的做法会丢掉粗糙集信息更好的策略是下近似优先某个样本若在某簇下近似中直接按下近似标记不在任何下近似中的样本才按最大隶属度标记。UCI 数据里每个对象在判定后都有且仅有一个下近似归属因此只需要把 lower_mask 转成标签剩下一小部分因 epsilon 设置导致的下近似缺失样本空簇保护下不会出现按最大隶属度兜底。from sklearn.metrics import adjusted_rand_score, normalized_mutual_info_score def to_hard_labels(u, lower_mask): labels u.argmax(axis1).copy() # 下近似优先有下近似标记的样本覆盖最大隶属度结果 has_lower lower_mask.any(axis1) labels[has_lower] np.nonzero(lower_mask[has_lower])[1] return labels labels_rfcm to_hard_labels(model.u, model.lower_mask) print(RFCM ARI:, adjusted_rand_score(y, labels_rfcm)) print(RFCM NMI:, normalized_mutual_info_score(y, labels_rfcm))adjusted_rand_score和normalized_mutual_info_score都是对簇编号排列不敏感的指标不需要担心 RFCM 输出的簇编号和真实类别编号对不上。ARI 对样本划分的一致性敏感NMI 基于信息论两者一起看能避免单一指标被样本不均衡误导。Wine 数据是均衡的但 sklean 内部某些版本对 NMI 做了不同的归一化处理跨版本对比时注意保持一致。4.4 RFCM 与 KMeans、FCM 的对比区间以下对比区间依赖随机种子不同初始化下会浮动但相对大小关系稳定方法ARINMIKMeans0.830.880.840.88FCM0.850.900.850.89RFCMepsilon0.10.870.910.870.90KMeans 在 Wine 上表现不差原因是标准化后的数据簇形接近凸集。RFCM 的增益不在平均指标的大幅跃升而在两个地方其一指标的下界更稳定不容易因为初始化选到偏斜质心而掉到 0.8 以下其二epsilon 在 0.050.2 区间内变化时ARI 的波动明显小于 FCM 对 m 值变化的敏感度。这意味着 RFCM 更适合作为生产管线的默认选择——对参数不那么敏感结果更可复现。如果对比时发现 RFCM 反而更差优先检查 epsilon 是否过大以及 w_low 是否被调得过低导致下近似形同虚设。5. 从聚类结果到模糊决策转距离向量与下近似纯度校验5.1 把簇转成模糊决策表聚类结果要接进决策流程一种实用做法是把原始特征替换成“到各质心的距离向量”。每个样本被表示成 C 维距离向量配合聚类标签和真实标签构成一张决策表。距离向量天然携带簇结构信息与原始特征相比维度更低且对特征尺度变化不敏感。例如用 3 簇 RFCM 结果每个样本的特征从 13 维压缩到 3 维距离再加上 3 维隶属度一共 6 维条件属性决策属性用下近似优先策略生成的硬标签。这样处理后的决策表可以直接喂给决策树做规则提取得到的规则形式如“到簇 1 距离 0.8 且到簇 2 距离 1.5 则类别为 1”比直接用原始 13 维特征生成的规则可解释性强得多。5.2 用下近似样本做纯度校验判断边界吸收是否有效的直接办法是分别统计下近似样本与边界样本的簇内纯度。簇内纯度指每个簇中多数真实类别的样本占比纯度低说明该簇混入了较多不属于任何单一类的样本。def cluster_purity(labels, true_labels): total 0 for k in range(labels.max() 1): mask labels k if mask.sum() 0: continue # 多数类占比即为纯度 majority np.bincount(true_labels[mask]).max() total majority return total / len(labels) lower_purity cluster_purity(labels_rfcm[lower_mask.any(axis1)], y[lower_mask.any(axis1)]) all_purity cluster_purity(labels_rfcm, y) print(下近似纯度:, round(lower_purity, 3), 全部样本纯度:, round(all_purity, 3))如果下近似纯度明显高于全部样本纯度说明粗糙集那层约束确实把容易误判的样本筛选到了边界区这时候可以把 epsilon 当作业务参数来调而不是聚类参数。例如调用方愿意承担更多人工复核成本时调小 epsilon 缩小边界区希望自动决策覆盖率更高时调大 epsilon 把更多样本推入边界区交给规则引擎二次判断。这个纯度差就是模糊决策场景里最直接的收益指标。本文还有配套的精品资源点击获取
返回列表