K 近邻、K-NN 算法图文详解

news2024/12/25 12:30:45

1. 为什么学习KNN算法

KNN是监督学习分类算法,主要解决现实生活中分类问题。根据目标的不同将监督学习任务分为了分类学习及回归预测问题。

KNN(K-Nearest Neihbor,KNN)K近邻是机器学习算法中理论最简单,最好理解的算法,是一个非常适合入门的算法,拥有如下特性:

  • 思想极度简单,应用数学知识少(近乎为零),对于很多不擅长数学的小伙伴十分友好
  • 虽然算法简单,但效果也不错

2. KNN 原理

在这里插入图片描述
上图中每一个数据点代表一个肿瘤病历:

  • 横轴表示肿瘤大小,纵轴表示发现时间
  • 恶性肿瘤用蓝色表示,良性肿瘤用红色表示

疑问:新来了一个病人(下图绿色的点),如何判断新来的病人(即绿色点)是良性肿瘤还是恶性肿瘤?

在这里插入图片描述

解决方法:k-近邻算法的做法如下:
(1)取一个值k=3(k值后面介绍,现在可以理解为算法的使用者根据经验取的最优值)
(2)在所有的点中找到距离绿色点最近的三个点
(3)让最近的点所属的类别进行投票
(4)最近的三个点都是蓝色的,所以该病人对应的应该也是蓝色,即恶性肿瘤。


3. 距离度量方法

机器学习算法中,经常需要 判断两个样本之间是否相似 ,比如KNN,K-means,推荐算法中的协同过滤等等,常用的套路是 将相似的判断转换成距离的计算 ,距离近的样本相似程度高,距离远的相似程度低。所以度量距离是很多算法中的关键步骤。

KNN算法中要求数据的所有特征都用数值表示。若在数据特征中存在非数值类型,必须采用手段将其进行量化为数值。

  • 比如样本特征中包含有颜色(红、绿、蓝)一项,颜色之间没有距离可言,可通过将颜色转化为 灰度值来实现距离计算
  • 每个特征都用数值表示,样本之间就可以计算出彼此的距离来
3.1 欧式距离

在这里插入图片描述

3.2 曼哈顿距离

在这里插入图片描述

3.3 切比雪夫距离(了解)
3.4 闵式距离

闵氏距离不是一种距离,而是一组距离的定义,是对多个距离度量公式的概括性的表述。
在这里插入图片描述

其中p是一个变参数:

  • 当 p=1 时,就是曼哈顿距离;
  • 当 p=2 时,就是欧氏距离;
  • 当 p→∞ 时,就是切比雪夫距离。

根据 p 的不同,闵氏距离可以表示某一类/种的距离。


4. 归一化和标准化

样本中有多个特征,每一个特征都有自己的定义域和取值范围,他们对距离计算也是不同的,如取值较大的影响力会盖过取值较小的参数。因此,为了公平,样本参数必须做一些归一化处理,将不同的特征都缩放到相同的区间或者分布内。
在这里插入图片描述

4.1 归一化
from sklearn.preprocessing import MinMaxScaler

# 1. 准备数据
data = [[90, 2, 10, 40],
        [60, 4, 15, 45],
        [75, 3, 13, 46]]
# 2. 初始化归一化对象
transformer = MinMaxScaler()
# 3. 对原始特征进行变换
data = transformer.fit_transform(data)
# 4. 打印归一化后的结果
print(data)

归一化受到最大值与最小值的影响,这种方法容易受到异常数据的影响, 鲁棒性较差,适合传统精确小数据场景

4.2 标准化
from sklearn.preprocessing import StandardScaler

# 1. 准备数据
data = [[90, 2, 10, 40],
        [60, 4, 15, 45],
        [75, 3, 13, 46]]
# 2. 初始化标准化对象
transformer = StandardScaler()
# 3. 对原始特征进行变换
data = transformer.fit_transform(data)
# 4. 打印归一化后的结果
print(data)

对于标准化来说,如果出现异常点,由于具有一定数据量,少量的异常点对于平均值的影响并不大


5. K 值选择问题

KNN算法的关键是什么?

答案一定是K值的选择,下图中K=3,属于红色三角形,K=5属于蓝色的正方形。这个时候就是K选择困难的时候。

在这里插入图片描述

使用 scikit-learn 提供的 GridSearchCV 工具, 配合交叉验证法可以搜索参数组合.

from sklearn.datasets import load_iris
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.model_selection import GridSearchCV


# 1. 加载数据集
x, y = load_iris(return_X_y=True)

# 2. 分割数据集
x_train, x_test, y_train, y_test = \
    train_test_split(x, y, test_size=0.2, stratify=y, random_state=0)

# 3. 创建网格搜索对象
estimator = KNeighborsClassifier()
param_grid = {'n_neighbors': [1, 3, 5, 7]}
estimator = GridSearchCV(estimator, param_grid=param_grid, cv=5, verbose=0)
estimator.fit(x_train, y_train)

# 4. 打印最优参数
print('最优参数组合:', estimator.best_params_, '最好得分:', estimator.best_score_)

# 4. 测试集评估模型
print('测试集准确率:', estimator.score(x_test, y_test))

6. 数据集划分

为了能够评估模型的泛化能力,可以通过实验测试对学习器的泛化能力进行评估,进而做出选择。因此需要使用一个 “测试集” 来测试学习器对新样本的判别能力,以测试集上的 “测试误差” 作为泛化误差的近似。

6.1 留出法(简单交叉验证)

留出法 (hold-out) 将数据集 D 划分为两个互斥的集合,其中一个集合作为训练集 S,另一个作为测试集 T。

from sklearn.model_selection import train_test_split
from sklearn.model_selection import StratifiedShuffleSplit
from sklearn.model_selection import ShuffleSplit
from collections import Counter
from sklearn.datasets import load_iris


def test01():

    # 1. 加载数据集
    x, y = load_iris(return_X_y=True)
    print('原始类别比例:', Counter(y))

    # 2. 留出法(随机分割)
    x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2)
    print('随机类别分割:', Counter(y_train), Counter(y_test))

    # 3. 留出法(分层分割)
    x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, stratify=y)
    print('分层类别分割:', Counter(y_train), Counter(y_test))


def test02():

    # 1. 加载数据集
    x, y = load_iris(return_X_y=True)
    print('原始类别比例:', Counter(y))
    print('*' * 40)

    # 2. 多次划分(随机分割)
    spliter = ShuffleSplit(n_splits=5, test_size=0.2, random_state=0)
    for train, test in spliter.split(x, y):
        print('随机多次分割:', Counter(y[test]))

    print('*' * 40)

    # 3. 多次划分(分层分割)
    spliter = StratifiedShuffleSplit(n_splits=5, test_size=0.2, random_state=0)
    for train, test in spliter.split(x, y):
        print('分层多次分割:', Counter(y[test]))


if __name__ == '__main__':
    test01()
    test02()
6.2 交叉验证法

K-Fold交叉验证,将数据随机且均匀地分成k分,每次使用k-1份数据作为训练,而使用剩下的一份数据进行测试

from sklearn.model_selection import KFold
from sklearn.model_selection import StratifiedKFold
from collections import Counter
from sklearn.datasets import load_iris

def test():

    # 1. 加载数据集
    x, y = load_iris(return_X_y=True)
    print('原始类别比例:', Counter(y))
    print('*' * 40)

    # 2. 随机交叉验证
    spliter = KFold(n_splits=5, shuffle=True, random_state=0)
    for train, test in spliter.split(x, y):
        print('随机交叉验证:', Counter(y[test]))

    print('*' * 40)

    # 3. 分层交叉验证
    spliter = StratifiedKFold(n_splits=5, shuffle=True, random_state=0)
    for train, test in spliter.split(x, y):
        print('分层交叉验证:', Counter(y[test]))


if __name__ == '__main__':
    test()
6.3 留一法

留一法( Leave-One-Out,简称LOO),即每次抽取一个样本做为测试集。

from sklearn.model_selection import LeaveOneOut
from sklearn.model_selection import LeavePOut
from sklearn.datasets import load_iris
from collections import Counter


def test01():

    # 1. 加载数据集
    x, y = load_iris(return_X_y=True)
    print('原始类别比例:', Counter(y))
    print('*' * 40)

    # 2. 留一法
    spliter = LeaveOneOut()
    for train, test in spliter.split(x, y):
        print('训练集:', len(train), '测试集:', len(test), test)

    print('*' * 40)

    # 3. 留P法
    spliter = LeavePOut(p=3)
    for train, test in spliter.split(x, y):
        print('训练集:', len(train), '测试集:', len(test), test)


if __name__ == '__main__':
    test01()
6.4 自助法

每次随机从D中抽出一个样本,将其拷贝放入D,然后再将该样本放回初始数据集D中,使得该样本在下次采样时仍有可能被抽到;
这个过程重复执行m次后,我们就得到了包含m个样本的数据集D′,这就是自助采样的结果。

import pandas as pd

if __name__ == '__main__':

    # 1. 构造数据集
    data = [[90, 2, 10, 40],
            [60, 4, 15, 45],
            [75, 3, 13, 46],
            [78, 2, 64, 22]]

    data = pd.DataFrame(data)
    print('数据集:\n',data)
    print('*' * 30)

    # 2. 产生训练集
    train = data.sample(frac=1, replace=True)
    print('训练集:\n', train)

    print('*' * 30)

    # 3. 产生测试集
    test = data.loc[data.index.difference(train.index)]
    print('测试集:\n', test)

7. 可执行示例代码

以下是 K-NN 算法的实现示例代码,使用 scikit-learn 库:

import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score

# 示例数据
X = np.array([[1, 2], [2, 3], [3, 4], [6, 7], [7, 8], [8, 9]])
y = np.array([0, 0, 0, 1, 1, 1])

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42)

# 创建KNN分类器
knn = KNeighborsClassifier(n_neighbors=3)

# 训练模型(实际上只是存储数据)
knn.fit(X_train, y_train)

# 进行预测
y_pred = knn.predict(X_test)

# 计算准确率,分类算法的评估
accuracy = accuracy_score(y_test, y_pred)
print("Accuracy:", accuracy)

通过这个示例,可以看到 K-NN 算法的基本流程和实现。该算法通过计算距离来进行分类,并可以通过调整 K 值来优化模型性能。


8. K-NN 算法总结

K-NN(K-Nearest Neighbors)算法是一种基于实例的学习方法,用于分类和回归。它通过计算样本与训练集中所有样本之间的距离,选择最近的 K 个邻居,然后根据这些邻居的标签进行预测。

特点
  1. 基于实例:没有显式的训练过程,直接使用训练数据进行预测。
  2. 懒惰学习:训练阶段只是存储数据,实际的计算发生在预测阶段。
  3. 非参数化:不对数据进行任何假设。
优点
  1. 简单易实现:实现起来相对简单,理解容易。
  2. 无需假设数据分布:对数据的分布没有任何假设。
  3. 适用于分类和回归:可以同时用于分类和回归问题。
  4. 灵活性:可以处理多类别分类问题。
缺点
  1. 计算复杂度高:预测时需要计算新样本与所有训练样本的距离,计算量大,尤其是数据量大时。
  2. 存储复杂度高:需要存储所有的训练数据。
  3. 对噪音敏感:容易受到噪音和异常值的影响。
  4. 维度灾难:高维数据时,计算距离的效果会变差,需要进行降维处理。
关键
  1. 选择合适的 K 值:K 值过小容易过拟合,K 值过大容易欠拟合。通常通过交叉验证选择合适的 K 值。
  2. 距离度量:常用的距离度量方法有欧氏距离、曼哈顿距离、闵可夫斯基距离等。
  3. 特征缩放:在计算距离前,需要对特征进行标准化或归一化处理,以避免特征值范围差异导致的计算偏差。
过程
  1. 数据准备:准备训练数据集和测试数据集。
  2. 计算距离:对于每个测试样本,计算它与所有训练样本之间的距离。
  3. 选择邻居:选择距离最近的 K 个邻居。
  4. 投票或平均
    • 分类:对 K 个邻居的类别进行投票,选择出现次数最多的类别作为预测结果。
    • 回归:对 K 个邻居的目标值进行平均,作为预测结果。
  5. 输出结果:输出测试样本的预测结果。

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

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

相关文章

利用python爬取上证指数股吧评论并保存到mongodb数据库

大家好,我是带我去滑雪! 东方财富网是中国领先的金融服务网站之一,以提供全面的金融市场数据、资讯和交易工具而闻名。其受欢迎的“股吧”论坛特别适合爬取股票评论,东方财富网的股吧聚集了大量投资者和金融分析师,他们…

50-2 内网信息收集 - 内网工作环境(域相关知识)

一、工作组 工作组(Work Group)是局域网中最基本的资源管理模式,适用于小规模网络环境。 工作组的定义: 工作组是将不同功能或部门的计算机分组管理的方式。它提供了层次化的网络资源管理,使得组织内的计算机可以按照功能或部门分类。每个工作组有一个自定义的主机名称,…

Java学习【IO流:深入理解与应用(上)】

Java学习【IO流:深入理解与应用(上)】 🍃1.IO流体系结构🍃2.FileOutputStream🍁2.1FileOutputStream写数据的三种方式🍁2.2换行和续写 🍃3.FileInputStream🍁3.1每次读取…

电脑文件kernel32.dll缺失要怎么处理?怎么才能一键修复kernel32.dll文件

关键系统文件kernel32.dll的缺失,这种情况不仅会导致系统运行不稳定,甚至可能完全无法启动某些应用程序。kernel32.dll 是一个至关重要的动态链接库文件,它与Windows操作系统的多个基本操作相关联,包括内存管理、进程和线程的控制…

java热部署idea插件「jrebel安装教程」

告别漫长的项目重启等待,让开发像写诗一样流畅~ jrebel安装包下载 jrebel版本需要下比较老的版本,我用的是22.4.1的版本(如果不差钱,可以支持一下正版,直接选择最新的版本即可) 下载地址:传送门…

.NET周刊【6月第4期 2024-06-23】

国内文章 C#.Net筑基-集合知识全解 https://www.cnblogs.com/anding/p/18229596 .Net中提供了数组、列表、字典等多种集合类型,分为泛型和非泛型集合。泛型集合具有更好的性能和类型安全性。集合的基础接口包括IEnumerator、IEnumerable、ICollection、IList、ID…

WPF UI交互专题 界面结构化处理 查看分析工具Snoopy 逻辑树与视觉树 平面图像 平面图形 几何图形 弧线 01

1、开发学习环境 2、XAML界面结构化处理 3、逻辑树与视觉树 4、基于XAML的标签扩展方式 5、基础控件应用分析 6、控件常用属性与事件总结 7、常用控件特别属性说明 8、平面图形控件与属性 9、平面几何图形 10、弧线的处理过程 WPF项目-XAML 项目表现形式 项目结…

HarmonyOS APP应用开发项目- MCA助手(持续更新中~)

简言: gitee地址:https://gitee.com/whltaoin_admin/money-controller-app.git端云一体化开发在线文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/agc-harmonyos-clouddev-view-0000001700053733-V5 注&#xff1…

揭示隐藏的模式:秩和检验和单因素方差分析的实战指南【考题】

1.研究一种新方法对于某实验结果准确性提高的效果,并将其与原有方法进行比较,结果见下表,请评价两者是否有不同? (行无序,列有序)-->单方向有序-->两独立样本的秩和检验) 如下图所示,先将相关数据导入spss。 图…

武汉星起航:一站式服务,助力亚马逊卖家高效运营,实现收益飞跃

在跨境电商的浪潮中,武汉星起航电子商务有限公司以其独特的一站式跨境电商服务,为众多亚马逊卖家提供了强有力的支持,助力他们在不断发展的市场中脱颖而出,实现收益的大幅提升。 武汉星起航的一站式跨境电商服务,以其…

Linux /proc目录总结

1、概念 在Linux系统中,/proc目录是一个特殊的文件系统,通常被称为"proc文件系统"或"procfs"。这个文件系统以文件系统的方式为内核与进程之间的通信提供了一个接口。/proc目录中的文件大多数都提供了关于系统状态的信息&#xff0…

计算Dice损失的函数

计算Dice损失的函数 def Dice_loss(inputs, target, beta1, smooth 1e-5):n,c, h, w inputs.size() #nt,ht, wt, ct target.size() #nt,if h ! ht and w ! wt:inputs F.interpolate(inputs, size(ht, wt), mode"bilinear", align_cornersTrue)temp_inputs t…

MySQL之如何分析慢查询

1、一个SQL语句执行很慢,如何分析? 可使用“explain”或者“desc”命令获取MySQL如何执行select语句的信息。 语法:直接在select语句前加关键字 explain或desc explain select job_desc from xxl_job_info where id 1; 2、执行计划中五个重…

【刷题】初步认识深搜(DFS)

送给大家一句话: 拥有希望的人,和漫天的星星一样,是永远不会孤独的。 -- 《星游记》 初步认识深搜(DFS) dfs算法二叉树中的深搜Leetcode 129. 求根节点到叶节点数字之和题目描述算法思路 Leetcode 814. 二叉树剪枝题…

FreeRTOS的裁剪与移植

文章目录 1 FreeRTOS裁剪与移植1.1 FreeRTOS基础1.1.1 RTOS与GPOS1.1.2 堆与栈1.1.3 FreeRTOS核心文件1.1.4 FreeRTOS语法 1.2 FreeRTOS移植和裁剪 1 FreeRTOS裁剪与移植 1.1 FreeRTOS基础 1.1.1 RTOS与GPOS ​ 实时操作系统(RTOS):是指当…

C语言中常用的运算符、表达式和语句

C语言是一种通用的、高级的编程语言,其历史可以追溯到20世纪60年代末至70年代初。C语言最初是由丹尼斯里奇(Dennis Ritchie)在贝尔实验室为开发UNIX操作系统而设计的。它继承了许多B语言的特性,而B语言则是由迷糊老师(…

基于YOLOv9+pyside的安检仪x光危险物物品检测(有ui)

安全检查在公共场所确保人身安全的关键环节,不可或缺。X光安检机作为必要工具,在此过程中发挥着重要作用。然而,其依赖人工监控和判断成像的特性限制了其应用效能。本文以此为出发点,探索了基于Torch框架的YOLO算法在安检X光图像中…

spring和springboot的关系是什么?

大家好,我是网创有方的站长,今天给大家分享下spring和springboot的关系是什么? Spring和Spring Boot之间的关系可以归纳为以下几个方面: 技术基础和核心特性: Spring:是一个广泛应用的开源Java框架&#…

深入理解一致性Hash和虚拟节点

在分布式系统中架构中我们经常提到一致性哈希算法,那么什么是一致性哈希算法,为什么需要一致性哈希算法呢? 1、为什么需要一致性哈希算法 假设现在有三台缓存服务器(缓存服务器A、缓存服务器B、缓存服务器C)&#xff…

每日一题——Python实现PAT乙级1059 C语言竞赛(举一反三+思想解读+逐步优化)四千字好文

一个认为一切根源都是“自己不够强”的INTJ 个人主页:用哲学编程-CSDN博客专栏:每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 我的写法 时间复杂度分析 空间复杂度分析 代码优化建议 总结 我要更强 优化方法…