模糊C均值聚類以及C實現
1. 基本介紹
同K均值類似,FCM演算法也是一種基於劃分的聚類演算法,它的思想就是使得被劃分到同一簇的物件之間相似度最大,而不同簇之間的相似度最小。
模糊C均值是普通C均值聚類演算法的改進,普通C均值對資料進行硬性劃分,一個樣本一定明確的屬於某一類,FCM對資料進行模糊劃分,使用隸屬度表示一個樣本屬於某一類的程度。實際聚類中可能會遇到這樣的情況,蝴蝶形資料集中樣本點的類別不好硬性判斷,所以引入隸屬度來進行模糊劃分。
隸屬度函式是表示一個物件x隸屬於集合A的程度的函式,通常記做μA(x),其自變數範圍是所有可能屬於集合A的物件(即集合A所在空間中的所有點),取值範圍是[0,1],即0<=1,μA
2. 引數更新公式推導
a.隸屬度矩陣:Uc×n
Uij 是第i 個樣本屬於第j個聚類的隸屬度。
3. 步驟
(a)確定類別數C,引數m,和迭代停止誤差Epslion以及最大迭代次數MaxIterationTimes
(b)初始化聚類中心P
(c)計算初始的距離矩陣D
(d)按下列公式更新隸屬度:
注意,如果有距離為0的情況出現,則把該點與相應類的隸屬度設為1,其它設為0。
(e)更新聚類中心
(f)重新計算距離矩陣,並計算目標函式的值
(g)若達到最大迭代次數或者前後兩次的J的絕對差小於迭代停止誤差則停止,否則轉(d),也可以使用前後兩次隸屬度矩陣的差來判斷。
(h)將樣本點劃分為隸屬度最大的那一類。
4. C實現(使用了opencv做資料顯示)
#include "highgui.h" #include <math.h> #include <time.h> #include "cv.h" //FCM聚類,得到的結果從0開始計數 void myFCMeans(float* pSamples,int* pClusterResult,int clusterNum,int sampleNum,int featureNum,int m_Value); void main() { #define MAX_CLUSTERS 5 CvScalar color_tab[MAX_CLUSTERS]; IplImage* img = cvCreateImage( cvSize( 500, 500 ), IPL_DEPTH_8U, 3 ); CvRNG rng = cvRNG(cvGetTickCount()); CvPoint ipt; color_tab[0] = CV_RGB(255,0,0); color_tab[1] = CV_RGB(0,255,0); color_tab[2] = CV_RGB(100,100,255); color_tab[3] = CV_RGB(255,0,255); color_tab[4] = CV_RGB(255,255,0); int i,k; int clusterNum = cvRandInt(&rng)%MAX_CLUSTERS + 2; //至少有兩類 int sampleNum = cvRandInt(&rng)%1000 + 100; //至少100個點 int feaNum=2; CvMat* sampleMat = cvCreateMat( sampleNum, 1, CV_32FC2 ); /* generate random sample from multigaussian distribution */ //產生高斯隨機數 for( k = 0; k < clusterNum; k++ ) { CvPoint center; int topIndex=k*sampleNum/clusterNum; int bottomIndex=(k == clusterNum - 1 ? sampleNum : (k+1)*sampleNum/clusterNum); CvMat* localMat=cvCreateMatHeader(bottomIndex-topIndex,1,CV_32FC2); center.x = cvRandInt(&rng)%img->width; center.y = cvRandInt(&rng)%img->height; cvGetRows( sampleMat, localMat, topIndex,bottomIndex, 1 ); //此函式不會給localMat分配空間,不包含bottomIndex cvRandArr( &rng, localMat, CV_RAND_NORMAL, cvScalar(center.x,center.y,0,0), cvScalar(img->width*0.1,img->height*0.1,0,0)); } // shuffle samples 混亂資料 for( i = 0; i < sampleNum/2; i++ ) { CvPoint2D32f* pt1 = (CvPoint2D32f*)sampleMat->data.fl + cvRandInt(&rng)%sampleNum; CvPoint2D32f* pt2 = (CvPoint2D32f*)sampleMat->data.fl + cvRandInt(&rng)%sampleNum; CvPoint2D32f temp; CV_SWAP( *pt1, *pt2, temp ); } float* pSamples=new float[sampleNum*feaNum]; int* pClusters=new int[sampleNum]; for (i=0;i<sampleNum;i++) { //feaNum=2 pSamples[i*feaNum]=(cvGet2D(sampleMat,i,0)).val[0]; pSamples[i*feaNum+1]=(cvGet2D(sampleMat,i,0)).val[1]; } cvReleaseMat( &sampleMat ); cvZero( img ); for( i = 0; i < sampleNum; i++ ) { //feaNum=2 ipt.x = (int)(pSamples[i*feaNum]); ipt.y = (int)(pSamples[i*feaNum+1]); cvCircle( img, ipt, 1, cvScalar(255,255,255), CV_FILLED, CV_AA, 0 ); } cvSaveImage("origin.jpg",img); myFCMeans(pSamples,pClusters,clusterNum,sampleNum,feaNum,2); cvZero( img ); for( i = 0; i < sampleNum; i++ ) { //feaNum=2 int cluster_idx = pClusters[i]; ipt.x = (int)(pSamples[i*feaNum]); ipt.y = (int)(pSamples[i*feaNum+1]); cvCircle( img, ipt, 1, color_tab[cluster_idx], CV_FILLED, CV_AA, 0 ); } delete[] pClusters; pClusters=NULL; delete[] pSamples; pSamples=NULL; cvNamedWindow( "clusters"); cvShowImage( "clusters", img ); cvWaitKey(0); cvDestroyWindow( "clusters" ); cvSaveImage("clusters.jpg",img); cvReleaseImage(&img); } //FCM聚類,得到的結果從0開始計數 void myFCMeans(float* pSamples,int* pClusterResult,int clusterNum,int sampleNum,int featureNum,int m_Value) { if (m_Value<=1 || clusterNum>sampleNum) { return; } int i,j,k; int int_temp; float* pUArr=NULL; //隸屬度矩陣 double* pDistances=NULL; //距離矩陣 float* pCenters=NULL; //聚類中心 int m=m_Value; //引數 int Iterationtimes; //迭代次數 int MaxIterationTimes=100; //最大迭代次數 double Epslion=0.001; //聚類停止誤差 double VarSum; //平方誤差和 double LastVarSum; //上一次的平方誤差和 //申請空間以及初始化 pUArr=new float[sampleNum*clusterNum]; pDistances=new double[sampleNum*clusterNum]; pCenters=new float[clusterNum*featureNum]; //初始化中心點 srand((unsigned int)time(NULL)); int_temp=rand()%sampleNum; for (i=0;i<clusterNum;i++) { for (j=0;j<featureNum;j++) { pCenters[i*featureNum+j]=pSamples[int_temp*featureNum+j]; } int_temp+=(sampleNum/clusterNum); if (int_temp>=sampleNum) { int_temp%=sampleNum; } } //計算初始距離矩陣 for (i=0;i<sampleNum;i++) { for (k=0;k<clusterNum;k++) { double distance_temp=0; for (j=0;j<featureNum;j++) { distance_temp+=(double)((double)(pCenters[k*featureNum+j]-pSamples[i*featureNum+j])*(pCenters[k*featureNum+j]-pSamples[i*featureNum+j])); } pDistances[i*clusterNum+k]=distance_temp; } } //開始聚類 Iterationtimes=0; LastVarSum=0; VarSum=0; while(1) { Iterationtimes++; //更新隸屬度矩陣 //計算隸屬度,聚類矩陣已更新 for (i=0;i<sampleNum;i++) { double denominator=0; //(1/Dik^2)^(1/(m-1)) bool isEqualCenter=false; //是否有距離為0的情況 int category_temp; //如果有距離為0的情況所判別的類 for (k=0;k<clusterNum;k++) { double currentDis=pDistances[i*clusterNum+k]; if (currentDis!=0) { denominator+=pow(1.0/pDistances[i*clusterNum+k],1.0/(m-1)); } else { isEqualCenter=true; category_temp=k; break; } } //如果有距離為0的情況,就把相應類的隸屬度置為1,其它為0 if (true==isEqualCenter) { for(k=0;k<clusterNum;k++) { pUArr[i*clusterNum+k]=0; } pUArr[i*clusterNum+clusterNum]=1.0; } else { for (k=0;k<clusterNum;k++) { pUArr[i*clusterNum+k]=pow(1.0/pDistances[i*clusterNum+k],1.0/(m-1))/denominator; } } } //更新聚類中心 for (k=0;k<clusterNum;k++) { double denominator=0; for (j=0;j<featureNum;j++) { double numerator=0; for (i=0;i<sampleNum;i++) { double U_m_temp=pow(pUArr[i*clusterNum+k],m_Value); if (0==j) { denominator+=U_m_temp; } numerator+=(U_m_temp*pSamples[i*featureNum+j]); } pCenters[k*featureNum+j]=numerator/denominator; } } //計算平方誤差函式值,並更新距離矩陣 VarSum=0; for (i=0;i<sampleNum;i++) { for (k=0;k<clusterNum;k++) { double distance_temp=0; for (j=0;j<featureNum;j++) { distance_temp+=(double)((double)(pCenters[k*featureNum+j]-pSamples[i*featureNum+j]) *(pCenters[k*featureNum+j]-pSamples[i*featureNum+j])); } pDistances[i*clusterNum+k]=distance_temp; VarSum+=(pow(pUArr[i*clusterNum+k],1.0/m)*distance_temp); //存在溢位危險 } } if (Iterationtimes>=MaxIterationTimes || fabsl(VarSum-LastVarSum)<=Epslion) { break; } LastVarSum=VarSum; } delete[] pDistances; pDistances=NULL; delete[] pCenters; pCenters=NULL; //分類,歸為隸屬度最大的那一類 for (i=0;i<sampleNum;i++) { float maxU; //最大隸屬度 int Category; //屬於類別 for (k=0;k<clusterNum;k++) { if (0==k || maxU<pUArr[i*clusterNum+k]) { Category=k; maxU=pUArr[i*clusterNum+k]; } } pClusterResult[i]=Category; } delete[] pUArr; pUArr=NULL; }
5. 結果