机器学习原理到Python代码实现之KNN【K近邻】

K-Nearest Neighbor K近邻算法

该文章作为机器学习的第三篇文章,主要介绍的是K紧邻算法,这是机器学习中最简单的一种分类算法,也是机器学习中最基础的一种算法。

难度系数:⭐

更多相关工作请参考:Github

算法介绍

K近邻算法(K-Nearest Neighbor,KNN)是一种基本分类与回归方法。该方法的思想是:如果一个样本在特征空间中距离一个集合中的样本最近的k个样本中的大多数属于某一个类别,则该样本也属于这个类别。该方法在定类决策上具有高度的局部性,属于懒惰学习(Lazy Learning)与距离近原则

算法原理解析

首先,我们需要明确KNN算法的基本步骤:

  1. 计算距离: 对于给定的新样本,我们需要计算它与训练集中每个样本的距离。距离的计算可以使用欧几里得距离、曼哈顿距离等不同的度量方式。
  2. 选择最近邻: 根据计算出的距离,选择距离最近的k个样本作为最近邻。
  3. 进行分类: 基于这k个最近邻的类别标签进行投票,得票最多的类别就是新样本的预测类别。

接下来,我会详细解释每个步骤的实现细节:

计算距离
  1. 欧几里得距离: 这是最常见的距离计算方式,适用于连续特征。如果两个样本分别为 ( x 1 ) (x_1) (x1) ( x 2 ) (x_2) (x2),每个特征维度分别为 ( d 1 ) (d_1) (d1) ( d 2 ) (d_2) (d2),则它们之间的欧几里得距离为:
    D ( x 1 , x 2 ) = ∑ i = 1 d ( x 1 i − x 2 i ) 2 D(x_1, x_2) = \sqrt{\sum_{i=1}^{d}(x_{1i} - x_{2i})^2} D(x1,x2)=i=1d(x1ix2i)2
  2. 曼哈顿距离: 也称为城市街区距离,适用于离散特征或有序特征。其计算方式为:
    D ( x 1 , x 2 ) = ∑ i = 1 d ∣ x 1 i − x 2 i ∣ D(x_1, x_2) = \sum_{i=1}^{d}|x_{1i} - x_{2i}| D(x1,x2)=i=1dx1ix2i
选择最近邻

在得到每个样本与新样本的距离后,我们需要选择距离最小的k个样本作为最近邻。通常可以使用排序算法对距离进行排序,然后选择最小的k个。
进行分类:

对于每个最近邻,根据其类别标签进行投票。通常使用简单投票法,即每个最近邻的投票权重与其距离成反比,距离越近的最近邻投票权重越大。

进行分类

最后,将所有投票的结果进行汇总,得票最多的类别即为新样本的预测类别。

这就是KNN算法的基本实现原理。需要注意的是,KNN算法的效果很大程度上取决于k的选择和距离度量的方式。在实际应用中,可以通过交叉验证等方法来选择合适的k值。同时,对于连续特征,可能需要进行离散化或使用其他度量方式来计算距离。

数据集介绍

本次实验依旧采用鸢尾花数据集作为实验数据,如果对这部分有不确定的同学可以访问机器学习原理到Python代码实现之NaiveBayes【朴素贝叶斯】这篇文章,看一下其中数据分析部分。

鸢尾花(Iris)数据集是一个常用的分类实验数据集,由Fisher在1936年收集整理。该数据集包含150个样本,每个样本有四个属性:花萼长度、花萼宽度、花瓣长度和花瓣宽度,这四个属性用于预测鸢尾花属于Setosa、Versicolour或Virginica三个种类中的哪一类。

鸢尾花数据集的特点是具有多重变量,即每个样本都有多个特征,这使得它成为进行多元分类任务的一个理想选择。通过分析这些特征,可以了解不同鸢尾花品种之间的差异,并建立分类器来自动识别未知样本的种类。

鸢尾花数据集的来源是实际的鸢尾花测量数据,因此它具有实际背景和应用价值。这个数据集经常被用于机器学习和数据挖掘算法的实验和验证,因为它提供了多变量分析的一种有效方式。

在本次朴素贝叶斯分类中,我们计划采用这个数据集作为我们的实验对象。

代码实现

这里我们依旧提供自己实现的KNN算法和sklearn库中的KNN算法实现调用。

KNN算法构建

考虑到一般我们所验证的数据都是在小数据集上,所以我们将数据转换成numpy格式,方便后续的计算。
KNN其实是没有任何参数的,所以我们只需要将数据集传入即可。
主要的问题是在验证阶段,KNN将验证的数据集与训练的数据集进行比较,这会花费大量的时间。
以下是代码实现:

# 准备好我们需要使用的第三方包
import numpy as np
import pandas as pd
# 构建K近邻算法
# 为了提升代码的效率,我们这里将代码转移到numpy格式
class KNN:def __init__(self):self.X_train = Noneself.y_train = Noneself.mean = None            # 通过记录训练集的mean和std来进行归一化,提升性能self.std = Nonedef fit(self, X_train, y_train):# 将X_train数据进行归一化处理self.X_train = self.get_normalized_data(X_train)# 将X_train增加一个维度,目的是在测试时可以和测试集快速匹配self.X_train = np.expand_dims(self.X_train, axis=1)# 将y_train赋值给self.y_trainself.y_train = y_train# 对X_train数据进行归一化处理def get_normalized_data(self, X_train):# 计算X_train的均值self.mean = np.mean(X_train, axis=0)# 计算X_train的标准差self.std = np.std(X_train, axis=0)# 返回归一化处理后的X_trainreturn (X_train - self.mean) / self.std# 对X数据进行归一化处理def feature_normalization(self, X):# 返回归一化处理后的Xreturn (X - self.mean) / self.std# 计算欧式距离def euclidean_distance(self, x1, x2):# 返回x1和x2之间的欧式距离return np.sqrt(np.sum((x1 - x2)**2))# 预测函数def predict(self, X_test, k=3):# 将X_train复制k份,拼接到X_test上x_train = np.tile(self.X_train, (1, X_test.shape[0], 1))# 将X_test进行归一化处理X_test = self.feature_normalization(X_test)# 将X_test增加一个维度X_test = np.expand_dims(X_test, axis=0)# 将X_test复制k份,拼接到x_train上X_test = np.tile(X_test, (x_train.shape[0], 1, 1))# 计算x_train和X_test之间的欧式距离distance = np.sqrt(np.sum((x_train - X_test)**2, axis=2))# 将distance按列排序,取出前k个距离distance = np.argsort(distance, axis=0)[:k, :]# 取出距离对应的y_traink_nearest_y = self.y_train[distance.T]# 计算k个距离对应的标签的最大值pred = np.max(k_nearest_y, axis=1)# 返回预测结果return pred# 计算准确率def score(self, X_test, y_test, k=3):# 调用predict函数,预测结果y_predict = self.predict(X_test, k)# 返回预测结果和真实结果的匹配率return np.sum(y_predict == y_test) / len(y_test)

思考题

我们已经重构出了KNN算法,但现在的代码是最有的吗?这里给大家一些提示,作为后期优化的思路;

  1. 在测试阶段,我们是选择直接选择最近的K个中出现最多的元素,那可不可以优化?
  2. K近邻在测试时需要消耗大量的计算资源,有没有什么方法可以减少计算量?【降维和聚类】关于这个点其实在现在的大模型中也被广泛应用。

数据加载

# 加载数据集train_dataset = pd.read_csv('dataset\\iris_training.csv')
test_dataset = pd.read_csv('dataset\\iris_test.csv')X_train = train_dataset.drop('virginica', axis=1).to_numpy()
y_train = train_dataset['virginica'].to_numpy()X_test = test_dataset.drop('virginica', axis=1).to_numpy()
y_test = test_dataset['virginica'].to_numpy()

算法调用

# 实验
knn = KNN()
knn.fit(X_train, y_train)
accuracy = knn.score(X_test, y_test, 3)
accuracy
0.9333333333333333

KNN的算法相较于其他算法无疑是简单的,但是其计算量是巨大的。可以看到其主要计算都集中在self.predict(X_test, k)这一步。
接下来我们通过SKlearn的方式实现以下:

from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=3)
knn.fit(X_train, y_train)
predictions = knn.predict(X_test)
accuracy = knn.score(X_test, y_test)
accuracy
0.9666666666666667

实验对比

和朴素贝叶斯一致,通过我们自己的数据集来验证一下K近邻算法的性能,这里西安简单对于距离做一个加权。

# 构建K近邻算法
# 为了提升代码的效率,我们这里将代码转移到numpy格式
class KNN:def __init__(self):self.X_train = Noneself.y_train = Noneself.mean = None            # 通过记录训练集的mean和std来进行归一化,提升性能self.std = Noneself.clas = Nonedef fit(self, X_train, y_train):# 将X_train数据进行归一化处理self.X_train = self.get_normalized_data(X_train)# 将X_train增加一个维度,目的是在测试时可以和测试集快速匹配self.X_train = np.expand_dims(self.X_train, axis=1)# 将y_train赋值给self.y_trainself.y_train = y_trainself.clas = len(np.unique(y_train))# 对X_train数据进行归一化处理def get_normalized_data(self, X_train):# 计算X_train的均值self.mean = np.mean(X_train, axis=0)# 计算X_train的标准差self.std = np.std(X_train, axis=0)# 返回归一化处理后的X_trainreturn (X_train - self.mean) / self.std# 对X数据进行归一化处理def feature_normalization(self, X):# 返回归一化处理后的Xreturn (X - self.mean) / self.std# 计算欧式距离def euclidean_distance(self, x1, x2):# 返回x1和x2之间的欧式距离return np.sqrt(np.sum((x1 - x2)**2))# 预测函数def predict(self, X_test, k=3):# 将X_train复制k份,拼接到X_test上x_train = np.tile(self.X_train, (1, X_test.shape[0], 1))# 将X_test进行归一化处理X_test = self.feature_normalization(X_test)# 将X_test增加一个维度X_test = np.expand_dims(X_test, axis=0)# 将X_test复制k份,拼接到x_train上X_test = np.tile(X_test, (x_train.shape[0], 1, 1))# 计算x_train和X_test之间的欧式距离distance = np.sqrt(np.sum((x_train - X_test)**2, axis=2))# 将distance按列排序,取出前k个距离weight = np.arange(k, 0, -1) / np.sum(np.arange(k, 0, -1))distance_index = np.argsort(distance, axis=0)[:k, :]distance = distance[:k, :].T * weightk_nearest_y = self.y_train[distance_index.T]pred = np.zeros((k_nearest_y.shape[0], self.clas))for i in range(self.clas):pred[:, i] = np.sum(distance * (k_nearest_y == i), axis=1)# 返回预测结果return np.argmax(pred, axis=1)# 计算准确率def score(self, X_test, y_test, k=3):# 调用predict函数,预测结果y_predict = self.predict(X_test, k)# 返回预测结果和真实结果的匹配率return np.sum(y_predict == y_test) / len(y_test)
train_dataset = pd.read_csv('dataset\\Mobile_phone_price_range_estimate_train.csv').to_numpy()
test_dataset = pd.read_csv('dataset\\Mobile_phone_price_range_estimate_test.csv').to_numpy()X_train = train_dataset[:, :-1]
y_train = train_dataset[:, -1]
X_test = test_dataset[:, :-1]
y_test = test_dataset[:, -1]knn = KNN()
knn.fit(X_train, y_train)
accuracy = knn.score(X_test, y_test, 1)
print("KNN K=1 acc:%7.5f" % accuracy)
accuracy = knn.score(X_test, y_test, 3)
print("KNN K=3 acc:%7.5f" % accuracy)
accuracy = knn.score(X_test, y_test, 8)
print("KNN K=8 acc:%7.5f" % accuracy)
accuracy = knn.score(X_test, y_test, 12)
print("KNN K=12 acc:%7.5f" % accuracy)
accuracy = knn.score(X_test, y_test, 32)
print("KNN K=32 acc:%7.5f" % accuracy)
accuracy = knn.score(X_test, y_test, 128)
print("KNN K=128 acc:%7.5f" % accuracy)
KNN K=1 acc:0.49000
KNN K=3 acc:0.48250
KNN K=8 acc:0.54500
KNN K=12 acc:0.56000
KNN K=32 acc:0.58000
KNN K=128 acc:0.65250

总结

在本章中,我们确实实现了K近邻(KNN)算法。KNN是一种基本的机器学习算法,它根据输入数据的K个最近邻的类别来进行分类或回归预测。

KNN算法具有简单、易于实现、无需训练模型的特点,因此它常被用作其他机器学习算法的基线模型。

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

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

相关文章

【python】python新年烟花代码【附源码】

欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 新年的钟声即将敲响,为了庆祝这个喜庆的时刻,我们可以用 Python 编写一个炫彩夺目的烟花盛典。本文将详细介绍如何使用 Pygame 库创建一个令人惊叹的烟花效果。 一、效果图: 二…

安防视频监控系统EasyCVR设备分组中在线/离线数量统计的开发与实现

安防视频监控EasyCVR系统具备较强的兼容性,它可以支持国标GB28181、RTSP/Onvif、RTMP,以及厂家的私有协议与SDK,如:海康ehome、海康sdk、大华sdk、宇视sdk、华为sdk、萤石云sdk、乐橙sdk等。EasyCVR平台可覆盖多类型的设备接入&am…

旋转图像(Rotate Image)- LeetCode 48

旋转图像(Rotate Image)- LeetCode 48 题目描述 给定一个n x n 2D矩阵表示的图像,我们需要将该图像旋转90度。并且,重要的是我们需要在原地修改这个2D矩阵,不能使用额外的2D矩阵。 解题思路 1. 转置操作 首先&…

R语言下载安装及VScode配置

文章目录 1. R 下载和安装1.1 下载1.2 安装 2. VSCODE 配置2.1 安装R拓展2.2 安装R语言辅助功能包2.3 DEBUG 1. R 下载和安装 1.1 下载 网址:https://www.r-project.org/ 选择一个镜像地址下载 选择对应的版本 一般选择base即可 1.2 安装 下载安装包后按提示安装…

Npm+BootStrap布局

NpmBootStrap布局 NodeJs NodeJs概述 Node.js是Ryan Dahl于2009年5月基于Chrome V8引擎构建的一个开源和跨平台的JavaScript运行环境。主要在Windows、Linux、Unix、MacOSX等不同平台上运行 NodeJs意义 Node.js是一个javascript运行环境,它使得javascript可以脱离…

jupyter notebook 配置conda 虚拟环境python

conda创建python环境 conda create -n openvoice python3.9 激活环境 source activate openvoice 在虚拟环境中安装ipykernel pip install ipykernel 添加虚拟环境进到 jupyter notebook python -m ipykernel install --user --name openvoice --display-name openvoice …

Springboot+vue的毕业论文管理系统(有报告)。Javaee项目,springboot vue前后端分离项目

演示视频: Springbootvue的毕业论文管理系统(有报告)。Javaee项目,springboot vue前后端分离项目 项目介绍: 本文设计了一个基于Springbootvue的前后端分离的毕业论文管理系统,采用M(model&…

QT延时五种实现方法

QT中没有提供专用延时函数,但有多种实现方法,各有特点,如下所示: 一.阻塞方式 1.多线程程序使用QThread::sleep()或者QThread::msleep()或QThread::usleep()或QThread::wait()进行延时处理。 Sleep不会释放对象锁,其…

第3章:python的判断语句

学一门语言,无外乎多敲,多用,记得回顾昨天写过的代码呀 布尔类型和比较运算符 布尔类型的定义 使用比较运算符进行比较运算得到布尔类型的结果 比较运算符 """ 演示布尔类型的定义 以及比较运算符的应用 ​ """…

并发前置知识一:线程基础

一、通用的线程生命周期:“五态模型” 二、java线程有哪几种状态? New:创建完线程Runable:start(),这里的Runnable包含操作的系统的Running(运行状态)和Ready(上面的可运行状态)Blo…

vscode配置Todo Tree插件

一、在VSCode中安装插件Todo Tree ​​​​ 二、按下快捷键ctrlshiftP,输入setting.jspn 选择相应的配置范围,我们选择的是用户配置 Open User Settings(JSON),将以下代码插入其中。 {//todo-tree 标签配置从这里开始 标签兼容大小写字母(…

强化学习9——免模型预测算法介绍(蒙特卡洛方法和时步差分方法)

对于大部分情况来说,环境是未知的,也就是说状态转移概率未知,对于这种情况的算法称为免模型预测算法。免模型算法与环境不断交互学习,但是需要大量的运算。 蒙特卡洛方法 蒙特卡罗方法通过重复随机抽选,之后运用统计…

Phaser详解

Phaser是一个相对较新且功能强大的同步原语,它于Java 7中引入,用于协调并行任务的执行。与CyclicBarrier和CountDownLatch等传统的同步工具相比,Phaser提供了更灵活和更高级的功能,特别是在处理动态和可变的并行任务集合时。 1.P…

Python-基础语法

标识符 第一个字符必须是字母表中字母或下划线 _ 。标识符的其他的部分由字母、数字和下划线组成。标识符对大小写敏感。在 Python 3 中,可以用中文作为变量名,非 ASCII 标识符也是允许的了。 python保留字 保留字即关键字,我们不能把它们用…

MATLAB全局最优搜索函数:GlobalSearch函数

摘要:本文介绍了 GlobalSearch 函数的使用句式(一)、三个运行案例(二)、 GlobalSearch 函数的参数设置(三)、GlobalSearch 注意事项及必要说明(五)等内容。详细介绍如下&…

超维空间S2无人机使用说明书——11、使用3维激光雷达实现ROS无人机的精准定位

引言:在工程应用中,往往需要在没有GPS信号的情况下实现无人机的资助或者稳定的飞行。实现这个的基础就是定位,有了准确的定位信息,无人机才能稳定的飞行。性比较于视觉定位效果,目前3D雷达相对更加稳定,视觉…

FineBI实战项目一(18):每小时上架商品个数分析开发

点击新建组件,创建每小时上架商品个数组件。 选择线图,拖拽cnt(总数)到纵轴,拖拽hourStr到横轴。 修改横轴和纵轴的文字。 调节连线样式。 添加组件到仪表板。

【LeetCode】59. 螺旋矩阵II(中等)——代码随想录算法训练营Day02

题目链接:59. 螺旋矩阵II 题目描述 给你一个正整数 n ,生成一个包含 1 到 n 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 示例 1: 输入:n 3 输出:[[1,2,3],[8,9,4],[7,6,5]] 示例 …

攒机到底能省多少钱?

昨天弄好了攒机配置,今天要求配置一些更为实用的配置,只是作为一般办公,单位买进来的计算机都是联想,价格普遍在7000元以上,出于省钱和实用目的,今天搭配了一个组机方案。 上面的配置对付一般办公足够&…

基于JAVA的婚恋交友网站 开源项目

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 会员管理模块2.3 新闻管理模块2.4 相亲大会管理模块2.5 留言管理模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 会员信息表3.2.2 新闻表3.2.3 相亲大会表3.2.4 留言表 四、系统展示五、核心代码5.…