模糊C均值聚类算法的实现
研究背景
https://blog.csdn.net/liu_xiao_cheng/article/details/50471981
聚类分析是多元统计分析的一种,也是无监督模式识别的一个重要分支,在模式分类 图像处理和模糊规则处理等众多领域中获得最广泛的应用。它把一个没有类别标记的样本按照某种准则划分为若干子集,使相似的样本尽可能归于一类,而把不相似的样本划分到不同的类中。硬聚类把每个待识别的对象严格的划分某类中,具有非此即彼的性质,而模糊聚类建立了样本对类别的不确定描述,更能客观的反应客观世界,从而成为聚类分析的主流。
模糊聚类算法是一种基于函数最优方法的聚类算法,使用微积分计算技术求最优代价函数,在基于概率算法的聚类方法中将使用概率密度函数,为此要假定合适的模型,模糊聚类算法的向量可以同时属于多个聚类,从而摆脱上述问题。
模糊聚类分析算法大致可分为三类
1)分类数不定,根据不同要求对事物进行动态聚类,此类方法是基于模糊等价矩阵聚类的,称为模糊等价矩阵动态聚类分析法。
2)分类数给定,寻找出对事物的最佳分析方案,此类方法是基于目标函数聚类的,称为模糊C均值聚类。
3)在摄动有意义的情况下,根据模糊相似矩阵聚类,此类方法称为基于摄动的模糊聚类分析法
我所学习的是模糊C均值聚类算法,要学习模糊C均值聚类算法要先了解虑属度的含义,隶属度函数是表示一个对象x隶属于集合A的程度的函数,通常记做μA(x),其自变量范围是所有可能属于集合A的对象(即集合A所在空间中的所有点),取值范围是[0,1],即0<=μA(x)<=1。μA(x)=1表示x完全隶属于集合A,相当于传统集合概念上的x∈A。一个定义在空间X={x}上的隶属度函数就定义了一个模糊集合A,或者叫定义在论域X={x}上的模糊子集。对于有限个对象x1,x2,……,xn模糊集合可以表示为:
(6.1)
有了模糊集合的概念,一个元素隶属于模糊集合就不是硬性的了,在聚类的问题中,可以把聚类生成的簇看成模糊集合,因此,每个样本点隶属于簇的隶属度就是[0,1]区间里面的值。
FCM算法需要两个参数一个是聚类数目C,另一个是参数m。一般来讲C要远远小于聚类样本的总个数,同时要保证C>1。对于m,它是一个控制算法的柔性的参数,如果m过大,则聚类效果会很次,而如果m过小则算法会接近HCM聚类算法。
算法的输出是C个聚类中心点向量和C*N的一个模糊划分矩阵,这个矩阵表示的是每个样本点属于每个类的隶属度。根据这个划分矩阵按照模糊集合中的最大隶属原则就能够确定每个样本点归为哪个类。聚类中心表示的是每个类的平均特征,可以认为是这个类的代表点。
从算法的推导过程中我们不难看出,算法对于满足正态分布的数据聚类效果会很好,另外,算法对孤立点是敏感的。
聚类算法是一种比较新的技术,基于曾次的聚类算法文献中最早出现的Single-Linkage层次聚类算法是1957年在Lloyd的文章中最早出现的,之后MacQueen独立提出了经典的模糊C均值聚类算法,FCM算法中模糊划分的概念最早起源于Ruspini的文章中,但关于FCM的算法的详细的分析与改进则是由Dunn和Bezdek完成的。
模糊c均值聚类算法因算法简单收敛速度快且能处理大数据集,解决问题范围广,易于应用计算机实现等特点受到了越来越多人的关注,并应用于各个领域。
算法描述
模糊C均值聚类算法的步骤还是比较简单的,模糊C均值聚类(FCM),即众所周知的模糊ISODATA,是用隶属度确定每个数据点属于某个聚类的程度的一种聚类算法。1973年,Bezdek提出了该算法,作为早期硬C均值聚类(HCM)方法的一种改进。
FCM把n个向量xi(i=1,2,…,n)分为c个模糊组,并求每组的聚类中心,使得非相似性指标的价值函数达到最小。FCM与HCM的主要区别在于FCM用模糊划分,使得每个给定数据点用值在0,1间的隶属度来确定其属于各个组的程度。与引入模糊划分相适应,隶属矩阵U允许有取值在0,1间的元素。不过,加上归一化规定,一个数据集的隶属度的和总等于1:
(6.9)
那么,FCM的价值函数(或目标函数)就是式(6.2)的一般化形式:
, (6.10)
这里uij介于0,1间;ci为模糊组I的聚类中心,dij=||ci-xj||为第I个聚类中心与第j个数据点间的欧几里德距离;且是一个加权指数。
构造如下新的目标函数,可求得使(6.10)式达到最小值的必要条件:
(6.11)
这里lj,j=1到n,是(6.9)式的n个约束式的拉格朗日乘子。对所有输入参量求导,使式(6.10)达到最小的必要条件为:
(6.12)
和
(6.13)
由上述两个必要条件,模糊C均值聚类算法是一个简单的迭代过程。在批处理方式运行时,FCM用下列步骤确定聚类中心ci和隶属矩阵U[1]:
步骤1:用值在0,1间的随机数初始化隶属矩阵U,使其满足式(6.9)中的约束条件
步骤2:用式(6.12)计算c个聚类中心ci,i=1,…,c。
步骤3:根据式(6.10)计算价值函数。如果它小于某个确定的阀值,或它相对上次价值函数值的改变量小于某个阀值,则算法停止。
步骤4:用(6.13)计算新的U矩阵。返回步骤2。
上述算法也可以先初始化聚类中心,然后再执行迭代过程。由于不能确保FCM收敛于一个最优解。算法的性能依赖于初始聚类中心。因此,我们要么用另外的快速算法确定初始聚类中心,要么每次用不同的初始聚类中心启动该算法,多次运行FCM。
模糊c均值聚类算法如下:
Reapeat for l=12 3……..
Step 1:computethe cluseter prototypes(means):
Step 2:competethe distance:
Step 3:Updatethe partition matrix:
算法改进
在模糊聚类的目标函数中Bezdek引入了加权指数m,使Dum的聚类准则变成m=2时候的特例,从数学上说m的出现不自然且没有必要,但如果不给以虑属度乘以权值,那么从硬聚类准则函数到软聚类目标函数的推广准则是无效的,参数m又称为平滑因子,控制着模式早模糊类间的分享程度,因此,要实现模糊c聚类就要选择一适合的m,然而最佳的m的选取目前还缺乏理论,监管存在一些经验值或经验范围,但没有面向问题的优选方法,也缺少参数m的有效性评价准则
尽管模糊聚类是一种无监督的分类,但现在的聚类算法却=需要应用聚类原型的先验条件,否则算法会产生误导,从未破坏算法的无监督性和自动化。
因为模糊聚类目标是非凸的,而模糊C均值聚类算法的计算过程又是迭代爬山,一次很容易陷入局部极值点,从而得不到最优解或满意解,同时,大数据量下算法耗时也是困扰人们的一大难题,这2个问题目前还不能得到全面的解决。
FCM类型的聚类算法属于划份方法,对于1组给定的样本集,不管数据中有无聚类结构,也不问分类结果是否有效,总把数据划分到C个子类中,换言之,现有的聚类分析与聚类趋势,以及有效分析是隔离的分离得。
FCM的聚类算法是针对特征空间中的点集设计的,对于特殊类型的数据,比如在样本每维特征的赋值不是一个数,而是一个区间。集合和模糊数时,FCM类型的算法无法直接处理
模糊C均值聚类算法存在上述缺点,改进的算法正确率能达到更高。Fcm算法在处理小数据集的时候是有效的,但随着数据容量和维数的增加,迭代步骤会显著增加,而且在迭代的每一步都要对整个数据集进行操作,无法满足数据挖掘时的需要。
改进算法的思想是首先采用随机抽样的办法,从数据集中选取多个样本,对每个样本应用FCM算法,将得到的结果作为初始群体,然后再利用遗传算法对聚类结果进行优化,选取其中的最优解做为问题的输出,由于采样技术显著的压缩了问题的规模,而遗传又可以对结果进行全局最优化处理,因此在时间性能和聚类质量上都能获得较满意的结果。
遗传算法是美国Michigon大学的John Holland研究机器学习时创立的一种新型的优化算法,它的主要优点是:遗传算法是从一系列点的群体开始搜索而不是从单个样本点进行搜索,遗传算法利用适应值的相关信息,无需连续可导或其他辅助信息,遗传算法利用转移概率规则,而非确定性规则进行迭代,遗传算法搜索过程中,以对群体进行分化以实现并行运算,遗传算法经过遗传变异和杂交算子的作用,以保证算法以概率1收敛到全局最优解—具有较好的全局特性,其次遗传算法占用计算机的内存小,尤其适用计算复杂的非线性问题。
遗传算法的设计部分
种群中个体的确定
聚类的关键问题是聚类中心的确定,因此可以选取聚类中心作为种群的个体,由于共有C个聚类中心,而每个聚类中心是一个S维的实数向量,因此每个个体的初始值是一个c*s维的市属向量。
编码
常用的编码方式有二进制与实数编码,由于二进制编码的方式搜索能力最强,且交叉变异操作简单高效,因此采用二进制的编码方式,同时防止在进行交叉操作时对优良个体造成较大的破坏,在二进制编码的方式中采用格雷码的编码形式。
每个染色体含c*s个基因链,每个基因链代表一维的数据,由于原始数据中各个属性的取值可能相差很大,因此需首先对数据进行交换以统一基因链的长度,可以有以下两种变换方式。
1扫描整个数据集,确定每维数据的取值范围,然后将其变换到同一量级,在保留一定有效位的基础上取整,根据有效位的个数动态的计算出基因链的长度。
2对数据进行正规化处理,即将各维数据都变换到相同的区间,可以算出此时的基因链长度为10。
适应度函数
由于在算法中只使用了聚类中心V,而未使用虑属矩阵u,因此需要对FCM聚类算法的目标函数进行改进,以适用算法的要求,和目标函数是等价的,由于遗传算法的适用度一般取值极大,因此可取上式的倒数作为算法的使用度函数。
初始种群的确定
初始种群的一般个体由通过采样后运行FCM算法得到的结果给出,另外的一般个体通过随机指定的方法给出,这样既保证了遗传算法在运算之初就利用背景知识对初始群体的个体进行了优化,使算法能在一个较好的基础上进行,又使得个体不至于过分集中在某一取值空间,保证了种群的多样性。
遗传操作
选择操作采用保持最优的锦标赛法,锦标赛规模为2,即每次随机取2个个体,比较其适应度,较大的作为父个体,并保留每代的最优个体作为下一代,交叉方式一般采用单点交叉或多点交叉法进行,经过试验表明单点交叉效果较好,因此采用单点交叉法,同时在交叉操作中,应该对每维数据分开进行,以保证较大的搜索空间和结果的有效性,变异操作采用基本位变异法。
终止条件的确定
遗传算法在以下二种情况下终止
a最佳个体保持不变的代数达到设定的阈值
b遗传操作以到达给定的最大世代数
算法具体步骤如下
1确定参数,如聚类个数 样本集大小 种群规模 最大世代数 交叉概率和变异概率等。
2对数据集进行多次采样并运行FCM算法,得到初始种群的一般个体,通过随机制定产生另一半个体。
3对数据集进行正规化处理并编码。
4计算初始种群中个体的适应度。
5对种群进行遗传操作产生下一代,在操作的过程中,应该排除产生的无效个体。
6计算个体的适应度,如果满足终止条件,则算法结束,否则转到5继续
在理论上讲进行遗传操作的样本容量越大,聚类的误差越小,由于采样技术显著压缩了问题规模,而遗传算法又可以对全局进行最优化处理,因此改进的算法在时间与性能上都能获得较满意的结果,此算法利用采样技术来提高算法的运行速度,利用遗传算法对聚类进行优化,避免陷入局部最优解,在性能上相比于传统的模糊C均值聚类算法获得较大提高。
算法实现
·采用VC++进行编写
文档的读取
#include "data.h"
//函数定义
double **DataRead(char*name,int row,intcol)
{
double**p=new double* [row];
ifstreaminfile;
infile.open(name,ios::in);
for(inti=0;i { p[i]=newdouble[col]; for(intj=0;j { infile>>p[i][j]; } } infile.close(); cout<<"成功读取数据文件:"< returnp; //释放内存 for(i=0;i { delete[]p[i]; } delete[]p; } 文档的保存 #include "data.h" void DataSave(double**data,int row,intcol,char*name) { inti,j; ofstreamoutfile; //打开文件,输出数据 outfile.open(name,ios::out); outfile.setf(ios::fixed); outfile.precision(4); for(i=0;i { for(j=0;j { outfile< } outfile< } outfile< outfile.close(); } 数据标准化处理 #include "data.h" double **Standardize(double **data,introw,int col) { inti,j; double*a=new double[col]; //矩阵每列的最大值 double*b=new double[col]; //矩阵每列的最小值 double*c=new double[row]; //矩阵列元素 for(i=0;i { //取出数据矩阵的各列元素 for(j=0;j { c[j]=Data[j][i]; } a[i]=c[0],b[i]=c[0]; for(j=0;j { //取出该列的最大值 if(c[j]>a[i]) { a[i]=c[j]; } //取出该列的最小值 if(c[j] { b[i]=c[j]; } } } //数据标准化 for(i=0;i { for(j=0;j { data[i][j]=(data[i][j]-b[j])/(a[j]-b[j]); } } cout<<"完成数据极差标准化处理!\n"; delete[]a; delete[]b; delete[]c; returndata; } 生成样本虑属矩阵 #include "data.h" void Initialize(double **u, int k, introw) { inti,j; //初始化样本隶属度矩阵 srand(time(0)); for(i=0;i { for(j=0;j { u[i][j]=(double)rand()/RAND_MAX;//得到一个小于1的小数隶属度 }//rand()函数返回0和RAND_MAX之间的一个伪随机数 } } 数据归一化处理 #include "data.h" void Normalize(double **u,int k,intcol) { inti,j; double*sum=new double[col]; //矩阵U的各列元素之和 for(j=0;j { doubledj=0; for(i=0;i { dj=dj+U[i][j]; sum[j]=dj;//隶属度各列之和 } } for(i=0;i { for(j=0;j { u[i][j]=U[i][j]/sum[j]; }//规一化处理(每列隶属度之和为1) } } 迭代过程 #include "data.h" #include "func.h"//对模糊C均值进行迭代运算,并返回有效性评价函数的值 doubleUpdate(double**u,double**data,double**center,int row,int col, int k) { inti,j,t; double**p=NULL; for(i=0;i { for(j=0;j { //模糊指数取2 u[i][j]=pow(u[i][j],2); } } //根据隶属度矩阵计算聚类中心 p=MatrixMul(u,k,row,data,row,col); for(i=0;i { //计算隶属度矩阵每行之和 doublesi=0; for(j=0;j { si+=u[i][j]; } for(t=0;t { center[i][t]=p[i][t]/si; //类中心 } } //计算各个聚类中心i分别到所有点j的距离矩阵dis(i,j) double*a=new double[col]; //第一个样本点 double*b=new double[col]; //第二个样本点 double**dis=newdouble*[k]; //中心与样本之间距离矩阵 for(i=0;i { dis[i]=newdouble[row]; } for(i=0;i { //聚类中心 for(t=0;t { a[t]=center[i][t]; //暂存类中心 } //数据样本 for(j=0;j { for(t=0;t { b[t]=data[j][t];//暂存一样本 } doubled=0; //中心与样本之间距离的计算 for(t=0;t { d+=(a[t]-b[t])*(a[t]-b[t]); //d为一中心与所有样本的距离的平方和 } dis[i][j]=sqrt(d); //距离 } } //根据距离矩阵计算隶属度矩阵 for(i=0;i { for(j=0;j { doubletemp=0; for(t=0;t { //dis[i][j]依次除以所在列的各元素,加和; //模糊指数为2.0 temp+=pow(dis[i][j]/dis[t][j],2/(2.0-1));//一个类中心和一个元素的距离平方与 } u[i][j]=1/temp;//所有类与该元素距离平方的和的商 } } //计算聚类有效性评价函数 doublefunc1=0; for(i=0;i { doublefunc2=0; for(j=0;j { func2+=pow(u[i][j],2.0)*pow(dis[i][j],2); } func1+=func2; } doubleobj_fcn=1/(1+func1); returnobj_fcn; //内存释放 delete[]a; delete[]b; for(i=0;i { delete[]dis[i]; } delete[]dis; } 详细过程 #include "data.h" #include "func.h" #include "max.h" //全局变量定义 double **Data; //数据矩阵 double **Center; //聚类中心矩阵 double **U; //样本隶属度矩阵 int m; //样本总数 int n; //样本属性数 int k; //设定的划分类别数 int main() { intLab; //数据文件标号 intnum; //算法运行次数 /// cout<<"模糊C均值聚类算法:"< cout<<"1-iris.txt; 2-wine.txt; 3-ASD_12_2.txt; 4-ASD_14_2.txt"< cout<<"请选择数据集: Lab="; cin>>Lab; cout<<"设定运行次数: mum="; cin>>num; //各次运行结束后的目标函数 double*Index=new double[num]; //各次运行结束后的聚类正确率 double*R=new double [num]; //num次运行的平均目标函数及平均正确率 doubleM_Index=0; doubleM_R=0; //FCM聚类算法运行num次,并保存记录与结果 for(inti=0;i { intj; doubleepsilon=1e-4; inte=0; intnx=0; //记录连续无改进次数 intE[200]={0}; if(i>0) { cout< cout< } cout<<"第"< //读取数据文件 if(Lab==1) { m=150; n=4; k=3; Data=DataRead("dataset\\iris.txt",m,n); } elseif(Lab==2) { m=178; n=13; k=3; Data=DataRead("dataset\\wine.txt",m,n); } elseif(Lab==3) { m=535; n=2; k=12; Data=DataRead("dataset\\ASD_12_2.txt",m,n); } elseif(Lab==4) { m=685; n=2; k=14; Data=DataRead("dataset\\ASD_14_2.txt",m,n); } //数据极差标准化处理 Data=Standardize(Data,m,n); //聚类中心及隶属度矩阵,内存分配 Center=newdouble*[k]; U=newdouble *[k]; for(j=0;j { Center[j]=newdouble[n]; U[j]=newdouble[m]; } //隶属度矩阵的初始化 Initialize(U,k, m); //对隶属度矩阵进行归一化 Normalize(U,k,m); //历次迭代过程中的目标函数 doubleObjfcn[100]={0}; cout<<"第"< cout<<"开始迭代过程!"< cout<<"*******************************"< //输出精度为小数点后5位 cout.precision(5); //固定格式 cout.setf(ios::fixed); //目标函数连续20代无改进,停止该次聚类迭代过程 while(e<20) { nx++; //聚类迭代过程 Objfcn[nx]=Update(U,Data,Center,m,n,k); //统计目标函数连续无改进次数e if(nx>0&& Objfcn[nx]-Objfcn[nx-1] { e++; } else { e=0; } E[nx]=e; } //输出结果到文件,保存 ofstreamoutfile("运行记录.txt",ios::app); outfile<<"第"< outfile<<"开始迭代过程!"< outfile<<"*******************************"< outfile.precision(5); outfile.setf(ios::fixed); for(intn1=1;n1<=nx;n1++) { cout<<"e["< < //保存数据文件 outfile<<"e["< < } cout< outfile< outfile.close(); //本次运行的最大目标函数 Index[i]=Objfcn[nx]; //保存聚类正确率,输出聚类结果: R[i]=Result(Lab,U, k, m, i); //内存释放 for(j=0;j { delete[]Center[j]; delete[]U[j]; } delete[]Center; delete[]U; } //统计平均/// doubletemp1=0, temp2=0; for(i=0;i { temp1+=Index[i]; temp2+=R[i]; } //计算各次结果的统计平均 M_Index=(double)temp1/num; M_R=(double)temp2/num; cout<<"//"< cout< //输出精度为小数点后6位 cout.precision(6); //固定格式 cout.setf(ios::fixed); cout<<"平均目标函数:"< //统计结果文件保存 ofstreamresultfile("聚类结果.txt",ios::app); resultfile<<"//"< resultfile< //输出精度为小数点后6位 resultfile.precision(6); //固定格式 resultfile.setf(ios::fixed); resultfile<<"平均目标函数:"< return0; } 采用著名的iris数据集对程序进行测试, 运算次数输入10次 能对数组实现分类,但是分类正确率不是很理想,没达到预期的90%以上 总结
这次综合实习,首先我学会了模糊C均值聚类算法,以前没接触过,也不知道何为数据集,数据集是做啥用的,增加了自己的见解,其次增强了自我学习能力,平时学习的学习都是老师已经安排好得内容,看啥知识都已经知道,只需要安心的看就能学会,都是书本上的知识,没有联系实际,一般都是一些理论算法,算法比较简单而且古老,但这次综合实习通过自己从网上查找资料学习,了解算法的详细步骤,研究算法的不足,虽然查找资料学习的过程比较繁琐,但总算在最后学会了模糊C均值聚类算法,也对算法的改进有所了解,人工智能学习的GA遗传算法进行了改进,通过这次综合实习,我对不懂得知识的学习有了一个比较系统的学习过程,为以后的自我学习打下了基础,模糊C均值聚类算法的程序是用c++实现的,通过对程序的而研究,我对于文件的读取保存 计算的迭代过程等都已经非常的了解,对于以前所学的进行了一次复习,这次综合实现收获很大,自己所学的东西学会了如何使用,不同的算法进行衔接进行优化,实现对最优解的优化,感谢朱老师的帮助使我能顺利的完成综合实习。
参考文献
[1] 高新波,模糊c均值聚类算法中加权指数m的研究[J],电子学报,2000,28(4):80-83
[2] 朱剑英,应用模糊数学方法的若干关键问题及处理方法[j].模糊系统与数学,1992,11(2):57-63
[3] 刘蕊洁张金波刘锐,模糊c均值聚类算法,重庆工学院学报,2007-21-1
[4] 高新波。模糊聚类分析及其应用[M].西安:西安电子科学出版社,2004
[5] 高新波,谢维信.模糊聚类理论发展及应用的研究进展[J].科学通报,1999,44(21):2241-2251
[6] 张洁,高新波,焦李成.基于特征加权的模糊聚类新算法[J],电子学报,2006,34(1):412-420
[7] Chan K P, CheungY S. Clustering of clusters [J]. Pattern Recognition,1992,25(2):211-217
[8] Spragins J.Learning withouta teacher[J].IEEE Transactions of Infornation Theory,2005,23(6):223-230.
[9] Babusk R . FUZZY AND NEURALCONTROL[M].Nethealands:Delft University of Technology,2001
[10]Theodoridis S.PatternRecongnition [M]. Second Edition.USA:Elsevier Scinece,2003
[11]MAULIK U.BANDYOPADHYAYS.Genetic algorithm-based clustering technique[J].patternRecognition,2000,33(9):144-1465
[12] J.C.Bezdek.Patternrecognition with fuzzy objective funcition algorithms.New York:Plenum,1981
[13] R.Duda and P.Hart .Patternclassification and scene analysis.New York:Wiley,1973
[14]J.K.Dunn.J.Cybern.1973,3(3):32-57
[15] J.C.Bezdek.IEEETrans.Syst.Man Cybern.1987,17(5):873-877
https://wenku.baidu.com/view/ee968c00eff9aef8941e06a2.html