使用FCM进行编码解码Python实现代码
文章目录
- 代码整体结构
- 第 1 部分:生成二维合成数据
- 固定随机种子
- means中心位置
- covs形状
- sizes点数
- 生成数据
- 裁剪到[0,1]
- 第 2 部分:初始化隶属度矩阵
- 为什么要按列归一化?
- 这一步的意义
- 第 3 部分:更新聚类中心
- 第 4 部分:计算距离(每个样本到每个中心的距离)
- 为什么要防止除零?
- 第 5 部分:更新隶属度矩阵
- 为什么先算距离?
- 关键部分
- 怎么理解ratio?
- 总结
- 第 6 部分:FCM 主循环
- 初始化
- 开始迭代
- 为什么看U - U_old?
- 第 7 部分:解码 / 重构
- 最终结果
- 为什么这就是解码?
- 第 8 部分:重构误差
- 第 9 部分:画图
- 1 原始数据
- 2 重构数据
- 3 聚类中心
- 第 10 部分:主程序
- 生成数据
- 设置参数
- 运行FCM
- 重构
- 算误差
- 输出结果
- 画图
- 10 总结
- 代码
- 1 导包
- 2 生成二维合成数据
- 3 初始化隶属度矩阵
- 4 更新聚类中心
- 5 计算距离
- 6 更新隶属度矩阵
- 7 FCM主函数
- 8 解码(解粒化)
- 9 重构误差
- 10 完整代码
代码整体结构
1. generate_data() # 生成二维合成数据 2. initialize_membership() # 初始化隶属度矩阵 3. update_centers() # 更新聚类中心 4. compute_distances() # 计算样本到中心的距离 5. update_membership() # 更新隶属度矩阵 6. fcm() # FCM主循环 7. reconstruct_data() # 解码/重构 8. reconstruction_error() # 计算重构误差 9. plot_result() # 画图 10. main # 主程序入口第 1 部分:生成二维合成数据
固定随机种子
means中心位置
covs形状
sizes点数
生成数据
裁剪到[0,1]
第 2 部分:初始化隶属度矩阵
为什么要按列归一化?
这一步的意义
给每个样本随机分配一个“模糊归属”
第 3 部分:更新聚类中心
哪个点对某个簇的隶属度大,它对这个簇中心的贡献就更大。所以聚类中心不是简单平均,而是加权平均。
第 4 部分:计算距离(每个样本到每个中心的距离)
为什么要防止除零?
第 5 部分:更新隶属度矩阵
为什么先算距离?
因为 FCM 的思想就是:离哪个中心近,就更属于哪个中心。所以要先知道每个点离各中心有多远。
关键部分
怎么理解ratio?
总结
隶属度是根据相对距离算出来的。
越近 → 隶属度越大 越远 → 隶属度越小第 6 部分:FCM 主循环
初始化
开始迭代
为什么看U - U_old?
因为 FCM 最终要收敛到一个稳定状态。
如果两次隶属度矩阵几乎没变化,说明:
中心也基本稳定了 隶属度也基本稳定了 可以停止迭代第 7 部分:解码 / 重构
最终结果
为什么这就是解码?
因为编码时,一个点被表示成:它对各中心的隶属度向量
现在解码时,又根据:隶属度、中心位置把它还原成二维点
第 8 部分:重构误差
第 9 部分:画图
1 原始数据
plt.scatter(X[:,0],X[:,1],marker='x',s=12,alpha=0.45,label='Original data')2 重构数据
plt.scatter(X_hat[:,0],X_hat[:,1],marker='+',s=18,alpha=0.95,label='Reconstruction data')3 聚类中心
plt.scatter(centers[:,0],centers[:,1],marker='o',s=80,alpha=0.9,label='Centers')第 10 部分:主程序
生成数据
X=generate_data(seed=42)设置参数
c=8m=2.0运行FCM
centers,U=fcm(X,c=c,m=m,max_iter=200,tol=1e-5,seed=42)重构
X_hat=reconstruct_data(centers,U,m)算误差
V=reconstruction_error(X,X_hat)输出结果
print("聚类中心 centers =")print(centers)print("\n隶属度矩阵 U 的形状 =",U.shape)print("重构误差 V =",V)画图
plot_result(X,X_hat,centers)10 总结
这段代码首先构造了一个二维合成数据集,然后使用 FCM 算法对数据进行模糊聚类,得到聚类中心和隶属度矩阵。这里的隶属度矩阵就是数据的编码结果,因为它表示每个样本对各个原型的模糊归属。接着利用聚类中心和隶属度矩阵,通过加权平均公式对每个样本进行解码重构,得到 reconstruction data,最后通过均方误差评价原始数据与重构数据之间的差异,并可视化展示结果。
代码
1 导包
importnumpyasnpimportmatplotlib.pyplotasplt2 生成二维合成数据
1000 个点 6 组高斯分布 做过归一化defgenerate_data(seed=42):np.random.seed(seed)means=np.array([[0.18,0.82],[0.20,0.55],[0.30,0.62],[0.42,0.40],[0.68,0.34],[0.82,0.18]])covs=[[[0.002,-0.001],[-0.001,0.006]],[[0.003,0.000],[0.000,0.005]],[[0.004,-0.002],[-0.002,0.006]],[[0.004,0.000],[0.000,0.003]],[[0.008,-0.004],[-0.004,0.003]],[[0.002,-0.001],[-0.001,0.002]]]sizes=[170,170,160,170,180,150]# 总共1000点X_list=[]formean,cov,sizeinzip(means,covs,sizes):pts=np.random.multivariate_normal(mean,cov,size)X_list.append(pts)X=np.vstack(X_list)X=np.clip(X,0,1)# 限制在 [0,1]returnX3 初始化隶属度矩阵
definitialize_membership(n_samples,c):U=np.random.rand(c,n_samples)U=U/np.sum(U,axis=0,keepdims=True)returnU4 更新聚类中心
defupdate_centers(X,U,m):Um=U**m centers=(Um @ X)/np.sum(Um,axis=1,keepdims=True)returncenters5 计算距离
defcompute_distances(X,centers):# 返回 shape = (c, N)diff=X[None,:,:]-centers[:,None,:]dist=np.linalg.norm(diff,axis=2)dist=np.fmax(dist,1e-10)# 防止除零returndist6 更新隶属度矩阵
defupdate_membership(X,centers,m):dist=compute_distances(X,centers)# (c, N)c=centers.shape[0]n=X.shape[0]U_new=np.zeros((c,n))exponent=2.0/(m-1.0)foriinrange(c):ratio=(dist[i:i+1,:]/dist)**exponent U_new[i,:]=1.0/np.sum(ratio,axis=0)returnU_new7 FCM主函数
deffcm(X,c=8,m=2.0,max_iter=200,tol=1e-5,seed=42):np.random.seed(seed)n_samples=X.shape[0]U=initialize_membership(n_samples,c)foriterationinrange(max_iter):U_old=U.copy()centers=update_centers(X,U,m)U=update_membership(X,centers,m)diff=np.linalg.norm(U-U_old)print(f"第{iteration+1}次迭代, 隶属度变化量 ={diff:.8f}")ifdiff<tol:print("FCM 收敛")breakcenters=update_centers(X,U,m)returncenters,U8 解码(解粒化)
defreconstruct_data(centers,U,m):Um=U**m numerator=Um.T @ centers# (N, d)denominator=np.sum(Um,axis=0,keepdims=True).T# (N, 1)X_hat=numerator/denominatorreturnX_hat9 重构误差
defreconstruction_error(X,X_hat):returnnp.mean(np.sum((X-X_hat)**2,axis=1))10 完整代码
importnumpyasnpimportmatplotlib.pyplotasplt# ========== 1. 生成二维合成数据 ==========defgenerate_data(seed=42):np.random.seed(seed)means=np.array([[0.18,0.82],[0.20,0.55],[0.30,0.62],[0.42,0.40],[0.68,0.34],[0.82,0.18]])covs=[[[0.002,-0.001],[-0.001,0.006]],[[0.003,0.000],[0.000,0.005]],[[0.004,-0.002],[-0.002,0.006]],[[0.004,0.000],[0.000,0.003]],[[0.008,-0.004],[-0.004,0.003]],[[0.002,-0.001],[-0.001,0.002]]]sizes=[170,170,160,170,180,150]# 总共1000点X_list=[]formean,cov,sizeinzip(means,covs,sizes):pts=np.random.multivariate_normal(mean,cov,size)X_list.append(pts)X=np.vstack(X_list)X=np.clip(X,0,1)# 限制在 [0,1]returnX# ========== 2. 初始化隶属度矩阵 ==========definitialize_membership(n_samples,c):U=np.random.rand(c,n_samples)U=U/np.sum(U,axis=0,keepdims=True)returnU# ========== 3. 更新聚类中心 ==========defupdate_centers(X,U,m):Um=U**m centers=(Um @ X)/np.sum(Um,axis=1,keepdims=True)returncenters# ========== 4. 计算距离 ==========defcompute_distances(X,centers):# 返回 shape = (c, N)diff=X[None,:,:]-centers[:,None,:]dist=np.linalg.norm(diff,axis=2)dist=np.fmax(dist,1e-10)# 防止除零returndist# ========== 5. 更新隶属度矩阵 ==========defupdate_membership(X,centers,m):dist=compute_distances(X,centers)# (c, N)c=centers.shape[0]n=X.shape[0]U_new=np.zeros((c,n))exponent=2.0/(m-1.0)foriinrange(c):ratio=(dist[i:i+1,:]/dist)**exponent U_new[i,:]=1.0/np.sum(ratio,axis=0)returnU_new# ========== 6. FCM主函数 ==========deffcm(X,c=8,m=2.0,max_iter=200,tol=1e-5,seed=42):np.random.seed(seed)n_samples=X.shape[0]U=initialize_membership(n_samples,c)foriterationinrange(max_iter):U_old=U.copy()centers=update_centers(X,U,m)U=update_membership(X,centers,m)diff=np.linalg.norm(U-U_old)print(f"第{iteration+1}次迭代, 隶属度变化量 ={diff:.8f}")ifdiff<tol:print("FCM 收敛")breakcenters=update_centers(X,U,m)returncenters,U# ========== 7. 解码 / 重构 ==========defreconstruct_data(centers,U,m):Um=U**m numerator=Um.T @ centers# (N, d)denominator=np.sum(Um,axis=0,keepdims=True).T# (N, 1)X_hat=numerator/denominatorreturnX_hat# ========== 8. 重构误差 ==========defreconstruction_error(X,X_hat):returnnp.mean(np.sum((X-X_hat)**2,axis=1))# ========== 9. 画图 ==========defplot_result(X,X_hat,centers):plt.figure(figsize=(7,6))plt.scatter(X[:,0],X[:,1],marker='x',s=12,alpha=0.45,label='Original data')plt.scatter(X_hat[:,0],X_hat[:,1],marker='+',s=18,alpha=0.95,label='Reconstruction data')plt.scatter(centers[:,0],centers[:,1],marker='o',s=80,alpha=0.9,label='Centers')plt.xlabel(r'$x_1$')plt.ylabel(r'$x_2$')plt.xlim(0,1)plt.ylim(0,1)plt.legend()plt.title('FCM Granulation-Degranulation')plt.show()# ========== 10. 主程序 ==========if__name__=="__main__":X=generate_data(seed=42)# 论文二维图常用参数c=8m=2.0centers,U=fcm(X,c=c,m=m,max_iter=200,tol=1e-5,seed=42)X_hat=reconstruct_data(centers,U,m)V=reconstruction_error(X,X_hat)print("\n===== 结果输出 =====")print("聚类中心 centers =")print(centers)print("\n隶属度矩阵 U 的形状 =",U.shape)print("重构误差 V =",V)plot_result(X,X_hat,centers)