1. 程式人生 > >模糊C均值聚類以及C實現

模糊C均值聚類以及C實現

1. 基本介紹

    同K均值類似,FCM演算法也是一種基於劃分的聚類演算法,它的思想就是使得被劃分到同一簇的物件之間相似度最大,而不同簇之間的相似度最小。

    模糊C均值是普通C均值聚類演算法的改進,普通C均值對資料進行硬性劃分,一個樣本一定明確的屬於某一類,FCM對資料進行模糊劃分,使用隸屬度表示一個樣本屬於某一類的程度。實際聚類中可能會遇到這樣的情況,蝴蝶形資料集中樣本點的類別不好硬性判斷,所以引入隸屬度來進行模糊劃分。


    隸屬度函式是表示一個物件x隸屬於集合A的程度的函式,通常記做μA(x),其自變數範圍是所有可能屬於集合A的物件(即集合A所在空間中的所有點),取值範圍是[0,1],即0<=1,μA

(x)<=1。μA(x)=1表示x完全隸屬於集合A,相當於傳統集合概念上的x∈A。一個定義在空間X={x}上的隸屬度函式就定義了一個模糊集合A,或者叫定義在論域X={x}上的模糊子集 。對於有限個物件x1,x2,……,xn模糊集合可以表示為:


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. 結果