用c語言寫了kmeans演算法的串列程式,再用mpi來寫並行版的,貌似參照著串列版來寫並行版,效果不是很賞心悅目~ 並行化思路: 使用主從模式。由一個節點充當主節點負責數據的劃分與分配,其他節點完成本地數據的計算,並將結果返回給主節點。大致過程如下: 1、進程0為主節點,先從文件中讀取數據集,然後將數 ...
用c語言寫了kmeans演算法的串列程式,再用mpi來寫並行版的,貌似參照著串列版來寫並行版,效果不是很賞心悅目~
並行化思路:
使用主從模式。由一個節點充當主節點負責數據的劃分與分配,其他節點完成本地數據的計算,並將結果返回給主節點。大致過程如下:
1、進程0為主節點,先從文件中讀取數據集,然後將數據集劃分並傳給其他進程;
2、進程0選擇每個聚類的中心點,併發送給其他進程;
3、其他進程計算數據塊中每個點到中心點的距離,然後標出每個點所屬的聚類,並計算每個聚類所有點到其中心點的距離之和,最後將這些結果返回給進程0;
4、進程0計算出新的中心點併發送給其他進程,並計算其他進程傳來的聚類所有點到其中心點的距離總和;
5、重覆3和4直到,直到步驟4中的所有聚類的距離之和不變(即收斂)。
code:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <math.h> 4 #include <time.h> 5 #include "mpi.h" 6 7 int main(int argc,char *argv[]) 8 { 9 int i,j; 10 MPI_Status status; 11 float temp1,temp2; 12 int K,N,D; //聚類的數目,數據量,數據的維數 13 float **data; //存放數據 14 int *all_in_cluster; //進程0標記每個點屬於哪個聚類 15 int *local_in_cluster; //其他進程標記每個點屬於哪個聚類 16 int *in_cluster; //進程0標記每個點屬於哪個聚類 17 int count=0; 18 float *sum_diff; 19 float *global_sum_diff; 20 float **cluster_center; //存放每個聚類的中心點 21 int rank,size; 22 float **array(int m,int n); 23 float **loadData(int *k,int *d,int *n); 24 float getDistance(float avector[],float bvector[],int n); 25 void cluster(int n,int k,int d,float **data,float **cluster_center,int *local_in_cluster); 26 float getDifference(int k,int n,int d,int *in_cluster,float **data,float **cluster_center,float *sum); 27 void getCenter(int k,int d,int n,int *in_cluster,float **data,float **cluster_center); 28 29 MPI_Init(&argc,&argv); 30 MPI_Comm_rank(MPI_COMM_WORLD,&rank); 31 MPI_Comm_size(MPI_COMM_WORLD,&size); 32 if(!rank){ 33 data=loadData(&K,&D,&N); //進程0讀入數據 34 if(size==1||size>N||N%(size-1)) MPI_Abort(MPI_COMM_WORLD,1); //若不滿足條件則退出 35 } 36 MPI_Bcast(&K,1,MPI_INT,0,MPI_COMM_WORLD); //進程0廣播 37 MPI_Bcast(&N,1,MPI_INT,0,MPI_COMM_WORLD); 38 MPI_Bcast(&D,1,MPI_INT,0,MPI_COMM_WORLD); 39 if(rank) data=array(N/(size-1),D); //其他進程分配存儲數據集的空間 40 all_in_cluster=(int *)malloc(N/(size-1)*size*sizeof(int)); //用於進程0 41 local_in_cluster=(int *)malloc(N/(size-1)*sizeof(int)); //用於每個進程 42 in_cluster=(int *)malloc(N*sizeof(int)); //用於進程0 43 sum_diff=(float *)malloc(K*sizeof(float)); //進程中每個聚類的數據點與其中心點的距離之和 44 global_sum_diff=(float *)malloc(K*sizeof(float)); 45 for(i=0;i<K;i++) sum_diff[i]=0.0; //初始化 46 47 if(!rank){ //進程0向其他進程分配數據集 48 for(i=0;i<N;i+=(N/(size-1))) 49 for(j=0;j<(N/(size-1));j++) 50 MPI_Send(data[i+j],D,MPI_FLOAT,(i+j)/(N/(size-1))+1,99,MPI_COMM_WORLD); 51 printf("Data sets:\n"); 52 for(i=0;i<N;i++) 53 for(j=0;j<D;j++){ 54 printf("%-8.2f",data[i][j]); 55 if((j+1)%D==0) putchar('\n'); 56 } 57 printf("-----------------------------\n"); 58 }else{ //其他進程接收進程0數據 59 for(i=0;i<(N/(size-1));i++) 60 MPI_Recv(data[i],D,MPI_FLOAT,0,99,MPI_COMM_WORLD,&status); 61 } 62 MPI_Barrier(MPI_COMM_WORLD); //同步一下 63 cluster_center=array(K,D); //中心點 64 if(!rank){ //進程0產生隨機中心點 65 srand((unsigned int)(time(NULL))); //隨機初始化k個中心點 66 for(i=0;i<K;i++) 67 for(j=0;j<D;j++) 68 cluster_center[i][j]=data[(int)((double)N*rand()/(RAND_MAX+1.0))][j]; 69 } 70 for(i=0;i<K;i++) MPI_Bcast(cluster_center[i],D,MPI_FLOAT,0,MPI_COMM_WORLD); //進程0向其他進程廣播中心點 71 if(rank){ 72 cluster(N/(size-1),K,D,data,cluster_center,local_in_cluster); //其他進程進行聚類 73 getDifference(K,N/(size-1),D,local_in_cluster,data,cluster_center,sum_diff); 74 for(i=0;i<N/(size-1);i++) 75 printf("data[%d] in cluster-%d\n",(rank-1)*(N/(size-1))+i,local_in_cluster[i]+1); 76 } 77 MPI_Gather(local_in_cluster,N/(size-1),MPI_INT,all_in_cluster,N/(size-1),MPI_INT,0,MPI_COMM_WORLD); //全收集於進程0 78 MPI_Reduce(sum_diff,global_sum_diff,K,MPI_FLOAT,MPI_SUM,0,MPI_COMM_WORLD); //歸約至進程0,進程中每個聚類的數據點與其中心點的距離之和 79 if(!rank){ 80 for(i=N/(size-1);i<N+N/(size-1);i++) 81 in_cluster[i-N/(size-1)]=all_in_cluster[i]; //處理收集的標記數組 82 temp1=0.0; 83 for(i=0;i<K;i++) temp1+=global_sum_diff[i]; 84 printf("The difference between data and center is: %.2f\n\n", temp1); 85 count++; 86 } 87 MPI_Bcast(&temp1,1,MPI_FLOAT,0,MPI_COMM_WORLD); 88 MPI_Barrier(MPI_COMM_WORLD); 89 90 do{ //比較前後兩次迭代,若不相等繼續迭代 91 temp1=temp2; 92 if(!rank) getCenter(K,D,N,in_cluster,data,cluster_center); //更新中心點 93 for(i=0;i<K;i++) MPI_Bcast(cluster_center[i],D,MPI_FLOAT,0,MPI_COMM_WORLD); //廣播中心點 94 if(rank){ 95 cluster(N/(size-1),K,D,data,cluster_center,local_in_cluster); //其他進程進行聚類 96 for(i=0;i<K;i++) sum_diff[i]=0.0; 97 getDifference(K,N/(size-1),D,local_in_cluster,data,cluster_center,sum_diff); 98 for(i=0;i<N/(size-1);i++) 99 printf("data[%d] in cluster-%d\n",(rank-1)*(N/(size-1))+i,local_in_cluster[i]+1); 100 } 101 MPI_Gather(local_in_cluster,N/(size-1),MPI_INT,all_in_cluster,N/(size-1),MPI_INT,0,MPI_COMM_WORLD); 102 if(!rank) 103 for(i=0;i<K;i++) global_sum_diff[i]=0.0; 104 MPI_Reduce(sum_diff,global_sum_diff,K,MPI_FLOAT,MPI_SUM,0,MPI_COMM_WORLD); 105 if(!rank){ 106 for(i=N/(size-1);i<N+N/(size-1);i++) 107 in_cluster[i-N/(size-1)]=all_in_cluster[i]; 108 temp2=0.0; 109 for(i=0;i<K;i++) temp2+=global_sum_diff[i]; 110 printf("The difference between data and center is: %.2f\n\n", temp2); 111 count++; 112 } 113 MPI_Bcast(&temp2,1,MPI_FLOAT,0,MPI_COMM_WORLD); 114 MPI_Barrier(MPI_COMM_WORLD); 115 }while(fabs(temp2-temp1)!=0.0); 116 if(!rank) printf("The total number of cluster is: %d\n\n",count); 117 MPI_Finalize(); 118 } 119 120 121 //動態創建二維數組 122 float **array(int m,int n) 123 { 124 int i; 125 float **p; 126 p=(float **)malloc(m*sizeof(float *)); 127 p[0]=(float *)malloc(m*n*sizeof(float)); 128 for(i=1;i<m;i++) p[i]=p[i-1]+n; 129 return p; 130 } 131 132 //從data.txt導入數據,要求首行格式:K=聚類數目,D=數據維度,N=數據量 133 float **loadData(int *k,int *d,int *n) 134 { 135 float **array(int m,int n); 136 int i,j; 137 float **arraydata; 138 FILE *fp; 139 if((fp=fopen("data.txt","r"))==NULL) fprintf(stderr,"cannot open data.txt!\n"); 140 if(fscanf(fp,"K=%d,D=%d,N=%d\n",k,d,n)!=3) fprintf(stderr,"load error!\n"); 141 arraydata=array(*n,*d); //生成數據數組 142 for(i=0;i<*n;i++) 143 for(j=0;j<*d;j++) 144 fscanf(fp,"%f",&arraydata[i][j]); //讀取數據點 145 return arraydata; 146 } 147 148 //計算歐幾裡得距離 149 float getDistance(float avector[],float bvector[],int n) 150 { 151 int i; 152 float sum=0.0; 153 for(i=0;i<n;i++) 154 sum+=pow(avector[i]-bvector[i],2); 155 return sqrt(sum); 156 } 157 158 //把N個數據點聚類,標出每個點屬於哪個聚類 159 void cluster(int n,int k,int d,float **data,float **cluster_center,int *local_in_cluster) 160 { 161 int i,j; 162 float min; 163 float **distance=array(n,k); //存放每個數據點到每個中心點的距離 164 for(i=0;i<n;++i){ 165 min=9999.0; 166 for(j=0;j<k;++j){ 167 distance[i][j] = getDistance(data[i],cluster_center[j],d); 168 if(distance[i][j]<min){ 169 min=distance[i][j]; 170 local_in_cluster[i]=j; 171 } 172 } 173 } 174 printf("-----------------------------\n"); 175 free(distance); 176 } 177 178 //計算所有聚類的中心點與其數據點的距離之和 179 float getDifference(int k,int n,int d,int *in_cluster,float **data,float **cluster_center,float *sum) 180 { 181 int i,j; 182 for(i=0;i<k;++i) 183 for(j=0;j<n;++j) 184 if(i==in_cluster[j]) 185 sum[i]+=getDistance(data[j],cluster_center[i],d); 186 } 187 188 //計算每個聚類的中心點 189 void getCenter(int k,int d,int n,int *in_cluster,float **data,float **cluster_center) 190 { 191 float **sum=array(k,d); //存放每個聚類中心 192 int i,j,q,count; 193 for(i=0;i<k;i++) 194 for(j=0;j<d;j++) 195 sum[i][j]=0.0; 196 for(i=0;i<k;i++){ 197 count=0; //統計屬於某個聚類內的所有數據點 198 for(j=0;j<n;j++){ 199 if(i==in_cluster[j]){ 200 for(q=0;q<d;q++) 201 sum[i][q]+=data[j][q]; //計算所屬聚類的所有數據點的相應維數之和 202 count++; 203 } 204 } 205 for(q=0;q<d;q++) 206 cluster_center[i][q]=sum[i][q]/count; 207 } 208 printf("The new center of cluster is:\n"); 209 for(i = 0; i < k; i++) 210 for(q=0;q<d;q++){ 211 printf("%-8.2f",cluster_center[i][q]); 212 if((q+1)%d==0) putchar('\n'); 213 } 214 free(sum); 215 }
1 //生成測試數據 2 #include<stdio.h> 3 #include<stdlib.h> 4 #include<time.h> 5 #define N 1000 6 7 int main() 8 { 9 int i; 10 float a; 11 int k,d,n; 12 FILE *fp; 13 fprintf(stdout,"input(k d n):"); 14 scanf("%d%d%d",&k,&d,&n); 15 if((fp=fopen("data.txt","w"))==NULL) exit(1); 16 fprintf(fp,"K=%d,D=%d,N=%d\n",k,d,n); 17 srand((unsigned int)(time(NULL))); 18 for(i=1;i<=d*n;i++){ 19 a=(int)(1.0+(double)N*rand()/(RAND_MAX+1.0)); 20 fprintf(fp,"%.2f ",a); 21 if(i%d==0) putc('\n',fp); 22 } 23 if(fclose(fp)) exit(2); 24 }
實驗:
聚類數K=10,數據的維度D=2,單位(秒):
數據量N |
10000 |
100000 |
500000 |
串列 |
1 |
21 |
109 |
並行(2個進程) |
2 |
25 |
101 |
並行(3個進程) |
3 |
26 |
101 |
分析:電腦配置是奔騰雙核,按照該並行程式,一個核心用作主節點以分配數據集,另一個核心作為承擔了大多數計算任務的節點。當數據量較小時,並行程式花在進程間數據通信的時間占了總體時間的很大比重,所以並行程式耗時要多於串列程式。在本電腦CPU為兩個核心的環境下,當數據量較大時,並行程式與串列程式耗時相當或者稍微偏小。在CPU核心數在3個以上時,該並行程式的優勢才突顯出來。