zoukankan      html  css  js  c++  java
  • 【CUDA并行编程之六】KNN算法的并行实现


    之前写了两篇文章一个是KNN算法的C++串行实现,另一个是CUDA计算向量的欧氏距离。那么这篇文章就可以说是前两篇文章的一个简单的整合。在看这篇文章之前可以先阅读前两篇文章。


    一、生成数据集

    现在需要生成一个N个D维的数据,没在一组数据都有一个类标,这个类标根据第一维的正负来进行标识样本数据的类标:Positive and Negative。

    [python] view plaincopy
    1. #!/usr/bin/python  
    2.   
    3. import re  
    4. import sys  
    5. import random  
    6. import os  
    7.   
    8. filename = "input.txt"  
    9.   
    10. if(os.path.exists(filename)):  
    11.     print("%s exists and del" % filename)  
    12.     os.remove(filename)  
    13.   
    14. fout = open(filename,"w")  
    15.   
    16. for i in range( 0,int(sys.argv[1]) ): #str to int  
    17.     x = []  
    18.     for j in range(0,int(sys.argv[2])):  
    19.         x.append( "%4f" % random.uniform(-1,1) ) #generate random data and limit the digits into 4  
    20.         fout.write("%s " % x[j])  
    21.         #fout.write(x) : TypeError:expected a character buffer object   
    22.   
    23.     if(x[0][0] == '-'):  
    24.         fout.write(" Negative"+" ")  
    25.     else:  
    26.         fout.write(" Positive"+" ")  
    27.   
    28. fout.close()  

    运行程序,生成4000个维度为8的数据:


    生成了文件"input.txt":



    二、串行代码:

    这个代码和之前的文章的代码一致,我们选择400个数据进行作为测试数据,3600个数据进行训练数据。

    KNN_2.cc:

    1. #include<iostream>  
    2. #include<map>  
    3. #include<vector>  
    4. #include<stdio.h>  
    5. #include<cmath>  
    6. #include<cstdlib>  
    7. #include<algorithm>  
    8. #include<fstream>  
    9.   
    10. using namespace std;  
    11.   
    12. typedef string tLabel;  
    13. typedef double tData;  
    14. typedef pair<int,double>  PAIR;  
    15. const int MaxColLen = 10;  
    16. const int MaxRowLen = 10000;  
    17. ifstream fin;  
    18.   
    19. class KNN  
    20. {  
    21. private:  
    22.         tData dataSet[MaxRowLen][MaxColLen];  
    23.         tLabel labels[MaxRowLen];  
    24.         tData testData[MaxColLen];  
    25.         int rowLen;  
    26.         int colLen;  
    27.         int k;  
    28.         int test_data_num;  
    29.         map<int,double> map_index_dis;  
    30.         map<tLabel,int> map_label_freq;  
    31.         double get_distance(tData *d1,tData *d2);  
    32. public:  
    33.         KNN(int k , int rowLen , int colLen , char *filename);  
    34.         void get_all_distance();  
    35.         tLabel get_max_freq_label();  
    36.         void auto_norm_data();  
    37.         void get_error_rate();  
    38.         struct CmpByValue  
    39.         {  
    40.             bool operator() (const PAIR& lhs,const PAIR& rhs)  
    41.             {  
    42.                 return lhs.second < rhs.second;  
    43.             }  
    44.         };  
    45.   
    46.         ~KNN();   
    47. };  
    48.   
    49. KNN::~KNN()  
    50. {  
    51.     fin.close();  
    52.     map_index_dis.clear();  
    53.     map_label_freq.clear();  
    54. }  
    55.   
    56. KNN::KNN(int k , int row ,int col , char *filename)  
    57. {  
    58.     this->rowLen = row;  
    59.     this->colLen = col;  
    60.     this->k = k;  
    61.     test_data_num = 0;  
    62.       
    63.     fin.open(filename);  
    64.   
    65.     if( !fin )  
    66.     {  
    67.         cout<<"can not open the file"<<endl;  
    68.         exit(0);  
    69.     }  
    70.       
    71.     //read data from file  
    72.     for(int i=0;i<rowLen;i++)  
    73.     {  
    74.         for(int j=0;j<colLen;j++)  
    75.         {  
    76.             fin>>dataSet[i][j];  
    77.         }  
    78.         fin>>labels[i];  
    79.     }  
    80.   
    81. }  
    82.   
    83. void KNN:: get_error_rate()  
    84. {  
    85.     int i,j,count = 0;  
    86.     tLabel label;  
    87.     cout<<"please input the number of test data : "<<endl;  
    88.     cin>>test_data_num;  
    89.     for(i=0;i<test_data_num;i++)  
    90.     {  
    91.         for(j=0;j<colLen;j++)  
    92.         {  
    93.             testData[j] = dataSet[i][j];          
    94.         }  
    95.           
    96.         get_all_distance();  
    97.         label = get_max_freq_label();  
    98.         if( label!=labels[i] )  
    99.             count++;  
    100.         map_index_dis.clear();  
    101.         map_label_freq.clear();  
    102.     }  
    103.     cout<<"the error rate is = "<<(double)count/(double)test_data_num<<endl;  
    104. }  
    105.   
    106. double KNN:: get_distance(tData *d1,tData *d2)  
    107. {  
    108.     double sum = 0;  
    109.     for(int i=0;i<colLen;i++)  
    110.     {  
    111.         sum += pow( (d1[i]-d2[i]) , 2 );  
    112.     }  
    113.   
    114.     //cout<<"the sum is = "<<sum<<endl;  
    115.     return sqrt(sum);  
    116. }  
    117.   
    118. //get distance between testData and all dataSet  
    119. void KNN:: get_all_distance()  
    120. {  
    121.     double distance;  
    122.     int i;  
    123.     for(i=test_data_num;i<rowLen;i++)  
    124.     {  
    125.         distance = get_distance(dataSet[i],testData);  
    126.         map_index_dis[i] = distance;  
    127.     }  
    128. }  
    129.   
    130. tLabel KNN:: get_max_freq_label()  
    131. {  
    132.     vector<PAIR> vec_index_dis( map_index_dis.begin(),map_index_dis.end() );  
    133.     sort(vec_index_dis.begin(),vec_index_dis.end(),CmpByValue());  
    134.   
    135.     for(int i=0;i<k;i++)  
    136.     {  
    137.         /* 
    138.         cout<<"the index = "<<vec_index_dis[i].first<<" the distance = "<<vec_index_dis[i].second<<" the label = "<<labels[ vec_index_dis[i].first ]<<" the coordinate ( "; 
    139.         int j; 
    140.         for(j=0;j<colLen-1;j++) 
    141.         { 
    142.             cout<<dataSet[ vec_index_dis[i].first ][j]<<","; 
    143.         } 
    144.         cout<<dataSet[ vec_index_dis[i].first ][j]<<" )"<<endl; 
    145.         */  
    146.         map_label_freq[ labels[ vec_index_dis[i].first ]  ]++;  
    147.     }  
    148.   
    149.     map<tLabel,int>::const_iterator map_it = map_label_freq.begin();  
    150.     tLabel label;  
    151.     int max_freq = 0;  
    152.     while( map_it != map_label_freq.end() )  
    153.     {  
    154.         if( map_it->second > max_freq )  
    155.         {  
    156.             max_freq = map_it->second;  
    157.             label = map_it->first;  
    158.         }  
    159.         map_it++;  
    160.     }  
    161.     //cout<<"The test data belongs to the "<<label<<" label"<<endl;  
    162.     return label;  
    163. }  
    164.   
    165. void KNN::auto_norm_data()  
    166. {  
    167.     tData maxa[colLen] ;  
    168.     tData mina[colLen] ;  
    169.     tData range[colLen] ;  
    170.     int i,j;  
    171.   
    172.     for(i=0;i<colLen;i++)  
    173.     {  
    174.         maxa[i] = max(dataSet[0][i],dataSet[1][i]);  
    175.         mina[i] = min(dataSet[0][i],dataSet[1][i]);  
    176.     }  
    177.   
    178.     for(i=2;i<rowLen;i++)  
    179.     {  
    180.         for(j=0;j<colLen;j++)  
    181.         {  
    182.             if( dataSet[i][j]>maxa[j] )  
    183.             {  
    184.                 maxa[j] = dataSet[i][j];  
    185.             }  
    186.             else if( dataSet[i][j]<mina[j] )  
    187.             {  
    188.                 mina[j] = dataSet[i][j];  
    189.             }  
    190.         }  
    191.     }  
    192.   
    193.     for(i=0;i<colLen;i++)  
    194.     {  
    195.         range[i] = maxa[i] - mina[i] ;   
    196.         //normalize the test data set  
    197.         testData[i] = ( testData[i] - mina[i] )/range[i] ;  
    198.     }  
    199.   
    200.     //normalize the training data set  
    201.     for(i=0;i<rowLen;i++)  
    202.     {  
    203.         for(j=0;j<colLen;j++)  
    204.         {  
    205.             dataSet[i][j] = ( dataSet[i][j] - mina[j] )/range[j];  
    206.         }  
    207.     }  
    208. }  
    209.   
    210. int main(int argc , char** argv)  
    211. {  
    212.     int k,row,col;  
    213.     char *filename;  
    214.       
    215.     if( argc!=5 )  
    216.     {  
    217.         cout<<"The input should be like this : ./a.out k row col filename"<<endl;  
    218.         exit(1);  
    219.     }  
    220.   
    221.     k = atoi(argv[1]);  
    222.     row = atoi(argv[2]);  
    223.     col = atoi(argv[3]);  
    224.     filename = argv[4];  
    225.   
    226.     KNN knn(k,row,col,filename);  
    227.   
    228.     knn.auto_norm_data();  
    229.     knn.get_error_rate();  
    230.   
    231.     return 0;  
    232. }  
    makefile:

    1. target:  
    2.     g++ KNN_2.cc  
    3.     ./a.out 7 4000 8 input.txt  
    4.   
    5. cu:  
    6.     nvcc KNN.cu  
    7.     ./a.out 7 4000 8 input.txt  

    运行结果:



    三、并行实现

    并行实现的过程就是将没一个测试样本到N个训练样本的距离进行并行化,如果串行计算的话,时间复杂度为:O(N*D),如果串行计算的话,时间复杂度为O(D),其实D为数据的维度。

    KNN.cu:

    1. #include<iostream>  
    2. #include<map>  
    3. #include<vector>  
    4. #include<stdio.h>  
    5. #include<cmath>  
    6. #include<cstdlib>  
    7. #include<algorithm>  
    8. #include<fstream>  
    9.   
    10. using namespace std;  
    11.   
    12. typedef string tLabel;  
    13. typedef float tData;  
    14. typedef pair<int,double>  PAIR;  
    15. const int MaxColLen = 10;  
    16. const int MaxRowLen = 10010;  
    17. const int test_data_num = 400;  
    18. ifstream fin;  
    19.   
    20. class KNN  
    21. {  
    22. private:  
    23.         tData dataSet[MaxRowLen][MaxColLen];  
    24.         tLabel labels[MaxRowLen];  
    25.         tData testData[MaxColLen];  
    26.         tData trainingData[3600][8];  
    27.         int rowLen;  
    28.         int colLen;  
    29.         int k;  
    30.         map<int,double> map_index_dis;  
    31.         map<tLabel,int> map_label_freq;  
    32.         double get_distance(tData *d1,tData *d2);  
    33. public:  
    34.         KNN(int k , int rowLen , int colLen , char *filename);  
    35.         void get_all_distance();  
    36.         tLabel get_max_freq_label();  
    37.         void auto_norm_data();  
    38.         void get_error_rate();  
    39.         void get_training_data();  
    40.         struct CmpByValue  
    41.         {  
    42.             bool operator() (const PAIR& lhs,const PAIR& rhs)  
    43.             {  
    44.                 return lhs.second < rhs.second;  
    45.             }  
    46.         };  
    47.   
    48.         ~KNN();   
    49. };  
    50.   
    51. KNN::~KNN()  
    52. {  
    53.     fin.close();  
    54.     map_index_dis.clear();  
    55.     map_label_freq.clear();  
    56. }  
    57.   
    58. KNN::KNN(int k , int row ,int col , char *filename)  
    59. {  
    60.     this->rowLen = row;  
    61.     this->colLen = col;  
    62.     this->k = k;  
    63.       
    64.     fin.open(filename);  
    65.   
    66.     if( !fin )  
    67.     {  
    68.         cout<<"can not open the file"<<endl;  
    69.         exit(0);  
    70.     }  
    71.   
    72.     for(int i=0;i<rowLen;i++)  
    73.     {  
    74.         for(int j=0;j<colLen;j++)  
    75.         {  
    76.             fin>>dataSet[i][j];  
    77.         }  
    78.         fin>>labels[i];  
    79.     }  
    80.   
    81. }  
    82.   
    83. void KNN:: get_training_data()  
    84. {  
    85.     for(int i=test_data_num;i<rowLen;i++)  
    86.     {  
    87.         for(int j=0;j<colLen;j++)  
    88.         {  
    89.             trainingData[i-test_data_num][j] = dataSet[i][j];  
    90.         }  
    91.     }  
    92. }  
    93.   
    94. void KNN:: get_error_rate()  
    95. {  
    96.     int i,j,count = 0;  
    97.     tLabel label;  
    98.   
    99.     cout<<"the test data number is : "<<test_data_num<<endl;  
    100.   
    101.     get_training_data();  
    102.   
    103.     //get testing data and calculate  
    104.     for(i=0;i<test_data_num;i++)  
    105.     {  
    106.         for(j=0;j<colLen;j++)  
    107.         {  
    108.             testData[j] = dataSet[i][j];          
    109.         }  
    110.           
    111.         get_all_distance();  
    112.         label = get_max_freq_label();  
    113.         if( label!=labels[i] )  
    114.             count++;  
    115.         map_index_dis.clear();  
    116.         map_label_freq.clear();  
    117.     }  
    118.     cout<<"the error rate is = "<<(double)count/(double)test_data_num<<endl;  
    119. }  
    120.   
    121. //global function  
    122. __global__ void cal_dis(tData *train_data,tData *test_data,tData* dis,int pitch,int N , int D)  
    123. {  
    124.     int tid = blockIdx.x;  
    125.     if(tid<N)  
    126.     {  
    127.         tData temp = 0;  
    128.         tData sum = 0;  
    129.         for(int i=0;i<D;i++)  
    130.         {  
    131.             temp = *( (tData*)( (char*)train_data+tid*pitch  )+i ) - test_data[i];  
    132.             sum += temp * temp;  
    133.         }  
    134.         dis[tid] = sum;  
    135.     }  
    136. }  
    137.   
    138. //Parallel calculate the distance  
    139. void KNN:: get_all_distance()  
    140. {  
    141.     int height = rowLen - test_data_num;  
    142.     tData *distance = new tData[height];  
    143.     tData *d_train_data,*d_test_data,*d_dis;  
    144.     size_t pitch_d ;  
    145.     size_t pitch_h = colLen * sizeof(tData);  
    146.     //allocate memory on GPU  
    147.     cudaMallocPitch( &d_train_data,&pitch_d,colLen*sizeof(tData),height);  
    148.     cudaMalloc( &d_test_data,colLen*sizeof(tData) );  
    149.     cudaMalloc( &d_dis, height*sizeof(tData) );  
    150.   
    151.     cudaMemset( d_train_data,0,height*colLen*sizeof(tData) );  
    152.     cudaMemset( d_test_data,0,colLen*sizeof(tData) );  
    153.     cudaMemset( d_dis , 0 , height*sizeof(tData) );  
    154.   
    155.     //copy training and testing data from host to device  
    156.     cudaMemcpy2D( d_train_data,pitch_d,trainingData,pitch_h,colLen*sizeof(tData),height,cudaMemcpyHostToDevice);  
    157.     cudaMemcpy( d_test_data,testData,colLen*sizeof(tData),cudaMemcpyHostToDevice);  
    158.     //calculate the distance  
    159.     cal_dis<<<height,1>>>( d_train_data,d_test_data,d_dis,pitch_d,height,colLen );  
    160.     //copy distance data from device to host  
    161.     cudaMemcpy( distance,d_dis,height*sizeof(tData),cudaMemcpyDeviceToHost);  
    162.   
    163.     int i;  
    164.     for( i=0;i<rowLen-test_data_num;i++ )  
    165.     {  
    166.         map_index_dis[i+test_data_num] = distance[i];  
    167.     }  
    168.   
    169. }  
    170.   
    171. tLabel KNN:: get_max_freq_label()  
    172. {  
    173.     vector<PAIR> vec_index_dis( map_index_dis.begin(),map_index_dis.end() );  
    174.     sort(vec_index_dis.begin(),vec_index_dis.end(),CmpByValue());  
    175.   
    176.     for(int i=0;i<k;i++)  
    177.     {  
    178.         /* 
    179.         cout<<"the index = "<<vec_index_dis[i].first<<" the distance = "<<vec_index_dis[i].second<<" the label = "<<labels[ vec_index_dis[i].first ]<<" the coordinate ( "; 
    180.         int j; 
    181.         for(j=0;j<colLen-1;j++) 
    182.         { 
    183.             cout<<dataSet[ vec_index_dis[i].first ][j]<<","; 
    184.         } 
    185.         cout<<dataSet[ vec_index_dis[i].first ][j]<<" )"<<endl; 
    186.         */  
    187.         map_label_freq[ labels[ vec_index_dis[i].first ]  ]++;  
    188.     }  
    189.   
    190.     map<tLabel,int>::const_iterator map_it = map_label_freq.begin();  
    191.     tLabel label;  
    192.     int max_freq = 0;  
    193.     while( map_it != map_label_freq.end() )  
    194.     {  
    195.         if( map_it->second > max_freq )  
    196.         {  
    197.             max_freq = map_it->second;  
    198.             label = map_it->first;  
    199.         }  
    200.         map_it++;  
    201.     }  
    202.     cout<<"The test data belongs to the "<<label<<" label"<<endl;  
    203.     return label;  
    204. }  
    205.   
    206. void KNN::auto_norm_data()  
    207. {  
    208.     tData maxa[colLen] ;  
    209.     tData mina[colLen] ;  
    210.     tData range[colLen] ;  
    211.     int i,j;  
    212.   
    213.     for(i=0;i<colLen;i++)  
    214.     {  
    215.         maxa[i] = max(dataSet[0][i],dataSet[1][i]);  
    216.         mina[i] = min(dataSet[0][i],dataSet[1][i]);  
    217.     }  
    218.   
    219.     for(i=2;i<rowLen;i++)  
    220.     {  
    221.         for(j=0;j<colLen;j++)  
    222.         {  
    223.             if( dataSet[i][j]>maxa[j] )  
    224.             {  
    225.                 maxa[j] = dataSet[i][j];  
    226.             }  
    227.             else if( dataSet[i][j]<mina[j] )  
    228.             {  
    229.                 mina[j] = dataSet[i][j];  
    230.             }  
    231.         }  
    232.     }  
    233.   
    234.     for(i=0;i<colLen;i++)  
    235.     {  
    236.         range[i] = maxa[i] - mina[i] ;   
    237.         //normalize the test data set  
    238.         testData[i] = ( testData[i] - mina[i] )/range[i] ;  
    239.     }  
    240.   
    241.     //normalize the training data set  
    242.     for(i=0;i<rowLen;i++)  
    243.     {  
    244.         for(j=0;j<colLen;j++)  
    245.         {  
    246.             dataSet[i][j] = ( dataSet[i][j] - mina[j] )/range[j];  
    247.         }  
    248.     }  
    249. }  
    250.   
    251. int main(int argc , char** argv)  
    252. {  
    253.     int k,row,col;  
    254.     char *filename;  
    255.       
    256.     if( argc!=5 )  
    257.     {  
    258.         cout<<"The input should be like this : ./a.out k row col filename"<<endl;  
    259.         exit(1);  
    260.     }  
    261.   
    262.     k = atoi(argv[1]);  
    263.     row = atoi(argv[2]);  
    264.     col = atoi(argv[3]);  
    265.     filename = argv[4];  
    266.   
    267.     KNN knn(k,row,col,filename);  
    268.   
    269.     knn.auto_norm_data();  
    270.     knn.get_error_rate();  
    271.   
    272.     return 0;  
    273. }  

    运行结果:


    因为内存分配的问题(之前文章提到过),那么就需要将训练数据trainingData进行静态的空间分配,这样不是很方便。

    可以看到,在测试数据集和训练数据集完全相同的情况下,结果是完全一样的。数据量小,没有做时间性能上的对比。还有可以改进的地方就是可以一次性的将所有testData载入到显存中,而不是一个一个的载入,这样就能够减少训练数据拷贝到显存中的次数,提高效率。


    Author:忆之独秀

    Email:leaguenew@qq.com

    注明出处:http://blog.csdn.net/lavorange/article/details/42172451


  • 相关阅读:
    WPF PrismDialog PopupWindowAction使用MetroWindow
    WPF 定义Lookless控件的默认样式、 OnApplyTemplate 如何使用(实现方式、如何工作的)!
    WPF MeasureOverride和 ArrangeOverride做个 页面导航
    MeasureOverride和ArrangeOverride 练手项目
    XAML 调试工具 不见了?
    未能加载文件或程序集,PublicKeyToken=“**********”,或它的某一个依赖项。强名称验证失败。
    .NET 中的 GAC
    软考过程知识整理记录
    定时执行exe、windows任务计划、windows服务
    为视图或函数指定的列名比其定义中的列多。
  • 原文地址:https://www.cnblogs.com/walccott/p/4956934.html
Copyright © 2011-2022 走看看