统计学习与方法实战——K近邻算法

news2024/12/25 9:24:27

K近邻算法

K近邻算法

备注

  1. kNN是一种基本分类与回归方法.
  • 多数表决规则等价于0-1损失函数下的经验风险最小化,支持多分类, 有别于前面的感知机算法
  • kNN的k和KDTree的k含义不同
  • KDTree是一种存储k维空间数据的树结构
  • 建立空间索引的方法在点云数据处理中也有广泛的应用,KDTree和八叉树在3D点云数据组织中应用比较广
  • KDTree是平衡二叉树
  • KDTree的搜索问题分为k近邻查找范围查找,一个是已知 k k k,求点集范围,一个是已知范围,求里面有k个点。范围查找问题在维度高的时候复杂度非常高,不太推荐用KDTree做范围查找。
  • K近邻问题在杭电ACM里面有收录,HUD4347
  • 图像的特征点匹配,数据库查询,图像检索本质上都是同一个问题–相似性检索问题。Facebook开源了一个高效的相似性检索工具Faiss,用于有效的相似性搜索和稠密矢量聚类。
    𝑘 近邻法是基本且简单的分类与回归方法。 𝑘 近邻法的基本做法是:对给定的训练实例点和输入实例点,首先确定输入实例点的 𝑘 个最近邻训练实例点,然后利用这 𝑘 个训练实例点的类的多数来预测输入实例点的类。

2. 𝑘 近邻模型对应于基于训练数据集对特征空间的一个划分。 𝑘 近邻法中,当训练集、距离度量、 𝑘 值及分类决策规则确定后,其结果唯一确定。

3. 𝑘 近邻法三要素:距离度量、 𝑘 值的选择和分类决策规则。常用的距离度量是欧氏距离及更一般的pL距离。 𝑘 值小时, 𝑘 近邻模型更复杂; 𝑘 值大时, 𝑘 近邻模型更简单。 𝑘 值的选择反映了对近似误差与估计误差之间的权衡,通常由交叉验证选择最优的 𝑘 。常用的分类决策规则是多数表决,对应于经验风险最小化。

4. 𝑘 近邻法的实现需要考虑如何快速搜索k个最近邻点。kd树是一种便于对k维空间中的数据进行快速检索的数据结构。kd树是二叉树,表示对 𝑘 维空间的一个划分,其每个结点对应于 𝑘 维空间划分中的一个超矩形区域。利用kd树可以省去对大部分数据点的搜索, 从而减少搜索的计算量。

k近邻模型

算法

输入: T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , … , ( x N , y N ) } , x i ∈ X ⊆ R n , y i ∈ Y = { c 1 , c 2 , … , c k } T=\{(x_1,y_1),(x_2,y_2),\dots,(x_N,y_N)\}, x_i\in \cal{X}\sube{\bf{R}^n}, y_i\in\cal{Y}=\{c_1,c_2,\dots, c_k\} T={(x1,y1),(x2,y2),,(xN,yN)}xiXRn,yiY={c1,c2,,ck}; 实例特征向量 x x x

输出: 实例所属的 y y y

K近邻算法三要素如下黑体:
步骤:

  1. 根据指定的距离度量,在 T T T中查找 x x x最近邻的 k k k个点,覆盖这 k k k个点的 x x x的邻域定义为 N k ( x ) N_k(x) Nk(x)

  2. N k ( x ) N_k(x) Nk(x)中应用分类决策规则决定 x x x的类别 y y y
    y = arg ⁡ max ⁡ c j ∑ x i ∈ N k ( x ) I ( y i = c j ) , i = 1 , 2 , … , N , j = 1 , 2 , … , K y=\arg\max_{c_j}\sum_{x_i\in N_k(x)}I(y_i=c_j), i=1,2,\dots,N, j=1,2,\dots,K y=argcjmaxxiNk(x)I(yi=cj),i=1,2,,N,j=1,2,,K

距离度量

特征空间中的两个实例点的距离是两个实例点相似程度的反映。
距离越近(数值越小), 相似度越大
这里用到了 L p L_p Lp距离,

  1. p = 1 p=1 p=1 对应 曼哈顿距离
  2. p = 2 p=2 p=2 对应 欧氏距离
  3. 任意 p p p 对应 闵可夫斯基距离

L p ( x i , x j ) = ( ∑ l = 1 n ∣ x i ( l ) − x j ( l ) ∣ p ) 1 p L_p(x_i, x_j)=\left(\sum_{l=1}^{n}{\left|x_{i}^{(l)}-x_{j}^{(l)}\right|^p}\right)^{\frac{1}{p}} Lp(xi,xj)=(l=1n xi(l)xj(l) p)p1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FR0NLtYn-1640089789079)(assets/fig3_2.png)]
考虑二维的情况, 上图给出了不同的 p p p值情况下与原点距离为1的点的图形。

这个图有几点理解下:

  1. 与原点的距离
  2. 与原点距离为1的点
  3. 前一点换个表达方式, 图中的点向量( x 1 x_1 x1, x 2 x_2 x2)的 p p p范数都为1
  4. 图中包含多条曲线, 关于p=1并没有对称关系
  5. 定义中 p ⩾ 1 p\geqslant1 p1,这一组曲线中刚好是凸的
    补充:
    范数是对向量或者矩阵的度量,是一个标量,这个里面两个点之间的 L p L_p Lp距离可以认为是两个点坐标差值的 p p p范数。

k k k值选择

  1. 关于 k k k大小对预测结果的影响, 书中给的参考文献是ESL, 这本书还有个先导书叫ISL.
  2. 通过交叉验证选取最优 k k k, 算是超参数
  3. 二分类问题, k k k选择奇数有助于避免平票

分类决策规则

Majority Voting Rule
误分类率:
1 k ∑ x i ∈ N k ( x ) I ( y i ≠ c i ) = 1 − 1 k ∑ x i ∈ N k ( x ) I ( y i = c i ) \frac{1}{k}\sum_{x_i\in N_k(x)}{I(y_i\ne c_i)}=1-\frac{1}{k}\sum_{x_i\in N_k(x)}{I(y_i= c_i)} k1xiNk(x)I(yi=ci)=1k1xiNk(x)I(yi=ci)

如果分类损失函数是0-1损失, 误分类率最低即经验风险最小.

构造KDTree

KDTree的构建是一个递归的过程
注意: KDTree左边的点比父节点小,右边的点比父节点大。
这里面有提到,KDTree搜索时效率未必是最优的,这个和样本分布有关系。随机分布样本KDTree搜索(这里应该是近邻搜索)的平均计算复杂度是 O ( log ⁡ N ) O(\log N) O(logN),空间维数 K K K接近训练样本数 N N N时,搜索效率急速下降,几乎 O ( N ) O(N) O(N)
看维度,如果维度比较高,搜索效率很低。当然,在考虑维度的同时也要考虑样本的规模。
考虑个例子

[[1, 1],
 [2, 1],
 [3, 1],
 [4, 1],
 [5, 1],
 [6, 1],
 [100, 1][1000, 1]]
k k k近邻查找

KNN查找已知查询点 p p p,树当前节点 o o o,近邻数目 k k k

可以用一个优先队列存储最优的 k k k个点,每次比对回溯节点是否比当前最优点更优的时候,就只需用当前最优中距离 p p p最远的节点来对比,而这个工作对于优先队列来说是 O ( 1 ) O(1) O(1)的[^3]

范围查询

给定一个范围,问其中有多少点。比较常见的应用是GIS类应用,使用者附近多大半径内包含多少单车,多少酒店等。
实际上为了实现快速搜索, 在空间数据的存储结构上要有考虑。
这段代码实现了一个简单的 K 最近邻(KNN)算法,使用了 KD 树(K-Dimensional Tree)来加速最近邻搜索。以下是对代码的逐行解释:

代码结构

  1. 导入必要的库

    from collections import namedtuple
    from pprint import pformat
    import numpy as np
    
    • namedtuple 用于创建轻量级的类,便于存储节点信息。
    • pformat 用于格式化输出。
    • numpy 是一个用于数值计算的库,提供了高效的数组操作。
  2. 定义 Node 类

    class Node(namedtuple('Node', 'location left_child right_child')):
        def __repr__(self):
            return pformat(tuple(self))
    
    • Node 类表示 KD 树中的一个节点,包含三个属性:
      • location:节点的位置(数据点)。
      • left_child:左子树。
      • right_child:右子树。
    • __repr__ 方法用于格式化节点的输出,便于调试。
  3. 定义 KNN 类

    class KNN(object):
    
    • 定义一个名为 KNN 的类,表示 K 最近邻算法的实现。
  4. 初始化方法

    def __init__(self, k=1, p=2):
        self.k = k
        self.p = p
        self.kdtree = None
    
    • __init__ 方法接受两个参数:
      • k:表示要查找的最近邻的数量,默认为 1。
      • p:表示距离度量的阶数,默认为 2(欧几里得距离)。
    • self.kdtree 用于存储构建的 KD 树。
  5. 构建 KD 树

    @staticmethod
    def _fit(X, depth=0):
        try:
            k = X.shape[1]
        except IndexError as e:
            return None
        axis = depth % k
        X = X[X[:, axis].argsort()]
        median = X.shape[0] // 2
    
        try:
            X[median]
        except IndexError:
            return None
    
        return Node(
            location=X[median],
            left_child=KNN._fit(X[:median], depth + 1),
            right_child=KNN._fit(X[median + 1:], depth + 1)
        )
    
    • _fit 方法是一个静态方法,用于递归构建 KD 树。
    • depth 参数用于跟踪当前的深度,以确定在哪个维度上进行分割。
    • 通过 X.shape[1] 获取数据的维度 k k k
    • 使用 depth % k 确定当前分割的轴。
    • 将数据按当前轴排序,并找到中位数。
    • 创建一个 Node,将中位数作为节点位置,并递归构建左子树和右子树。
  6. 计算距离

    def _distance(self, x, y):
        return np.linalg.norm(x - y, ord=self.p)
    
    • _distance 方法计算两个点之间的距离,使用 NumPy 的 linalg.norm 函数,根据给定的 p p p 值计算距离。
  7. 搜索最近邻

    def _search(self, point, tree=None, depth=0, best=None):
        if tree is None:
            return best
    
        k = point.shape[0]
        if best is None or self._distance(point, tree.location) < self._distance(best, tree.location):
            next_best = tree.location
        else:
            next_best = best
    
        if point[depth % k] < tree.location[depth % k]:
            next_branch = tree.left_child
        else:
            next_branch = tree.right_child
        return self._search(point, tree=next_branch, depth=depth + 1, best=next_best)
    
    • _search 方法用于在 KD 树中查找最近邻。
    • 如果树为空,返回当前最佳结果。
    • 更新最佳结果 next_best,如果当前节点比最佳结果更接近目标点。
    • 根据当前点在当前维度上的值决定搜索左子树还是右子树。
    • 递归调用 _search 方法,继续在选定的子树中查找。
  8. 训练模型

    def fit(self, X):
        self.kdtree = KNN._fit(X)
        return self.kdtree
    
    • fit 方法用于训练模型,构建 KD 树并存储在 self.kdtree 中。
  9. 预测最近邻

    def predict(self, X):
        rst = self._search(X, self.kdtree)
        return rst
    
    • predict 方法用于预测给定点的最近邻,调用 _search 方法进行查找。

总结

这段代码实现了一个基于 KD 树的 K 最近邻算法。通过构建 KD 树,能够高效地进行最近邻搜索。该实现包括了 KD 树的构建、距离计算和搜索功能,适合用于处理多维数据的最近邻查询。

from collections import namedtuple
from pprint import pformat
import numpy as np


class Node(namedtuple('Node', 'location left_child right_child')):
    def __repr__(self):
        return pformat(tuple(self))


class KNN(object):

    def __init__(self,
                 k=1,
                 p=2):
        """

        :param k: knn
        :param p:
        """
        self.k = k
        self.p = p
        self.kdtree = None

    @staticmethod
    def _fit(X, depth=0):
        try:
            k = X.shape[1]
        except IndexError as e:
            return None
        # todo: 这里可以展开,通过方差选择
        axis = depth % k
        X = X[X[:, axis].argsort()]
        median = X.shape[0] // 2

        try:
            X[median]
        except IndexError:
            return None

        return Node(
            location=X[median],
            left_child=KNN._fit(X[:median], depth + 1),
            right_child=KNN._fit(X[median + 1:], depth + 1)
        )

    def _distance(self, x, y):
        return np.linalg.norm(x-y, ord=self.p)

    def _search(self, point, tree=None, depth=0, best=None):
        if tree is None:
            return best

        k = point.shape[0]
        # update best
        if best is None or self._distance(point, tree.location) < self._distance(best, tree.location):
            next_best = tree.location
        else:
            next_best = best

        # update branch
        if point[depth%k] < tree.location[depth%k]:
            next_branch = tree.left_child
        else:
            next_branch = tree.right_child
        return self._search(point, tree=next_branch, depth=depth+1, best=next_best)

    def fit(self, X):
        self.kdtree = KNN._fit(X)
        return self.kdtree

    def predict(self, X):
        rst = self._search(X, self.kdtree)
        return rst

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

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

相关文章

深度学习——强化学习算法介绍

强化学习算法介绍 强化学习讨论的问题是一个智能体(agent) 怎么在一个复杂不确定的环境(environment)里面去极大化它能获得的奖励。 强化学习和监督学习 强化学习有这个试错探索(trial-and-error exploration)&#xff0c;它需要通过探索环境来获取对环境的理解。强化学习 ag…

嵌入式全栈开发学习笔记---C++(继承和派生)

目录 继承的概念inherit 继承的使用场景 继承的权限 继承对象的模型 继承中的构造和析构 初始化列表的第三个使用场景 场景1&#xff1a;类成员变量被const修饰&#xff1b; 场景2&#xff1a;类对象作为另一个类的成员变量&#xff0c;同时该类没有提供无参构造函数&a…

刷题记录-HOT 100(三)

链表 1、环形链表找环起始结点 使用快慢指针检测环&#xff1a; 初始化两个指针 slow 和 fast&#xff0c;都指向链表的头节点。slow 每次移动一步&#xff0c;fast 每次移动两步。如果 fast 和 slow 相遇&#xff08;即 fast slow&#xff09;&#xff0c;说明链表中存在环。…

探讨马丁格尔策略应用的3问和昂首平台的3答

问&#xff1a;为什么在使用马丁格尔策略时要如此谨慎?毕竟最大的市场波动可能根本不会发生。 答&#xff1a;让我们以一个具体的例子来说明这个问题。假设我们进行交易&#xff0c;计算出一个小于最大预期值的市场动量&#xff0c;比如说这个值为90便士。试想&#xff0c;如…

C#笔记6 网络编程基础,解释端口套接字,代码实例分析DNS,IPAddress等类

一、计算机网络基础 这一点毋庸置疑&#xff0c;想要写一个使用网络接口传输数据的程序&#xff0c;不知道计算机网络的基本知识是很难的。 局域网与广域网 所谓的WAN和LAN其实就是网络的一个范围界定。WAN为广域网&#xff0c;中间会包含更多的互联网设备&#xff0c;由无数…

OpenAI正在努力解决其面临的版权问题

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Web大学生网页作业成品——心理健康教育介绍网页设计与实现(HTML+CSS+JS)(4个页面)

&#x1f389;&#x1f389;&#x1f389; 常见网页设计作业题材有**汽车、环保、明星、文化、国家、抗疫、景点、人物、体育、植物、公益、图书、节日、游戏、商城、旅游、家乡、学校、电影、动漫、非遗、动物、个人、企业、美食、婚纱、其他**等网页设计题目, 可满足大学生网…

Redis Zset 类型:Score 属性在数据排序中的作用

Zset 有序集合 一 . zset 的引入二 . 常见命令2.1 zadd、zrange2.2 zcard2.3 zcount2.4 zrevrange、zrangebyscore2.5 zpopmax、zpopmin2.6 bzpopmax、bzpopmin2.7 zrank、zrevrank2.8 zscore2.9 zrem、zremrangebyrank、zremrangebyscore2.10 zincrby2.11 集合间操作交集 : zi…

自动化运维之SaltStack 部署应用

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

009.数据库管理-数据字典动态性能视图

我 的 个 人 主 页&#xff1a;&#x1f449;&#x1f449; 失心疯的个人主页 &#x1f448;&#x1f448; 入 门 教 程 推 荐 &#xff1a;&#x1f449;&#x1f449; Python零基础入门教程合集 &#x1f448;&#x1f448; 虚 拟 环 境 搭 建 &#xff1a;&#x1f449;&…

唯众发布职业院校信创实训室解决方案 助力职教数字化高质量发展

在数字化转型的大潮中&#xff0c;教育行业正迎来前所未有的变革机遇。为了积极响应国家关于自主可控、信息技术应用创新&#xff08;信创&#xff09;的战略部署&#xff0c;唯众近日发布了专为职业院校量身定制的信创实训室解决方案&#xff0c;旨在通过先进的技术平台和丰富…

摄影竞赛系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;教师管理&#xff0c;学生管理&#xff0c;辅导员管理&#xff0c;项目信息管理&#xff0c;作品信息管理&#xff0c;留言板管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#…

代码随想录刷题day21丨669. 修剪二叉搜索树,108.将有序数组转换为二叉搜索树,538.把二叉搜索树转换为累加树,二叉树总结

代码随想录刷题day21丨669. 修剪二叉搜索树&#xff0c;108.将有序数组转换为二叉搜索树&#xff0c;538.把二叉搜索树转换为累加树&#xff0c;二叉树总结 1.题目 1.1修剪二叉搜索树 题目链接&#xff1a;669. 修剪二叉搜索树 - 力扣&#xff08;LeetCode&#xff09; 视频…

bootstrap下拉多选框

1、引用(引用资源下载) <!-- Latest compiled and minified CSS --> <link rel"stylesheet" href"static/css/bootstrap-select.min.css"> <!-- Latest compiled and minified JavaScript --> <script src"static/js/bootstrap…

golang-开发工具及package

1. 开发工具 工欲善其事&#xff0c;必先利其器&#xff0c;我选择vscode&#xff0c;其它的工具比如goland也不错 下载地址&#xff1a;Download Visual Studio Code - Mac, Linux, Windows 我的环境是是debian linux&#xff0c;所以我下载deb包&#xff0c;下载完成后&am…

CTFHub技能树-备份文件下载-vim缓存

目录 方法一&#xff1a;直接浏览器访问 方法二&#xff1a;使用kali恢复vim缓存文件 方法三&#xff1a;直接使用curl访问 最后同样备份文件系列的都可用dirsearch扫描 当开发人员在线上环境中使用 vim 编辑器&#xff0c;在使用过程中会留下 vim 编辑器缓存&#xff0c;当…

江科大/江协科技 STM32学习笔记P30

文章目录 一、FlyMcu串口下载1、串口下载的流程2、串口烧录的选项字节区 二、STLINK Utility 一、FlyMcu串口下载 1、串口下载的流程 例如机器人给自己换电池&#xff0c;需要拆掉旧电池再装上新电池&#xff0c;为了实现这个步骤需要再做一个小机器人&#xff0c;需要换电池时…

WinCC Modbus TCP 通信

概述 从版本WinCC V7.0 开始&#xff0c;WinCC支持Modbus TCP通讯&#xff0c;WinCC中的Modbus TCP驱动主要是针对施耐德PLC开发的&#xff0c;支持的PLC类型如下&#xff1a; 图1 本文档以Quantum CPU651和 Premium P57为例&#xff0c;介绍WinCC V7.2 的Modbus TCP通讯的组…

随手记:小程序体积超出2M包大小如何优化

小程序的包体积限制是2M&#xff0c;超出包大小如何优化 先简单列出&#xff0c;最近比较忙&#xff0c;后续优化明细&#xff0c;有着急的先留言踢我 1.分包 留几个主要的页面体积小的&#xff0c;剩下的在page.json中拆到subpackages中&#xff0c;简单举个例子 "page…

总结一下windows电脑字体模糊的优化方案

问题&#xff1a;谷歌浏览器上页面显示的字体非常细&#xff0c;有点费眼睛了&#x1f47e; 解决方案&#xff1a; 方案1&#xff1a;手动调整ClearType文本。方案2&#xff1a;英伟达显卡控制面板->管理3d设置->关闭全局平滑FXAA&#xff08;如果某个软件需要使用平滑处…