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

目录

背景

一、算法介绍

1. 基本原理

1.1 概述

1.2 常用距离

1.3 模型参数

2. 适用场景

2.1 分类

适用场景

KNN 分类的优缺点

2.2 回归

适用场景

KNN 回归的优缺点

3. 适用数据集特征

3.1 特征维度(维数)

3.2 样本量(行数)

4. 总结

二、分类

1. 本部分数据集介绍

1.1 基本信息

1.2 下载

2. 代码

2.1 数据准备

2.2 建模

2.3 调参

2.4 降维

三、回归

1. 本部分数据集介绍

1.1 基本信息

1.2 数据集下载

2. 代码

2.1 数据准备

2.2 建模

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

2.4 模型比较可视化

3. 结果分析

内容参考


背景

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 数据准备

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 建模

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 调参

# 计算不同 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%

# 使用 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 数据准备

#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 建模

# 分离特征和目标变量
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树/超参数调优

# 搜索最佳 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 模型比较可视化

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优势
初始 KNN0.430.33默认参数,精度一般
KD 树优化0.430.33提升搜索效率,但精度无变化
超参数调优 (优化)0.400.39精度提升明显,优化效果最佳

结论:

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

内容参考

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

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

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

[4] Prediction of quality of Wine | Kaggle

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lu rong_qq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值