KNN 原理及实现

一、KNN介绍
KNN(K- Nearest Neighbor)法即K最邻近法,最初由 Cover和Hart于1968年提出,是一个理论上比较成熟的方法,也是最简单的机器学习算法之一。该方法的思路非常简单直观:如果一个样本在特征空间中的K个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。该方法在定类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。

KNN 是一种基本的分类算法,也可以用于回归。其核心思想就是:“物以类聚,人以群分”,即认为待分类样本的类别与相邻样本的类别相关。所以,待分类样本A的类别取决于:与A距离最近的K个样本的类别。

优点:

①简单,易于理解,易于实现,无需参数估计,无需训练;

②精度高,对异常值不敏感(个别噪音数据对结果的影响不是很大);

③适合对稀有事件进行分类;

④特别适合于多分类问题(multi-modal,对象具有多个类别标签),KNN要比SVM表现要好.;

缺点:

①对测试样本分类时的计算量大,空间开销大,因为对每一个待分类的文本都要计算它到全体已知样本的距离,才能求得它的K个最近邻点。目前常用的解决方法是事先对已知样本点进行剪辑,事先去除对分类作用不大的样本;

②可解释性差,无法给出决策树那样的规则;(黑盒模型)

③最大的缺点是当样本不平衡时,如一个类的样本容量很大,而其他类样本容量很小时,有可能导致当输入一个新样本时,该样本的K个邻居中大容量类的样本占多数。该算法只计算“最近的”邻居样本,某一类的样本数量很大,那么或者这类样本并不接近目标样本,或者这类样本很靠近目标样本。无论怎样,数量并不能影响运行结果。可以采用权值的方法(和该样本距离小的邻居权值大)来改进;

④消极学习方法。
————————————————
版权声明:本文为CSDN博主「义山李公子」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_40637396/article/details/103002719

二、KNN算法流程
1、计算距离
样本在特征空间中的分布是用特征向量描述的,因此可以使用向量之间的距离来衡量样本之间的相似度。两个向量之间距离的计算,在数学上称为向量距离。常见的向量距离计算公式有:

欧式距离,欧式空间中两点的距离
曼哈顿距离,n维向量各维度绝对值之和
闵可夫斯基距离
汉明距离
选定距离公式后,计算待测样本与数据集中的每一个样本的距离。

2、升序排序
根据计算好的距离,进行升序排序。与待测样本距离更近的样本在前,远的在后。

3、取前K样本
根据距离进行升序排序后,选取距离最近的前K个样本。

4、加权平均
由于距离不同的样本代表的相似度也不一样,所以对于不同距离的样本需要按照距离进行加权计算。一般来说,距离越近,代表相似度越高;距离越远,代表相似度越小。可见距离与加权值应该成反比,所以一般设定加权值为:1-d/sum。d代表当前样本与待测样本的距离,sum表示所有样本与待测样本距离的总和。

举个例子:判断某一样本是苹果还是梨子。现在设定K=4,并计算到待测样本A最近的4个样本有2个苹果和2个梨子,距离d分别为:2、2、4、4。所以sum=2+2+4+4=10,因此:

苹果的加权值为:(1-2/10) + (1-2/10) = 1.6
梨子的加权值为:(1-4/10) + (1-4/10) = 1.2
可见待测样本A与苹果的相似度更高,因此待测样本A的预测类别是苹果。

当然,在有的问题中,没有精确考虑距离远近的因素。而是直接统计K邻域内,样本中出现最多次数的类别作为该样本的类别。

三、影响因素
一般影响KNN分类效果最明显的两个因素是:距离公式的选取和K值的选取。

一般来说,K值太小,受个例的影响,结果波动较大;K值太大,可能会导致分类模糊。

选取K的方法:一般经过经验选择;也可以将K值视为KNN的模型参数,并根据训练和验证集的误差分析选择最佳K值,毕竟最小化误差是我们的最终目标。

四、findNearst函数
float cv::ml::KNearest::findNearest(InputArray sample, //测试样本
                                    int k, //最邻近个数
                                    OutputArray results, 
                                    OutputArray neighborResponses = noArray(), 
                                    OutputArray dist = noArray() //从输入向量到相应邻近点的距离
                                    )const
1

五、KNN数字分类实战
本实战,采用OpenCV中的KNN对小型的手写数字数据集MINIST进行分类,MINIST中包含若干0~9的手写数字图像,如下图所示:


整个实战过程需要从如下几个步骤来完成:

1、读取原始数据图像

Mat img = imread("data.png", 1); // 使用图片格式的MNIST数据集(部分)
cvtColor(img, img, COLOR_BGR2GRAY);
1
2
2、制作数据集

int train_sample_count = 4000; // 设置训练集大小
int test_sample_count = 1000; // 设置测试集大小
int train_rows = 4; // 每类用于训练的行数,4000/10类/100(样本/行)=4
Mat trainData, testData; // 声明训练集与测试集
Mat trainLabel(train_sample_count, 1, CV_32FC1); // 声明训练集标签
Mat testLabel(test_sample_count, 1, CV_32FC1);   // 声明测试集标签
generateDataSet(img, trainData, testData, trainLabel, testLabel/*, train_rows*/); // 生成训练集、测试集与标签


3、创建并初始化KNN模型

cv::Ptr<cv::ml::KNearest> knn = cv::ml::KNearest::create(); // 创建knn模型
int K = 8; // 考察的最邻近样本个数
knn->setDefaultK(K);
knn->setIsClassifier(true); // 用于分类
knn->setAlgorithmType(cv::ml::KNearest::BRUTE_FORCE);
1
2

4、训练

knn->train(trainData, cv::ml::ROW_SAMPLE, trainLabel);
1
5、测试

Mat result;
knn->findNearest(testData, K, result);
// 计算分类精度
int count = 0;
for (int i = 0; i < test_sample_count; i++)
{
    int predict = int(result.at<float>(i));
    int actual = int(testLabel.at<float>(i));        
    if (predict == actual)
    {
        printf("label: %d, predict: %d\n", actual, predict);
        count++;            
    }
    else
        printf("label: %d, predict: %d ×\n", actual, predict);
}
double accuracy = double(count) / double(test_sample_count);
printf("K = %d, accuracy = %.4f\n", K, accuracy);

1
2
6、分类精确度

构建工程后,运行程序,可以输出选择的K值,以及对应的分类精确度


六、KNN的优缺点
KNN方法的一些优点:

核心思路简单,理论成熟,既可以用来做分类也可以用来做回归
可以用于非线性分类
训练时间复杂度为O(n)
准确度高,对数据没有假设,对离群值不敏感
KNN方法的一些不足之处:

计算量较大,因为对每一个待分类的文本都要计算它到全体已知样本的距离,才能求得它的K个最邻近点。
对于样本分类不均衡的问题,会产生误判
需要大量的内存
针对KNN计算量较大的问题,目前常用的解决方法是事先对已知样本点进行剪辑,事先去除对分类作用不大的样本。另外还有一种 Reverse KNN法,它能降低KNN算法的计算复杂度,提高分类的效率。

总的来说,KNN算法比较适用于样本容量比较大的类域的自动分类,而那些样本容量较小的类域采用这种算法比较容易产生误分。

————————————————
版权声明:本文为CSDN博主「AI 菌」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wjinjie/article/details/120675357

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值