【机器学习】7 ——k近邻算法

机器学习7——k近邻

输入:实例的特征向量
输出:类别

懒惰学习(lazy learning)的代表算法


文章目录

  • 机器学习7——k近邻
  • 1.k近邻
  • 2.模型——距离,k,分类规则
    • 2.1距离——相似程度的反映
    • 2.2 k值
    • 分类规则
  • 算法实现——kd树
    • 构造平衡kd树
      • 代码
    • 搜索kd树——最近邻搜索


1.k近邻

少数服从多数,物以类聚

和这个实例最近的那几个实例,属于哪个类别的多,这个实例就是这个类别的
这个最近。二维举例,就是确定某个距离,画圆,圆内所有实例参与比较

算法

input:训练集T,要判断类别的实例x
output:实例x的类别
步骤:

  1. 根据具体的距离度量,找T中和x最近的k个实例,这k个实例构成Nk(x)判断依据的范围
  2. 根据分类规则,判断x的类别

2.模型——距离,k,分类规则

2.1距离——相似程度的反映

在这里插入图片描述

2.2 k值

决定了那些实例决定判断结果
过小的k值会使模型对噪声敏感,容易过拟合

近似误差:简化了模型
主要源于近似模型的局限性。由于真实模型往往非常复杂,难以直接求解或处理,所以采用较为简单的近似模型。而这种简化过程必然会导致与真实模型存在一定的偏差。
例如,在数值计算中,用有限项的泰勒级数去逼近一个函数,由于舍去了高阶无穷小项,就会产生近似误差。
估计误差
一方面是由于样本的随机性。我们通常只能通过有限的样本数据来进行估计,而样本只是总体的一个部分,具有随机性,不能完全代表总体,从而导致估计值与真实值之间存在误差。
另一方面,估计方法的选择也会影响估计误差。不同的估计方法可能会产生不同大小的估计误差。

分类规则

通常是多数表决


算法实现——kd树

kd树:组织k维数据的二叉树,常用于高维空间的最近邻搜索、范围查询等问题。它通过将数据集递归划分为不同的维度,构建一棵可以快速查询的树结构。

对特征空间的划分

构造平衡kd树

  • 选择维度:从数据集中选择一个轴(或维度)来划分数据。常见的策略是每次选择不同的维度,以轮流划分不同的维度空间。
    通常,这个轴是数据点的某个坐标轴,例如 x 轴或 y 轴。在每一层递归中,选择的轴通常是当前节点深度d对n取余的结果,其中 n 是数据的维度。

  • 选择分割点:在选定的轴上,找到一个切分点,该切分点将数据集分为两部分。常见的选择是中位数这可以平衡树的构造,使得左右子树中的数据量大致相同。

  • 创建节点:用选定的切分点创建一个 kd 树的节点,并将数据集分为两部分,一部分包含所有在该轴上小于切分点的点,另一部分包含所有在该轴上大于切分点的点。

  • 递归构建:将数据点按中位数划分为两部分,左子树包含小于中位数的点,右子树包含大于中位数的点。然后在剩余的数据上重复此过程,选择下一个维度继续递归构建。

示例
假设我们有一系列二维点:points = [(3, 6), (17, 15), (13, 15), (6, 12), (9, 1), (2, 7), (10, 19)]

  • 首先,我们选择 x 轴作为第一层的划分轴,因为当前深度d=0,n=2,d%n=0对应 x 轴。这里有的时候也会叫做第0层
    根据x轴的数值排序[(2, 7), (3, 6), (6, 12), (9, 1), (10, 19), (13, 15), (17, 15)],中位数点为 (9, 1),作为根节点。如果双数的数据量,中间两个随便哪个都可以,但整个过程最好都一致,比如都选右侧的
  • 第2层(选择y轴,即第2维度);
    左子树:[(2, 7), (3, 6), (6, 12)]——对左子树按y轴排序,中位数点为 (2, 7)
    右子树:[(10, 19), (13, 15), (17, 15)]——对右子树按y轴排序,中位数点为 (13, 15)
  • 第3层(选择x轴)左子树继续递归:
    [(3, 6), (6, 12)]——中位数点为 == (6, 12)==
    右子树为 == (17, 15)==
结果

x            (9, 1)
           /         \
y     (2, 7)       (13, 15)
       /           /       \
x (6, 12)      (10, 19)  (17, 15)
   /
y(3, 6)

            
         

代码

class Node:
    def __init__(self, point, left=None, right=None):
        self.point = point  # 节点存储的点
        self.left = left     # 左子树
        self.right = right   # 右子树

def create_kd_tree(data, depth=0):
    if not data:
        return None

    # 选择切分轴(根据深度选择维度)
    k = len(data[0])  # 数据的维度
    axis = depth % k  # 当前的切分轴

    # 对数据按照切分轴的值进行排序,并选择中位数对应的点作为切分点
    data.sort(key=lambda x: x[axis])
    median = len(data) // 2  # 中位数的索引

    # 创建节点,并递归地创建子树
    return Node(
        point=data[median],
        left=create_kd_tree(data[:median], depth + 1),
        right=create_kd_tree(data[median + 1:], depth + 1)
    )

# 示例数据(每个点是二维空间中的一个坐标)
points = [(2, 3), (5, 4), (1, 8), (4, 7), (6, 3), (5, 2)]
kd_tree = create_kd_tree(points)

# 函数用于打印Kd树(递归)
def print_kd_tree(node, depth=0, prefix="Root"):
    if node is not None:
        print(f"{prefix}('x{depth % 2 + 1}': {node.point})")
        print_kd_tree(node.left, depth + 1, prefix="L---")
        print_kd_tree(node.right, depth + 1, prefix="R---")

# 打印Kd树
print_kd_tree(kd_tree)

搜索kd树——最近邻搜索

  • 从根节点开始:将查询点与根节点进行比较。
  • 决定搜索方向:根据查询点在当前分割轴上的值与节点在该轴上的值的比较结果,决定是向左子树搜索还是向右子树搜索
  • 递归搜索:在选定的子树中递归地执行步骤1和2,直到达到叶子节点。
  • 回溯:在回溯过程中,检查是否有可能在之前跳过的子树中找到更近的邻居。这通常涉及到检查当前节点的另一个子树(如果之前没有搜索过),并考虑是否有可能在该子树中找到比当前最近点更近的点。这通常通过计算一个“边界框”的距离来实现,该边界框包围了未搜索的子树中的所有点。
  • 更新最近点:如果在搜索过程中找到了比当前最近点更近的点,则更新最近点。
  • 继续回溯:继续回溯,直到回到根节点。

假设我们使用KD树进行最近邻查找,查询点是 (12, 8)。具体过程如下:


x            (9, 1)
           /         \
y     (2, 7)       (13, 15)
       /           /       \
x (6, 12)      (10, 19)  (17, 15)
   /
y(3, 6)
                   

根节点比较:我们在x轴上选择 (9, 1) 作为根节点,比较 (12, 8) 在右边12>9,因此进入右子树。
第二层比较:在y轴上比较,选择的点是 (13, 15),查询点 (12, 8) 位于左侧8<15,因此进入左子树。
第三层比较:进入x轴上选择 (10, 19),查询点 (12, 8) 在右侧12>10,进入右子树(没有,开始回溯)。
叶节点返回:到达叶节点后,返回当前最近邻点(此时为 (10, 19))。
回溯:回溯到(13, 15),发现更近,更新当前最近邻点(此时为(13, 15))
查看另一子树:查看(17, 15),不更新,继续回溯
到根节点: (9, 1),还是(13, 15),所以结果是(13, 15)

# 导入数学库用于计算距离
import math

# 定义KD树节点类
class KDNode:
    def __init__(self, point, axis=0):
        # 节点存储的数据点
        self.point = point
        # 左子树
        self.left = None
        # 右子树
        self.right = None
        # 划分维度
        self.axis = axis

# 构建KD树
def build_kd_tree(points, depth=0):
    # 如果当前数据集为空,则返回空节点
    if not points:
        return None
    # 根据深度确定划分维度
    axis = depth % len(points[0])
    # 按照当前维度排序数据点
    points.sort(key=lambda point: point[axis])
    # 找到中位数的位置
    median_idx = len(points) // 2
    # 创建当前维度的节点
    node = KDNode(points[median_idx], axis)
    # 递归构建左子树
    node.left = build_kd_tree(points[:median_idx], depth + 1)
    # 递归构建右子树
    node.right = build_kd_tree(points[median_idx + 1:], depth + 1)
    # 返回当前节点
    return node

# 打印KD树结构
def print_kd_tree(node, depth=0):
    if node is None:
        return
    # 缩进显示层次结构
    indent = "    " * depth
    print(f"{indent}Point: {node.point}, Axis: {node.axis}")
    # 递归打印左子树
    print_kd_tree(node.left, depth + 1)
    # 递归打印右子树
    print_kd_tree(node.right, depth + 1)

# 在KD树中搜索最近邻点
def search_kd_tree(node, target, best=None):
    if node is None:
        return best
    # 当前节点的划分维度
    axis = node.axis
    # 根据目标点与当前节点在划分维度上的值决定先访问哪一侧
    if target[axis] < node.point[axis]:
        next_node = node.left
        other_node = node.right
    else:
        next_node = node.right
        other_node = node.left
    # 递归搜索下一层
    best = search_kd_tree(next_node, target, best)
    # 更新最佳结果
    if best is None or distance(target, node.point) < distance(target, best):
        best = node.point
    # 计算目标点与当前节点在划分维度上的距离
    dist = abs(target[axis] - node.point[axis])
    # 如果这个距离小于当前最佳结果的距离,则需要检查另一侧
    if dist < distance(target, best):
        best = search_kd_tree(other_node, target, best)
    return best

# 计算两点之间的欧氏距离
def distance(p1, p2):
    return math.sqrt(sum((a - b)**2 for a, b in zip(p1, p2)))

# 定义一组二维数据点
points = [(3, 6), (17, 15), (13, 15), (6, 12), (9, 1), (2, 7), (10, 19)]
# 构建KD树
root = build_kd_tree(points)
# 打印KD树结构
print("KD Tree:")
print_kd_tree(root)

# 定义一个目标点
target_point = (12, 8)
# 在KD树中搜索最近邻点
result = search_kd_tree(root, target_point)
# 输出结果
print(f"Nearest point to {target_point}: {result}")

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

排序(插入,希尔,选择,堆,冒泡,快速,归并,计数)

本文中的Swap()函数都是下面这段代码 // 交换 void Swap(int* p1, int* p2) {int tmp *p1;*p1 *p2;*p2 tmp; }文章目录 常见排序&#xff1a;一.插入排序1.直接插入排序&#xff1a;2.希尔排序&#xff1a; 二.选择排序1.选择排序&#xff1a;2.堆排序&#xff1a; 三.交换排…

docker部署rabbitMQ 单机版

获取rabbit镜像&#xff1a;我们选择带有“mangement”的版本&#xff08;包含web管理页面&#xff09;&#xff1b; docker pull rabbitmq:management 创建并运行容器&#xff1a; docker run -d --name rabbitmq -p 5677:5672 -p 15677:15672 rabbitmq:management --name:…

【OpenCV3】图像的翻转、图像的旋转、仿射变换之图像平移、仿射变换之获取变换矩阵、透视变换

1 图像的放大与缩小 2 图像的翻转 3 图像的旋转 4 仿射变换之图像平移 5 仿射变换之获取变换矩阵 6 透视变换 1 图像的放大与缩小 resize(src, dsize[, dst[, fx[, fy[, interpolation]]]]) src: 要缩放的图片dsize: 缩放之后的图片大小, 元组和列表表示均可.dst: 可选参数, 缩…

秋招春招,在线测评题库包含哪些?

各位小伙伴们&#xff0c;秋招春招的号角已经吹响&#xff0c;作为HR&#xff0c;我们又要开始忙碌起来了。面对众多的候选人&#xff0c;如何高效、准确地筛选出合适的人选呢&#xff1f; 在线测评就是一个非常有用的工具。本文就说说在线测评题库里的那些事儿&#xff0c;主…

ant-design-vue中实现a-tree树形控件父子关联选中过滤的算法

在使用ant-design-vue的框架时&#xff0c;a-tree是比较常用的组件&#xff0c;比较适合处理树形结构的数据。 但是在与后台数据进行授权交互时&#xff0c;就不友好了。 在原生官方文档的例子中&#xff0c;若子项被勾选&#xff0c;则父级节点会被关联勾选&#xff0c;但这勾…

天通报警呼叫柱:为边防哨所筑起坚固的通信堡垒

一、背景 边防哨所是国家安全的重要防线&#xff0c;肩负着守护边境安全、维护国家主权和领土完整的神圣使命。由于边防哨所通常位于地理位置偏远、环境恶劣的地区&#xff0c;通信问题成为影响边防工作的重要因素&#xff0c;给边防官兵的日常工作和应急响应带来了不小的挑战…

vue3封装数字上下滚动翻牌器,

优点&#xff1a;可以传入字符串设置初始数字位数&#xff0c;也可以直接传入数字&#xff0c;让他自己根据位数渲染 组件代码&#xff1a; <template><div class"count-flop" :key"compKey"><!-- --><div:class"item ! . ?…

欺诈文本分类检测(十四):GPTQ量化模型

1. 引言 量化的本质&#xff1a;通过将模型参数从高精度&#xff08;例如32位&#xff09;降低到低精度&#xff08;例如8位&#xff09;&#xff0c;来缩小模型体积。 本文将采用一种训练后量化方法GPTQ&#xff0c;对前文已经训练并合并过的模型文件进行量化&#xff0c;通…

判断奇偶数的小妙招

要判断一个数是奇数还是偶数&#xff0c;一般首先想到的都是对2取余&#xff0c;但其实有更高明的算法。 首先咱们要知道一个知识点&#xff1a;偶数的二进制末位为0&#xff0c;奇数的二进制末位为1。 这是进位制本身的规则决定的&#xff0c;二进制是“逢二进一”。如果末位…

Docker 学习 Day 2

docker 基本命令和操作 学习视频一、docker 常用命令1、帮助启动类命令2、镜像命令2.1、docker images2.2、docker search 某个 xxx 镜像的名字2.3、docker pull 某个 xxx 镜像的名字2.4、docker system df2.5、docker rmi 某个 xxx 镜像的名字 ID2.6、面试题&#xff1a;谈谈 …

谷歌seo网址如何快速被收录?

想让你的网站快速被搜索引擎收录&#xff0c;可以采取几种不同的策略。首先&#xff0c;确保你的网站内容丰富、有价值&#xff0c;搜索引擎更喜欢收录内容质量高的网站。同时&#xff0c;增强网站的外链建设&#xff0c;做好这些站内优化&#xff0c;接下来就是通过谷歌搜索控…

windows下自启springboot项目(jar+nginx)

1、将springboot项目打包为jar 2、新建文本文档 test.txt&#xff0c;并输入 java -jar D:\test\test.jar&#xff08;修改为自己的jar包位置&#xff09; 保存 然后修将后缀名改为 .bat 3、在同一目录再新建 文本文档test.txt&#xff0c;输入以下内容&#xff0c;&…

“杏鲍菇驱动机器人创新前行:康奈尔大学最新研究亮相Science子刊“

未来科技新篇章&#xff1a;杏鲍菇操控下的机器人奇旅&#xff01; 在这个日新月异的科技时代&#xff0c;你或许听说过机器人由AI驱动、由人脑操控&#xff0c;但你是否能想象&#xff0c;一颗看似平凡的杏鲍菇也能成为控制机器人的“大脑”&#xff1f; 没错&#xff0c;这不…

对抗性EM用于变分深度学习:在低剂量PET和低剂量CT中的半监督图像质量增强应用|文献速递--Transformer架构在医学影像分析中的应用

Title 题目 Adversarial EM for variational deep learning: Application to semi-supervised image quality enhancement in low-dose PET and low-dose CT 对抗性EM用于变分深度学习&#xff1a;在低剂量PET和低剂量CT中的半监督图像质量增强应用 01 文献速递介绍 医学影…

新专利:作物生长期预测方法及装置

近日,国家知识产权局正式授权了一项由北京市农林科学院智能装备技术研究中心、江苏省农业科学院联合申请的发明专利"作物生长期预测方法及装置"(专利号:ZL 2024 1 0185298.1)。该专利由 于景鑫 、任妮、吕志远、李友丽、吴茜等发明人耗时多年潜心研发&#xff0c;犹如…

6、关于Medical-Transformer

6、关于Medical-Transformer Axial-Attention原文链接&#xff1a;Axial-attention Medical-Transformer原文链接&#xff1a;Medical-Transformer Medical-Transformer实际上是Axial-Attention在医学领域的运行&#xff0c;只是在这基础上增加了门机制&#xff0c;实际上也就…

Java入门:08.Java中的static关键字01

1 static关键字 可以修饰属性变量&#xff0c;方法和代码段 static修饰的属性称为静态属性或类属性&#xff0c; 在类加载时就在方法区为属性开辟存储空间&#xff0c;无论创建多少个对象&#xff0c;静态属性在内存中只有一份。 可以使用 类名.静态属性 的方式引用 static修饰…

无人机动力系统设计之桨叶推力计算

无人机动力系统设计之桨叶推力计算 1. 源由2. 关键参数2.1 特性参数2.1.1 材质&#xff08;Material&#xff09;2.1.2 叶片数量&#xff08;Number of Blades&#xff09;2.1.3 重量&#xff08;Weight&#xff09;2.1.4 噪音水平&#xff08;Noise Level&#xff09; 2.2 安装…

一文为你详解期权波动率是什么?

今天期权懂带你了解一文为你详解期权波动率是什么&#xff1f;采用合适的期权组合来对冲或利用波动率变化带来的机会。不同策略适用于不同的市场条件和投资目标。 期权波动率 假如我们为地震灾害去买一份保险&#xff0c;你认为什么样地震的保险费会更贵呢&#xff0c;是深圳…

备忘录模式memento

学习笔记&#xff0c;原文链接 https://refactoringguru.cn/design-patterns/memento 允许生成对象状态的快照并在以后将其还原。备忘录不会影响它所处理的对象的内部结构&#xff0c; 也不会影响快照中保存的数据。