用C语言搞机器学习,来个最基础的Knn入门

本来是准备周末加班两天的,然后,临时突然其他事情又取消了。

顺便看了下csdn,看到一篇介绍KNN的,因为我现在做的也是属于机器学习方向,那自然也要了解一些这部分。

KNN是什么?

KNN可以说是最简单的分类算法之一,同时,它也是最常用的分类算法之一,注意KNN算法是有监督学习中的分类算法,它看起来和另一个机器学习算法Kmeans有点像(Kmeans是无监督学习算法),但却是有本质区别的。那么什么是KNN算法呢,接下来我们就来介绍介绍吧。

KNN算法介绍

KNN的全称是K Nearest Neighbors,意思是K个最近的邻居,从这个名字我们就能看出一些KNN算法的蛛丝马迹了。K个最近邻居,毫无疑问,K的取值肯定是至关重要的。那么最近的邻居又是怎么回事呢?其实啊,KNN的原理就是当预测一个新的值x的时候,根据它距离最近的K个点是什么类别来判断x属于哪个类别。听起来有点绕,还是看看图吧。

67c9bc55b111677437695a7b6717e3b9.png

图中绿色的点就是我们要预测的那个点,假设K=3。那么KNN算法就会找到与它距离最近的三个点(这里用圆圈把它圈起来了),看看哪种类别多一些,比如这个例子中是蓝色三角形多一些,新来的绿色点就归类到蓝三角了。

eec6654637c9f6682c9af980a24c7776.png

但是,当K=5的时候,判定就变成不一样了。这次变成红圆多一些,所以新来的绿点被归类成红圆。从这个例子中,我们就能看得出K的取值是很重要的。

明白了大概原理后,我们就来说一说细节的东西吧,主要有两个,K值的选取和点距离的计算。

距离计算

要度量空间中点距离的话,有好几种度量方式,比如常见的曼哈顿距离计算,欧式距离计算等等。不过通常KNN算法中使用的是欧式距离,这里只是简单说一下,拿二维平面为例,,二维空间两个点的欧式距离计算公式如下:

8053ef45950cece5970594e60767eff7.png

这个高中应该就有接触到的了,其实就是计算(x1,y1)和(x2,y2)的距离。拓展到多维空间,则公式变成这样:

6c6d148ebe31da084fecce1e3882aaeb.png 

这样我们就明白了如何计算距离,KNN算法最简单粗暴的就是将预测点与所有点距离进行计算,然后保存并排序,选出前面K个值看看哪些类别比较多。但其实也可以通过一些数据结构来辅助,比如最大堆,这里就不多做介绍,有兴趣可以百度最大堆相关数据结构的知识。

K值选择

通过上面那张图我们知道K的取值比较重要,那么该如何确定K取多少值好呢?答案是通过交叉验证(将样本数据按照一定比例,拆分出训练用的数据和验证用的数据,比如6:4拆分出部分训练数据和验证数据),从选取一个较小的K值开始,不断增加K的值,然后计算验证集合的方差,最终找到一个比较合适的K值。

通过交叉验证计算方差后你大致会得到下面这样的图:

08e8aeb6ac2d72cf5641fff458a8705a.png

这个图其实很好理解,当你增大k的时候,一般错误率会先降低,因为有周围更多的样本可以借鉴了,分类效果会变好。但注意,和K-means不一样,当K值更大的时候,错误率会更高。这也很好理解,比如说你一共就35个样本,当你K增大到30的时候,KNN基本上就没意义了。

所以选择K点的时候可以选择一个较大的临界K点,当它继续增大或减小的时候,错误率都会上升,比如图中的K=10。具体如何得出K最佳值的代码,下一节的代码实例中会介绍。


下面这个例程是使用KNN的方法来做的一个机器学习例程

首先这个例子是用c实现的,里面的代码大家都可以看得明白,而且这个东西确实是机器学习相关的,如果想了解什么是机器学习,可以从这个入手去学习。

3cee25e94f7c95b24148094853d27f71.png

完整的c语言代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdint.h>
#include <stdbool.h>/*一个手写数字的结构体*/
typedef struct {int pixel[1024];int number;
} SampleData;/*一个有label的距离结构体*/
typedef struct {float distance;int number;
} CalculationData;/*文件路径+名称*/
static const char* TrainingFilePath = "./my-digit-training.txt";
static const char* TestingFilePath  = "./my-digit-testing.txt";
static const char* CalculationFilePath  = "./my-digit-predict.txt";/*每个数据集的数字个数*/
#define TRAINING_SIZE 943
#define TRAINING_COUNT 10
#define TEST_SIZE 196
#define CALCULATION_SIZE 9/*求距离*/
float CalculateDistance(SampleData sample_01, SampleData sample_02) {int i, square_sum=0.0;for(i=0; i<1024; i++) {square_sum += pow(sample_01.pixel[i]-sample_02.pixel[i], 2.0);}return sqrtf(square_sum);
}/*把文件的數據加載到內存中*/
int DataLoading(SampleData *pSampleDate_t, FILE *fp, int *pNumber) {int index=0;for(index = 0; index<1024; index++) {if(!fscanf(fp, "%d", &pSampleDate_t->pixel[index])) {printf("FILE already read finish.\n");return -true;}}fscanf(fp, "%d", &pSampleDate_t->number);*pNumber = pSampleDate_t->number;return true;
}/*显示一个Digit 结构体*/
void ShowSampleDate(SampleData SampleData_t) {int i, j, id;for(i=0;i<32;i++) {for(j=0;j<32;j++) {printf("%d", SampleData_t.pixel[i*32+j]);}printf("\n");}printf(" %d \n", SampleData_t.number);
}/*交换字符串两项*/
void IndexExchange(CalculationData *in, int index1, int index2) {CalculationData tmp = (CalculationData)in[index1];in[index1] = in[index2];in[index2] = tmp;
}/*选择排序*/
void SelectSort(CalculationData *in, int length) {int i, j, min;int N = length;for(i=0; i<N-1; i++) {min = i;for(j=i+1;j<N;j++) {if(in[j].distance<in[min].distance) min = j;}IndexExchange(in,i,min);}
}/*利用训练数据预测一个数据digit*/
int DataForecast(int K, SampleData in, SampleData *train, int nt)
{int i, it;CalculationData distance[nt];/*求取输入digit与训练数据的距离*/for(it=0; it<nt; it++) {distance[it].distance = CalculateDistance(in, train[it]);distance[it].number = train[it].number;}/*给计算的距离排序(选择排序)*/int predict = 0;SelectSort(distance, nt);for(i=0; i<K; i++) {predict += distance[i].number;}return (int)(predict/K);
}/*用测试数据集进行测试*/
void KnnDatasetClassification(int K)
{printf(".KnnDatasetClassification.\n");int i;FILE *fp;/*读入训练数据*/int trainLabels[TRAINING_SIZE];int trainCount[TRAINING_COUNT] = {0};SampleData *pSampleTrain_t = (SampleData*)malloc(TRAINING_SIZE*sizeof(SampleData));fp = fopen(TrainingFilePath,"r");if (fp < 0) {printf("..Open %s Error.\n", TrainingFilePath);exit(0);}printf("..load training digits.\n");for(i=0; i<TRAINING_SIZE; i++) {DataLoading(&pSampleTrain_t[i], fp, &trainLabels[i]);trainCount[pSampleTrain_t[i].number] ++;}fclose(fp);printf("..Done.\n");/*读入测试数据*/int testLabels[TEST_SIZE];int testCount[TRAINING_COUNT] = {0};SampleData *pSampleDataTest_t = (SampleData*)malloc(TEST_SIZE*sizeof(SampleData));fp = fopen(TestingFilePath,"r");if (fp < 0) {printf("..Open %s Error.\n", TestingFilePath);exit(0);}printf("..load testing digits.\n");for(i=0;i<TEST_SIZE;i++) {DataLoading(&pSampleDataTest_t[i], fp, &testLabels[i]);testCount[pSampleDataTest_t[i].number] ++;}fclose(fp);printf("..Done.\n");/*求测试数据与训练数据之间的距离*/printf("..Cal CalculationData begin.\n");CalculationData Distance2Train[TRAINING_SIZE];int CorrectCount[TRAINING_COUNT] = {0};int itrain, itest, predict;for(itest=0; itest < TEST_SIZE; itest++) {predict = DataForecast(K, pSampleTrain_t[itest], pSampleTrain_t, TRAINING_SIZE);//printf("%d-%d\n",predict, Dtest[itest].number);/*给预测准确的进行计数*/if(predict == pSampleDataTest_t[itest].number) {CorrectCount[predict] ++;}}/*输出测试数据的准确率*/printf("    Correct radio:   \n\n");for(i=0;i<10;i++) {printf("%d:  (  %2d / %2d ) =  %.2f%%\n",i,CorrectCount[i],testCount[i],(float)(CorrectCount[i]*1.0/testCount[i]*100));}
}
/*预测数据*/
void KnnPredict(int K) {int i;FILE *fp = NULL;/*读入训练数据*/int trainLabels[TRAINING_SIZE];int trainCount[TRAINING_COUNT] = {0};SampleData *Dtrain = (SampleData*)malloc(TRAINING_SIZE*sizeof(SampleData));fp = fopen(TrainingFilePath,"r");printf("..load training digits.\n");for(i=0; i<TRAINING_SIZE; i++) {DataLoading(&Dtrain[i], fp, &trainLabels[i]);trainCount[Dtrain[i].number] ++;}fclose(fp);printf("..Done.\n");/*读入需要预测的数据*/int predictLabels[CALCULATION_SIZE];int predictCount[TRAINING_COUNT] = {0};SampleData *Dpredict = (SampleData*)malloc(CALCULATION_SIZE*sizeof(SampleData));fp = fopen(CalculationFilePath,"r");printf("..load predict digits.\n");for(i=0;i<CALCULATION_SIZE;i++) {DataLoading(&Dpredict[i], fp, &predictLabels[i]);predictCount[Dpredict[i].number] ++;}fclose(fp);printf("..Done.\n");/*求输入数据与训练数据之间的距离*/printf("..Cal CalculationData begin.\n");CalculationData Distance2Train[TRAINING_SIZE];int itrain, ipredict, predict;for(ipredict=0; ipredict<CALCULATION_SIZE; ipredict++) {predict = DataForecast(K, Dpredict[ipredict], Dtrain, TRAINING_SIZE);printf("%d\n",predict);}
}int main(int argc, char** argv)
{int K = 1;/*对已知数据进行测试,统计预测的正确率*/KnnDatasetClassification(K);/*对位置数据进行预测*/KnnPredict(K);return 1;
}

代码输出如下:

.KnnDatasetClassification.
..load training digits.
..Done.
..load testing digits.
..Done.
..Cal CalculationData begin.Correct radio:   0:  (   1 / 20 ) =  5.00%
1:  (   2 / 20 ) =  10.00%
2:  (   4 / 25 ) =  16.00%
3:  (   0 / 18 ) =  0.00%
4:  (   1 / 25 ) =  4.00%
5:  (   3 / 16 ) =  18.75%
6:  (   1 / 16 ) =  6.25%
7:  (   3 / 19 ) =  15.79%
8:  (   3 / 17 ) =  17.65%
9:  (   2 / 20 ) =  10.00%
..load training digits.
..Done.
..load predict digits.
..Done.
..Cal CalculationData begin.
5
2
1
8
2
9
9
1
5
百分比的输出是说明对每个不同数字的识别准确率。
最后的输出是输入单个例子的时候得到的结果,这个结果是和输入的样本
完全一致的。
代码里面会用到三个文件,一个是训练的文件,一个是测试文件,一个是
单例的测试文件。
完整的代码下载链接:

链接: https://pan.baidu.com/s/13OpeHEVD4zWCz60XjRC6pA?pwd=2hf9 

提取码: 2hf9 

文章内容来自:

https://blog.csdn.net/weixin_45014385/article/details/123618841

https://rtoax.blog.csdn.net/article/details/80309077

80ddc462dd48872e2112cd1a00fd4571.jpeg

b7c4424824c797370b9c4acfe04a882a.jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/463591.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何解决padding标记在ie7、ie6以及firefox中的兼容问题

*html 与 *html 是IE特有的标签, firefox 暂不支持.而*html 又为 IE7特有标签。所以要解决padding的兼容问题就要靠前面提到的标签。 以sccas-site为例&#xff0c;左侧导航栏在padding上产生了ie6、ie7以及ff浏览器不兼容&#xff0c;修改代码如下&#xff1a; #menu7 li a {h…

linux编译动态库之fPIC

转载&#xff1a;https://blog.csdn.net/sinc00/article/details/44833839 今天在用g编译代码时&#xff0c;提示说.rdata错误&#xff0c;然后网上找了一堆资料&#xff0c;最后明白了一个要重新编译对应的链接库。 在生成动态库时&#xff0c;常常习惯性的加上fPIC选项&…

每周一题 扫雷问题

扫雷问题 #include<iostream> #include<vector> #include<iostream> using namespace std; int direct[8][2]{ {1,0}, {1,1}, {1,-1}, {0,1}, {0,-1}, {-1,0}, {-1,1}, {-1,-1} }; int main(){int m, n, t0;while(1){cin>>m>>n;if(m0&&n…

Oracle学习笔记:通过种子数据库设置dbid为指定值

简介&#xff1a;dbms_backup_restore包真是太强大了。和设置dbid有关的存储过程如下&#xff1a; PROCEDURE nidbegin  (newdbname IN varchar2,       olddbname IN varchar2,       newdbid IN number, …

最简单的断线断点检测器电路

要在长长的电线中找到究竟是哪里断开了&#xff0c;可以做一个断线断点检测器。而且几个元器件就可以实现&#xff0c;非常简单。这个断线断点检测器不仅可以识别火线、零线&#xff0c;还可以检测电线是哪里断开了。实际是检测哪里的磁场强&#xff0c;哪里的电磁辐射大。来看…

gcc/g++ 编译时报错原因分析之expected type-specifier before

因为没有引入相应的头文件&#xff0c;找到对应的头文件&#xff0c;加入相应的cpp中即可解决。

python 文本处理2

接上文&#xff0c;我们定义了判断某行是否file copy,或者file overwrite的两个函数&#xff0c;事实上我们如果使用startswith函数代替slice&#xff0c;会更稳定。从函数式编程角度&#xff0c;我们给出下面几个表达式&#xff1a; isRegDBRoot lambda line: line[:11]…

iOStextField/textView在输入时限制emoji表情的输入

https://www.jianshu.com/p/5227e6aab4d4 2017.02.27 13:08* 字数 146 阅读 6109评论 6喜欢 14 又遇到输入框输入表情的情况了&#xff0c;之前写了一篇文章“UITextView/UITextField检测并过滤Emoji表情符号”http://www.jianshu.com/p/90d68e7e5d53,但是总觉得那两种方式都各…

声学发展史之——人工智能(AI)声学

引言最近接手了一个EOL (End of Line)的项目&#xff0c;用高斯混合模型GMM (Gaussian Mixture Model)作生产线上产品的质量检测。虽然提取特征的过程很痛苦&#xff0c;不过还是很有意思。也是因为兴趣&#xff0c;去年在Coursera上了吴恩达的Machine Learning&#xff0c;算是…

Mysql数据库存储原理

转载&#xff1a;https://blog.csdn.net/weixin_40612082/article/details/82179714 现在在做数据库服务器的开发工作&#xff0c;今天被问到存储过程&#xff0c;当时只是简单地回答了下&#xff0c;在网上搜索了下资料&#xff0c;才对存储过程有了新的认识。转载内容如下&a…

seo专题之开篇有益

想写这篇文章好久了.但一直不敢写,一怕自己技术有限误导了园子里的各位朋友.二怕自己文笔有限不能很好的表达自己的意图,但既然是抱着交流的态度来的,我还是愿意写一写这方面的文章与大家一起交流和分享,欢迎大家拍砖.做SEO没有什么高深技术可言,靠的是经验的不断累积,各位SEO高…

Rails不用localhost访问的时候很慢

修改文件/usr/lib/ruby/1.9.1/webrick/config.rb,找到:DoNotReverseLookup > nil&#xff0c;修改为:DoNotReverseLookup > true。转载于:https://blog.51cto.com/tianbymy/926589

一个application多个 URL

需求&#xff1a; 希望一个sharepoint网站&#xff0c;有多个网址去访问。例如&#xff1a;http://moss:8080/的网站&#xff0c;http://aphla.prismshareusa.int/ 和 http://aphla.carat.int/ 两个网站同时访问。 解决方法如下&#xff1a; 1&#xff09;创建DNS…

gcc/g++编译器的优化

gcc/g编译器的优化 gcc提供了从O0-O3以及Os这几种不同的优化级别供大家选择 O0是编译器默认的设置 (1)、-O1&#xff1a;它主要对代码的分支&#xff0c;常量以及表达式等进行优化 (2)、-O2&#xff1a;尝试更多的寄存器级的优化以及指令级的优化&#xff0c;它会在编译期间…

C#中Equals和==的区别 (面试官经常会问到)

首先对于值类型来说&#xff0c;并没有什么区别。区别只针对于引用类型。 一、值类型 1.对于值类型来说&#xff0c;两者比较的都是“内容”是否相同&#xff0c;也就是值是否相同&#xff08;值类型只有存在栈上&#xff09;。 二、引用类型 1.对于引用类型来说&#xff0c;本…

写的重采样文章被大佬看到了

他让我看重采样昨天写的重采样文章被一个大佬看到了&#xff0c;给我发了消息如下大佬是个技术原厂Linux方向的负责人&#xff0c;我在工作上遇到的好几次疑难杂症都是在他的指点下得到解决&#xff0c;而且平时讨论技术的时候&#xff0c;能感觉到他对技术问题理解很深。从他的…

根据IP地址获取主机名称

IP地址获得主机名称 1. 根据IP地址获得主机名称///<summary>///根据IP地址获得主机名称 ///</summary>///<param name"ip">主机的IP地址</param>///<returns>主机名称</returns>publicstringGetHostNameByIp(stringip) …

vector中的reserve() 与 resize()

resize()与reserve()都是vector容器中的方法&#xff1a; resize():改变了capacity()和size() reserve():增加了vector的capacity()&#xff0c;但是它的size()没有改变 #include <iostream> #include <stdio.h> #include <vector> #include<functiona…

【C++】考虑virtual函数以外的其他选择

假设你正在写一个视频游戏软件&#xff0c;游戏里有各种各样的人物&#xff0c;每个人物都有健康状态&#xff0c;而且不同的人物可能以不同的方式计算他们的健康指数&#xff0e;该如何设计游戏里的人物&#xff0c;主要如何提供一个返回人物健康指数的接口&#xff0e; 方法一…

不知道你们遇到这样的问题没?

最近在网上看到这样一个内容https://developer.horizon.ai/forumDetail/118363914936419003关于J5/J3/J2平台的底层软件地平线内部的释放计划和形式&#xff1f;您好&#xff1a;问题如题&#xff0c;我们当前在地平线J5平台展开进行的项目居多&#xff0c;跟贵司接触和合作的部…