谱聚类的实现


开始用accumulate做加和,结果发现laplacian矩阵求特征值后最小的特征值不是0,这是有问题的,聚类的准确率不是很高,原因也找不到。后来一步步查,发现diag矩阵有问题,最终查到accumulate是int相加,不准确,修改后准确率增加。


// spectral-cluster.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream> 
#include<vector>
#include<set>
#include <numeric>
#include <cv.h>
#include <highgui.h>
/*********************author Marshall**************************/
/************************2015.10.20****************************/
/*********************version 1.0******************************/

using namespace std;


class spectral;

class kmeans
{
	friend class spectral;
private:
	double*dataset;
	unsigned int k;
	unsigned int dim;
	unsigned int numofdata;
	typedef vector<double> Centroid;
	vector<Centroid> center;
	vector<set<int>>cluster_ID;
	vector<Centroid>new_center;
	vector<set<int>>new_cluster_ID;
	double threshold;
	int iter;
private:
	void init();
	void assign();
	double distance(Centroid cen, int k2);
	void split(vector<set<int>>&clusters, int kk);
	void update_centers();
	bool isfinish();
	void show_result();

public:
	kmeans(double*data, unsigned int datanums, const int dim, const int numofcluster)
		:dataset(data), numofdata(datanums), dim(dim), k(numofcluster)
	{
		threshold = 0.0000001;
	}
	void apply();
};

//template <typename T>
void kmeans::init()
{
	center.resize(k);

	set<int>bb;
	for (int i = 0; i < k; i++)
	{
		int id = double(rand()) / double(RAND_MAX + 1.0)*numofdata;
		while (bb.find(id) != bb.end())
		{
			id = double(rand()) / double(RAND_MAX + 1.0)*numofdata;
		}
		bb.insert(id);
		center[i].resize(dim);
		for (int j = 0; j < dim; j++)
			center[i][j] = dataset[id*dim + j];
	}
}
bool kmeans::isfinish()
{
	double error = 0;
	for (int i = 0; i < k; i++)
	{
		for (int j = 0; j < dim; j++)
			error += pow(center[i][j] - new_center[i][j], 2);
	}
	return error < threshold ? true : false;
}
void kmeans::assign()
{

	for (int j = 0; j < numofdata; j++)
	{
		double mindis = 10000000;
		int belongto = -1;
		for (int i = 0; i < k; i++)
		{
			double dis = distance(center[i], j);
			if (dis < mindis)
			{
				mindis = dis;
				belongto = i;
			}
		}
		new_cluster_ID[belongto].insert(j);
	}
	for (int i = 0; i < k; i++)
	{
		if (new_cluster_ID[i].empty())
		{
			split(new_cluster_ID, i);
		}
	}
}

double kmeans::distance(Centroid cen, int k2)
{
	double dis = 0;
	for (int i = 0; i < dim; i++)
		dis += pow(cen[i] - dataset[k2*dim + i], 2);
	return sqrt(dis);
}

void kmeans::split(vector<set<int>>&clusters, int kk)
{
	int maxsize = 0;
	int th = -1;
	for (int i = 0; i < k; i++)
	{
		if (clusters[i].size() > maxsize)
		{
			maxsize = clusters[i].size();
			th = i;
		}
	}
#define DELTA 3
	vector<double>tpc1, tpc2;
	tpc1.resize(dim);
	tpc2.resize(dim);
	for (int i = 0; i < dim; i++)
	{
		tpc2[i] = center[th][i] - DELTA;
		tpc1[i] = center[th][i] + DELTA;
	}
	for (set<int>::iterator it = clusters[th].begin(); it != clusters[th].end(); it++)
	{
		double d1 = distance(tpc1, *it);
		double d2 = distance(tpc2, *it);
		if (d2 < d1)
		{
			clusters[kk].insert(*it);
		}
	}
	_ASSERTE(!clusters[kk].empty());
	for (set<int>::iterator it = clusters[kk].begin(); it != clusters[kk].end(); it++)
		clusters[th].erase(*it);

}

void kmeans::update_centers()
{
	for (int i = 0; i < k; i++)
	{
		Centroid temp;
		temp.resize(dim);
		for (set<int>::iterator j = new_cluster_ID[i].begin(); j != new_cluster_ID[i].end(); j++)
		{
			for (int m = 0; m < dim; m++)
				temp[m] += dataset[(*j)*dim + m];
		}
		for (int m = 0; m < dim; m++)
			temp[m] /= new_cluster_ID[i].size();
		new_center[i] = temp;
	}
}

void kmeans::apply()
{
	init();
	new_center.resize(k);
	new_cluster_ID.resize(k);
	assign();
	update_centers();
	iter = 0;
	while (!isfinish())
	{
		center = new_center;
		cluster_ID = new_cluster_ID;
		new_center.clear();
		new_center.resize(k);
		new_cluster_ID.clear();
		new_cluster_ID.resize(k);
		assign();
		update_centers();
		iter++;
	}
	//new_cluster_ID.clear();
}


class spectral
{
private:
	unsigned int dim;
	unsigned int N;
	double**normalized_data;
	double*laplacian_matrix;
	double*diag_matrix;
	double*similarity_matrix;
	bool is_normalized;
	double sigma2;
	unsigned int k_nearest;
	unsigned int K;
	unsigned int numofcluster;
private:
	void create_similarity_matrix();
	void create_diag_matrix();
	void create_laplacian_matrix();
	void eigen_of_laplacian(double*&newdata);
	//void kmeans();
	void normalize_data(double*&data, int datanums, int dim);
	double euclidean(const double*data1, const double*data2);
	bool is_knearest(const double*distance_matrix, const int kk, const int mm);
public:
	spectral(double**data, const int N, const int dim, const int numofcluster);
	void spectral_cluster(int*pClusters);
	~spectral();
};

spectral::~spectral()
{

}
spectral::spectral(double**data, const int N, const int dim, const int numofcluster)
	:N(N), dim(dim), numofcluster(numofcluster)
{
	K = 4;
	k_nearest = 15;
	sigma2 = 600;
	normalized_data = data;
	is_normalized = false;
}
void spectral::normalize_data(double*&data, int datanums, int dim)
{
	double*mins = new double[dim];
	memset(mins, 0x3f3f3f3f, sizeof(double)*dim);
	double*maxs = new double[dim];
	memset(maxs, 0, sizeof(double)*dim);
	for (int i = 0; i < datanums; i++)
		for (int j = 0; j < dim; j++)
		{
			if (data[i*dim + j] > maxs[j])
			{
				maxs[j] = data[i*dim + j];
			}
			if (data[i*dim + j] < mins[j])
				mins[j] = data[i*dim + j];
		}
	for (int i = 0; i < datanums; i++)
		for (int j = 0; j < dim; j++)
			data[i*dim + j] = 100 * (data[i*dim + j] - mins[j]) / (maxs[j] - mins[j]);
	delete[]maxs, mins;
}
double spectral::euclidean(const double*data1, const double*data2)
{
	double dis = 0;
	for (int i = 0; i < dim; i++)
		dis += pow(data1[i] - data2[i], 2);
	return dis;
}
bool spectral::is_knearest(const double*distance_matrix,
	const int kk, const int mm)
{
	double dis = distance_matrix[kk*N + mm];
	int count = 0;
	//还要考虑distance_matrix[kk*N + kk]=0;
	//因为k_nearest相比N很小(大多数情况),判断false好
	for (int i = 0; i < N; i++)
		if (i != kk&&distance_matrix[kk*N + i] < dis)
		{
			count++;
			if (count > k_nearest)
				return false;
		}
	return true;
}

void spectral::create_similarity_matrix()
{
	double*distance_matrix = new double[N*N];
	memset(distance_matrix, 0, N*N*sizeof(double));
	for (int i = 0; i < N - 1; i++)
		for (int j = i + 1; j < N; j++)
		{
			distance_matrix[i*N + j] = euclidean
				(normalized_data[i], normalized_data[j]);
			distance_matrix[j*N + i] = distance_matrix[i*N + j];
		}
	if (similarity_matrix)
		delete[]similarity_matrix;
	similarity_matrix = new double[N*N];
	memset(similarity_matrix, 0, N*N*sizeof(double));

	//这里使用对称k最近邻

	for (int i = 0; i < N - 1; i++)
	{
		for (int j = i + 1; j < N; j++)
		{
			if (is_knearest(distance_matrix, i, j)
				|| is_knearest(distance_matrix, j, i))
			{
				similarity_matrix[i*N + j] = similarity_matrix[j*N + i]
					= 100 * exp(-distance_matrix[i*N + j] / sigma2);
			}
			//cout << similarity_matrix[i*N + j] << "     ";
		}
		//cout << endl << endl << endl << endl;
	}

	/*for (int i = 0; i < N; i++)
	{
	for (int j = 0; j < N; j++)
	{
	cout << similarity_matrix[i*N + j] << "     ";
	}
	cout << endl << endl << endl << endl;
	}*/
	delete[]distance_matrix;
}

void spectral::create_diag_matrix()
{
	if (diag_matrix)
		delete[]diag_matrix;
	diag_matrix = new double[N*N];
	memset(diag_matrix, 0, N*N*sizeof(double));
	for (int i = 0; i < N; i++)
	{
		//accumulate是int相加,不准确,修改后准确率增加
		//diag_matrix[i*N + i] = accumulate(similarity_matrix + i*N, similarity_matrix + i*N + N, 0);
		double sum = 0;
		for (int j = 0; j < N; j++)
			sum += similarity_matrix[i*N + j];
		diag_matrix[i*N + i] = sum;
		//cout << "diag" << i << "   " << diag_matrix[i*N + i] << endl;
	}
}

void spectral::create_laplacian_matrix()
{
	if (laplacian_matrix)
		delete[]laplacian_matrix;
	laplacian_matrix = new double[N*N];
	for (int i = 0; i < N; i++)
		for (int j = i; j < N; j++)
		{
			laplacian_matrix[i*N + j] = laplacian_matrix[j*N + i]
				= diag_matrix[i*N + j] - similarity_matrix[i*N + j];
		}
	if (is_normalized)
	{
		for (int i = 0; i < N; i++)
			for (int j = 0; j < N; j++)
			{
				int temp = i == j ? 1 : 0;
				laplacian_matrix[i*N + j] = temp - 1.0 / sqrt(diag_matrix[j*N + j]
					+ 1.0e-13)*laplacian_matrix[i*N + j] * 1.0 / sqrt(diag_matrix[i*N + i] + 1.0e-13);
			}
	}

	delete[]diag_matrix, similarity_matrix;
}

void spectral::eigen_of_laplacian(double*&newdata)
{
	//CvMat Ma = cvMat(N, N, CV_64FC1, laplacian_matrix);

	CvMat *pSrcMatrix = cvCreateMat(N, N, CV_64FC1);
	// E = 对应的特征向量 (每行)
	CvMat* E = cvCreateMat(N, N, CV_64FC1);
	CvMat* l = cvCreateMat(N, 1, CV_64FC1);
	for (int i = 0; i < N; i++)
	{
		cvmSet(l, i, 0, 0);
		for (int j = 0; j < N; j++)
		{
			cvmSet(pSrcMatrix, i, j, laplacian_matrix[i*N + j]);
			cvmSet(E, i, j, 0);
		}
	}


	cvEigenVV(pSrcMatrix, E, l);   // l = A的特征值 (降序排列)

	for (int i = 0; i < N; i++)
		cout << cvmGet(l, i, 0) << "      ";//半正定,最小的特征值应该是0,应该在此处验证
	
	for (int i = 0; i < N; i++)
	{
		//cout << cvmGet(E, N - 1 , i);
		for (int j = 0; j < K; j++)
		{
			newdata[i*K + j] = cvmGet(E, N - 2 - j, i);
		}
	}
	delete[]laplacian_matrix;
	// 释放矩阵所占内存空间
	cvReleaseMat(&pSrcMatrix);
	cvReleaseMat(&E);
	cvReleaseMat(&l);
}


void spectral::spectral_cluster(int*pClusters)
{
	create_similarity_matrix();
	create_diag_matrix();
	create_laplacian_matrix();
	double*newdata = new double[N*K];
	eigen_of_laplacian(newdata);
	//normalize_data(newdata, this->N, dim);
	kmeans km(newdata, N, K, this->numofcluster);
	km.apply();
	delete[]newdata;
	newdata = NULL;
	int count = 0;
	for (int i = 0; i < km.new_cluster_ID.size(); i++)
		for (set<int>::iterator it = km.new_cluster_ID[i].begin(); it != km.new_cluster_ID[i].end(); it++)
		{
			pClusters[*it] = i;
			count++;
		}
	_ASSERTE(count == N);
}

int _tmain(int argc, _TCHAR* argv[])
{

#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 clusterNum = 4;
	int sampleNum = 500;
	int feaNum = 2;
	CvMat* sampleMat = cvCreateMat(sampleNum, 1, CV_32FC2);

	/* generate random sample from multigaussian distribution */
	//产生高斯随机数  
	for (int 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 (int 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);
	}

	double** pSamples = new double*[sampleNum];
	for (int i = 0; i < sampleNum; i++)
		pSamples[i] = new double[feaNum];
	int* pClusters = new int[sampleNum];
	for (int i = 0; i < sampleNum; i++)
	{
		//feaNum=2  
		pSamples[i][0] = (cvGet2D(sampleMat, i, 0)).val[0];
		//cout << pSamples[i][0] << "  ";
		pSamples[i][1] = (cvGet2D(sampleMat, i, 0)).val[1];
		//cout << pSamples[i][1] << endl;
	}
	cvReleaseMat(&sampleMat);

	cvZero(img);
	for (int i = 0; i < sampleNum; i++)
	{
		//feaNum=2  
		ipt.x = pSamples[i][0];
		ipt.y = pSamples[i][1];
		cvCircle(img, ipt, 1, cvScalar(255, 255, 255), CV_FILLED, CV_AA, 0);
	}
	cvSaveImage("origin.jpg", img);


	//执行谱聚类
	spectral sp(pSamples, sampleNum, feaNum, clusterNum);
	sp.spectral_cluster(pClusters);

	cvZero(img);
	for (int i = 0; i < sampleNum; i++)
	{
		//feaNum=2  
		int cluster_idx = pClusters[i];
		ipt.x = pSamples[i][0];
		ipt.y = pSamples[i][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);

	cvSaveImage("clusters.jpg", img);

	cvReleaseImage(&img);


	system("pause");
	cvDestroyWindow("clusters");
	return 0;
}



下面是聚类结果。


      谱聚类的实现_第1张图片


嫌c++冗长的看这个python的

#!/usr/bin/python
# copyright (c) 2008 Feng Zhu, Yong Sun

import heapq
from functools import partial
from numpy import *
from scipy.linalg import *
from scipy.cluster.vq import *
import pylab

def line_samples ():
    vecs = random.rand (120, 2)
    vecs [:,0] *= 3
    vecs [0:40,1] = 1
    vecs [40:80,1] = 2
    vecs [80:120,1] = 3
    return vecs

def gaussian_simfunc (v1, v2, sigma=1):
    tee = (-norm(v1-v2)**2)/(2*(sigma**2))
    return exp (tee)

def construct_W (vecs, simfunc=gaussian_simfunc):
    n = len (vecs)
    W = zeros ((n, n))
    for i in xrange(n):
        for j in xrange(i,n):
            W[i,j] = W[j,i] = simfunc (vecs[i], vecs[j])
    return W

def knn (W, k, mutual=False):
    n = W.shape[0]
    assert (k>0 and k<(n-1))

    for i in xrange(n):
        thr = heapq.nlargest(k+1, W[i])[-1]
        for j in xrange(n):
            if W[i,j] < thr:
                W[i,j] = -W[i,j]

    for i in xrange(n):
        for j in xrange(i, n):
            if W[i,j] + W[j,i] < 0:
                W[i,j] = W[j,i] = 0
            elif W[i,j] + W[j,i] == 0:
                W[i,j] = W[j,i] = 0 if mutual else abs(W[i,j])

vecs = line_samples()
W = construct_W (vecs, simfunc=partial(gaussian_simfunc, sigma=2))
knn (W, 10)
D = diag([reduce(lambda x,y:x+y, Wi) for Wi in W])
L = D - W

evals, evcts = eig(L,D)
vals = dict (zip(evals, evcts.transpose()))
keys = vals.keys()
keys.sort()

Y = array ([vals[k] for k in keys[:3]]).transpose()
res,idx = kmeans2(Y, 3, minit='points')

colors = [(1,2,3)[i] for i in idx]
pylab.scatter(vecs[:,0],vecs[:,1],c=colors)
pylab.show()


你可能感兴趣的:(谱聚类)