您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 咨询培训 > K近邻算法(KNN)的C++实现
本文不对KNN算法做过多的理论上的解释,主要是针对问题,进行算法的设计和代码的注解。KNN算法:优点:精度高、对异常值不敏感、无数据输入假定。缺点:计算复杂度高、空间复杂度高。适用数据范围:数值型和标称性。工作原理:存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一个数据与所属分类的对应关系。输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据及中前k个最相似的数据,这就是k-近邻算法中k的出处,通常k选择不大于20的整数。最后,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。K-近邻算法的一般流程:(1)收集数据:可以使用任何方法(2)准备数据:距离计算所需要的数值,最好是结构化的数据格式(3)分析数据:可以使用任何方法(4)训练算法:此步骤不适用k-邻近算法(5)测试算法:计算错误率(6)使用算法:首先需要输入样本数据和结构化的输出结果,然后运行k-近邻算法判定输入数据分别属于哪个分类,最后应用对计算出的分类执行后续的处理。问题一:现在我们假设一个场景,就是要为左边上的点进行分类,如下图所示:上图一共12个左边点,每个坐标点都有相应的坐标(x,y)以及它所属的类别A/B,那么现在需要做的就是给定一个点坐标(x1,y1),判断它属于的类别A或者B。所有的坐标点在data.txt文件中:0.01.1A1.01.0A2.01.0B0.50.5A2.50.5B0.00.0A1.00.0A2.00.0B3.00.0B0.0-1.0A1.0-1.0A2.0-1.0Bstep1:通过类的默认构造函数去初始化训练数据集dataSet和测试数据testData。step2:用get_distance()来计算测试数据testData和每一个训练数据dataSet[index]的距离,用map_index_dis来保存键值对index,distance,其中index代表第几个训练数据,distance代表第index个训练数据和测试数据的距离。step3:将map_index_dis按照value值(即distance值)从小到大的顺序排序,然后取前k个最小的value值,用map_label_freq来记录每一个类标签出现的频率。step4:遍历map_label_freq中的value值,返回value最大的那个key值,就是测试数据属于的类。看一下代码KNN_0.cc:#includeiostream#includemap#includevector#includestdio.h#includecmath#includecstdlib#includealgorithm#includefstreamusingnamespacestd;typedefchartLabel;typedefdoubletData;typedefpairint,doublePAIR;constintcolLen=2;constintrowLen=12;ifstreamfin;ofstreamfout;classKNN{private:tDatadataSet[rowLen][colLen];tLabellabels[rowLen];tDatatestData[colLen];intk;mapint,doublemap_index_dis;maptLabel,intmap_label_freq;doubleget_distance(tData*d1,tData*d2);public:KNN(intk);voidget_all_distance();voidget_max_freq_label();structCmpByValue{booloperator()(constPAIR&lhs,constPAIR&rhs){returnlhs.secondrhs.second;}};};KNN::KNN(intk){this-k=k;fin.open(data.txt);if(!fin){coutcannotopenthefiledata.txtendl;exit(1);}/*inputthedataSet*/for(inti=0;irowLen;i++){for(intj=0;jcolLen;j++){findataSet[i][j];}finlabels[i];}coutpleaseinputthetestdata:endl;/*inuputthetestdata*/for(inti=0;icolLen;i++)cintestData[i];}/**calculatethedistancebetweentestdataanddataSet[i]*/doubleKNN::get_distance(tData*d1,tData*d2){doublesum=0;for(inti=0;icolLen;i++){sum+=pow((d1[i]-d2[i]),2);}//coutthesumis=sumendl;returnsqrt(sum);}/**calculateallthedistancebetweentestdataandeachtrainingdata*/voidKNN::get_all_distance(){doubledistance;inti;for(i=0;irowLen;i++){distance=get_distance(dataSet[i],testData);//key,value=i,distancemap_index_dis[i]=distance;}//traversethemaptoprinttheindexanddistancemapint,double::const_iteratorit=map_index_dis.begin();while(it!=map_index_dis.end()){coutindex=it-firstdistance=it-secondendl;it++;}}/**checkwhichlabelthetestdatabelongstotoclassifythetestdata*/voidKNN::get_max_freq_label(){//transformthemap_index_distovec_index_disvectorPAIRvec_index_dis(map_index_dis.begin(),map_index_dis.end());//sortthevec_index_disbydistancefromlowtohightogetthenearestdatasort(vec_index_dis.begin(),vec_index_dis.end(),CmpByValue());for(inti=0;ik;i++){couttheindex=vec_index_dis[i].firstthedistance=vec_index_dis[i].secondthelabel=labels[vec_index_dis[i].first]thecoordinate(dataSet[vec_index_dis[i].first][0],dataSet[vec_index_dis[i].first][1])endl;//calculatethecountofeachlabelmap_label_freq[labels[vec_index_dis[i].first]]++;}maptLabel,int::const_iteratormap_it=map_label_freq.begin();tLabellabel;intmax_freq=0;//findthemostfrequentlabelwhile(map_it!=map_label_freq.end()){if(map_it-secondmax_freq){max_freq=map_it-second;label=map_it-first;}map_it++;}coutThetestdatabelongstothelabellabelendl;}intmain(){intk;coutpleaseinputthekvalue:endl;cink;KNNknn(k);knn.get_all_distance();knn.get_max_freq_label();system(pause);return0;}我们来测试一下这个分类器(k=5):testData(5.0,5.0):testData(-5.0,-5.0):testData(1.6,0.5):分类结果的正确性可以通过坐标系来判断,可以看出结果都是正确的。问题二:使用k-近邻算法改进约会网站的匹配效果情景如下:我的朋友海伦一直使用在线约会网站寻找合适自己的约会对象。尽管约会网站会推荐不同的人选,但她没有从中找到喜欢的人。经过一番总结,她发现曾交往过三种类型的人:不喜欢的人魅力一般的人极具魅力的人尽管发现了上述规律,但海伦依然无法将约会网站推荐的匹配对象归入恰当的分类。她觉得可以在周一到周五约会哪些魅力一般的人,而周末则更喜欢与那些极具魅力的人为伴。海伦希望我们的分类软件可以更好的帮助她将匹配对象划分到确切的分类中。此外海伦还收集了一些约会网站未曾记录的数据信息,她认为这些数据更有助于匹配对象的归类。海伦已经收集数据一段时间。她把这些数据存放在文本文件datingTestSet.txt(文件链接:)中,每个样本占据一行,总共有1000行。海伦的样本主要包含3中特征:每年获得的飞行常客里程数玩视频游戏所耗时间的百分比每周消费的冰淇淋公升数数据预处理:归一化数据我们可以看到,每年获取的飞行常客里程数对于计算结果的影响将远大于其他两个特征。而产生这种现象的唯一原因,仅仅是因为飞行常客书远大于其他特征值。但是这三种特征是同等重要的,因此作为三个等权重的特征之一,飞行常客数不应该如此严重地影响到计算结果。处理这种不同取值范围的特征值时,我们通常采用的方法是数值归一化,如将取值范围处理为0到1或者-1到1之间。公式为:newValue=(oldValue-min)/(max-min)其中min和max分别是数据集中的最小特征值和最大特征值。我们增加一个auto_norm_data函数来归一化数据。同事还要设计一个get_error_rate来计算分类的错误率,选总体数据的10%作为测试数据,90%作为训练数据,当然也可以自己设定百分比。其他的算法设计都与问题一类似。代码如下KNN_2.cc(k=7):/*addtheget_error_ratefunction*/#includeiostream#includemap#includevector#includestdio.h#includecmath#includecstdlib#includealgorithm#includefstreamusingnamespacestd;typedefstringtLabel;typedefdoubletData;typedefpairint,doublePAIR;constintMaxColLen=10;constintMaxRowLen=10000;ifstreamfin;ofstreamfout;classKNN{private:tDatadataSet[MaxRowLen][MaxColLen];tLab
本文标题:K近邻算法(KNN)的C++实现
链接地址:https://www.777doc.com/doc-4817584 .html