一、生活中的 "分类难题" 与 k 近邻的灵感

你有没有这样的经历:在超市看到一种从没见过的水果,表皮黄黄的,拳头大小,形状圆滚滚。正当你犹豫要不要买时,突然想起外婆家的橘子好像就是这个样子 —— 黄色、圆形、大小和拳头差不多。于是你推断:"这应该是橘子吧!"

其实,这个看似平常的判断过程,竟然藏着机器学习中最经典的分类算法 ——k 近邻(k-Nearest Neighbors,简称 kNN)的核心思想!

1.1 现实中的解法拆解

当我们判断未知水果时,大脑会自动完成三个步骤:

  1. 收集特征:观察颜色(黄色)、形状(圆形)、大小(拳头大)
  1. 匹配经验:调动记忆中 "橘子" 的特征库(黄色、圆形、拳头大)
  1. 做出判断:因为新水果的特征和记忆中的橘子最像,所以归类为橘子

这和 k 近邻算法的工作流程惊人地相似!唯一的区别是:计算机需要我们把这些 "看得到的特征" 变成 "算得出的数据"。

1.2 k 近邻算法的有趣灵魂

k 近邻算法的有趣之处在于它的 "懒惰" 和 "实在":

  • 懒惰:它不像其他算法那样先总结规律(比如 "黄色圆形水果都是橘子"),而是等到需要判断时才去比对已知数据
  • 实在:它的判断逻辑简单粗暴 ——"少数服从多数",看新样本周围最像的 k 个样本里哪种类型占多数

就像你纠结新水果是橘子还是苹果时,会找 5 个见过这两种水果的人投票,哪种意见多就信哪种。

二、从生活到代码:k 近邻算法的实现之路

我们用一个具体案例来实现:根据 "颜色深度"(0-10,数值越大越黄)和 "大小"(0-10,数值越大越大)两个特征,判断水果是橘子(标签 1)还是苹果(标签 0)。

2.1 准备数据:把生活观察变成数字

首先,我们需要把已知的水果数据整理成计算机能理解的格式:

# 导入必要的库

import numpy as np # 用于数值计算

import matplotlib.pyplot as plt # 用于画图

# 已知水果数据:[颜色深度, 大小],标签:0=苹果,1=橘子

# 想象这些数据来自我们之前见过的水果:

# 苹果通常偏红(颜色深度小),大小不一;橘子偏黄(颜色深度大)

known_fruits = np.array([

[2, 3], # 苹果:颜色偏红(2),小个(3)

[3, 4], # 苹果:颜色较红(3),中个(4)

[1, 5], # 苹果:颜色很红(1),大个(5)

[7, 6], # 橘子:颜色较黄(7),中个(6)

[8, 5], # 橘子:颜色很黄(8),中个(5)

[9, 4] # 橘子:颜色极黄(9),小个(4)

])

# 对应的标签:0代表苹果,1代表橘子

labels = np.array([0, 0, 0, 1, 1, 1])

# 未知水果:颜色深度6,大小5(就是我们在超市看到的那个)

unknown_fruit = np.array([6, 5])

2.2 数据可视化:让计算机 "看见" 差异

我们用散点图把数据画出来,直观感受苹果和橘子的特征差异:

# 绘制已知水果

plt.scatter(known_fruits[labels==0, 0], known_fruits[labels==0, 1],

color='red', marker='o', label='苹果') # 苹果标为红色圆点

plt.scatter(known_fruits[labels==1, 0], known_fruits[labels==1, 1],

color='orange', marker='o', label='橘子') # 橘子标为橙色圆点

# 绘制未知水果(用五角星标记)

plt.scatter(unknown_fruit[0], unknown_fruit[1],

color='purple', marker='*', s=200, label='未知水果') # 紫色五角星,放大显示

# 加上坐标轴标签和标题

plt.xlabel('颜色深度(0-10,数值越大越黄)')

plt.ylabel('大小(0-10,数值越大越大)')

plt.title('水果特征分布图')

plt.legend() # 显示图例

plt.show() # 展示图像

运行这段代码,你会看到:红色圆点(苹果)集中在左侧(颜色偏红),橙色圆点(橘子)集中在右侧(颜色偏黄),而紫色五角星(未知水果)刚好在橘子群附近 —— 这就是我们肉眼判断的依据!

三、k 近邻算法的核心步骤:用数学实现 "投票选举"

计算机怎么判断未知水果的类别呢?它会执行四个关键步骤,我们一步步用代码实现:

3.1 第一步:计算距离(谁离我最近?)

生活中我们靠 "感觉" 判断相似,计算机则靠 "距离" 计算。最常用的是欧氏距离(就像直尺测量两点距离):

\(distance = \sqrt{(x_1-x_2)^2 + (y_1-y_2)^2}\)

用代码实现这个计算:

def calculate_distance(known_point, unknown_point):

"""

计算两个点之间的欧氏距离

参数:

known_point:已知点的特征(如[2,3])

unknown_point:未知点的特征(如[6,5])

返回:

两点之间的距离

"""

# 计算每个特征的差值平方,再求和,最后开平方

squared_diff = (known_point[0] - unknown_point[0])**2 + (known_point[1] - unknown_point[1])** 2

distance = np.sqrt(squared_diff)

return distance

# 计算未知水果与每个已知水果的距离

distances = []

for fruit in known_fruits:

dist = calculate_distance(fruit, unknown_fruit)

distances.append(dist)

# 打印计算过程,方便理解

print(f"已知水果特征{fruit}与未知水果的距离:{dist:.2f}")

运行后会得到类似这样的结果:

已知水果特征[2 3]与未知水果的距离:4.47

已知水果特征[3 4]与未知水果的距离:3.16

已知水果特征[1 5]与未知水果的距离:5.10

已知水果特征[7 6]与未知水果的距离:1.41 # 这个最近!

已知水果特征[8 5]与未知水果的距离:2.00

已知水果特征[9 4]与未知水果的距离:3.61

3.2 第二步:找邻居(选 k 个最像的)

k 近邻算法中的 "k" 就是要选的邻居数量。比如 k=3,就是找距离最近的 3 个已知水果:

 

# 把距离和对应的标签组合起来,方便排序

distance_with_label = list(zip(distances, labels))

# 按距离从小到大排序

sorted_distance = sorted(distance_with_label, key=lambda x: x[0])

# 选择k=3个最近的邻居

k = 3

nearest_neighbors = sorted_distance[:k]

print(f"\n距离最近的{k}个邻居是:")

for dist, label in nearest_neighbors:

fruit_type = "橘子" if label == 1 else "苹果"

print(f"距离{dist:.2f},类别:{fruit_type}")

此时会输出:

距离最近的3个邻居是:

距离1.41,类别:橘子

距离2.00,类别:橘子

距离3.16,类别:苹果

3.3 第三步:投票表决(少数服从多数)

看看这 3 个邻居里哪种水果占多数:

# 提取邻居的标签

neighbor_labels = [label for (dist, label) in nearest_neighbors]

# 统计每个标签出现的次数

label_counts = np.bincount(neighbor_labels)

# 找到出现次数最多的标签

predicted_label = np.argmax(label_counts)

# 输出结果

if predicted_label == 1:

print("\n根据k近邻算法判断,这个未知水果是:橘子!")

else:

print("\n根据k近邻算法判断,这个未知水果是:苹果!")

最终结果会显示 "橘子",和我们的直觉判断完全一致!

四、完整代码:可直接运行的 k 近邻分类器

把上面的步骤整合起来,再加上一些优化,就得到了一个完整的 k 近邻分类器:

import numpy as np

import matplotlib.pyplot as plt

class SimpleKNN:

"""简单的k近邻分类器"""

def __init__(self, k=3):

"""

初始化分类器

参数:

k:要选择的邻居数量,默认3个

"""

self.k = k

self.known_data = None # 用于存储已知数据

self.known_labels = None # 用于存储已知标签

def fit(self, X, y):

"""

训练模型(其实就是记住已知数据)

参数:

X:已知样本的特征数据,形状为[样本数, 特征数]

y:已知样本的标签,形状为[样本数]

"""

self.known_data = X

self.known_labels = y

print(f"模型训练完成,记住了{len(X)}个样本")

def predict(self, X):

"""

预测新样本的类别

参数:

X:新样本的特征数据,形状为[特征数]

返回:

预测的标签

"""

# 计算与所有已知样本的距离

distances = []

for data in self.known_data:

# 计算欧氏距离

dist = np.sqrt(np.sum((data - X) **2))

distances.append(dist)

# 把距离和标签绑定,按距离排序

distance_with_label = list(zip(distances, self.known_labels))

sorted_distance = sorted(distance_with_label, key=lambda x: x[0])

# 取前k个邻居的标签

nearest_labels = [label for (dist, label) in sorted_distance[:self.k]]

# 少数服从多数

return np.argmax(np.bincount(nearest_labels))

# ----------------------

# 用水果数据测试我们的分类器

# ----------------------

if __name__ == "__main__":

# 已知水果特征:[颜色深度, 大小]

fruits = np.array([

[2, 3], [3, 4], [1, 5], # 苹果(标签0)

[7, 6], [8, 5], [9, 4] # 橘子(标签1)

])

labels = np.array([0, 0, 0, 1, 1, 1])

# 创建分类器,选择5个邻居(试试把k改成1或5,看结果会不会变)

knn = SimpleKNN(k=5)

# 训练模型(其实就是记住数据)

knn.fit(fruits, labels)

# 要预测的未知水果:颜色深度6,大小5

unknown_fruit = np.array([6, 5])

prediction = knn.predict(unknown_fruit)

# 输出结果

fruit_names = {0: "苹果", 1: "橘子"}

print(f"\n未知水果的特征:颜色深度{unknown_fruit[0]},大小{unknown_fruit[1]}")

print(f"预测结果:这是一个{fruit_names[prediction]}!")

# 画图展示

plt.scatter(fruits[labels==0, 0], fruits[labels==0, 1],

color='red', marker='o', label='苹果')

plt.scatter(fruits[labels==1, 0], fruits[labels==1, 1],

color='orange', marker='o', label='橘子')

plt.scatter(unknown_fruit[0], unknown_fruit[1],

color='purple', marker='*', s=200, label='未知水果')

plt.xlabel('颜色深度(0-10,越大越黄)')

plt.ylabel('大小(0-10,越大越大)')

plt.title(f'k={knn.k}的k近邻分类结果')

plt.legend()

plt.show()

五、k 近邻算法的关键知识点

5.1 如何选择最佳的 k 值?

k 值是 k 近邻算法中最重要的参数:

  • k 太小:容易被噪声干扰(比如刚好有个奇怪的苹果长得像橘子)
  • k 太大:会把不相关的样本也算进来(比如远在天边的苹果也参与投票)

一个简单的方法是:从 k=3 开始尝试,逐渐增大,看哪个 k 值的预测效果最好。

5.2 特征需要 "标准化"

生活中如果特征的单位不一样(比如一个特征是厘米,一个是千克),会影响距离计算。解决办法是标准化:

# 标准化特征:让每个特征的平均值为0,标准差为1

def standardize(X):

return (X - np.mean(X, axis=0)) / np.std(X, axis=0)

5.3 k 近邻的优缺点

优点

  • 简单易懂,几乎不用数学基础就能理解
  • 不需要提前训练模型,拿到新数据可以直接用
  • 可以处理多种类型的数据

缺点

  • 数据量大的时候,计算距离会很慢
  • 对特征的数量敏感(特征太多时会 "迷路")

六、动手实践:用 scikit-learn 实现更专业的 k 近邻

真实项目中,我们会用成熟的库来实现 k 近邻。试试用 scikit-learn(Python 最流行的机器学习库)重写上面的水果分类:

# 安装scikit-learn(如果没安装的话)

# !pip install scikit-learn

from sklearn.neighbors import KNeighborsClassifier

import numpy as np

# 数据准备(和之前一样)

fruits = np.array([

[2, 3], [3, 4], [1, 5], # 苹果

[7, 6], [8, 5], [9, 4] # 橘子

])

labels = np.array([0, 0, 0, 1, 1, 1])

# 创建k近邻分类器,k=3

knn = KNeighborsClassifier(n_neighbors=3)

# 训练模型

knn.fit(fruits, labels)

# 预测未知水果

unknown_fruit = np.array([[6, 5]]) # 注意这里要写成二维数组

prediction = knn.predict(unknown_fruit)

print("scikit-learn预测结果:", "橘子" if prediction[0]==1 else "苹果") # 输出"橘子"

是不是更简单了?这就是专业库的力量!

七、总结:一篇博客掌握 k 近邻

通过辨别水果的例子,我们学会了:

  1. k 近邻算法的核心思想:"看邻居投票"
  1. 实现步骤:计算距离→找邻居→投票表决
  1. 关键参数 k 的选择方法
  1. 如何用代码实现(从手写简单版本到专业库)

k 近邻就像机器学习世界的 "Hello World",它简单却蕴含了机器学习的基本思想 ——从数据中找规律。下一次当你在超市辨别水果时,不妨想想:"这个过程如果写成代码,应该怎么实现呢?"

现在就动手修改代码里的参数(比如 k 值、水果特征),看看会得到什么有趣的结果吧!

祝你的机器学习之旅,从这个甜甜的 "橘子分类器" 开始,越来越精彩!

  还想看更多,来啦!!!

1,大数据比赛篇全国职业院校技能大赛-大数据比赛心得体会_全国职业职业技能比赛 大数据-CSDN博客

2,求职简历篇(超实用)大学生简历写作指南:让你的简历脱颖而出-CSDN博客

3,AIGC心得篇aigc时代,普通人需要知道的-CSDN博客

4,数据分析思维篇学习数据分析思维的共鸣-CSDN博客

5,中年危机篇“中年危机”如何转变为“中年机遇”-CSDN博客

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

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

相关文章

【WPF】NumericUpDown的用法

在 WPF(Windows Presentation Foundation)中,NumericUpDown 控件并不是内置的标准控件之一,但它是一个非常常用的控件,用于让用户输入一个数值(整数或浮点数),并提供上下箭头来递增或…

Kotlin位运算

Kotlin 提供了几种用于操作整数各个位(bit) 的运算符。这些操作是由处理器直接支持的,速度快且操作简单。在底层编程中非常重要,比如设备驱动、低级图形处理、网络通信、加密和压缩等。 尽管计算机通常都有高效的硬件指令来执行算…

墨者:通过手工解决SQL手工注入漏洞测试(MongoDB数据库)

一、SQL手工注入漏洞测试(MongoDB数据库) 本文以墨者学院靶场为例,演示MongoDB数据库的手工SQL注入全过程。靶场以自己的地址为准:http://124.70.71.251:42286/new_list.php?id1 二、注入原理说明 MongoDB作为NoSQL数据库,其注入方式与传…

Kafka——CommitFailedException异常处理深度解析

引言在分布式消息系统Kafka的生态中,消费者组(Consumer Group)机制是实现高吞吐量和负载均衡的核心设计。然而,消费过程中位移提交(Offset Commit)的稳定性始终是开发者面临的最大挑战之一。当消费者尝试提…

kafka的部署和jmeter连接kafka

zookeeper的安装 kafka依赖Zookeeper所以要先安装Zookeeper kafka的安装文章引用来源:Kafka下载和使用(linux版)-CSDN博客 通过wget命令安装 # 安装wget https://downloads.apache.org/zookeeper/stable/apache-zookeeper-3.7.1-bin.tar.gz# 解压tar…

Android UI 组件系列(八):ListView 基础用法与适配器详解

博客专栏:Android初级入门UI组件与布局 源码:通过网盘分享的文件:Android入门布局及UI相关案例 链接: https://pan.baidu.com/s/1EOuDUKJndMISolieFSvXXg?pwd4k9n 提取码: 4k9n 一、引言 在上一篇文章《Android UI 组件系列(…

Android学习专题目录(持续更新)

1.Android 调试 1.1:Logcat日志分析 2.Android编译 2.1:android编译过程中的mk文件和bp文件的扫描机制 2.2:Android 构建系统中常见的 .mk 文件及其作用 2.3:Android构建系统中的mk文件语法函数 2.4:安卓中定…

c#Lambda 表达式与事件核心知识点整理

一、Lambda 表达式1. 概念 Lambda 表达式是一种匿名函数(无名称的函数),简化了委托和匿名方法的写法,格式为: (参数列表) > 表达式或语句块 它可以作为参数传递,或赋值给委托类型变量。2. 基本语法与简写…

Springboot+Layui英语单词学习系统的设计与实现

文章目录前言详细视频演示具体实现截图后端框架SpringBootLayUI框架持久层框架MyBaits成功系统案例:参考代码数据库源码获取前言 博主介绍:CSDN特邀作者、985高校计算机专业毕业、现任某互联网大厂高级全栈开发工程师、Gitee/掘金/华为云/阿里云/GitHub等平台持续输…

主要分布于内侧内嗅皮层的层Ⅲ的边界向量细胞(BVCs)对NLP中的深层语义分析的积极影响和启示

边界向量细胞(Boundary Vector Cells, BVCs)主要分布于内侧内嗅皮层(MEC)层Ⅲ,通过编码环境边界(如墙壁、障碍物)的距离和方向信息,为空间导航提供几何参考框架。这一神经机制对自然…

Selenium是解决了什么问题的技术?

Selenium 是一种用于自动化浏览器操作的技术,主要解决了以下问题:1. 自动化测试 Selenium 最初是为了解决 Web 应用程序的自动化测试 问题而设计的。它可以帮助开发者和测试人员: 模拟用户操作:如点击按钮、填写表单、选择下拉菜单…

JavaSE知识点(2)

目录 访问修饰符的区别 this关键字的作用 抽象类和接口有什么区别 抽象类可以定义构造方法吗 但是接口不可以定义构造方法 Java支持多继承吗 接口可以多继承吗 继承和抽象的区别? 抽象类和普通类的区别 成员变量和局部变量的区别? staic关键字…

(实用教程)Linux操作系统(二)

centos配置静态ip 注意: 1.系统中的网关要与虚拟机编辑器中的网关保持一致 2.如果配置虚拟机编辑器后发现ping不通外网的时候,就要还原默认设置再进行配置 总结: 虚拟机编辑器需要配置ip,网关,其中ip网段以及最后一…

ThinkPHP8集成RabbitMQ的完整案例实现

ThinkPHP8集成RabbitMQ的完整案例实现一、安装依赖:需通过Composer安装php-amqplib库‌二、配置RabbitMQ三、生产者1、发送一个邮件,将任务发送到RabbitMQ队列中。2、运行结果展示四、启动消费者:命令行执行php think rabbitmq:consumer1&…

解密负载均衡:如何轻松提升业务性能

什么是负载均衡 负载均衡:Load Balance,简称LB,是一种服务或基于硬件设备等实现的高可用反向代理技术,负载均衡将特定的业务(web服务、网络流量等)分担给指定的一个或多个后端特定的服务器或设备,从而提高了 公司业务的…

mac neo4j install verifcation

本文使用conda环境安装,neo4j所依赖jdk也采用conda install的方式安装。 1 neo4j下载 点击如下链接,选择community, Linux/Mac Executor,点击Download Community。 本文下载的安装包是 neo4j-community-2025.06.2-unix.tar.gz 2 安装neo4j …

【Oracle】Oracle分区表“排雷“指南:当ORA-14400错误找上门时如何优雅应对

引言:分区表里的"定时炸弹"凌晨三点的机房,你盯着屏幕上刺眼的ORA-14400: 插入的分区键值超出所有分区范围错误,后背发凉。这个错误就像埋在分区表里的定时炸弹,一旦触发就会让整个应用瘫痪。但别慌!本文将带…

设计模式(十四)行为型:职责链模式详解

设计模式(十四)行为型:职责链模式详解职责链模式(Chain of Responsibility Pattern)是 GoF 23 种设计模式中的行为型模式之一,其核心价值在于将多个处理对象(处理器)连接成一条链&am…

WAIC 2025 热点解读:如何构建 AI 时代的“视频神经中枢”?

一、🌐 WAIC 2025 大会看点:AI 正在“长出眼睛与身体” 在 2025 年的人工智能大会(WAIC 2025)上,“大模型退幕后,具身智能登场”成为最具共识的趋势转向。从展区到主论坛,再到各大企业发布的新…

OpenCV+Python

安装 OpenCV: Python:直接 pip install opencv-python(核心库)和 opencv-contrib-python(扩展功能)。 pip install opencv-python pip install opencv-contrib-python 验证安装: import cv2…