OpenCV4机器学习:KNN 原理及实现
Posted AI 菌
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCV4机器学习:KNN 原理及实现相关的知识,希望对你有一定的参考价值。
前言:
本专栏主要结合OpenCV4,来实现一些基本的图像处理操作、经典的机器学习算法(比如K-Means、KNN、SVM、决策树、贝叶斯分类器等),以及常用的深度学习算法。
系列文章,持续更新:
- OpenCV4机器学习(一):OpenCV4+VS2017环境搭建与配置
- OpenCV4机器学习(二):图像的读取、显示与存储
- OpenCV4机器学习(三):颜色空间(RGB、HSI、HSV、Lab、Gray)之间的转换
- OpenCV4机器学习(四):图像的几何变换、仿射变换
- OpenCV4机器学习(五):标注文字和矩形框
- OpenCV4机器学习(六):K-means原理及实现
一、KNN介绍
KNN(K- Nearest Neighbor)法即K最邻近法,最初由 Cover和Hart于1968年提出,是一个理论上比较成熟的方法,也是最简单的机器学习算法之一。该方法的思路非常简单直观:如果一个样本在特征空间中的K个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。该方法在定类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。
KNN 是一种基本的分类算法,也可以用于回归。其核心思想就是:“物以类聚,人以群分”,即认为待分类样本的类别与相邻样本的类别相关。所以,待分类样本A的类别取决于:与A距离最近的K个样本的类别。
二、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的方法:一般经过经验选择,或者根据均方根误差进行选择。
四、findNearst函数
float cv::ml::KNearest::findNearest(InputArray sample, //测试样本
int k, //最邻近个数
OutputArray results,
OutputArray neighborResponses = noArray(),
OutputArray dist = noArray() //从输入向量到相应邻近点的距离
)const
五、KNN数字分类实战
本实战,采用OpenCV中的KNN对小型的手写数字数据集MINIST进行分类,MINIST中包含若干0~9的手写数字图像,如下图所示:
整个实战过程需要从如下几个步骤来完成:
1、读取原始数据图像
Mat img = imread("data.png", 1); // 使用图片格式的MNIST数据集(部分)
cvtColor(img, img, COLOR_BGR2GRAY);
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);
4、训练
knn->train(trainData, cv::ml::ROW_SAMPLE, trainLabel);
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);
6、分类精确度
构建工程后,运行程序,可以输出选择的K值,以及对应的分类精确度
六、KNN的优缺点
KNN方法的一些优点:
- 核心思路简单,理论成熟,既可以用来做分类也可以用来做回归
- 可以用于非线性分类
- 训练时间复杂度为O(n)
- 准确度高,对数据没有假设,对离群值不敏感
KNN方法的一些不足之处:
- 计算量较大,因为对每一个待分类的文本都要计算它到全体已知样本的距离,才能求得它的K个最邻近点。
- 对于样本分类不均衡的问题,会产生误判
- 需要大量的内存
针对KNN计算量较大的问题,目前常用的解决方法是事先对已知样本点进行剪辑,事先去除对分类作用不大的样本。另外还有一种 Reverse KNN法,它能降低KNN算法的计算复杂度,提高分类的效率。
总的来说,KNN算法比较适用于样本容量比较大的类域的自动分类,而那些样本容量较小的类域采用这种算法比较容易产生误分。
本专栏所有完整的代码将在我的GitHub仓库上更新,欢迎大家前往学习:
进入GitHub仓库,点击 star (红色箭头所示),第一时间获取干货:
最好的关系是互相成就,各位的「三连」就是【AI 菌】创作的最大动力,我们下期见!
以上是关于OpenCV4机器学习:KNN 原理及实现的主要内容,如果未能解决你的问题,请参考以下文章