kmeans算法并行化的mpi程序
用c语言写了kmeans算法的串行程序,再用mpi来写并行版的,貌似参照着串行版来写并行版,效果不是很赏心悦目~
并行化思路:
使用主从模式。由一个节点充当主节点负责数据的划分与分配,其他节点完成本地数据的计算,并将结果返回给主节点。大致过程如下:
1、进程0为主节点,先从文件中读取数据集,然后将数据集划分并传给其他进程;
2、进程0选择每个聚类的中心点,并发送给其他进程;
3、其他进程计算数据块中每个点到中心点的距离,然后标出每个点所属的聚类,并计算每个聚类所有点到其中心点的距离之和,最后将这些结果返回给进程0;
4、进程0计算出新的中心点并发送给其他进程,并计算其他进程传来的聚类所有点到其中心点的距离总和;
5、重复3和4直到,直到步骤4中的所有聚类的距离之和不变(即收敛)。
code:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include "mpi.h" int main(int argc,char *argv[])
{
int i,j;
MPI_Status status;
float temp1,temp2;
int K,N,D; //聚类的数目,数据量,数据的维数
float **data; //存放数据
int *all_in_cluster; //进程0标记每个点属于哪个聚类
int *local_in_cluster; //其他进程标记每个点属于哪个聚类
int *in_cluster; //进程0标记每个点属于哪个聚类
int count=;
float *sum_diff;
float *global_sum_diff;
float **cluster_center; //存放每个聚类的中心点
int rank,size;
float **array(int m,int n);
float **loadData(int *k,int *d,int *n);
float getDistance(float avector[],float bvector[],int n);
void cluster(int n,int k,int d,float **data,float **cluster_center,int *local_in_cluster);
float getDifference(int k,int n,int d,int *in_cluster,float **data,float **cluster_center,float *sum);
void getCenter(int k,int d,int n,int *in_cluster,float **data,float **cluster_center); MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
MPI_Comm_size(MPI_COMM_WORLD,&size);
if(!rank){
data=loadData(&K,&D,&N); //进程0读入数据
if(size==||size>N||N%(size-)) MPI_Abort(MPI_COMM_WORLD,); //若不满足条件则退出
}
MPI_Bcast(&K,,MPI_INT,,MPI_COMM_WORLD); //进程0广播
MPI_Bcast(&N,,MPI_INT,,MPI_COMM_WORLD);
MPI_Bcast(&D,,MPI_INT,,MPI_COMM_WORLD);
if(rank) data=array(N/(size-),D); //其他进程分配存储数据集的空间
all_in_cluster=(int *)malloc(N/(size-)*size*sizeof(int)); //用于进程0
local_in_cluster=(int *)malloc(N/(size-)*sizeof(int)); //用于每个进程
in_cluster=(int *)malloc(N*sizeof(int)); //用于进程0
sum_diff=(float *)malloc(K*sizeof(float)); //进程中每个聚类的数据点与其中心点的距离之和
global_sum_diff=(float *)malloc(K*sizeof(float));
for(i=;i<K;i++) sum_diff[i]=0.0; //初始化 if(!rank){ //进程0向其他进程分配数据集
for(i=;i<N;i+=(N/(size-)))
for(j=;j<(N/(size-));j++)
MPI_Send(data[i+j],D,MPI_FLOAT,(i+j)/(N/(size-))+,,MPI_COMM_WORLD);
printf("Data sets:\n");
for(i=;i<N;i++)
for(j=;j<D;j++){
printf("%-8.2f",data[i][j]);
if((j+)%D==) putchar('\n');
}
printf("-----------------------------\n");
}else{ //其他进程接收进程0数据
for(i=;i<(N/(size-));i++)
MPI_Recv(data[i],D,MPI_FLOAT,,,MPI_COMM_WORLD,&status);
}
MPI_Barrier(MPI_COMM_WORLD); //同步一下
cluster_center=array(K,D); //中心点
if(!rank){ //进程0产生随机中心点
srand((unsigned int)(time(NULL))); //随机初始化k个中心点
for(i=;i<K;i++)
for(j=;j<D;j++)
cluster_center[i][j]=data[(int)((double)N*rand()/(RAND_MAX+1.0))][j];
}
for(i=;i<K;i++) MPI_Bcast(cluster_center[i],D,MPI_FLOAT,,MPI_COMM_WORLD); //进程0向其他进程广播中心点
if(rank){
cluster(N/(size-),K,D,data,cluster_center,local_in_cluster); //其他进程进行聚类
getDifference(K,N/(size-),D,local_in_cluster,data,cluster_center,sum_diff);
for(i=;i<N/(size-);i++)
printf("data[%d] in cluster-%d\n",(rank-)*(N/(size-))+i,local_in_cluster[i]+);
}
MPI_Gather(local_in_cluster,N/(size-),MPI_INT,all_in_cluster,N/(size-),MPI_INT,,MPI_COMM_WORLD); //全收集于进程0
MPI_Reduce(sum_diff,global_sum_diff,K,MPI_FLOAT,MPI_SUM,,MPI_COMM_WORLD); //归约至进程0,进程中每个聚类的数据点与其中心点的距离之和
if(!rank){
for(i=N/(size-);i<N+N/(size-);i++)
in_cluster[i-N/(size-)]=all_in_cluster[i]; //处理收集的标记数组
temp1=0.0;
for(i=;i<K;i++) temp1+=global_sum_diff[i];
printf("The difference between data and center is: %.2f\n\n", temp1);
count++;
}
MPI_Bcast(&temp1,,MPI_FLOAT,,MPI_COMM_WORLD);
MPI_Barrier(MPI_COMM_WORLD); do{ //比较前后两次迭代,若不相等继续迭代
temp1=temp2;
if(!rank) getCenter(K,D,N,in_cluster,data,cluster_center); //更新中心点
for(i=;i<K;i++) MPI_Bcast(cluster_center[i],D,MPI_FLOAT,,MPI_COMM_WORLD); //广播中心点
if(rank){
cluster(N/(size-),K,D,data,cluster_center,local_in_cluster); //其他进程进行聚类
for(i=;i<K;i++) sum_diff[i]=0.0;
getDifference(K,N/(size-),D,local_in_cluster,data,cluster_center,sum_diff);
for(i=;i<N/(size-);i++)
printf("data[%d] in cluster-%d\n",(rank-)*(N/(size-))+i,local_in_cluster[i]+);
}
MPI_Gather(local_in_cluster,N/(size-),MPI_INT,all_in_cluster,N/(size-),MPI_INT,,MPI_COMM_WORLD);
if(!rank)
for(i=;i<K;i++) global_sum_diff[i]=0.0;
MPI_Reduce(sum_diff,global_sum_diff,K,MPI_FLOAT,MPI_SUM,,MPI_COMM_WORLD);
if(!rank){
for(i=N/(size-);i<N+N/(size-);i++)
in_cluster[i-N/(size-)]=all_in_cluster[i];
temp2=0.0;
for(i=;i<K;i++) temp2+=global_sum_diff[i];
printf("The difference between data and center is: %.2f\n\n", temp2);
count++;
}
MPI_Bcast(&temp2,,MPI_FLOAT,,MPI_COMM_WORLD);
MPI_Barrier(MPI_COMM_WORLD);
}while(fabs(temp2-temp1)!=0.0);
if(!rank) printf("The total number of cluster is: %d\n\n",count);
MPI_Finalize();
} //动态创建二维数组
float **array(int m,int n)
{
int i;
float **p;
p=(float **)malloc(m*sizeof(float *));
p[]=(float *)malloc(m*n*sizeof(float));
for(i=;i<m;i++) p[i]=p[i-]+n;
return p;
} //从data.txt导入数据,要求首行格式:K=聚类数目,D=数据维度,N=数据量
float **loadData(int *k,int *d,int *n)
{
float **array(int m,int n);
int i,j;
float **arraydata;
FILE *fp;
if((fp=fopen("data.txt","r"))==NULL) fprintf(stderr,"cannot open data.txt!\n");
if(fscanf(fp,"K=%d,D=%d,N=%d\n",k,d,n)!=) fprintf(stderr,"load error!\n");
arraydata=array(*n,*d); //生成数据数组
for(i=;i<*n;i++)
for(j=;j<*d;j++)
fscanf(fp,"%f",&arraydata[i][j]); //读取数据点
return arraydata;
} //计算欧几里得距离
float getDistance(float avector[],float bvector[],int n)
{
int i;
float sum=0.0;
for(i=;i<n;i++)
sum+=pow(avector[i]-bvector[i],);
return sqrt(sum);
} //把N个数据点聚类,标出每个点属于哪个聚类
void cluster(int n,int k,int d,float **data,float **cluster_center,int *local_in_cluster)
{
int i,j;
float min;
float **distance=array(n,k); //存放每个数据点到每个中心点的距离
for(i=;i<n;++i){
min=9999.0;
for(j=;j<k;++j){
distance[i][j] = getDistance(data[i],cluster_center[j],d);
if(distance[i][j]<min){
min=distance[i][j];
local_in_cluster[i]=j;
}
}
}
printf("-----------------------------\n");
free(distance);
} //计算所有聚类的中心点与其数据点的距离之和
float getDifference(int k,int n,int d,int *in_cluster,float **data,float **cluster_center,float *sum)
{
int i,j;
for(i=;i<k;++i)
for(j=;j<n;++j)
if(i==in_cluster[j])
sum[i]+=getDistance(data[j],cluster_center[i],d);
} //计算每个聚类的中心点
void getCenter(int k,int d,int n,int *in_cluster,float **data,float **cluster_center)
{
float **sum=array(k,d); //存放每个聚类中心
int i,j,q,count;
for(i=;i<k;i++)
for(j=;j<d;j++)
sum[i][j]=0.0;
for(i=;i<k;i++){
count=; //统计属于某个聚类内的所有数据点
for(j=;j<n;j++){
if(i==in_cluster[j]){
for(q=;q<d;q++)
sum[i][q]+=data[j][q]; //计算所属聚类的所有数据点的相应维数之和
count++;
}
}
for(q=;q<d;q++)
cluster_center[i][q]=sum[i][q]/count;
}
printf("The new center of cluster is:\n");
for(i = ; i < k; i++)
for(q=;q<d;q++){
printf("%-8.2f",cluster_center[i][q]);
if((q+)%d==) putchar('\n');
}
free(sum);
}
//生成测试数据
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define N 1000 int main()
{
int i;
float a;
int k,d,n;
FILE *fp;
fprintf(stdout,"input(k d n):");
scanf("%d%d%d",&k,&d,&n);
if((fp=fopen("data.txt","w"))==NULL) exit();
fprintf(fp,"K=%d,D=%d,N=%d\n",k,d,n);
srand((unsigned int)(time(NULL)));
for(i=;i<=d*n;i++){
a=(int)(1.0+(double)N*rand()/(RAND_MAX+1.0));
fprintf(fp,"%.2f ",a);
if(i%d==) putc('\n',fp);
}
if(fclose(fp)) exit();
}
实验:
聚类数K=10,数据的维度D=2,单位(秒):
|
数据量N |
10000 |
100000 |
500000 |
|
串行 |
1 |
21 |
109 |
|
并行(2个进程) |
2 |
25 |
101 |
|
并行(3个进程) |
3 |
26 |
101 |
分析:电脑配置是奔腾双核,按照该并行程序,一个核心用作主节点以分配数据集,另一个核心作为承担了大多数计算任务的节点。当数据量较小时,并行程序花在进程间数据通信的时间占了总体时间的很大比重,所以并行程序耗时要多于串行程序。在本电脑CPU为两个核心的环境下,当数据量较大时,并行程序与串行程序耗时相当或者稍微偏小。在CPU核心数在3个以上时,该并行程序的优势才突显出来。
kmeans算法并行化的mpi程序的更多相关文章
- K-means算法的matlab程序
K-means算法的matlab程序 在“K-means算法的matlab程序(初步)”这篇文章中已经用matlab程序对iris数据库进行简单的实现,下面的程序最终的目的是求准确度. 作者:凯鲁嘎吉 ...
- K-means算法的matlab程序(初步)
K-means算法的matlab程序 在https://www.cnblogs.com/kailugaji/p/9648369.html 文章中已经介绍了K-means算法,现在用matlab程序实现 ...
- kmeans算法c语言实现,能对不同维度的数据进行聚类
最近在苦于思考kmeans算法的MPI并行化,花了两天的时间把该算法看懂和实现了串行版. 聚类问题就是给定一个元素集合V,其中每个元素具有d个可观察属性,使用某种算法将V划分成k个子集,要求每个子集内 ...
- 【原创】数据挖掘案例——ReliefF和K-means算法的医学应用
数据挖掘方法的提出,让人们有能力最终认识数据的真正价值,即蕴藏在数据中的信息和知识.数据挖掘 (DataMiriing),指的是从大型数据库或数据仓库中提取人们感兴趣的知识,这些知识是隐含的.事先未知 ...
- 利用Mahout实现在Hadoop上运行K-Means算法
利用Mahout实现在Hadoop上运行K-Means算法 一.介绍Mahout Mahout是Apache下的开源机器学习软件包,目前实现的机器学习算法主要包含有协同过滤/推荐引擎,聚类和分类三个部 ...
- 数据挖掘十大经典算法[0]-K-Means算法
K-Means算法的输入N,K和一个size为N的向量组vector.输出K个两两互不相交的向量组.其本质是将给定的向量组划分成K个类别,使得同类别的向量相似度比较大,而不同类别的向量之间的相似度较小 ...
- 转载: scikit-learn学习之K-means聚类算法与 Mini Batch K-Means算法
版权声明:<—— 本文为作者呕心沥血打造,若要转载,请注明出处@http://blog.csdn.net/gamer_gyt <—— 目录(?)[+] ================== ...
- hadoop在实现kmeans算法——一个mapreduce实施
写mapreduce程序实现kmeans算法.我们的想法可能是 1. 次迭代后的质心 2. map里.计算每一个质心与样本之间的距离,得到与样本距离最短的质心,以这个质心作为key,样本作为value ...
- 基于ReliefF和K-means算法的医学应用实例
基于ReliefF和K-means算法的医学应用实例 数据挖掘方法的提出,让人们有能力最终认识数据的真正价值,即蕴藏在数据中的信息和知识.数据挖掘 (DataMiriing),指的是从大型数据库或数据 ...
随机推荐
- PHP_VERSION_ID是如何定义的
PHP_VERSION_ID是一个整数,表示当前PHP的版本,从php5.2.7版本开始使用的,比如50207表示5.2.7.和PHP版本相关的宏定义在文件 phpsrcdir/main/php_ve ...
- 解析大型.NET ERP系统 单据编码功能实现
单据编码是ERP系统中必备的功能,用于生成各种单据的流水号,常常借助于日期时间等字符来生成一个唯一的单据号码.从软件的角度来说,就是为生成数据表的主键值(参考编号),从用户的角度来说,就是给业务单据制 ...
- EntityFramework linq 多条件查询,不定条件查询
一.场景描述: 开发的时候,有些查询功能,往往查询的条件是不确定的,用户没有填的不参与到查询中去. 如图1所示,用户可能只要给根据名称来查询即可,有时候开始时间和结束时间并不需要填写. 图 1 二.解 ...
- TextView使用大全
最近打算写一个系列的android初级开发教程,预计40篇以上的文章,结合我实际工作中的经验,写一些工作中经常用到的技术,让初学者可以少走弯路,写一个系列的话,大家学习起来也有头有尾. 今天就从我们每 ...
- 计算机程序的思维逻辑 (41) - 剖析HashSet
上节介绍了HashMap,提到了Set接口,Map接口的两个方法keySet和entrySet返回的都是Set,本节,我们来看Set接口的一个重要实现类HashSet. 与HashMap类似,字面上看 ...
- EXP/IMP 导出生产库表的指定数据到测试库一例
一般来讲,EXP/IMP是上一代导出导入程序,EXPDP/IMPDP是新一代的导出导入程序.对于大数据量的导出导入首选EXPDP/IMPDP,可以用到并行度,对表空间等操作上也更加的灵活.对于小数据量 ...
- XLT架构图(自己 画的)
- 游戏启示录 关于Update的相关问题
游戏启示录 关于Update的相关问题 这里说的Update是指的游戏的主循环.一般情况下.为了程序的方便控制我们一般只会有一个主循环.所有的游戏逻辑都会在这一个循环中完成.(额,其实这么做有点浪费. ...
- Autofac 组件、服务、自动装配 《第二篇》
一.组件 创建出来的对象需要从组件中来获取,组件的创建有如下4种(延续第一篇的Demo,仅仅变动所贴出的代码)方式: 1.类型创建RegisterType AutoFac能够通过反射检查一个类型,选择 ...
- is not in the sudoers file的解决方法
遇到这个问题 修改sudoers 文件 /etc/sudoers 添加