FCM聚类算法详解(Python实现iris数据集)

2024-05-20 19:38

本文主要是介绍FCM聚类算法详解(Python实现iris数据集),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

参考:https://blog.csdn.net/on2way/article/details/47087201

模糊C均值(Fuzzy C-means)算法简称FCM算法,是一种基于目标函数的模糊聚类算法,主要用于数据的聚类分析。理论成熟,应用广泛,是一种优秀的聚类算法。本文关于FCM算法的一些原理推导部分介绍,加上自己的理解和在课题项目中的应用以文字的形式呈现出来。

首先介绍一下模糊这个概念,所谓模糊就是不确定,确定性的东西就是信息量很小的东西,而不确定性的东西就说很像什么,这种信息量很大。比如说把20岁作为年轻不年轻的标准,那么一个人21岁按照确定性的划分就属于不年轻,而我们印象中的观念是21岁也很年轻,这个时候可以模糊一下,认为21岁有0.9分像年轻,有0.1分像不年轻,这里0.9与0.1不是概率,而是一种相似的程度,把这种一个样本属于结果的这种相似的程度称为样本的隶属度,一般用u表示,表示一个样本相似于不同结果的一个程度指标。

基于此,假定数据集为X,如果把这些数据划分成c类的话,那么对应的就有c个类中心为C,每个样本j属于某一类i的隶属度为uij,那么定义一个FCM目标函数(1)及其约束条件(2)如下所示:
J = ∑ i = 1 n ∑ j = 1 n u i j m ∣ ∣ x j − c i ∣ ∣ 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ( 1 ) J=∑_{i=1}^{n}∑_{j=1}^{n}u_{ij}^{m}||xj−ci||^2 ...............................(1) J=i=1nj=1nuijmxjci2...............................(1)

∑ i = 1 c u i j = 1 , j = 1 , 2... , n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ( 2 ) ∑_{i=1}^{c}u_{ij}=1,j=1,2...,n..............................(2) i=1cuij=1,j=1,2...,n..............................(2)

看一下目标函数(式1)而知,由相应样本的隶属度与该样本到各个类中心的距离相乘组成的,m是一个隶属度的因子,个人理解为属于样本的轻缓程度,就像 x 2 x^2 x2 x 3 x^3 x3这种一样。式(2)为约束条件,也就是一个样本属于所有类的隶属度之和要为1。观察式(1)可以发现,其中的变量有 u i j 、 c i u_{ij}、c_i uijci,并且还有约束条件,那么如何求这个目标函数的极值呢?

这里首先采用拉格朗日乘数法将约束条件拿到目标函数中去,前面加上系数,并把式(2)的所有j展开,那么式(1)变成下列所示:

J = ∑ i = 1 c ∑ j = 1 n u i j m ∣ ∣ x j − c i ∣ ∣ 2 + λ 1 ( ∑ i = 1 c u i 1 − 1 ) + . . . + λ j ( ∑ i = 1 c u i j − 1 ) + . . . + λ n ( ∑ i = n c u i n − 1 ) ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ( 3 ) J=∑_{i=1}^{c}∑_{j=1}^{n}u_{ij}^{m}||x_j−c_i||^2+λ_1(∑_{i=1}^{c}u_{i1}−1)+...+λ_j(∑_{i=1}^{c}u_{ij}−1)+...+λ_n(∑_{i=n}cu_{in}−1)).....................................(3) J=i=1cj=1nuijmxjci2+λ1(i=1cui11)+...+λj(i=1cuij1)+...+λn(i=ncuin1)).....................................(3)

现在要求该式的目标函数极值,那么分别对其中的变量uij、ci求导数,首先对uij求导。

分析式(3),先对第一部分的两级求和的uij求导,对求和形式下如果直接求导不熟悉,可以把求和展开如下:

在这里插入图片描述
这个矩阵要对uij求导,可以看到只有uij对应的 u m i j ∣ ∣ x j − c i ∣ ∣ 2 u_{m}^{ij}||x_j−c_i||^2 umijxjci2保留,其他的所有项中因为不含有uijuij,所以求导都为0。那么 u i j m ∣ ∣ x j − c i ∣ ∣ 2 u_{ij}^{m}||x_j−c_i||^2 uijmxjci2对uij求导后就为 m ∣ ∣ x j − c i ∣ ∣ 2 u i j m − 1 m||x_j−c_i||^2u_{ij}^{m-1} mxjci2uijm1

再来看后面那个对uij求导,同样把求和展开,再去除和uij不相关的(求导为0),那么只剩下这一项: λ j ( u i j − 1 ) λ_j(u_{ij}−1) λj(uij1),它对uij求导就是λj了。

那么最终J对uij的求导结果并让其等于0就是:
∂ J ∂ u i j = m ∣ ∣ x j − c i ∣ ∣ 2 u i j m − 1 + λ j = 0 \frac{∂J}{∂uij}=m||x_j−c_i||^2u_{ij}^{m-1}+λ_j=0 uijJ=mxjci2uijm1+λj=0

这个式子化简下,将uij解出来就是:

u i j m − 1 = − λ j m ∣ ∣ x j − c i ∣ ∣ 2 u_{ij}^{m-1}=\frac{−λ_j}{m||xj−ci||^2} uijm1=mxjci2λj

进一步:

u i j = ( − λ j m ∣ ∣ x j − c i ∣ ∣ 2 ) 1 m − 1 = ( − λ j m ) 1 m − 1 1 ( 1 ∣ ∣ x j − c i ∣ ∣ ) 2 m − 1 . . . . . . . ( 4 ) u_{ij}=(\frac{−λ_j}{m||xj−ci||^2})^\frac{1}{m−1}=(\frac{−λ_j}{m})^\frac{1}{m−1}\frac{1}{(\frac{1}{||xj−ci||})^\frac{2}{m−1}}.......(4) uij=(mxjci2λj)m11=(mλj)m11(xjci1)m121.......(4)

要解出uij则需要把λj去掉才行。这里重新使用公式(2)的约束条件,并把算出来的uij代入式(2)中有:
在这里插入图片描述

这样就有(其中把符号i换成k): 在这里插入图片描述
把这个重新代入到式(4)中有:
在这里插入图片描述

好了,式子(5)就是最终的uij迭代公式。

下面在来求J对ci的导数。由公式(2)可以看到只有 ∑ i = 1 c ∑ j = 1 n u i j m ∣ ∣ x j − c i ∣ ∣ 2 ∑_{i=1}^{c}∑_{j=1}^{n}u_{ij}^{m}||xj−ci||^2 i=1cj=1nuijmxjci2这一部分里面含有ci,对其二级求和展开如前面所示的,那么它对ci的导数就是:
在这里插入图片描述
即:
在这里插入图片描述
在这里插入图片描述
好了,公式(6)就是类中心的迭代公式。

我们发现uij与ci是相互关联的,彼此包含对方,有一个问题就是在fcm算法开始的时候既没有uij也没有ci,那要怎么求解呢?很简单,程序开始的时候我们随便赋值给uij或者ci其中的一个,只要数值满足条件即可。然后就开始迭代,比如一般的都赋值给uij,那么有了uij就可以计算ci,然后有了ci又可以计算uij,反反复复,在这个过程中还有一个目标函数J一直在变化,逐渐趋向稳定值。那么当J不在变化的时候就认为算法收敛到一个比较好的解了。可以看到uij和ci在目标函数J下似乎构成了一个正反馈一样,这一点很像EM算法,先E在M,有了M在E,在M直至达到最优。

公式(5),(6)是算法的关键。现在来重新从宏观的角度来整体看看这两个公式,先看(5),在写一遍

在这里插入图片描述
假设看样本集中的样本1到各个类中心的隶属度,那么此时j=1,i从1到c类,此时上述式中分母里面求和中,分子就是这个点相对于某一类的类中心距离,而分母是这个点相对于所有类的类中心的距离求和,那么它们两相除表示什么,是不是表示这个点到某个类中心在这个点到所有类中心的距离和的比重。当求和里面的分子越小,是不是说就越接近于这个类,那么整体这个分数就越大,也就是对应的uij就越大,表示越属于这个类,形象的图如下:
在这里插入图片描述
再来宏观看看公式(6),考虑当类i确定后,式(6)的分母求和其实是一个常数,那么式(6)可以写成:
在这里插入图片描述
这是类中心的更新法则。说这之前,首先让我们想想kmeans的类中心是怎么更新的,一般最简单的就是找到属于某一类的所有样本点,然后这一类的类中心就是这些样本点的平均值。那么FCM类中心怎么样了?看式子可以发现也是一个加权平均,类i确定后,首先将所有点到该类的隶属度u求和,然后对每个点,隶属度除以这个和就是所占的比重,乘以xj就是这个点对于这个类i的贡献值了。画个形象的图如下:
在这里插入图片描述
由上述的宏观分析可知,这两个公式的迭代关系式是这样的也是可以理解的。

数据集用的是iris数据,有需要数据的朋友可以调用sklearn.load_iris。或者下载下来

from pylab import *
from numpy import *
import pandas as pd
import numpy as np
import operator
import math
import matplotlib.pyplot as plt
import random# 数据保存在.csv文件中
df_full = pd.read_csv("iris.csv")
columns = list(df_full.columns)
features = columns[:len(columns) - 1]
# class_labels = list(df_full[columns[-1]])
df = df_full[features]
# 维度
num_attr = len(df.columns) - 1
# 分类数
k = 3
# 最大迭代数
MAX_ITER = 100
# 样本数
n = len(df)  # the number of row
# 模糊参数
m = 2.00# 初始化模糊矩阵U
def initializeMembershipMatrix():membership_mat = list()for i in range(n):random_num_list = [random.random() for i in range(k)]summation = sum(random_num_list)temp_list = [x / summation for x in random_num_list]  # 首先归一化membership_mat.append(temp_list)return membership_mat# 计算类中心点
def calculateClusterCenter(membership_mat):cluster_mem_val = zip(*membership_mat)cluster_centers = list()cluster_mem_val_list = list(cluster_mem_val)for j in range(k):x = cluster_mem_val_list[j]xraised = [e ** m for e in x]denominator = sum(xraised)temp_num = list()for i in range(n):data_point = list(df.iloc[i])prod = [xraised[i] * val for val in data_point]temp_num.append(prod)numerator = map(sum, zip(*temp_num))center = [z / denominator for z in numerator]  # 每一维都要计算。cluster_centers.append(center)return cluster_centers# 更新隶属度
def updateMembershipValue(membership_mat, cluster_centers):#    p = float(2/(m-1))data = []for i in range(n):x = list(df.iloc[i])  # 取出文件中的每一行数据data.append(x)distances = [np.linalg.norm(list(map(operator.sub, x, cluster_centers[j]))) for j in range(k)]for j in range(k):den = sum([math.pow(float(distances[j] / distances[c]), 2) for c in range(k)])membership_mat[i][j] = float(1 / den)return membership_mat, data# 得到聚类结果
def getClusters(membership_mat):cluster_labels = list()for i in range(n):max_val, idx = max((val, idx) for (idx, val) in enumerate(membership_mat[i]))cluster_labels.append(idx)return cluster_labelsdef fuzzyCMeansClustering():# 主程序membership_mat = initializeMembershipMatrix()curr = 0while curr <= MAX_ITER:  # 最大迭代次数cluster_centers = calculateClusterCenter(membership_mat)membership_mat, data = updateMembershipValue(membership_mat, cluster_centers)cluster_labels = getClusters(membership_mat)curr += 1print(membership_mat)return cluster_labels, cluster_centers, data, membership_matdef xie_beni(membership_mat, center, data):sum_cluster_distance = 0min_cluster_center_distance = inffor i in range(k):for j in range(n):sum_cluster_distance = sum_cluster_distance + membership_mat[j][i] ** 2 * sum(power(data[j, :] - center[i, :], 2))  # 计算类一致性for i in range(k - 1):for j in range(i + 1, k):cluster_center_distance = sum(power(center[i, :] - center[j, :], 2))  # 计算类间距离if cluster_center_distance < min_cluster_center_distance:min_cluster_center_distance = cluster_center_distancereturn sum_cluster_distance / (n * min_cluster_center_distance)labels, centers, data, membership = fuzzyCMeansClustering()
print(labels)
print(centers)
center_array = array(centers)
label = array(labels)
datas = array(data)# Xie-Beni聚类有效性
print("聚类有效性:", xie_beni(membership, center_array, datas))
xlim(0, 10)
ylim(0, 10)
# 做散点图
fig = plt.gcf()
fig.set_size_inches(16.5, 12.5)
f1 = plt.figure(1)
plt.scatter(datas[nonzero(label == 0), 0], datas[nonzero(label == 0), 1], marker='o', color='r', label='0', s=10)
plt.scatter(datas[nonzero(label == 1), 0], datas[nonzero(label == 1), 1], marker='+', color='b', label='1', s=10)
plt.scatter(datas[nonzero(label == 2), 0], datas[nonzero(label == 2), 1], marker='*', color='g', label='2', s=10)
plt.scatter(center_array[:, 0], center_array[:, 1], marker='x', color='m', s=30)
plt.show()

在这里插入图片描述
效果一般啊。。。
竟然聚类的结果感觉和刚开始的好像差不多啊。不会被聚成一类?但是回过头来看这个算法,本身要得到的结果就是隶属度u(也就是各样本的权重)以及Ci聚类中心,有几个聚类中心就有几类,可以看到,还是挺准的。

这篇关于FCM聚类算法详解(Python实现iris数据集)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/995912

相关文章

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

关于数据埋点,你需要了解这些基本知识

产品汪每天都在和数据打交道,你知道数据来自哪里吗? 移动app端内的用户行为数据大多来自埋点,了解一些埋点知识,能和数据分析师、技术侃大山,参与到前期的数据采集,更重要是让最终的埋点数据能为我所用,否则可怜巴巴等上几个月是常有的事。   埋点类型 根据埋点方式,可以区分为: 手动埋点半自动埋点全自动埋点 秉承“任何事物都有两面性”的道理:自动程度高的,能解决通用统计,便于统一化管理,但个性化定

python: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

异构存储(冷热数据分离)

异构存储主要解决不同的数据,存储在不同类型的硬盘中,达到最佳性能的问题。 异构存储Shell操作 (1)查看当前有哪些存储策略可以用 [lytfly@hadoop102 hadoop-3.1.4]$ hdfs storagepolicies -listPolicies (2)为指定路径(数据存储目录)设置指定的存储策略 hdfs storagepolicies -setStoragePo

Hadoop集群数据均衡之磁盘间数据均衡

生产环境,由于硬盘空间不足,往往需要增加一块硬盘。刚加载的硬盘没有数据时,可以执行磁盘数据均衡命令。(Hadoop3.x新特性) plan后面带的节点的名字必须是已经存在的,并且是需要均衡的节点。 如果节点不存在,会报如下错误: 如果节点只有一个硬盘的话,不会创建均衡计划: (1)生成均衡计划 hdfs diskbalancer -plan hadoop102 (2)执行均衡计划 hd

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu