机器学习-基于KNN算法手动实现kd树
目录
一、概括
二、KD树的构建流程
1.循环选轴
2.选择分裂点
三、kd树的查询
1.输入我们要搜索的点
2.递归向下遍历:
3.记录最近点
4.回溯父节点:
四、KD树的优化与变种:
五、KD树代码:
上一章我们将了机器学习-手搓KNN算法,这一章我们加上kd树对它进行优化,下面先来讲讲kd树。
KD 树(K-Dimensional Tree)是一种高效的K 维空间数据索引结构,主要用于最近邻搜索和范围搜索。以下从原理、构建、查询、优化等方面详细讲解:
一、概括
KD树通过递归划分k维空间,将数据点组织成二叉树结构:每一个节点代表一个k维超矩形空,比如在二维空间中,就是一个矩形包围一个点,三维就是一个体来包围一个点。然后使用二叉树将这些点连接起来,父节点选择一个维度作为分裂轴,用该维度的中位数将区域划分维左子树(小于分裂轴的点)和右子树(大于等于分裂轴的点)
二、KD树的构建流程
以X = [(2,3), (5,4), (9,6), (4,7), (8,1), (7,2)]为例
1.循环选轴
先计算每个维度的方差,X的x轴数据有(2,5,9,4,8,7)方差为5.8055。x轴数据有(3,4,6,7,1,2)方差为4.4722。因为x轴的方差大于y轴,那么先选择x轴。等到x轴分裂后下一次就是y轴,如果还有别的维度那么继续循环,循环结束后又回到y轴开始下一轮的循环
2.选择分裂点
第一次在上面选择完成后的轴x上,选择该轴的中位数,数据为((2,5,9,4,8,7)那么中位数为(5,4)那么在该点上分裂,分裂后的左子树为[(2,3)],右子树为[(9,6), (4,7), (8,1), (7,2)]
第二次选择y轴:在上面的右子树中的中位数为(7,2),那么根据中位数分裂后左子树为[(4,7), (8,1)],右子树为:[(9,6)]。继续循环,循环结束后树结构为:
(5,4) (x轴分裂)/ \
(2,3) (7,2) (y轴分裂)/ \(4,7) (9,6)\(8,1)
三、kd树的查询
既然设计到树,那么肯定有增删改查。
1.输入我们要搜索的点
最近邻搜索的目的是找到我们要查询的点的最近的K个点,那么目标就变成了在我们的KD树中寻找到距离搜索点的最小距离的K个点。
2.递归向下遍历:
从根节点开始,根据当前分裂轴比较我们要搜索的点,如果比我们要搜索的点大就去右子树,小就去左子树。
3.记录最近点
等到第二步递归到叶子节点时,那么这个叶子节点就是距离我们要搜索的点最近的点,将这个点记录下来
4.回溯父节点:
计算我们搜索到的点到我们要搜索的点的距离,因为还要遍历另外一边的最近点,比如刚刚遍历的是左子树,那么现在要遍历右子树了,每次回溯到父节点后都要将搜索到的点与上一次搜索的最近点比较距离大小,将小的留下
示例:
以上面的例子为例:比如查找(6,3)的最近点
1.从根节点(5,4)出发,x 轴分裂,6>5,进入右子树(7,2)。
2.(7,2)是 y 轴分裂,3>2,进入右子树(9,6),记录最近点为(9,6)(距离√[(6-9)²+(3-6)²]=√18)。
3.回溯到(7,2),计算 y 轴分裂超平面距离为 | 3-2|=1 < √18,检查左子树(4,7)和(8,1)。
在左子树中,(8,1)距离为√[(6-8)²+(3-1)²]=√8,更近,更新最近点。
4.回溯到根节点(5,4),计算 x 轴分裂超平面距离为 | 6-5|=1 < √8,检查左子树(2,3),距离√[(6-2)²+(3-3)²]=4 > √8,不更新。最终最近点为 (8,1)。
2.范围搜索
目标:找到所有在k维超矩形区域内的点。
这个方法是先设置一个距离,然后递归遍历树,若当前节点的分裂轴到我们查询点的距离超过了我们设置的距离,则直接剪枝就是不去遍历这个节点以后的点了,如果这个节点在查询区域内则加入结果集,继续搜索子树
四、KD树的优化与变种:
1.BBF算法:使用有线队列优化最近邻搜索,减少回溯次数
2.Ball树:用超球体代替超矩形,更高效处理高维数据(普通KD树在维度>20时性能明显下降)
3.k-d-B树:结合KD树和B树,支持动态插入和删除
五、KD树代码:
import numpy as np
from collections import dequefrom sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScalerclass KDNode:def __init__(self,point,left=None,right=None,axis=None):self.point=point # 数据点[]self.left=leftself.right=rightself.axis=axisclass KDTree:def __init__(self,data,labels):self.data=np.c_[data,labels]self.root=self.build_tree(self.data)def build_tree(self,points,depth=0):if len(points)==0:return Nonek=points.shape[1]-1axis=depth%ksorted_points=points[points[:,axis].argsort()]median_idx=len(sorted_points)//2median_point=sorted_points[median_idx]left=self.build_tree(sorted_points[:median_idx],depth+1)right=self.build_tree(sorted_points[median_idx+1:],depth+1)return KDNode(median_point,left,right,axis)def query_knn(self, target, k):best_candidates = [] # 保存最近的k个邻居(按距离倒序存储)candidates = deque() # 使用双端队列实现非递归遍历candidates.append((self.root, False)) # (当前节点, 是否已访问)while candidates:node, visited = candidates.pop()if node is None:continueif not visited:# 计算当前节点到目标的欧氏距离(排除标签列)distance = np.sqrt(np.sum((node.point[:-1] - target) ** 2))# 维护长度为k的优先队列(使用负距离实现最大堆)if len(best_candidates) < k:best_candidates.append((-distance, node.point))best_candidates.sort(reverse=True) # 按距离从大到小排序else:if distance < -best_candidates[0][0]:best_candidates.pop() # 移除最远候选best_candidates.append((-distance, node.point))best_candidates.sort(reverse=True)# 根据切分维度决定搜索路径(类似二叉搜索树)axis = node.axisif target[axis] < node.point[axis]:candidates.append((node, True)) # 标记当前节点已访问candidates.append((node.left, False)) # 先搜索左子树else:candidates.append((node, True))candidates.append((node.right, False)) # 先搜索右子树else:# 回溯检查另一侧子树是否需要搜索(剪枝优化)axis = node.axisworst_dist = -best_candidates[0][0] if best_candidates else np.inf# 判断目标点到分割超平面的距离是否小于当前最远邻居距离if (len(best_candidates) < k) or \(abs(target[axis] - node.point[axis]) < worst_dist):if target[axis] < node.point[axis]:candidates.append((node.right, False)) # 搜索右子树else:candidates.append((node.left, False)) # 搜索左子树# 返回前k个邻居的标签(按距离从近到远排序)return [point[-1] for (dist, point) in sorted(best_candidates, reverse=True)]class KNNWithKDTree:def __init__(self, k=5):self.k = k # 最近邻数量Kself.kdtree = None # 存储构建好的KD树def fit(self, X, y):# 构建KD树(将训练数据和标签传入)self.kdtree = KDTree(X, y)def predict(self, X_test):predictions = []for x in X_test:# 获取当前测试样本的K个最近邻标签neighbors = self.kdtree.query_knn(x, self.k)# 多数投票(取出现次数最多的类别)most_common = max(set(neighbors), key=neighbors.count)predictions.append(most_common)return np.array(predictions)if __name__ == '__main__':# 加载鸢尾花数据集iris = load_iris()X, y = iris.data, iris.target# 数据标准化(消除量纲影响)scaler = StandardScaler()X = scaler.fit_transform(X)# 划分训练集和测试集(70%训练,30%测试)X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)# 初始化KNN分类器(K=5)knn = KNNWithKDTree(k=5)knn.fit(X_train, y_train) # 训练模型(构建KD树)# 预测测试集结果y_pred = knn.predict(X_test)# 计算准确率accuracy = np.sum(y_pred == y_test) / len(y_test)print(f"准确率: {accuracy:.4f}") # 输出如:准确率: 0.9778
相关文章:
机器学习-基于KNN算法手动实现kd树
目录 一、概括 二、KD树的构建流程 1.循环选轴 2.选择分裂点 三、kd树的查询 1.输入我们要搜索的点 2.递归向下遍历: 3.记录最近点 4.回溯父节点: 四、KD树的优化与变种: 五、KD树代码: 上一章我们将了机器学习-手搓KN…...
Unity Shader 的编程流程和结构
Unity Shader 的编程流程和结构 Unity Shader 的编程主要由以下三个核心部分组成:Properties(属性)、SubShader(子着色器) 和 Fallback(回退)。下面是它们的具体作用和结构: 1. Pr…...
vue3 项目的最新eslint9 + prettier 配置
注意:eslint目前升级到9版本了 在 ESLint v9 中,配置文件已经从 .eslintrc 迁移到了 eslint.config.js 配置的方式和之前的方式不太一样了!!!! 详见自己的语雀文档:5、新版eslint9prettier 配…...
SAP GUI Script for C# SAP脚本开发快速指南与默认主题问题
SAP GUI Script for C# 快速指南 SAP 脚本的快速使用与设置. 解决使用SAP脚本执行后,默认打开的SAP是经典主题的问题 1. 解决默认主题问题 如果您使用的是SAP GUI 740,并遇到无法打开对话框的问题,请先将主题设置为经典主题(Classic Theme…...
JAVA泛型的作用
1. 类型安全(Type Safety) 在泛型出现之前,集合类(如 ArrayList、HashMap)只能存储 Object 类型元素,导致以下问题: 问题:从集合中取出元素时,需手动强制类型转…...
Git Flow 分支管理策略
优势 清晰的分支结构:每个分支都有明确的用途,便于团队协作。 稳定的 master 分支:生产环境代码始终稳定。 灵活的发布管理:通过发布分支和热修复分支,可以灵活管理版本发布和紧急修复。 主要分支 master 分支 代表…...
FFmpeg + Qt 简单视频播放器代码
一个基于 FFmpeg 4.x 和 Qt 的简单视频播放器代码示例,实现视频解码和渲染到 Qt 窗口的功能。 1)ffmpeg库界面,视频解码支持软解和硬解方式。 2)QImage/QPixmap显示视频图片。 1. Qt 项目配置(.pro 文件&…...
Unity跨平台构建快速回顾
知识点来源:人间自有韬哥在,豆包 目录 一、发布应用程序1. 修改发布必备设置1.1 打开设置面板1.2 修改公司名、游戏项目名、版本号和默认图标1.3 修改 Package Name 和 Minimum API Level 2. 发布应用程序2.1 配置 Build Settings2.2 选择发布选项2.3 构…...
【嵌入式学习2】内存管理
## C语言编译过程 预处理:宏定义展开、头文件展开、条件编译,这里并不会检查语法,将#include #define这些头文件内容插入到源码中 gcc -E main.c -o main.i 编译:检查语法,将预处理后文件编译生成汇编文件ÿ…...
密码学(Public-Key Cryptography and Discrete Logarithms)
Public-Key Cryptography and Discrete Logarithms Discrete Logarithm 核心概念:离散对数是密码学中一个重要的数学问题,特别是在有限域和循环群中。它基于指数运算在某些群中是单向函数这一特性。也就是说,给定一个群 G G G和一个生成元 …...
TDengine又新增一可视化工具 Perspective
概述 Perspective 是一款开源且强大的数据可视化库,由 Prospective.co 开发,运用 WebAssembly 和 Web Workers 技术,在 Web 应用中实现交互式实时数据分析,能在浏览器端提供高性能可视化能力。借助它,开发者可构建实时…...
【Linux文件IO】Linux中标准IO的API的描述和基本用法
Linux中标准IO的API的描述和基本用法 一、标准IO相关API1、文件的打开和关闭示例代码: 2、文件的读写示例代码:用标准IO(fread、fwrite)实现文件拷贝(任何文件均可拷贝) 3、文件偏移设置示例代码: 4、fgets fputs fget…...
深度学习篇---PaddleDetectionPaddleOCR
文章目录 前言1.代码2.代码介绍2.1 **导入模块**2.2 **配置区域**2.3 ExpressInfoProcessor类2.4 **主程序**: 3.使用说明3.1环境准备3.2模型准备3.3数据库初始化3.4串口配置3.5信息提取优化3.6注意事项 前言 本文简单介绍了PaddleDetection和PaddleOCR相结合的示例…...
Ant Design Vue Select 选择器 全选 功能
Vue.js的组件库Ant Design Vue Select 选择器没有全选功能,如下图所示: 在项目中,我们自己实现了全选和清空功能,如下所示: 代码如下所示: <!--* 参数配置 - 风力发电 - 曲线图 * 猴王软件学院 - 大强 …...
系统与网络安全------网络应用基础(1)
资料整理于网络资料、书本资料、AI,仅供个人学习参考。 TCP/IP协议及配置 概述 TCP/IP协议族 计算机之间进行通信时必须共同遵循的一种通信规定 最广泛使用的通信协议的集合 包括大量Internet应用中的标准协议 支持跨网络架构、跨操作系统平台的数据通信 主机…...
ZIP_STORED和ZIP_LZMA没有compresslevel参数!
在使用py的zipfile库进行压缩的时候,有这么一个函数: def write(self, filename, arcnameNone,compress_typeNone, compresslevelNone): 一般我们在压缩文件进去的时候都是用这个函数的; 对于compresslevel这个函数,它是用来指…...
坦克大战(c++)
今天我给大家分享一个c游戏。 废话不多说,作品展示: #include <stdio.h> #include <windows.h> #include <time.h> //里规格:长39*278 (真坐标)(假坐标宽为39) 高39 //外规格:长…...
论文阅读:2023 EMNLP SeqXGPT: Sentence-level AI-generated text detection
总目录 大模型安全相关研究:https://blog.csdn.net/WhiffeYF/article/details/142132328 SeqXGPT: Sentence-level AI-generated text detection https://aclanthology.org/2023.emnlp-main.73/ https://github.com/Jihuai-wpy/SeqXGPT https://www.doubao.com/chat/21003…...
JDK 24 发布,新特性解读!
一、版本演进与技术格局新动向 北京时间3月20日,Oracle正式发布Java SE 24。作为继Java 21之后的第三个非LTS版本,其技术革新力度远超预期——共集成24项JEP提案,相当于Java 22(12项)与Java 23(12项&#…...
k8s中service概述(二)NodePort
NodePort 是 Kubernetes 中一种用于对外暴露服务的 Service 类型。它通过在集群的每个节点上开放一个静态端口(NodePort),使得外部用户可以通过节点的 IP 地址和该端口访问集群内部的服务。以下是关于 NodePort Service 的详细说明࿱…...
Oracle归档配置及检查
配置归档位置到 USE_DB_RECOVERY_FILE_DEST,并设置存储大小 startup mount; !mkdir /db/archivelog ALTER SYSTEM SET db_recovery_file_dest_size100G SCOPEBOTH; ALTER SYSTEM SET db_recovery_file_dest/db/archivelog SCOPEBOTH; ALTER SYSTEM SET log_archive…...
计算机二级:函数基础题
函数基础题 第一题 rinput("请输入半径:") c3.1415926*r*2 print("{:.0f}".format(c))输出: Type Error第二题 a7 b2 print(a%2)输出 1第三题 ab4 def my_ab(ab,xy):abpow(ab,xy)print(ab,end"\n") my_ab(ab,2)prin…...
Python爬虫-爬取AliExpress商品搜索词排名数据
前言 本文是该专栏的第49篇,后面会持续分享python爬虫干货知识,记得关注。 本文,笔者以AliExpress平台为例。基于Python爬虫,通过某个指定的“搜索关键词”,批量获取该“搜索关键词”的商品排名数据。 具体实现思路和详细逻辑,笔者将在正文结合完整代码进行详细介绍。废…...
AI 时代,我们需要什么样的数据库?
AI 时代,我们需要什么样的数据库? 人工智能正在悄然改变软件开发的方式。过去一年间,诸如 GitHub Spark、Replit 和 Bolt 等新兴 AI 工具层出不穷,能够快速生成简单的前端应用,甚至无需传统意义上的后端服务就能部署上…...
刷机维修进阶教程-----adb禁用错了系统app导致无法开机 如何保数据无损恢复机型
在刷机维修过程中 。我们会遇到一些由于客户使用adb指令来禁用手机app而导致手机无法开机进入系统的故障机型。通常此类问题机型有好几种解决方法。但如果客户需要保数据来恢复机型。其实操作也是很简单的.还有类似误删除应用导致不开机等等如何保数据。 通过博文了解💝💝�…...
Vue3 实战:基于 mxGraph 与 WebSocket 的动态流程图构建
本文将详细介绍如何在 Vue3 项目中集成 mxGraph 可视化库,并通过 WebSocket 实现画布元素的实时更新。适合有 Vue 基础的前端开发者学习参考。 一、技术栈准备 Vue3:采用 Composition API 开发mxGraph:JavaScript 流程图库(版本 …...
Ubuntu AX200 iwlwifi-cc-46.3cfab8da.0.tgz无法下载的解决办法
文章目录 前言一、检查网卡是否被识别二、确认内核模块是否可用1.AX200 wifi 要求内核5.12.检查 iwlwifi.ko 是否存在:3.如果未找到,可能是内核模块未正确生成。尝试安装 linux-modules-extra:4.再次检查 iwlwifi.ko 是否存在:5.确…...
蓝桥杯,利用 Vue.js 构建简易任务管理器
在日常开发中,我们经常需要处理各种任务和计划。一个简单且高效的任务管理器可以帮助我们更好地组织和安排时间。今天,我将向大家展示如何使用 Vue.js 构建一个简易的任务管理器。这个项目不仅能够帮助我们更好地理解 Vue.js 的基本语法和功能࿰…...
国际机构Gartner发布2025年网络安全趋势
转自:中国新闻网 中新网北京3月14日电 国际机构高德纳(Gartner)14日发布的消息称,网络安全和风险管理在2025年“面临挑战与机遇并存的局面”,“实现转型和提高弹性”对确保企业在快速变化的数字世界中,实现安全且可持续的创新至关…...
【设计模式】单件模式
七、单件模式 单件(Singleton) 模式也称单例模式/单态模式,是一种创建型模式,用于创建只能产生 一个对象实例 的类。该模式比较特殊,其实现代码中没有用到设计模式中经常提起的抽象概念,而是使用了一种比较特殊的语法结构&#x…...
