KNN中不同距离度量对比和介绍

news2024/11/20 14:19:31

k近邻算法KNN是一种简单而强大的算法,可用于分类和回归任务。他实现简单,主要依赖不同的距离度量来判断向量间的区别,但是有很多距离度量可以使用,所以本文演示了KNN与三种不同距离度量(Euclidean、Minkowski和Manhattan)的使用。

KNN算法概述

KNN是一种惰性、基于实例的算法。它的工作原理是将新样本的特征与数据集中现有样本的特征进行比较。然后算法选择最接近的k个样本,其中k是用户定义的参数。新样本的输出是基于“k”最近样本的多数类(用于分类)或平均值(用于回归)确定的。

有很多距离度量的算法,我们这里选取3个最常用度量的算法来进行演示:

1、欧氏距离 Euclidean Distance

 def euclidean_distance(x1, x2):
     return math.sqrt(np.sum((x1 - x2)**2))

euclidean_distance函数计算多维空间中两点(x1和x2)之间的欧氏距离,函数的工作原理如下:

  • 从x1元素中减去x2,得到对应坐标之间的差值。
  • 使用**2运算将差值平方。
  • 使用np.sum()对差的平方求和。
  • 使用math.sqrt()取总和的平方根。

欧几里得距离是欧几里得空间中两点之间的直线距离。通过计算欧几里得距离,可以识别给定样本的最近邻居,并根据邻居的多数类(用于分类)或平均值(用于回归)进行预测。在处理连续的实值特征时,使用欧几里得距离很有帮助,因为它提供了一种直观的相似性度量。

2、曼哈顿距离 Manhattan Distance

两点坐标的绝对差值之和。

 def manhattan_distance(x1, x2):
     return np.sum(np.abs(x1 - x2))

Manhattan _distance函数计算多维空间中两点(x1和x2)之间的曼哈顿距离,函数的工作原理如下:

  • 用np计算x1和x2对应坐标的绝对差值。Abs (x1 - x2)
  • 使用np.sum()对绝对差进行求和。

曼哈顿距离,也称为L1距离或出租车距离,是两点坐标的绝对差值之和。它代表了当运动被限制为网格状结构时,点之间的最短路径,类似于在城市街道上行驶的出租车。

在数据特征具有不同尺度的情况下,或者当问题域的网格状结构使其成为更合适的相似性度量时,使用曼哈顿距离可能会有所帮助。曼哈顿距离可以根据样本的特征来衡量样本之间的相似性或差异性。

与欧几里得距离相比,曼哈顿距离对异常值的敏感性较低,因为它没有对差异进行平方。这可以使它更适合于某些数据集或异常值的存在可能对模型的性能产生重大影响的问题。

3、闵可夫斯基距离 Minkowski Distance

它是欧几里得距离和曼哈顿距离的一般化的表现形式,使用p进行参数化。当p=2时,它变成欧氏距离,当p=1时,它变成曼哈顿距离。

 def minkowski_distance(x1, x2, p):
     return np.power(np.sum(np.power(np.abs(x1 - x2), p)), 1/p)

minkowski_distance函数计算多维空间中两点(x1和x2)之间的闵可夫斯基距离。

当你想要控制单个特征的差异对整体距离的影响时,使用闵可夫斯基距离会很有帮助。通过改变p值,可以调整距离度量对特征值或大或小差异的灵敏度,使其更适合特定的问题域或数据集。

闵可夫斯基距离可以根据样本的特征来衡量样本之间的相似性或不相似性。该算法通过计算适当p值的闵可夫斯基距离,识别出给定样本的最近邻居,并根据邻居的多数类(用于分类)或平均值(用于回归)进行预测。

KNN 算法的代码实现

因为KNN算法的原理很简单,所以我们这里直接使用Python实现,这样也可以对算法有一个更好的理解:

 defknn_euclidean_distance(X_train, y_train, X_test, k):
     # List to store the predicted labels for the test set
     y_pred= []
     
     # Iterate over each point in the test set
     foriinrange(len(X_test)):
         distances= []
         # Iterate over each point in the training set
         forjinrange(len(X_train)):
             # Calculate the distance between the two points using the Euclidean distance metric
             dist=euclidean_distance(X_test[i], X_train[j])
             distances.append((dist, y_train[j]))
         
         # Sort the distances list by distance (ascending order)
         distances.sort()
         
         # Get the k nearest neighbors
         neighbors=distances[:k]
         
         # Count the votes for each class
         counts= {}
         forneighborinneighbors:
             label=neighbor[1]
             iflabelincounts:
                 counts[label] +=1
             else:
                 counts[label] =1
         
         # Get the class with the most votes
         max_count=max(counts, key=counts.get)
         y_pred.append(max_count)
     
     returny_pred

这个’ knn_euclidean_distance ‘函数对于解决分类问题很有用,因为它可以根据’ k '个最近邻居中的大多数类进行预测。该函数使用欧几里得距离作为相似性度量,可以识别测试集中每个数据点的最近邻居,并相应地预测它们的标签。我们实现的代码提供了一种显式的方法来计算距离、选择邻居,并根据邻居的投票做出预测。

在使用曼哈顿距离时,KNN算法与欧氏距离保持一致,只需要将距离计算函数euclidean_distance修改为manhattan_distance。而闵可夫斯基距离则需要多加一个参数p,实现代码如下:

 defknn_minkowski_distance(X_train, y_train, X_test, k, p):
     # List to store the predicted labels for the test set
     y_pred= []
     
     # Iterate over each point in the test set
     foriinrange(len(X_test)):
         distances= []
         # Iterate over each point in the training set
         forjinrange(len(X_train)):
             # Calculate the distance between the two points using the Minkowski distance metric
             dist=minkowski_distance(X_test[i], X_train[j], p)
             distances.append((dist, y_train[j]))
         
         # Sort the distances list by distance (ascending order)
         distances.sort()
         
         # Get the k nearest neighbors
         neighbors=distances[:k]
         
         # Count the votes for each class
         counts= {}
         forneighborinneighbors:
             label=neighbor[1]
             iflabelincounts:
                 counts[label] +=1
             else:
                 counts[label] =1
         
         # Get the class with the most votes
         max_count=max(counts, key=counts.get)
         y_pred.append(max_count)
     
     returny_pred

距离度量对比

我使用的数据集是乳腺癌数据集,可以在kaggle上直接下载

这个数据集是机器学习和数据挖掘中广泛使用的基准数据集,用于二元分类任务。它是由威廉·h·沃尔伯格(William H. Wolberg)博士及其合作者在20世纪90年代从麦迪逊的威斯康星大学医院收集的。该数据集可通过UCI机器学习存储库公开获取。

Breast Cancer Wisconsin数据集包含569个实例,每个实例有32个属性。这些属性是:

ID number:每个样本的唯一标识符。

Diagnosis:目标变量有两个可能的值——“M”(恶性)和“B”(良性)。

剩下的是30个从乳腺肿块的细针抽吸(FNA)的数字化图像中计算出来的特征。它们描述了图像中细胞核的特征。对每个细胞核计算每个特征,然后求平均值,得到10个实值特征:

  • Radius:从中心到周边点的平均距离。
  • Texture:灰度值的标准偏差。
  • Perimeter:细胞核的周长。
  • Area:细胞核的面积。
  • Smoothness:半径长度的局部变化。
  • Compactness:周长²/面积- 1.0。
  • Concavity:轮廓中凹部分的严重程度。
  • Concave points:轮廓的凹部分的数目。
  • Symmetry:细胞核的对称性。
  • Fractal dimension:“Coastline approximation”- 1

对每张图像计算这十个特征的平均值、标准误差和最小或最大值(三个最大值的平均值),总共得到30个特征。数据集不包含任何缺失的属性值。

由于数据集包含30个特征,我们需要对数据集进行特征选择。这种方法的主要目的是通过选择与目标变量具有强线性关系的较小的特征子集来降低数据集的维数。通过选择高相关性的特征,目的是保持模型的预测能力,同时减少使用的特征数量,潜在地提高模型的性能和可解释性。这里需要注意的是,该方法只考虑特征与目标变量之间的线性关系,如果底层关系是非线性的,或者特征之间存在重要的交互作用,则该方法可能无效。

读取数据并计算相关系数:

 df=pd.read_csv('/kaggle/input/breast-cancer-wisconsin-data/data.csv')
 corr=df.corr()
 corr_threshold=0.6
 selected_features=corr.index[np.abs(corr['diagnosis']) >=corr_threshold]
 new_cancer_data=df[selected_features]

训练代码:

 X_train_np=np.array(X_train) 
 X_test_np=np.array(X_test)
 
 # Convert y_train and y_test to numpy arrays
 y_train_np=np.array(y_train)
 y_test_np=np.array(y_test)
 
 k_values=list(range(1, 15))
 accuracies= []
 
 forkink_values:
     y_pred=knn_euclidean_distance(X_train_np, y_train_np, X_test_np, k)
     accuracy=accuracy_score(y_test_np, y_pred)
     accuracies.append(accuracy)
 
 # Create a data frame to store k values and accuracies
 results_df=pd.DataFrame({'k': k_values, 'Accuracy': accuracies})
 
 # Create the interactive plot using Plotly
 fig=px.line(results_df, x='k', y='Accuracy', title='KNN Accuracy for Different k Values', labels={'k': 'k', 'Accuracy': 'Accuracy'})
 fig.show()
 
 # Get the best k value
 best_k=k_values[accuracies.index(max(accuracies))]
 best_accuracy=max(accuracies)
 print("Best k value is:", best_k , "where accuracy is:" ,best_accuracy)

上面代码使用欧几里得距离将KNN算法应用于分类问题,同时改变邻居的数量(k)以找到最高精度的最佳k值。它使用训练集(X_train_np和y_train_np)来训练模型,使用测试集(X_test_np和y_test_np)来进行预测和评估模型的性能。

k是KNN算法的一个超参数,选择正确的k值对于实现最佳模型性能至关重要,因为k值太小可能导致过拟合,而k值太大可能导致欠拟合。通过可视化k值与其对应的精度之间的关系,可以深入了解模型的性能,并为问题选择最合适的k值。

闵可夫斯基距离的代码修改如下:

 # Run the KNN algorithm on the test set for different k and p values
 k_values=list(range(1, 15))
 p_values=list(range(1, 6))
 results= []
 
 forkink_values:
     forpinp_values:
         y_pred=knn_minkowski_distance(X_train_np, y_train_np, X_test_np, k, p)
         accuracy=accuracy_score(y_test_np, y_pred)
         results.append((k, p, accuracy))
 
 # Create a data frame to store k, p values, and accuracies
 results_df=pd.DataFrame(results, columns=['k', 'p', 'Accuracy'])
 
 # Create the 3D plot using Plotly
 fig=go.Figure(data=[go.Scatter3d(
     x=results_df['k'],
     y=results_df['p'],
     z=results_df['Accuracy'],
     mode='markers',
     marker=dict(
         size=4,
         color=results_df['Accuracy'],
         colorscale='Viridis',
         showscale=True,
         opacity=0.8
     ),
     text=[f"k={k}, p={p}, Acc={acc:.2f}"fork, p, accinresults]
 )])
 
 fig.update_layout(scene=dict(
     xaxis_title='k',
     yaxis_title='p',
     zaxis_title='Accuracy'
 ))
 
 fig.show()

为了进一步改善我们的结果,我们还可以数据集进行缩放。应用特征缩放的主要目的是确保所有特征具有相同的尺度,这有助于提高基于距离的算法(如KNN)的性能。在KNN算法中,数据点之间的距离对确定它们的相似度起着至关重要的作用。如果特征具有不同的尺度,则算法可能会更加重视尺度较大的特征,从而导致次优预测。通过将特征缩放到均值和单位方差为零,算法可以平等地对待所有特征,从而获得更好的模型性能。

本文将使用StandardScaler()和MinMaxScaler()来扩展我们的数据集。StandardScaler和MinMaxScaler是机器学习中两种流行的特征缩放技术。这两种技术都用于将特征转换为公共尺度,这有助于提高许多机器学习算法的性能,特别是那些依赖于距离的算法,如KNN或支持向量机(SVM)。

使用不同的尺度和不同的距离函数训练KNN,可以进行比较并选择最适合数据集的技术。我们得到了以下结果:

可以使用柱状图表示来更好地分析和理解这些结果。

总结

根据上面的结果,我们可以得到以下的结论:

在不进行特征缩放时,欧几里得距离和闵可夫斯基距离都达到了0.982456的最高精度。

曼哈顿离在所有情况下的精度都比较低,这表明欧几里得或闵可夫斯基距离可能更适合这个问题。当闵可夫斯基距离度量中的p值为2时,它等于欧几里得距离。在我们这个实验中这两个指标的结果是相同的,也证明了这是正确的。

对于欧几里得和闵可夫斯基距离度量,不应用任何特征缩放就可以获得最高的精度。而对于曼哈顿距离,与非缩放数据相比,StandardScaler和MinMaxScaler都提高了模型的性能。这表明特征缩放的影响取决于所使用的距离度量。

最佳k值:最佳k值取决于距离度量和特征缩放技术。例如,k=11是不应用缩放并且使用欧几里得距离或闵可夫斯基距离时的最佳值,而k=9是使用曼哈顿距离时的最佳值。当应用特征缩放时,最佳k值通常较低,范围在3到11之间。

最后,该问题的最佳KNN模型使用欧式距离度量,无需任何特征缩放,在k=11个邻居时达到0.982456的精度。这应该是我们这个数据集在使用KNN时的最佳解。

如果你想自己实验,完整代码和数据都可以在这里找到:

https://avoid.overfit.cn/post/19200b7f741f4686a9b6ada15552d1ba

作者:Abdullah Siddique

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

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

相关文章

Three——四、几何体、高光网络材质、锯齿模糊以及GUI库的使用

文章: Three.js——一、初识Three以及基础的前端场景搭建(结尾含源码)Three——二、加强对三维空间的认识Three——三、动画执行、画布大小、渲染帧率和相机适配体验Three——四、几何体、高光网络材质、锯齿模糊以及GUI库的使用 Threejs 常见几何体简介 Three.j…

为什么我们要使用向量化运算

问题背景 如果你是matlab用户,你一般都会使用向量化运算进行编程。原因也许很简单,因为matlab针对向量化运算在底层做了深度优化,尤其是针对矩阵乘法调用了MKL之类的高度优化的第三库来加速。所以我们在推演算法的阶段,尽量的以向…

管理后台项目-08-首页使用echarts展示图表数据

目录 1-需求页面 2-头部需求分析 3-中间部分需求分析 3.1-mock数据 3.2-动态渲染数据 4-底部需求分析 1-需求页面 2-头部需求分析 上面头部有四个card,card分为上中下三部分,其中上都是文字描述和一个小图标;中间部分有文字,折…

达梦数据库管理系统 DM8

检查数据库版本及服务状态 //查看达梦数据库运行状态 SELECT status$ as 状态 FROM v$instance; //查看达梦数据库版本 SELECT banner as 版本信息 FROM v$version;创建用户 //创建用户 CREATE USER DM IDENTIFIED BY "dameng123";授予用户基本权限 使用 GRANT 语…

重大问题,Windows11出现重大BUG(开始菜单掉帧,卡顿)

重大问题,Windows11出现重大BUG 这种Windows11操作系统出现BUG已经可以说是非常常见的,但是,今天我将代表所有微软用户,解决一个关于UI设计非常不舒服的功能 关闭多平面覆盖 事情叙述问题 微软社区解决方案自己发现的解决方案解决…

[链表OJ题 2] 链表的中间结点 -- 快慢指针找链表的中间节点

目录 题目来源: 代码实现 思路分析: 1.当链表个数为奇数 2.当链表个数为偶数 总结: 题目来源: 876. 链表的中间结点 - 力扣(LeetCode) (leetcode-cn.com) 题目描述: 代码实现 struct Li…

Dart中的factory关键字用法

factory简介 在Dart中,factory关键字用于定义工厂构造函数。它与普通的构造函数有以下几个区别: factory构造函数的调用可以返回子类型或其它类型的实例。普通构造函数总是返回其包含的类型的实例。factory构造函数可以有返回值。普通构造函数的返回值永远是其包含的类型的实…

服务器中了勒索病毒,malox勒索病毒的加密方式及如何应对勒索病毒攻击

随着计算机技术的发展,计算机成为现代人工作和生活中必不可少的电子产品。但随着很多企业和个人用户的信息化建设不断升级,也经常会出现许多恶意软件。其中包括malox勒索病毒,malox勒索病毒是mallox勒索病毒新升级的加密程序,下面…

使用 NutUI 搭建「自定义业务风格」的组件库 | 京东云技术团队

作者:京东零售 佟恩 本文介绍,如何使用 NutUI 组件库,搭建一套为专属业务风格的业务组件库。 NutUI 是一款京东风格的移动端组件库。NutUI 目前支持 Vue 和 React技术栈,支持Taro多端适配。 当下的实现方式 一般组件库&#xff…

Vue最新状态管理工具Pinia——彻底搞懂Pinia是什么

Pinia从了解到实际运用——彻底搞懂什么是Pinia 知识回调(不懂就看这儿!)场景复现什么是piniapinia相比vuex的优势为什么要使用pinia?基本示例 知识回调(不懂就看这儿!) 知识专栏专栏链接Vuex知…

Nginx 静态文件、反向代理、负载均衡、缓存、SSL/TLS 加密、gzip 压缩 等等

Nginx的功能 1. 静态文件服务器2. 反向代理服务器3. 负载均衡4. 缓存5. SSL/TLS 加密6. URL 重写7. HTTP/28. WebSocket9. 反向代理缓存10. 安全限制11. gzip 压缩12. 请求限速13. 日志记录14. SSL 证书续订 Nginx 是一个高性能的开源 Web 服务器和反向代理服务器,它…

ASEMI代理ADI亚德诺LTC3309AEV#TRMPBF车规级芯片

编辑-Z LTC3309AEV#TRMPBF特点: 与 LTC3307(3A) 和 LTC3308(4A) 引脚兼容 高效率:8mΩ NMOS、31mΩ PMOS 可编程频率 1MHz 至 3MHz 微型电感器和电容器 峰值电流模式控制 22ns 最短接通时间 宽带宽,快速瞬态响应 Silent Switcher 架…

虹科荣誉 | 虹科工业物联网产品荣获中国自动化产业年会用户信赖产品奖!

2023 虹科荣获2021年度中国自动化产业年会用户信赖产品奖 近日,2023中国自动化产业年会于北京隆重举行。虹科工业物联网的产品“OPC UA Tunneller软件”凭借其产品优势和市场美誉度,通过层层选拔,在本次大会中荣获2021年度用户信赖产品奖。…

【c语言】字符串计算长度 | API仿真

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 给大家跳段街舞感谢支持&#xff01;ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ …

AB跳转轮询,让支付收款更加安全

伴随跨境电商行业对出海用户运营精细化的要求愈发严格&#xff0c;移动支付作为一个快速发展的领域&#xff0c;很快就进入了“高台期”&#xff0c;这也意味着市场对于支付环节提出了更多新的要求——不只是能收款&#xff0c;还要收款更智能、服务更全面。 但事实上&#xff…

数据链路层的重点协议

数据链路层的重点协议 &#x1f50e;认识以太网以太网帧格式 &#x1f50e;认识MTU&#x1f50e;结尾 &#x1f50e;认识以太网 “以太网” 不是一种具体的网络, 而是一种技术标准. 既包含了数据链路层的内容, 也包含了一些物理层的内容(例如规定了网络拓扑结构, 访问控制方式…

Windows平台下把Julia语言的程序编译打包为.exe可执行文件

一、创建工程 1.在D盘(其它盘亦可)目录新建一个工程文件&#xff1a;Julia Projects&#xff08;自定义名称&#xff09; 目录即 D:\Julia Projects 2.打开Atom IDE编辑器 --->File--->Add Project Folder...--->选择刚刚新建的文件夹Julia Projects 3.右键Julia P…

评判需求优先级5大规则和方法(纯干货):

在划分用户需求时&#xff0c;需秉承需求任务紧跟核心业务指标&#xff0c;按照一定的规则和方法进行优先级的划分。 常见评判需求优先级规则有&#xff1a;四象限法则、KANO模型、二八原则、产品生命周期法、ROI评估法。 一、四象限法则 四象限法则是以【重要】和【紧急】程度…

OPC UA/DA协议库open62541的win10下,cmake+MinGW/VC编译

目录 一、下载源码 二、编译源码 三、案例编译 3.1 工程目录 3.2 server端 3.3 client端 3.4 程序运行 一、下载源码 win10系统&#xff0c;安装git工具 下载open62541库源码&#xff0c;该源码是MPLv2的License git clone --recursive https://github.com/open62541/open6…

MobileNet(V1、V2、V3)入门

MobileNet 良好的移植性 可以很好的使用在移动设备上做分类、检测、分割等任务 深度可分离卷积&#xff08;v1核心&#xff09; 经典卷积算法中&#xff0c;输入的通道数要和过滤器的保持一致&#xff0c;这样很耗费参数 深度可分离卷积 第一步&#xff1a;有多少个输入&a…