- 实验目的:
(1) 熟悉 VC++编程工具和 k-均值聚类算法。
(2) 在训练样本集上用 VC++编程工具编写用于 k-均值聚类的程序,对任务 相关数据运行 k-均值聚类算法,调试实验。
(3) 掌握距离计算方法和聚类的评价准则。
(4) 写出实验报告。
二、 实验原理:
1、k-均值聚类
k-均值聚类是一种基于形心的划分技术,具体迭代的计算步骤如下:
1)在属性向量空间随机产生 k 个形心坐标。
2)分别计算数据集 D 中的每个数据对象 Ti (1≤i≤n)到所有 k 个形心的距离度量 Dist(i,j) (1≤i≤n, 1≤j≤k),并将数据对象 Ti 聚到最小距离度量的那一簇中。即 Ti∈CJ,表示数据对象 Ti 被聚到第 J 簇中。其中 J=argmin(Dist(i,j)),表示 J 为可使得 Dist(i,j)取最小值的那个 j。
3)按照形心的定义计算每一簇的形心坐标,形成下一代的 k 个形心坐标。
4)如果不满足终结条件,转到 2)继续迭代;否则结束。
其中,簇的形心可以有不同的的定义,例如可以是簇内数据对象属性向量的均值(也就是重心),也可以是中心点等;距离度量也可以有不同的定义,常用的有欧氏距离、曼哈顿(或城市块、街区)距离、闵可夫斯基距离等;终结条件可采用当对象的重新分配不再发生时,程序迭代结束。
2、终止条件
终止条件可以是以下任何一个:
1)没有(或最小数目)对象被重新分配给不同的聚类。
2)没有(或最小数目)聚类中心再发生变化。
3)误差平方和局部最小。
三、 实验内容:
1、实验内容
根据 k-均值聚类算法的计算步骤,画出 k=3 时的程序流程图;
由 k-均值程序流程图编程实现 k-均值聚类算法;
在实验报告中显示 k-均值聚类过程的一系列截图,指明各个簇的逐渐演化过程;
在报告中指出实验代码中的初始质心的选择,终止条件的选择,以及距离度量的选择并予以说明。
2、实验步骤
编程实现如下功能:
1)首先将数据集 D={D1,D2,D3}中的属性向量作为实验数据输入;
2)由 k-均值程序流程图编程实现 k-均值聚类算法,并用实验数据运行;
3)运行过程中在适当的迭代代数暂停并显示实时迭代的结果,如簇心的位置、按距离最近邻聚类的结果等;
3、程序框图
- 关键代码
#include<iostream>
#include<string>
#include<fstream>
#include<algorithm>
#include <math.h>
using namespace std;
#define k 3 //聚类数
#define n 2 //数据维数
#define size 30 //数据大小
定义存储结构//
typedef struct {
double d[n];
double distance[k];
}Data;
typedef struct {
Data center[k]; // 即簇类中心
int cluster[k][size]; //簇数组
int cluster_num[k];// 簇类中一组数据的编号
Data old_center[k];
int isend[k][n]; //各簇类中心是否相等标示值
int is;
}Tdata;
声明函数//
void input_data();
void Init_center();
void calculate_distance();
double Euclid(int x, int y);
void new_cluster();
void new_center();
void output_info();
void comp();
Data data[size];
Tdata td;
///读入数据
void Init()
{
char name1[50];
ifstream infile;
cout<<"输入要打开的文件:*.txt"<<endl;
cin>>name1;
infile.open(name1,ios::in);
if(infile.fail())
{
cout << "error open!" << endl;
}
for(int i = 0;i < size;i++)
for(int j = 0;j<n;j++)
{
infile>>data[i].d[j];
}
cout<<"the data are:"<<endl;
for(int i=0;i<size;i++) { //输出要处理的数据
for(int j = 0;j<n;j++)
{
cout<<data[i].d[j]<<" ";
}
cout<<endl;
}
infile.close();//关闭文件
}
//初始化质心//
void Init_center()
{
for(int i = 0;i<k;i++){
cout<<"初始质心"<<i+1<<":"<<endl;
for(int j = 0;j<n;j++)
{
td.center[i].d[j] = data[i].d[j];
cout<<td.center[i].d[j]<<" ";
}
cout<<endl;
}
}
///计算数据到K个质心的欧几里德距离//
void calculate_distance()
{
int i, j;
for(i = 0; i < size; i++)
for(j = 0; j < k; j++){
data[i].distance[j] = Euclid(i, j); //i表示第几个数组j表示距离第几个质心
}
}
//计算一组数组到质心的欧几里得距离//
double Euclid(int x, int y)
{
double distance = 0;
for(int i = 0; i < n; i++){
distance += pow((data[x].d[i] - td.center[y].d[i]), 2);
}
distance = sqrt(distance);
return distance;
}
//将数据进行簇归类///
void new_cluster()
{
int i, j;
double min;
for(i = 0; i < k; i++) //初始化编号
td.cluster_num[i] = 0;
for(i = 0; i < size; i++){
int index = 0; //找出最小的欧几里德距离编号
min = data[i].distance[0];
for(j = 1; j < k; j++){ // 筛选到簇心欧几里德最小的值
if(data[i].distance[j] < min){
min = data[i].distance[j];
index = j;
}
}
//划分簇集
td.cluster[index][td.cluster_num[index]++] = i;
}
}
///更新质心///
void new_center()
{
int i, j, m;
double sum;
for(i = 0; i < k; i++)
for(j = 0; j < n; j++){
sum = 0;
td.old_center[i].d[j] = td.center[i].d[j];
for(m = 0; m < td.cluster_num[i]; m++){
// 第i个簇的第j维数的所有数据和
sum += data[td.cluster[i][m]].d[j];
}
// 取平均数得到新的簇中心
td.center[i].d[j] = sum / td.cluster_num[i];
}
}
比较质心
void comp( )
{
int i , j,m = 0;
new_center();
for(i = 0;i<k;i++){
for(j=0;j<n;j++){
if(td.old_center[i].d[j] != td.center[i].d[j]) td.isend [i][n]= 0;
else td.isend [i][n]= 1;
}
}
for(i = 0;i<k;i++){
for(j=0;j<n;j++){
td.is=td.isend [i][n]*td.is;
}
}
if(td.is==0)td.is=1;
else td.is=0;
}
输出结果函数/
void output_info( )
{
int i, j, m;
for(i = 0; i < k; i++){
cout<<"质心"<<i+1<<":"<<endl;
for(m = 0; m < n; m++)
cout<<td.center[i].d[m]<<" ";
cout<<endl;
cout<<"簇类"<<i+1<<":"<<endl;
for(j = 0; j < td.cluster_num[i]; j++){
for(m = 0; m < n; m++){
cout<<data[td.cluster[i][j]].d[m]<<" ";
}
cout<<endl;
}
}
}
int main ()
{
int count = 0;
Init();
Init_center();
td.is =1;
while (td.is)
{
calculate_distance();
new_cluster();
count ++;
cout<<"______________________________________________________"<<endl;
cout<<"第"<<count<<"次聚类:"<<endl;
output_info();
comp(); //比较质心
}
system( "PAUSE ");
return 0;
}
四、 实验结果:
1、实验数据(data.txt)
聚类的类数已知,设 k=3。数据集 D 有三个互不交叠的子集构成,即 D={D1, D2, D3}。为方便显示,本实验采用二维属性向量。其中,所有属性已被规范化到 [0, 1]区间,每一行代表一个属性向量,第一列代表属性空间的横轴坐标,第二列代表属性空间的纵轴坐标。
- 处理结果
(首先读入文件,并初始化质心)
(进行第一次聚类运算)
(进行第二次聚类运算)
(进行第三次聚类运算)
该程序通过自动读取文件,获得初始质心,通过欧几里得计算实现3-均值聚类算法,当满足终止条件时获得目标结果。
3. 实验结论
k-均值聚类算法是一种迭代算法,它甚至可以没有终止条件,而通过将数据分成K类,每个类可以方便区分,然后进行运算。