K近邻算法学习(KNN)
K近邻算法——KNN
- 机器学习——K近邻算法(KNN)
- 基本知识点
- 基本原理
- 示例
- 关于KNN的基本问题
- 距离如何计算?
- k如何定义大小?
- k为为什么不定义一个偶数?
- KNN的优缺点
- 代码实现
- 第一次写(2022.10.25)
- 增强(2022.10.28)
- 结果分析
机器学习——K近邻算法(KNN)
基本知识点
基本原理
给定测试样本,基于某种距离度量找出训练集中与其最靠近的k个训练样本,然后基于这k个“邻居”的信息进行预测。
——周志华,西瓜书
存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一个数据与所属分类的对应关系。输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本几种特征最相似数据(最近邻)的分类标签(前k个)。 ——机器学习实战
自我理解:也就是说,有一堆做好标注的训练集样本,然后你抛一个样本进行预测,通过待预测样本最近得k个训练样本点得标注情况来判断要预测得样本属于那个类别。
示例
看完原理,对于这个KNN算法你应该有了基本的了解了,所以看看下面的示例吧!(类型模式来自于课本,数据是自己编写的,只是为了理解算法!)
每部电影的打斗镜头数、接吻镜头数以及电影评估类型
电影名称 | 打斗镜头 | 接吻镜头 | 电影类型 |
---|---|---|---|
爱在黎明破晓前 | 3 | 104 | 爱情片 |
怦然心动 | 2 | 100 | 爱情片 |
侧耳倾听 | 1 | 81 | 爱情片 |
罗小黑战记 | 101 | 5 | 动作片 |
集结号 | 99 | 2 | 动作片 |
末日之战 | 98 | 2 | 动作片 |
? | 18 | 90 | 未知 |
从上表,我们前面已知的6部信息可以构建如下坐标图:
然后通过距离公式来计算k个与“ ? ”最近的点,通过这k个点来判断“ ? ”的电影类型。很显然通过与他最近的k个点可以确定它是爱情片。
接下来通过图形来进行理解,如下:
橙色的正方形和蓝色的三角形是我们已经训练好的结果,而绿色的圆形是我们需要进行预测的样本,从图上我们可以发现有两个⭕,这是用来探测待测样本与训练样本距离的最小距离圆(我自己说的)。可以发现k=1和k=3,获得的结果是不一样的,k=1时,预测结果时正方形,而k=3时,预测结果应该为三角形。我们发现,不同的k对我们的预测结果的影响很大,那么这个k的取值应该如何取值呢?通过上表也很容易发现,k为啥都是基数,为啥不定义一个偶数呢?
关于KNN的基本问题
距离如何计算?
当我看到这个算法的时候,我第一时间想到的是,这个算法的最短距离怎么算嘞?一想到的就是一篇空白,距离是怎么算的,这不是用眼睛看的吗?(然后我发现,我真的老了,啥也不会了)
欧式距离:两点之间的直线距离
公式:
当然,利用的这个公式的话,需要计算待测样本与每一个训练样本之间的距离,然后进行筛选留下最下的k个样本,通过k个样本的标签来判断待测样本的预测结果。
曼哈顿距离:又成为城市街区范围。两个点在坐标轴上的绝对轴距总和。
公式:
这个比较适合那些维度比较高(特征比较多)的预测分类。
以上最多的是用欧式距离,毕竟简单,直接,最重要的是我们都理解!
说一下我比较喜欢的方法:
直接以待测样本的点作为圆的中心点,然后确定一个最小半径,逐步扩大半径,直到我们圈内的训练样本数>=k时结束,然后根据通过圈内的训练样本数来判断待测样本的预测类型。
k如何定义大小?
通过示例中的正方形和三角形案例,我们可知不同取值的k所造成的影响是不同的,它的泛化能力是比较差的,毕竟它相对于其他的算法而言,没有一个学习(训练)的过程。
k值 | 影响 |
---|---|
过大 | 预测标签稳定,过于平塌,分类模糊,对于远邻的样本也会起作用 |
过小 | 容易造成过拟合,对近邻的样本点过于敏感 |
网络上的结果是:通过交叉验证不断尝试最优的K值,从选取一个较小的K值开始,不断增加K的值,然后计算验证集合的方差,最终找到一个比较合适的K值。
k为为什么不定义一个偶数?
为什么不定义偶数,完全就是为了避免纠结。KNN中的训练样本没有既不也不的情况,不是这个就是那个,是确定的!定义奇数,那么就不可能出现平票的结果。(当然,这里说的是二分类!其余分类需要进行对k进行设计,比如三分类可以用4,7…,总之就是为了避免相对的情况)
KNN的优缺点
我们先看一下KNN的一般流程,如下:
- 收集数据:任何正当手段
- 准备数据:结构化数据格式,就是二分类中的训练样本在坐标中的点,要确定x、y,以及训练样本的(x,y)
- 分析数据:任何正当手段
- 训练算法:不适用!所以——无
- 测试算法:计算错误率
- 使用算法:首先输入样本数据和结构化的输出结果,运行knn算法确定输入样本属于那个分类,然后处理。
优点 | 缺点 |
---|---|
精度高 | 没有训练过程 |
对异常值不敏感 | 计算复杂度高 |
无数据输入假定 | 空间复杂度高 |
代码实现
第一次写(2022.10.25)
数据收集、处理,代码撰写,请往下看:
从百度地图截取下集美大学校本部以及附近周边的区域,通过下图进行数据的划分,分为两部分,一部分为jmu校区内的数据样本,标签我们定义为jmu,一部分为集美大学校区外的数据样本,我们定义它的标签为unjmu,通过它们的横纵坐标来判别是否在jmu校本部内,还是在jmu校本部外。
训练集:
选取地图的建筑物 | 自定义的位置信息 | label |
---|---|---|
禹州 | (3,85) | jmu |
尚大 | (15,70) | jmu |
陆大 | (7,58) | jmu |
吕振万 | (17,62) | jmu |
亚朵酒店 | (33,28) | unjmu |
嘉庚图书馆 | (30,100) | jmu |
万达 | (10,10) | unjmu |
周麻婆 | (2,1) | unjmu |
鑫捷汽车维修 | (45,31) | unjmu |
集美区政府 | (50,40) | unjmu |
广厦花园 | (53,55) | unjmu |
集美广电 | (60,58) | unjmu |
地震局 | (52,15) | unjmu |
测试集:
位置 | label |
---|---|
(5, 7) | unjmu |
(10,100) | jmu |
(49,49) | jmu |
(35,40 ) | unjmu |
话不多说,贴代码了:
import matplotlib.pyplot as plt
import numpy as np
import math
class KNN:def __init__(self, x_train, x_test, k):# 保留测试点与所以训练样本的距离self.distance = np.zeros((len(x_test), len(x_train)))# 保留预测结果self.predicted = []# KNN中k的取值(不懂看上面基本知识点)self.k = k# KNN核心算法def knn(self, x_test, x_train, y_train):print(y_train)for i in range(len(x_test)):for j in range(len(x_train)):self.distance[i][j] = self.knn_distance(x_test[i], x_train[j])self.predicted.append(self.knn_predicted(self.distance[i], y_train))return self.predicted# 利用欧拉公式计算距离def knn_distance(self, x1, x2):dis = math.sqrt(math.pow((x1[0]-x2[0]),2) + math.pow((x1[1]-x2[1]),2))return disdef knn_predicted(self, distances, y_train):#利用numpy的argsort方法获取前K小样本的索引k_predicted_index = distances.argsort()[:self.k]# 由于对一些库的函数学习不深,所以选择下面我自己可以实现的方法count_jmu = 0count_other =0for i in range(len(k_predicted_index)):if(y_train[k_predicted_index[i]] == 'jmu'):count_jmu += 1else:count_other += 1if(count_jmu > count_other):return 'jmu'else:return 'unjmu'
# 自定义训练数据集
x_train = [[3, 85], [15, 70], [7, 58], [17,62], [33,28], [30,100], [10,10], [2,1], [45,31], [50,40], [53,55], [60,58], [52,15]]
y_train = ['jmu', 'jmu', 'jmu', 'jmu', 'unjmu','jmu' ,'unjmu' ,'unjmu' ,'unjmu' ,'unjmu' ,'unjmu' ,'unjmu' ,'unjmu']# 自定义测试数据集
x_test = [[5,7], [10,100], [19,49], [35,40]]
y_test = ['unjum','jum','unjum','jum']# 设置KNN中的k
k = 3
knn = KNN(x_train, x_test, k)# 获得测试集的预测结果
pred = knn.knn(x_test, x_train, y_train)
print(pred)
输出结果展示:
增强(2022.10.28)
数据集:
链接:https://pan.baidu.com/s/1yrDGiK9yXFxB_JyC3Q5ycg
提取码:1234
如果你觉得上面的描述或者代码不够清晰,请看这里,对于上述的代码,如果想要改变数据集好像很困难,而且变化不大,不易于修改,所以进行了一定的精炼,请看下面:
代码:
首先,对于python来说,典型的黑盒子,我们需要导入我们所需方法的库进行调用。
import matplotlib.pyplot as plt
import numpy as np
import math
import pandas as pd
from sklearn.model_selection import train_test_split
然后,根据KNN的算法思想进行编写KNN主体函数
class KNN:def __init__(self, x_train, x_test, k):# 保存距离self.distance = np.zeros((len(x_test), len(x_train)))# 预测结果self.predicted = []# knn中的k值self.k = k # knn的主要函数 def knn(self, x_test, x_train, y_train):for i in range(len(x_test)):for j in range(len(x_train)):self.distance[i][j] = self.knn_distance(x_test[i], x_train[j])self.predicted.append(self.knn_predicted(self.distance[i], y_train))return self.predicted# 欧式距离的计算def knn_distance(self, x1, x2):dis = math.sqrt(math.pow((x1[0]-x2[0]),2) + math.pow((x1[1]-x2[1]),2))return dis# 预测knn函数def knn_predicted(self, distances, y_train):k_predicted_index = distances.argsort()[:self.k]count_jmu = 0count_other =0for i in range(len(k_predicted_index)):if(y_train[k_predicted_index[i]] == 'jmu'):count_jmu += 1else:count_other += 1if(count_jmu > count_other):return 'jmu'else:return 'unjmu'
通过绘制测试集和训练集的样本分布来视觉上查看预测结果
# 绘图(看数据集分布)
def paint(x_train, x_test):
# 绘制图像, X、Y是存储unjmu的数据,X1、Y1存储的是jmu的数据,Z是用于过渡X = []X1 = []X2 = []Y = []Y1 = []X2 = []Z = []# 根据训练样本获取x、yx_train = np.array( x_train)X = x_train[:,0]Y = x_train[:,1]# 对数据进行处理,根据训练集的数据以及label划分出jmu的点和unjum的点for i in range(len(y_train)):if(y_train[i] == 'jmu'):Z.append(i)X1.append(X[i])Y1.append(Y[i])X = np.delete(X,Z)Y = np.delete(Y,Z)# 绘制测试集的数据准备x_test = np.array(x_test)X2 = x_test[:,0]Y2 = x_test[:,1] # 绘图,红色为jmu的数据,绿色是unjmu数据,蓝色为测试样本plt.scatter(X, Y, color = 'g')plt.scatter(X1, Y1, color ='r')plt.scatter(X2, Y2, color ='b')
# 数据处理,将csv获得的数据变成列表
def data_tolist(x_train, x_test, y_train, y_test):x_train = np.array(x_train)x_train = x_train.tolist()y_train = np.array(y_train)y_train = y_train.tolist()x_test = np.array(x_test)x_test = x_test.tolist()y_test = np.array(y_test)y_test = y_test.tolist()return x_train, x_test, y_train, y_test
# 计算精确度
def predicted(pred, y_test):count = 0for i in range(len(pred)):if(y_test[i] == pred[i]):count += 1pred1 = count / len(y_test)return pred1
# 利用panda库进行对csv文件的读取和处理操作
data=pd.read_csv("D:/桌面/1.csv")
X = data.iloc[:,:2]
Y = data.iloc[:,2]# 划分数据集,并且将数据集转换成list类型,0.8的训练集,0.2的测试集
x_train, x_test, y_train, y_test = train_test_split(X, Y, test_size=0.2)x_train, x_test, y_train, y_test = data_tolist(x_train, x_test, y_train, y_test)paint(x_train, x_test)for i in range(len(x_train)):if (i%2 != 0):k = iknn = KNN(x_train, x_test, k)pred = knn.knn(x_test, x_train, y_train)print(y_test)print(f"预测结果:{pred}")predicte = predicted(pred, y_test)print(f"k = {k}时,测试精度为:{predicte}")
注:红色定义为jmu样本,蓝色为待遇测样本,绿色为unjmu样本
结果分析
以上面增强代码和运行结果进行分析,去十次结果(理应进行对k=0,到k=len(x_train)进行分析),之所以取10十因为k取值越大,其实结果过于模糊,说白了k越大,等于比较数据集那个label的样本数更多了。
k = ? | predicate |
---|---|
1 | 1 |
3 | 0.83333 |
5 | 1 |
7 | 1 |
9 | 1 |
11 | 0.83333 |
13 | 0.83333 |
15 | 0.83333 |
21 | 0.66666 |
23 | 0.66666 |
从上表看:貌似k取越小越好,k越大预测的精度就越差了,这是为什么呢?难道k真的取值越小越好吗?
首先来说第一个问题:
k越大精度就越差,为什么呢?
首先,先分析一下我的数据集,我的数据集中label为unjmu的样本和jmu的样本数量上是不匹配的,unjmu的样本明显大于jmu,那么在k取值越大的情况下unjmu的样本就会在那些label标签为jmu中的作用越大,导致将label将jmu样本预测成unjmu。所以说,当k大于一定的值时,预测结果和样本数据集标签种类的数量关系会被放大。
再说一下第二个问题:
k取越小越好吗?
看下图:
框中待预测的样本为unjmu,但是与他最近的标签是人为标注错误的标签,如果k越小越好,那么理应取k=1(最近邻),可是这样的话很大程度需要样本0错误,但凡出现了一个错误标签都可能导致预测结果出现错误,而人工标注的数据集想要实现0错误是很难的。(就像我刚开始编写数据的时候,出现了label撰写错误的情况)。所以说k的取值也不是越小越好。
综上:那么k应该如何定义大小呢。在上文对于KNN的基本问题的提出中,提到了交叉验证的方法,大家可以尝试一下。我个人认为k的取值主要与一下几个方面有关:
- 数据集的大小。(太大,则k不能取太小的值,否则过拟合严重;太小,k不能取大值,否则模糊性太强)
- 样本标签种类。(种类多了,那么出现标注错误的可能性就大了)
- 样本的数据维度。(不同维度最好取不同的距离计算公式,计算方法不同,则k值得选取也需要进行调控)
K近邻算法学习(KNN)相关推荐
- 09_分类算法--k近邻算法(KNN)、案例、欧氏距离、k-近邻算法API、KNeighborsClassifier、及其里面的案例(网络资料+学习资料整理笔记)
1 分类算法–k近邻算法(KNN) 定义:如果一个样本在特征空间中**k个最相似(即特征空间中最邻近)**的样本中的大多数属于某一个类别,则该样本也属于这个类别,则该样本也属于这个类别. k-近邻算法 ...
- 机器学习——K近邻算法(KNN)(K Nearest Neighbor)
参考视频与文献: python与人工智能-KNN算法实现_哔哩哔哩_bilibili 机器学习--K近邻算法(KNN)及其python实现_清泉_流响的博客-CSDN博客_python实现knn 机器 ...
- python机器学习 | K近邻算法学习(1)
K近邻算法学习 1 K近邻算法介绍 1.1算法定义 1.2算法原理 1.3算法讨论 1.3.1 K值选择 1.3.2距离计算 1.3.3 KD树 2 K近邻算法实现 2.1scikit-learn工具 ...
- 1. K近邻算法(KNN)
1. K近邻算法(KNN) 2. KNN和KdTree算法实现 1. 前言 K近邻法(k-nearest neighbors,KNN)是一种很基本的机器学习方法了,在我们平常的生活中也会不自主的应用, ...
- 机器学习:k近邻算法(KNN)介绍
k近邻算法是一种最简单最经典的机器学习算法之一.该算法的原理为:当对测试样本进行分类时,首先通过扫描训练样本集,找到与该测试样本最相似的k个训练样本,根据这个样本的类别进行投票确定测试样本的类别.也可 ...
- 机器学习算法——系统性的学会使用 K近邻算法(KNN)
目录 1.K-近邻算法简介 1.1 什么是K-近邻算法 1.2 K-近邻算法(KNN)概念 (1)定义: (2)距离公式: 1.3 电影类型分析 1.4 KNN算法流程总结 2.k近邻算法api初步使 ...
- 机器学习:K近邻算法(K-NN)
K近邻(K-Nearest Neighbor, KNN)是一种最经典和最简单的有监督学习方法之一,它非常有效而且易于掌握. 1 K近邻算法概述 一个样本与数据集中的k个样本最相似, 如果这k个样本中的 ...
- 机器学习--K近邻算法(KNN)(2)
一.简介 K-Nearest-Neighbor 算法是一种常用的监督学习算法,它没有显式的训练过程,是'懒惰学习'的显著代表,此类学习算法仅在训练阶段将训练集保存起来,训练时间开销为0,待收到测试样本 ...
- 机器学习——K近邻算法(KNN)及其python实现
参考视频与文献: https://www.bilibili.com/video/BV1HX4y137TN/?spm_id_from=333.788&vd_source=77c874a500ef ...
最新文章
- cass插件_南方CASS专题系列,全套教程+视频讲解+插件汇总,全部打包速带走
- 笔记-知识产权与标准化知识-GB/T16260-2006软件工程产品质量-质量模型
- 移动端https抓包那些事--进阶篇
- 富二代谈起中国做技术的地位(转载)
- 你是信用卡卡奴吗?怎么摆脱卡奴?
- 端口隔离配置命令、端口镜像(抓包配置)详解(附图,建议PC观看)
- catia 版本_KeyShot软件各版本对比详细信息!
- 如何绘制平台框架的设计图:使用UML工具
- Swift 拷贝文件夹,实现文件夹内容整体复制
- oracle rac的特征
- IOS逆向分析—终极详细(一)
- html怎样修改背景图片大小,css中如何设置背景图片的大小?
- 互联网3D数字化时代,3D产品展示开启新商机
- pandas生成日期去掉时分秒
- 团员大会如何写组织学习计算机知识,团支部召开接收新团员的支部大会的会议记录怎么写?...
- 相似度计算之(二)——余弦距离
- GitHub 使用教程 -- (1)开始使用GitHub(GitHub Guides)
- Android音频格式转换,android音频文件转换格式
- 爬虫爬取斗鱼小姐姐直播间的封面
- 打开Win2000的自动补齐功能
热门文章
- 题目:中国有句俗话叫“三天打渔,两天晒网”,某人从2010年1月1日期开始“三天打渔,两天晒网” 问这个人在以后的某一天是“打渔”还是“晒网”。用C或着C++语言实现程序解决问题。
- BMP格式补充(16bbp 32bbp 4字节对齐 pixel data存放顺序)
- CSS—移动端适配方案flexible.js
- Spring中Singleton模式的线程安全
- Htm 转换 安卓java_Android开发 Html工具类详解
- Flink janino,跟老铁又学到了
- 音频知识点(12)- P.563 工具编译及使用教程
- 元宇宙是什么,如何看待未来元宇宙的发展?
- 【每周一读】——你的孤独,虽败犹荣
- 推荐系统(三) —— 利用用户行为数据 —— 隐语义模型