用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,一经查实,立即删除!

相关文章

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

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

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

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

一个application多个 URL

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

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

他让我看重采样昨天写的重采样文章被一个大佬看到了&#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;跟贵司接触和合作的部…

boost::split()的使用方式

引用的头文件 <boost/algorithm/string.hpp> boost::split()函数用于切割string字符串&#xff0c;将切割之后的字符串放到一个std::vector<std::string> 之中&#xff1b; 有4个参数&#xff1a; 以boost::split(type, select_list, boost::is_any_of(",&…

第四周:机器学习知识点回顾

前言&#xff1a; 讲真&#xff0c;复习这块我是比较头大的&#xff0c;之前的线代、高数、概率论、西瓜书、樱花书、NG的系列课程、李宏毅李沐等等等等…那可是花了三年学习佳实践下来的&#xff0c;现在一想脑子里就剩下几个名词就觉得废柴一个了&#xff0c;朋友们有没有同感…

移植linux内核到i.MX6ULL过程

本文描述移植NXP官方 linux 5.4 内核到i.MX6ULL开发板。一、NXP官方linux内核1. 下载 NXP官方linux仓库地址为&#xff1a;https://github.com/Freescale/linux-fslc/tree/5.4-2.1.x-imx。选择该分支下载zip包即可&#xff0c;不要整个仓库下载&#xff0c;太大了&#xff1a;2…

Go语言之进阶篇http服务器获取客户端的一些信息

1、http服务器获取客户端的一些信息 示例: package mainimport ("fmt""net/http" )//w, 给客户端回复数据 //r, 读取客户端发送的数据 func HandConn(w http.ResponseWriter, r *http.Request) {fmt.Println("r.Method ", r.Method)fmt.Println…

R学习之——R用于文本挖掘(tm包)

首先需要安装并加载tm包。 1、读取文本 x readLines("222.txt") 2、建立语料库 > rCorpus(VectorSource(x))> rA corpus with 7012 text documents 3、语料库输出&#xff0c;保存到硬盘 > writeCorpus(r) 4、查看语料库 > print(r) A corpus with 7012…

学了STM32要继续学习Linux吗?

关注我的读者中&#xff0c;有很大一部分是单片机&#xff08;STM32&#xff09;的开发者&#xff0c;经常看到有人问类似的问题&#xff1a;学了STM32要继续学习Linux吗&#xff1f;每个人的情况不同&#xff0c;到底要不要学习Linux&#xff0c;要结合自身的情况。有的人已经…

模板函数与特化函数

本文转自&#xff1a;https://www.cnblogs.com/dracohan/p/3401660.html 转来收藏以便查阅&#xff0c;感谢原作者 今天在写代码时&#xff0c;遇到了模板和特化&#xff0c;在网上找了资料后问题呗一一解决&#xff0c;转载此文用于以后查阅&#xff0c;感谢原创者。其中增加…

这样调试内核启动流程

内核生命周期uboot 打印完 Starting kernel . . .&#xff0c;就完成了自己的使命&#xff0c;控制权便交给了 kernel 的第一条指令&#xff0c;也就是下面这个函数init/main.casmlinkage __visible void __init start_kernel(void){...rest_init();}start_kernel 相当于内核的…

ios 图片添加阴影

2019独角兽企业重金招聘Python工程师标准>>> UIimageView *imageView [[UIImageView alloc ] init]; imageView.layer.shadowColor [UIColor blackColor].CGColor; imageView.layer.shadowOffset CGSizeMake(3,2); imageView.layer.shadowOpacity 0.6; imageVie…

asp.net定时执行任务-解决应用池回收问题----转载

在复杂的业务应用程序中&#xff0c;有时候会要求一个或者多个任务在一定的时间或者一定的时间间隔内计划进行&#xff0c;比如定时备份或同步数据库&#xff0c;定时发送电子邮件&#xff0c;定期处理用户状态信息&#xff0c;支付系统中定期同步异常账单等等&#xff0c;我们…

bool与string互转

今天在工作中遇到了将string转换成bool类型数据&#xff0c;查阅了工具书解决了问题&#xff0c;现将注意要点总结如下&#xff1a; 增加头文件&#xff1a;#include <sstream> 代码如下&#xff1a; 在codeblocks软件上测试结果如下&#xff1a; 使用者需要根据自己的实…

Go语言之高级篇beego框架之参数配置与路由配置

一、参数配置 beego默认会解析当前应用下的conf/app.conf文件 1.1、beego的参数配置 appname WEB httpport 8080 runmode dev 几种开发模式 [dev] httpprot 8080 [prod] httpport 8081 [test] httpport 8082 //备注&#xff1a; beego.AppConfig.String( "dev::m…