【机器学习】Lesson 5 - K近邻(KNN)分类/回归

背景

K近邻(KNN)是一种常见的监督学习算法,主要用于分类回归 问题。在前文《L4 垃圾邮件数据集分类延申 - NB/KNN/SVC/随机森林》中,已经有涉及到适用 KNN 进行自然语言的分类处理。在本文中,将详细介绍 KNN 的模型原理、使用方法等,并且分为分类回归分别选取数据集进行模型训练。

两种模型使用的数据集和完整跑通的 ipynb 类型文件可在文章绑定资源中下载获取。

一、算法介绍

1. 基本原理

1.1 概述

KNN 算法基于一个非常简单的思想:给定一个未知样本,找到训练集中与该样本距离最近的 K 个邻居,并根据这些邻居的标签来进行预测。其核心假设是:相似的样本具有相似的标签

KNN 三要素分别是:距离度量、K 值的选择和分类决策规则。常用的距离度量是欧氏距离及更一般的pL距离。K 值小时,K 近邻模型更复杂;K 值大时,K 近邻模型更简单。K 值的选择反映了对近似误差与估计误差之间的权衡,通常由交叉验证选择最优的 K。它的实现需要考虑如何快速搜索 K 个最近邻点。

1.2 常用距离

1)欧氏距离

欧几里得度量(也称欧氏距离),是一个通常采用的距离定义,指在 m 维空间中两个点之间的真实距离,或者向量的自然长度(即该点到原点的距离)。在二维和三维空间中的欧氏距离就是两点之间的实际距离。

距离公式:

2)曼哈顿距离

想象你在城市道路里,要从一个十字路口开车到另外一个十字路口,驾驶距离是两点间的直线距离吗?显然不是,除非你能穿越大楼。实际驾驶距离就是这个"曼哈顿距离"。而这也是曼哈顿距离名称的来源,曼哈顿距离也称为城市街区距离(City Block distance)。

距离公式:

3)切比雪夫距离

在数学中,切比雪夫距离(Chebyshev distance)或是L∞度量,是向量空间中的一种度量,二个点之间的距离定义是其各坐标数值差绝对值的最大值。以数学的观点来看,切比雪夫距离是由一致范数(uniform norm)(或称为上确界范数)所衍生的度量,也是超凸度量(injective metric space)的一种。

距离公式:

1.3 模型参数

即设置模型时,KNeighborsClassifier() 的括号内可以设置的参数,存在默认值,需要更改为默认值外的参数时,可以在建模时设定调整。

  1. n_neighbors:邻居的数量,即 k的个数,默认值是 5

  2. weights:权衡样本距离对结果影响的方式,可以是 'uniform'(等权重,就说所有的邻近点的权重都是相等的)或 'distance'(基于距离加权,距离近的点比距离远的点的影响大)。默认值是 'uniform'

  3. algorithm:计算相似性的算法,近邻算法,可选 {'auto', 'ball_tree', 'kd_tree', 'brute'}。

    • 默认值是'auto',可以理解为算法自己决定合适的搜索算法。

    • brute 是蛮力搜索,也就是线性扫描,当训练集很大时,计算非常耗时。

    • kd_tree,构造 kd 树存储数据以便对其进行快速检索的树形数据结构,kd 树也就是数据结构中的二叉树。以中值切分构造的树,每个结点是一个超矩形,在维数小于 20 时效率高。

    • ball_tree 是为了克服 kd 树高纬失效而发明的,其构造过程是以质心 C 和半径 r 分割样本空间,每个节点是一个超球体。

  4. metric:衡量样本间距离的标准,如'deuclidean'(欧氏距离)、'minkowski'(p-norm)、'manhattan'(曼哈顿距离)等。默认值取决于数据的特征类型,也就是p=2的欧氏距离。

  5. leaf_size:KDTree 和 BallTree 构建时叶节点的最大样本数。默认值是30。这个值的设置会影响树构建的速度和搜索速度,同样也影响着存储树所需的内存大小。

  6. p:对于Minkowski距离,指定p值(metric='minkowski'时)。默认值是2,对应欧氏距离。可以设置为1,使用曼哈顿距离公式进行距离度量。

  7. metric_params:针对特定度量的额外参数,距离公式的其他关键参数,这个可以不管,使用默认值 None即可。

  8. n_jobs:并行处理的线程数表示最多可用核心数。默认为1,临近点搜索并行工作数。并行设置,如果为-1,那么CPU的所有cores都用于并行工作。

2. 适用场景

以下是 KNN 在分类回归任务中分别的适用场景、数据维度和行数的适配性。

2.1 分类

适用场景
  • 图像分类:如手写数字识别(MNIST 数据集),KNN 可以在低维空间内通过图像的像素特征进行分类,找到相似的图像类别。
  • 文本分类:例如垃圾邮件过滤、情感分析等,KNN 可以根据文档的词频或嵌入特征,找到邻近的类别标签。
  • 医学诊断:用于疾病预测,基于病人的生理数据、症状等特征预测疾病类别(例如乳腺癌良性和恶性分类)。
KNN 分类的优缺点
  • 优点:简单直观、对数据分布无假设、适合小数据集和低维特征空间、对非线性决策边界有良好表现。
  • 缺点:对噪声敏感;大数据时效率低;高维时容易受"维度灾难"影响,距离计算变得不准确。

2.2 回归

适用场景
  • 房价预测:例如房价数据集。房价受到多个地理和社会经济特征的影响,KNN 可以根据相邻的房屋价格进行预测。
  • 健康指标预测:如根据过去的心率、血压等生理特征预测某一时间的健康指标。KNN 可以利用相似患者的健康数据进行回归预测。
  • 金融市场预测:在股票等金融数据中,根据相似的历史数据预测未来的价格或收益率。
KNN 回归的优缺点
  • 优点:在局部区域内对非线性关系有较好建模效果,无需假设数据分布;适合处理复杂的非线性问题。
  • 缺点:容易受到异常点影响,特别是在稀疏数据上表现不佳。计算成本较高,且对维度的增加敏感。

3. 适用数据集特征

3. 1 特征维度(维数)

KNN 的计算复杂度较高,在处理高维数据时效率低下,并且容易出现"维度灾难",即特征空间维度增加后,距离计算变得不准确,导致模型性能下降。

一般适合低维数据 ,例如在 2 到 20 个维度 的范围内效果较好。超过 20 维的数据集,需要先进行降维(例如使用 PCA、t-SNE 等方法)以减少噪声,降低维数,提升模型效果。

3.2 样本量(行数)

KNN 算法在小数据集上效果较好,因为它需要对每个测试样本计算其与所有训练样本的距离,这会随着样本量的增加而导致计算成本显著上升。

通常适合中小规模数据集,即在几千至几万样本的范围内效果较好;如果样本量达到百万级别,KNN 的预测效率会降低,除非采用更快的距离计算方法或近似算法(例如 KD 树或 Ball 树)进行加速。

4. 总结

场景 分类任务 回归任务
适用领域 图像分类、文本分类、医疗诊断等 房价预测、健康指标预测、金融市场预测等
特征维度 2-20 维效果较佳;适合低维数据,超过 20 维建议降维处理 同样适合 2-20 维的低维数据,对于高维回归任务不推荐使用
样本量 适合几千至几万样本的小数据集,样本量过大需要优化 几千至几万样本的小数据集,若数据规模过大需要加速或采用其他方法
优缺点总结 优点是简单直观、对非线性数据有效;缺点是对噪声敏感、受维度灾难影响 优点是能处理非线性关系、在局部效果好;缺点是对异常点敏感,计算成本高,对维度变化敏感

二、分类

1. 本部分数据集介绍

1.1 基本信息

Cancer Data 是一个二分类数据集,用于预测乳腺癌肿瘤的类型(良性或恶性)。这个数据集包含 569 条样本和 30 个数值特征。每个特征描述了细胞核图像的几何特征,目标变量是肿瘤的类别。

  • 样本数:569
  • 特征数:30
  • 目标变量:肿瘤类别(良性 or 恶性)

字段介绍:

  • ID:样本的唯一标识符(通常在分析中不使用)。
  • Diagnosis :诊断结果(标签),表示肿瘤的类型:
    • M 表示恶性(Malignant)
    • B 表示良性(Benign)
  • 除 diagnosis 以外其余 29 个特征,均由分析细胞核的图像得出,包括半径、质地、周长、平滑度、紧致度、凹度、凸点等。

1.2 下载

下载地址:https://www.kaggle.com/datasets/erdemtaha/cancer-data

也可以在文章绑定资源中直接下载获取。

2. 代码

2.1 数据准备

python 复制代码
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

from sklearn.neighbors import KNeighborsClassifier # KNN
from sklearn.decomposition import PCA #降维-主成分分析

from sklearn.metrics import confusion_matrix, classification_report,accuracy_score
from sklearn.preprocessing import StandardScaler, LabelEncoder
from sklearn.model_selection import train_test_split, GridSearchCV, cross_val_score
%matplotlib inline


# 加载数据集
cancer = pd.read_csv("D:/project/Jupyter/csdn/AI_ML/datasets/L5_1Cancer.csv")

cancer.info()

cancer.head()

cancer.columns

# 删除无意义的第32列
to_drop = ["Unnamed: 32"]
cancer = cancer.drop(cancer[to_drop], axis=1)

# 设定特征和标签
X = cancer.drop(columns=["diagnosis"])  
y = cancer["diagnosis"]

# 标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

2.2 建模

python 复制代码
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.2, random_state=42)

knn=KNeighborsClassifier()

knn.fit(X_train,y_train)

y_pred_test_knn=knn.predict(X_test)

# 打印准确率报表,准确率、召回率、F1 值等指标
print(classification_report(y_test,y_pred_test_knn))

2.3 调参

python 复制代码
# 计算不同 K 值(从 1 到 9)下的模型准确率。
knn_score=[KNeighborsClassifier(n_neighbors=i).
           fit(X_train,y_train).score(X_test,y_test) for i in range(1,10)]

# 绘制图表展示 K 值与模型准确率的关系
plt.plot(range(1,10),knn_score)

# 增加权重为 distance,距离越近的邻居对分类结果的影响越大。
knn_score_w=[KNeighborsClassifier(n_neighbors=i,weights="distance").
             fit(X_train,y_train).score(X_test,y_test) for i in range(1,10)]

# 绘制图表以观察在基于距离权重下 K 值与准确率的关系
plt.plot(range(1,10),knn_score_w)

# 输出 knn_score_w 和 knn_score 列表中的最大值,用于查看基于距离权重和均匀权重情况下的最佳准确率
max(knn_score_w),max(knn_score)

# 使用曼哈顿距离来计算邻居间的距离
knn_score_w_pl=[KNeighborsClassifier(n_neighbors=i,weights="distance",metric='minkowski',p=1).
                fit(X_train,y_train).score(X_test,y_test) for i in range(1,10)]

plt.plot(range(1,10),knn_score_w_pl)

max(knn_score_w_pl)#最高点

2.4 降维

在调参之后对数据进行降维处理,会比无调参步骤直接降维正确率高 2%

python 复制代码
# 使用 PCA 将数据降至 2 维
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)

X_train, X_test, y_train, y_test = train_test_split(X_pca, y, test_size=0.2, random_state=42)

# 创建 KNN 模型,选择 K=3
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(f"KNN 分类模型在降维后数据集上的准确率: {accuracy:.4f}")

三、回归

1. 本部分数据集介绍

1.1 基本信息

红酒质量数据集(Red Wine Quality Dataset)是一个公开可用的数据集,通常用于回归或分类任务,目标是根据红酒的化学特性预测其质量评分,数据集来自葡萄牙维诺·贝尔德红酒的化学分析实验,最早由 Paulo Cortez 等人在研究中提出,记录了红酒的物理化学性质和感官评估得分。

数据量

  • 样本数:1599 条记录
  • 特征数:10 个(均为数值型数据,包括酸碱度、酒精含量等化学性质数据)
  • 目标变量quality,即质量评分(整数,范围 0 到 10)

1.2 数据集下载

下载网址:https://www.kaggle.com/datasets/uciml/red-wine-quality-cortez-et-al-2009/data

也可以在文章绑定资源中直接下载获取。

2. 代码

完整代码中包含了数据探索的一些可视化图形,以及更为复杂详细的建立KD树流程,在此部分省略,有兴趣可以下载文章绑定资源查看 ipynb 格式完整代码文件。

2.1 数据准备

python 复制代码
#Importing required packages.
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt

from sklearn.neighbors import KNeighborsRegressor # KNN回归
from sklearn.decomposition import PCA #降维-主成分分析

from collections import namedtuple
from pprint import pformat
from math import sqrt
from collections import namedtuple
from operator import itemgetter
from time import process_time
from random import random
from sklearn.neighbors import KDTree

from sklearn.metrics import confusion_matrix, classification_report,mean_squared_error,r2_score
from sklearn.preprocessing import StandardScaler, LabelEncoder
from sklearn.model_selection import train_test_split, GridSearchCV, cross_val_score
%matplotlib inline

# 加载数据集
wine = pd.read_csv("D:/project/Jupyter/csdn/AI_ML/datasets/L5_2RedWine.csv")

wine

wine.info()

2.2 建模

python 复制代码
# 分离特征和目标变量
X = wine.drop("quality", axis=1)
y = wine["quality"]

# 数据集划分
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 数据标准化
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

# 创建 KNN 回归器,基于默认的欧氏距离
knn = KNeighborsRegressor(n_neighbors=5)
knn.fit(X_train, y_train)

# 预测测试集
y_pred = knn.predict(X_test)

# 计算性能指标
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
print(f"MSE: {mse:.2f}, R²: {r2:.2f}")

2.3 优化-KD树/超参数调优

python 复制代码
# 搜索最佳 n_neighbors 值
mse_scores = []
neighbors_range = range(1, 21)

for n in neighbors_range:
    knn = KNeighborsRegressor(n_neighbors=n)
    knn.fit(X_train, y_train)
    y_pred = knn.predict(X_test)
    mse_scores.append(mean_squared_error(y_test, y_pred))

# 绘制 MSE 随邻居数量变化的曲线
plt.figure(figsize=(10, 6))
plt.plot(neighbors_range, mse_scores, marker='o', linestyle='-', color='b')
plt.title("MSE vs. Number of Neighbors")
plt.xlabel("Number of Neighbors")
plt.ylabel("Mean Squared Error")
plt.grid()
plt.show()

# 选择最佳邻居数量
best_n = neighbors_range[np.argmin(mse_scores)]
print(f"Optimal Number of Neighbors: {best_n}")

# 使用优化后的 KNN 建模
# 使用最佳邻居数重新训练模型
knn_optimized = KNeighborsRegressor(n_neighbors=best_n)
knn_optimized.fit(X_train, y_train)
y_pred_optimized = knn_optimized.predict(X_test)

# 计算优化后的性能指标
mse_optimized = mean_squared_error(y_test, y_pred_optimized)
r2_optimized = r2_score(y_test, y_pred_optimized)
print(f"Optimized MSE: {mse_optimized:.2f}, Optimized R²: {r2_optimized:.2f}")

# 构建 KD 树
kd_tree = KDTree(X_train)

# 使用 KD 树的邻近查找加速 KNN
knn_kd = KNeighborsRegressor(algorithm="kd_tree")
knn_kd.fit(X_train, y_train)
y_pred_kd = knn_kd.predict(X_test)

# 评估性能
mse_kd = mean_squared_error(y_test, y_pred_kd)
r2_kd = r2_score(y_test, y_pred_kd)
print(f"KNN with KD Tree - MSE: {mse_kd:.2f}, R²: {r2_kd:.2f}")

2.4 模型比较可视化

python 复制代码
print(f"Final Optimized MSE: {mse_optimized:.2f}")
print(f"Final Optimized R²: {r2_optimized:.2f}")

# 绘制预测点与真实点的对比散点图
plt.figure(figsize=(12, 6))

# 散点图 - KD 树优化
plt.subplot(1, 2, 1)
plt.scatter(y_test, y_pred_kd, alpha=0.6, label="KD Tree")
plt.plot([y.min(), y.max()], [y.min(), y.max()], 'r--', label="Ideal")
plt.xlabel("True Quality")
plt.ylabel("Predicted Quality")
plt.title("KD Tree Optimization")
plt.legend()

# 散点图 - 超参数调优
plt.subplot(1, 2, 2)
plt.scatter(y_test, y_pred_optimized, alpha=0.6, label="Optimized")
plt.plot([y.min(), y.max()], [y.min(), y.max()], 'r--', label="Ideal")
plt.xlabel("True Quality")
plt.ylabel("Predicted Quality")
plt.title("Hyperparameter Optimization")
plt.legend()

plt.tight_layout()
plt.show()

3. 结果分析

将三种模型性能进行比较:

方法 MSE 优势
初始 KNN 0.43 0.33 默认参数,精度一般
KD 树优化 0.43 0.33 提升搜索效率,但精度无变化
超参数调优 (优化) 0.40 0.39 精度提升明显,优化效果最佳

结论:

  1. KD 树更适合大型数据集和实时应用场景,它的优化主要体现在 效率提升,即邻近点搜索速度显著加快,尤其在样本量较大时作用明显。
  2. 超参数调优 更注重模型性能提升,通过找到最佳参数配置,提高了预测精度,优化效果更显著,因为调整了 n_neighbors 和加权方式,使模型能更好地适配数据分布。
  3. 如果需要兼顾效率和精度,可以结合两种方法:在 KD 树基础上进行超参数调优

内容参考

[1] 黄海广.Github 课程项目:机器学习课程

[2] 周志华.机器学习(西瓜书)

[3] 周老师的学生们. 南瓜书

[4] Prediction of quality of Wine | Kaggle

相关推荐
迅易科技28 分钟前
借助腾讯云质检平台的新范式,做工业制造企业质检的“AI慧眼”
人工智能·视觉检测·制造
古希腊掌管学习的神1 小时前
[机器学习]XGBoost(3)——确定树的结构
人工智能·机器学习
ZHOU_WUYI2 小时前
4.metagpt中的软件公司智能体 (ProjectManager 角色)
人工智能·metagpt
靴子学长3 小时前
基于字节大模型的论文翻译(含免费源码)
人工智能·深度学习·nlp
AI_NEW_COME3 小时前
知识库管理系统可扩展性深度测评
人工智能
海棠AI实验室4 小时前
AI的进阶之路:从机器学习到深度学习的演变(一)
人工智能·深度学习·机器学习
hunteritself4 小时前
AI Weekly『12月16-22日』:OpenAI公布o3,谷歌发布首个推理模型,GitHub Copilot免费版上线!
人工智能·gpt·chatgpt·github·openai·copilot
IT古董5 小时前
【机器学习】机器学习的基本分类-强化学习-策略梯度(Policy Gradient,PG)
人工智能·机器学习·分类
落魄君子5 小时前
GA-BP分类-遗传算法(Genetic Algorithm)和反向传播算法(Backpropagation)
算法·分类·数据挖掘
centurysee5 小时前
【最佳实践】Anthropic:Agentic系统实践案例
人工智能