【初中生讲机器学习】8. KNN 算法原理 实践一篇讲清!

创建时间:2024-02-11
最后编辑时间:2024-02-12
作者:Geeker_LStar

你好呀~这里是 Geeker_LStar 的人工智能学习专栏,很高兴遇见你~
我是 Geeker_LStar,一名初三学生,热爱计算机和数学,我们一起加油~! ⭐(●’◡’●) ⭐ 那就让我们开始吧!

前两篇在讲模型评价标准 & 交叉验证,没讲算法,这一篇我们来讲一个新的算法——KNN(K 近邻)算法
KNN 被称为 “最简单的分类算法”,理论方面没有太多需要讲的,so 这一篇会把理论 & 例子都讲完!

文章目录

  • 一、理论
    • 距离的计算
      • 欧氏距离
      • 余弦距离
      • 曼哈顿距离
      • 切比雪夫距离
      • 闵可夫斯基距离
    • K 值的选取
  • 二、代码实现
    • 手搓算法原理
    • sklearn 实例

一、理论

KNN 算法,中文 K 近邻算法,大致思路就是:对于一个待分类的样本,看看和它距离最近的 K 个样本都属于什么分类,这 K 个样本中出现最多的分类就是这个待分类样本最可能的分类。

这涉及到两个问题:

  1. 距离怎么计算
  2. K 值怎么选取

距离的计算

先来看第一个。
因为对于带有标签的数据来说,在被送到模型去训练以前,所有的标签都已经被转化成数字(n 维向量,n 个数字)的形式了,这大大方便了距离的计算。

一般而言,计算距离的方式主要有以下几种:

  • 欧式距离
  • 余弦距离
  • 曼哈顿距离
  • 切比雪夫距离
  • 闵可夫斯基距离

欧氏距离

欧式距离即欧几里得距离,二维空间中的形式你肯定见过,就是两点之间距离公式嘛。
二维:
d = ( x 1 − x 2 ) 2 + ( y 1 − y 2 ) 2 d = \sqrt{(x_{1}-x_{2})^{2}+(y_{1}-y_{2})^{2}} d=(x1x2)2+(y1y2)2
ok 推广到 n 维,用 Σ \Sigma Σ 变形一下:
d = ∑ k = 1 n ( x 1 k − x 2 k ) 2 d = \sqrt{\sum_{k=1}^{n}(x_{1k}-x_{2k})^2} d=k=1n(x1kx2k)2

ok 很简单的一种距离公式,不过它的使用(牵扯到 knn 的使用)受到维数限制,当维度过高(有说法说超过 10 维)时,欧氏距离算出的最大值和最小值会无限接近,导致这种方法失效,这种情况也被称作 “维数灾难”
不过先别担心,有降维算法比如 PCA 可以帮助我们,不过那就是后话了。

余弦距离

嗯作为假期刚学完必修二向量的我,这题我来()

必修二课本告诉我们,向量的数量积 A ⋅ B = ∣ A ∣ × ∣ B ∣ × c o s θ A · B = |A|×|B|×cos\theta AB=A×B×cosθ
那么 c o s θ = A ⋅ B ∣ A ∣ ∣ B ∣ cos\theta = \frac{A·B}{|A||B|} cosθ=A∣∣BAB,化成坐标表示就是:
c o s θ = x 1 x 2 + y 1 y 2 x 1 2 + y 1 2 + x 2 2 + y 2 2 cos \theta = \frac{x_{1}x_{2}+y_{1}y_{2}}{\sqrt{x_{1}^{2}+y_{1}^{2}}+\sqrt{x_{2}^{2}+y_{2}^{2}}} cosθ=x12+y12 +x22+y22 x1x2+y1y2
c o s θ cos\theta cosθ 的取值范围在 [ − 1 , 1 ] [-1, 1] [1,1] 之间。当两个向量方向相同时, c o s θ cos \theta cosθ 取到最大值 1,两个向量方向相反时, c o s θ cos \theta cosθ 取到最小值 -1。
为了符合人们 “数字大距离远” 的习惯,常常会用 1 − c o s θ 1-cos \theta 1cosθ 表示余弦距离, 1 − c o s θ 1-cos \theta 1cosθ 的取值在 [ 0 , 2 ] [0, 2] [0,2] 之间,数字越大表示两个向量的方向越不相似。

不过,余弦距离也有局限性,它只关注向量之间的夹角,而忽略了向量模长的影响。在一些分类任务中,可能两个向量方向相同,但它们的模长相距比较远,并不是同一类的。此时如果使用余弦距离作为分类依据,准确率会较低。
同时,余弦距离和欧氏距离一样,也会受到 “维数灾难” 的影响。

曼哈顿距离

曼哈顿距离也好理解,比如平面上有两个点 ( x 1 , y 1 ) , ( x 2 , y 2 ) (x_{1}, y_{1}), (x_{2}, y_{2}) (x1,y1),(x2,y2),它们之间的距离就是它们横坐标的差的绝对值与纵坐标差的绝对值的和,类似于直角三角形两条直角边的长度之和,即: d = ∣ x 1 − x 2 ∣ + ∣ x 2 − y 2 ∣ d = |x_{1}-x_{2}|+|x_{2}-y_{2}| d=x1x2+x2y2

推广到 n 维空间,用 Σ \Sigma Σ 表示,有:
d = ∑ k = 1 n ∣ x 1 k − x 2 k ∣ d = \sum_{k = 1}^{n}|x_{1k}-x_{2k}| d=k=1nx1kx2k

曼哈顿距离的使用场景有限,knn 不怎么用它。

切比雪夫距离

emmm 这名字怎么越来越长了()不过没事,它也很简单。
切比雪夫距离是指两个点的各组坐标(x、y、z 等)的差值的绝对值的最大值,别看定语这么多,其实在二维空间中就是 d = m a x ( ∣ x 1 − x 2 ∣ , ∣ y 1 − y 2 ∣ ) d = max(|x_{1}-x_{2}|, |y_{1}-y_{2}|) d=max(x1x2,y1y2),也就是说 x 坐标差得远就选 x 坐标的差值,y 坐标差得远就选 y 坐标的差值。推广到 n 维就是:
d = m a x ( ∣ x 1 i − x 2 i ∣ ) , i ∈ [ 1 , n ] d = max(|x_{1i}-x_{2i}|), i ∈[1, n] d=max(x1ix2i),i[1,n]
切比雪夫距离的使用场景有限,常用在聚类算法中,knn 不怎么用它。

不知道你发现没有,其实切比雪夫、曼哈顿、欧式之间很暧昧()它们之间的关系怎么用数学表示呢?

闵可夫斯基距离

没错!(接上问)闵可夫斯基距离就是专门干这个的,换言之,闵可夫斯基距离不是某一种距离,而是一组距离的汇总
在 n 维空间中,闵氏距离的公式是:
d = ∑ k = 1 n ∣ x 1 k − x 2 k ∣ p p d = \sqrt[p]{\sum_{k=1}^{n}|x_{1k}-x_{2k}|^p} d=pk=1nx1kx2kp

有了前面欧式、曼哈顿和切比雪夫距离的铺垫,这个公式应该不难理解,同时不难发现:

  • p = 1 时,闵氏距离就是曼哈顿距离
  • p = 2 时,闵氏距离就是欧氏距离
  • p -> +∞ 时,闵氏就是切比雪夫距离(好吧这个挺难发现的,不过可以用放缩法证明)

这是在 p 取不同值的时候,等距离的点组成的图像,还是蛮形象的。

不同的 p 值

讲一下闵氏距离的局限性吧,因为它是其它三个的汇总,所以它的局限性也能反映其它三个的局限性。
来看一个例子,现在有三个人的身高体重数据,A(160, 50),B(170, 50),C(160, 60),不管用闵氏三种(欧式、曼哈顿、切比雪夫)中的哪一种,AB 的距离都等于 AC 的距离,等于 10. 但是身高 10cm 和体重 10kg 的含义并不一样,这反映出闵氏距离:

  • 忽略了不同分量的量纲(单位)不同
  • 忽略了不同分量的分布特征(方差等)
  • 各个维度必须是相互独立的,表现在几何上就是 “正交” 的

emm 跑题有点远了()距离这块就先到这吧。重点还在 knn 呢。

ok,这些就是各种度量距离的方法,没有讲全,我考虑开一篇单独的博客专门讲这些。最后,寄出神图:
真的这图实在是太形象了()要不是这篇讲 knn,我绝对把它当封面图。

距离

K 值的选取

ok,讲完怎么计算距离,K 值的选取也是个重要的话题,我们先来看看不同的 K 值会带来怎样的效果。

比如,下边这个绿色方块是要被分类的对象,K 等于 3 时,它的 3 个邻居有 2 个是蓝色三角,所以它也被分为蓝色三角。
3

但是 K = 5 时,情况发生了变化,它的 5 个邻居中有 3 个是红色圆,于是它也被分到红色圆圈了。

在这里插入图片描述

由此可见,K 值的选取很重要,K 选的太小容易过拟合,太大又容易欠拟合(如果一共就 35 个数据,K 取到 30,那 KNN 基本就没啥用了emm)。

k 值的选取一般可以采用 k 折交叉验证法(两个 k 不一样,不要混淆),大致思路是:换用不同的 k 值,采用 k 折交叉验证法看哪种 k 值下模型的平均准确率最高(或用其它评价标准下的平均性能最好),就选用这个 k 值。
关于交叉验证,详细地可以看我上一篇文章:【初中生讲机器学习】7. 交叉验证是什么?有哪些?怎么实现?来看!

如果实在选不好 k 值也可以使用默认值,sklearn 中的默认值是 5.

二、代码实现

手搓算法原理

嗯嗯嗯嗯。。。觉得 KNN 不难于是手搓了一个函数(暴力)模拟了一下它,计算用的是欧氏距离(yes sklearn 用的也是欧式)。
ok 先别笑我(()暴力甚至是 sklearn 中 knn 函数的方法之一,不过也有别的,后面讲。

# 自己实现 knn 算法代码# 导入库 & 预处理
from sklearn.datasets import load_iris    # 引入鸢尾花数据集,共 150 组数据
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_splitiris = load_iris()
X = iris.data
y = iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=3, test_size=0.3)# x:样本特征
# y:分类结果
# x_test:测试集样本特征
# k:k 值(选取最近的 k 个样本点),默认为 5
def knn(x, y, x_test, k=5):y_predict = []for sample_test in x_test:    # 取出测试集中每一个样本(n 维向量)distance = []    # 储存测试集中的一个样本和训练集中所有样本的欧氏距离for sample in x:    # 训练集中每一个样本(n 维向量)d = 0    # 临时储存距离的变量index_list = []    # 存储 k 个距离最小的元素的索引,方便看它们的分类结果for n in range(0, len(sample_test)):    # 每一个向量(一共 n 个)d += abs(sample_test[n]**2 - sample[n]**2)    # 欧氏距离distance.append(d)classification_list = []for i in range(0, k):    # 选取 k 个距离最小的元素index = distance.index(min(distance))classification_list.append(y[index])    # 记录这些元素的分类情况distance[distance.index(min(distance))] = float('inf')    # 设置为无穷大# k 个邻居的分类中出现最多的一个记作新元素的分类classification = max(set(classification_list), key=classification_list.count)y_predict.append(classification)return y_predicty_predict = knn(X_train, y_train, X_test, 3)
print("准确率:", accuracy_score(y_test, y_predict))

准确率其实还可以嘿嘿嘿,93%.

在这里插入图片描述

sklearn 实例

下面我们用 sklearn 中封装好的算法来实现同样的实例。

看一下这行代码:

knn = KNeighborsClassifier(n_neighbors=5, algorithm='auto')

n_neighbors = 5 表示选择最近的 5 个邻居。
algorithm 值得好好讲一下,你也看到了,手搓部分我是怎么实现 knn 的——纯暴力。虽然暴力比较也是 KNeighborsClassifier() 的一种实现方式,但是暴力比较会在数据维度升高和数据量增大的时候迅速暴露出弊端,so 除了暴力,sklearn 中的 KNeighborsClassifier() 额外提供了两种算法——kd 树和球树
至于在使用的时候到底选择哪种算法,可以自己设定,如果不好评估,设置为 auto,knn 会自己决定。

代码如下,详细的每一步的分析已经在第四篇(SVM 实例)中讲过了,这里不再重复。

# 利用 KNN 算法实现鸢尾花分类
# 导入数据集+函数
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_iris
from sklearn.metrics import accuracy_score
from sklearn.neighbors import KNeighborsClassifier
# 加载并预处理数据
iris = load_iris()
X = iris.data
y = iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=3, test_size=0.3)
# 训练
knn = KNeighborsClassifier(n_neighbors=5, algorithm='auto')
knn.fit(X_train, y_train)
# 测试
y_predict = knn.predict(X_test)
print("准确率:", accuracy_score(y_test, y_predict))

这个其实和手搓版没有任何区别,只是我把 k 从 3 改到 5 了,准确率上升了一点。

结果


ok~以上就是关于 KNN 算法的内容啦!这个算法比较简单,很好理解~

这篇文章讲了一些距离度量方式 and KNN 算法的原理及实现,希望对你有所帮助!⭐
欢迎三连!!一起加油!🎇
——Geeker_LStar

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

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

相关文章

Nature Machine Intelligence 使用机器学习驱动的可拉伸智能纺织手套捕捉复杂的手部动作和物体交互

研究背景 对灵巧手运动的精确实时跟踪在人机交互、元宇宙、机器人和远程医疗等领域有着广泛的应用。当前的可穿戴设备中的大多数仅用于检测精度有限的特定手势,并且没有解决与设备的可靠性、准确性和可清洗相关的挑战。对传感器直接放置在用户的手上有严格的要求&am…

第四节 zookeeper集群与分布式锁

目录 1. Zookeeper集群操作 1.1 客户端操作zk集群 1.2 模拟集群异常操作 1.3 curate客户端连接zookeeper集群 2. Zookeeper实战案例 2.1 创建项目引入依赖 2.2 获取zk客户端对象 2.3 常用API 2.4 客户端向服务端写入数据流程 2.5 服务器动态上下线、客户端动态监听 2…

JUC-并发面试题

一、基础 1、为什么要并发编程 充分利用多核CPU的资源2、并发编程存在的问题 上下文切换:PU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再加载这个任务的状态。任务从保…

一个三极管引脚识别的小技巧,再也不用对照手册啦

三极管是一个非常常用的器件,时不时的就需要用到他们,有些时候当我们拿到一颗三极管时 ,对于常用的友来说,三极管的引脚可能早已烂熟于心,而对于不常用或者初学者来说,三极管的引脚可以说是今天记下明天忘&…

Pytorch卷积层原理和示例 nn.Conv1d卷积 nn.Conv2d卷积

内容列表 一,前提 二,卷积层原理 1.概念 2.作用 3. 卷积过程 三,nn.conv1d 1,函数定义: 2, 参数说明: 3,代码: 4, 分析计算过程 四,nn.conv2d 1, 函数定义 2, 参数: 3, 代码 4, 分析计算过程 …

面了滴滴的数据分析师(实习),几道面试题都是原题啊。。。

年前,技术群组织了一场数据类的技术&面试讨论会,邀请了一些同学分享他们的面试经历,讨论会会定期召开,如果你想加入我们的讨论群或者希望要更详细的资料,文末加入。 喜欢本文记得收藏、关注、点赞 。技术交流文末…

C++之继承

一,概念及用法 1)概念 首先我们来了解一下官方的概念:继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类&a…

leetcode:131.分割回文串

树形结构: 切割到字符串的尾部,就是叶子节点。 回溯算法三部曲: 1.递归的参数和返回值: 参数字符串s和startIndex切割线 2.确定终止条件: 当分割线到字符串末尾时到叶子节点,一种方案出现 3.单层搜索…

使用R语言建立回归模型并分割训练集和测试集

通过简单的回归实例&#xff0c;可以说明数据分割为训练集和测试集的必要性。以下先建立示例数据: set.seed(123) #设置随机种子 x <- rnorm(100, 2, 1) # 生成100个正态分布的随机数&#xff0c;均值为2&#xff0c;标准差为1 y exp(x) rnorm(5, 0, 2) # 生成一个新的变…

【C语言】assert断言:保护程序的利器

在软件开发过程中&#xff0c;我们经常会遇到一些假设条件或者预期行为。例如&#xff0c;我们可能假设一个函数的输入参数必须在某个范围内&#xff0c;或者某个变量的值应该满足特定的条件。当这些假设或预期行为被打破时&#xff0c;程序可能会出现异常行为&#xff0c;甚至…

Java+SpringBoot:高校竞赛管理新篇章

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

2024年Midjourney 付费订阅流程 | Midjourney 各版本介绍,使用虚拟信用卡支付买Midjourney流程指南

1.Midjourney介绍 Midjourney 是一款备受欢迎的人工智能生成图像工具&#xff0c;它可以通过输入文字描述&#xff0c;自动生成精美的图像。与许多其他图像生成工具不同&#xff0c;Midjourney 不需要安装任何软件&#xff0c;也不受个人电脑性能的限制&#xff0c;因为它运行…

73. 矩阵置零(Java)

目录 题目描述&#xff1a;输入&#xff1a;输出&#xff1a;代码实现&#xff1a; 题目描述&#xff1a; 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 输入&#xff1a; matrix [[1,1,1],[1,0,…

(五)elasticsearch 源码之查询流程分析

https://www.cnblogs.com/darcy-yuan/p/17039526.html 1.概述 上文我们讨论了es&#xff08;elasticsearch&#xff0c;下同&#xff09;索引流程&#xff0c;本文讨论es查询流程&#xff0c;以下是基本流程图 2.查询流程 为了方便调试代码&#xff0c;笔者在电脑上启动了了…

S32 Design Studio PE工具配置GPIO

首先我们来讲最简单的GPIO配置 代码生成 按照下图步骤就能配置一个基本的GPIO口&#xff0c;在组件里面选择pin_mux&#xff0c;选中就能配置使能和方向&#xff0c;no pin routed就是没有配置的。GPIO口分ABCDE组&#xff0c;每组从0到最大的序号。 然后在functional prope…

多机多卡运行nccl-tests和channel获取

nccl-tests 环境1. 安装nccl2. 安装openmpi3. 单机测试4. 多机测试mpirun多机多进程多节点运行nccl-testschannel获取 环境 Ubuntu 22.04.3 LTS (GNU/Linux 5.15.0-91-generic x86_64)cuda 11.8 cudnn 8nccl 2.15.1NVIDIA GeForce RTX 4090 *2 1. 安装nccl #查看cuda版本 nv…

npm config set registry https://registry.npm.taobao.org 这个设置了默认的镜像源之后如何恢复默认的镜像源

要恢复npm默认的镜像源&#xff0c;你可以使用以下命令将registry设置回npm的官方源&#xff1a; npm config set registry https://registry.npmjs.org/这个命令会修改你的全局npm配置&#xff0c;将包的下载源改回npm官方的源。这样做之后&#xff0c;任何后续的npm install…

逐鹿比特币生态,Elastos 携新作 BeL2「重出江湖」

撰文&#xff1a;Babywhale&#xff0c;Techub News 文章来源Techub News&#xff0c;搜Tehub News下载查看更多Web3资讯。 刚刚过去的 2023 年&#xff0c;「比特币生态」成为了市场的绝对焦点之一。从铭文开始&#xff0c;到重新走进大众视野的 Stacks 与比特币闪电网络&am…

算法竞赛进阶指南——基本算法(倍增)

ST表 可以求区间最大、最小、gcd、lcm&#xff0c;符合 f(a, a) a都可以 求区间最值&#xff0c;一个区间划分成两段 f[i][j]: 从i开始&#xff0c;长度为2^j的区间最值 #include<iostream> #include<cmath> using namespace std; const int N 1e6 10; int n,…