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),指的是从大型数据库或数据 ...
随机推荐
- Android测试网络是否连接
一.布局页面 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns ...
- .Net使用RabbitMQ详解
序言 这里原来有一句话,触犯啦天条,被阉割!!!! 首先不去讨论我的日志组件怎么样.因为有些日志需要走网络,有的又不需要走网路,也是有性能与业务场景的多般变化在其中,就把他抛开,我们只谈消息Rabbi ...
- 【Win 10应用开发】手动调用WCF服务
调用服务最简单的方法就是,直接在VS里面添加服务引用,输入服务的地址即可,无论是普通Web服务,还是WCF服务均可.VS会根据获取到的元数据,自动生成客户端代码. 如果服务的调用量很大,应用广泛,可以 ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统--任务调度系统解析
1.任务主界面.任务可以被挂起,启动,和删除.来自著名Quartz扩展 2.任务可以是执行的SQL命令,存储过程,或者是一个后台方法 3.极其复杂的调度任务,循环次数,可以自行设置.并可以间隔执行,比 ...
- angular2系列教程(二)模板语法
今天我们要讲的是angualr2的模板语法,官网写的很清楚,但我也用通俗易懂的讲法再罗列一下吧! 例子
- linux内核数据结构之kfifo
1.前言 最近项目中用到一个环形缓冲区(ring buffer),代码是由linux内核的kfifo改过来的.缓冲区在文件系统中经常用到,通过缓冲区缓解cpu读写内存和读写磁盘的速度.例如一个进程A产 ...
- Spark 入门
Spark 入门 目录 一. 1. 2. 3. 二. 三. 1. 2. 3. (1) (2) (3) 4. 5. 四. 1. 2. 3. 4. 5. 五. Spark Shell使用 ...
- 用JWT来保护我们的ASP.NET Core Web API
在上一篇博客中,自己动手写了一个Middleware来处理API的授权验证,现在就采用另外一种方式来处理这个授权验证的问题,毕竟现在也 有不少开源的东西可以用,今天用的是JWT. 什么是JWT呢?JW ...
- POI读取EXCEL(2007以上)
import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; im ...
- 【视频处理】YUV与RGB格式转换
YUV格式具有亮度信息和色彩信息分离的特点,但大多数图像处理操作都是基于RGB格式. 因此当要对图像进行后期处理显示时,需要把YUV格式转换成RGB格式. RGB与YUV的变换公式如下: YUV(25 ...