在机器学习中,K-最近邻算法(K-Nearest Neighbors, KNN)是一种既直观又实用的算法。它既可以用于分类,也可以用于回归任务。本文将简单介绍KNN算法的基本原理、优缺点以及常见应用场景,并通过一个简单案例帮助大家快速入门。
1. KNN算法简介
KNN算法基于一个非常直观的思想:对于一个未知类别的数据点,可以通过查看它在特征空间中距离最近的K个邻居的类别或数值信息,来决定该数据点的类别或预测其值。算法的主要步骤如下:
1. 计算距离:常用的距离度量方法有欧氏距离、曼哈顿距离等。对于一个待预测的数据点,计算它与训练集中所有数据点的距离。
2. 选择最近邻:根据计算得到的距离,选取距离最小的K个数据点。
3. 决策机制:
• 分类:采用投票机制,将待预测点归为K个邻居中出现频率最高的类别。
• 回归:计算K个邻居的数值平均值或加权平均值,作为预测结果。
由于KNN算法没有显式的训练过程,所以它属于一种懒惰学习(Lazy Learning)方法,即在训练阶段只存储数据,在预测时才进行计算。
2. KNN的优缺点
优点
• 简单易懂:KNN算法实现简单,容易理解,非常适合初学者入门机器学习。
• 无需训练过程:KNN不需要构建复杂的模型,直接利用存储的训练数据进行预测。
• 适应性强:既可以用于分类问题,也可以用于回归问题,具有较强的通用性。
缺点
• 计算成本高:当数据量较大时,每次预测都需要计算与所有训练样本之间的距离,计算量较大。
• 对噪声敏感:噪声数据或异常点可能会影响预测结果,尤其是当K值较小时。
• 数据不平衡问题:在类别分布不平衡的情况下,少数类可能会被多数类所掩盖,影响模型效果。
3. 应用场景
KNN算法在许多领域都有应用,包括但不限于:
• 手写数字识别:利用KNN对手写数字图片进行分类,实现简单而高效的数字识别。
• 推荐系统:基于用户相似性推荐商品或电影,利用KNN寻找兴趣相似的用户。
• 医学诊断:通过分析病人数据,预测疾病类别或风险值。
• 回归预测:例如房价预测,通过相似特征房屋的历史价格进行估值。
4. 实战案例:KNN分类
下面通过一个简单的案例,使用Python和scikit-learn库对Iris数据集进行KNN分类,帮助大家直观了解KNN的实际应用。
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score# 加载Iris数据集
iris = load_iris()
X = iris.data
y = iris.target# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)# 构建KNN分类器,设置K值为3
knn_classifier = KNeighborsClassifier(n_neighbors=3)
knn_classifier.fit(X_train, y_train)# 对测试集进行预测
y_pred = knn_classifier.predict(X_test)# 计算并输出准确率
accuracy = accuracy_score(y_test, y_pred)
print("KNN分类器在Iris数据集上的准确率:{:.2f}%".format(accuracy * 100))
运行上述代码,你将会看到KNN分类器在Iris数据集上的表现。通过调整K值或选择不同的距离度量方式,可以进一步优化模型效果。
下面给出两个案例,分别使用在线下载的数据集,演示如何用 KNN 实现分类和回归。我们分别用 OpenML 上的 Iris 数据集(分类)和 scikit-learn 内置的 California Housing 数据集(回归)来说明。
案例 1:KNN 分类(Iris 数据集)
我们通过 fetch_openml 从 OpenML 下载 Iris 数据集,然后用 KNeighborsClassifier 进行分类,并输出预测准确率。
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score# 下载 Iris 数据集(注意:as_frame=True 会返回 Pandas DataFrame 格式)
iris = fetch_openml(name='iris', version=1, as_frame=True)
X = iris.data
y = iris.target# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)# 构造并训练 KNN 分类器(这里取 k=3)
knn_classifier = KNeighborsClassifier(n_neighbors=3)
knn_classifier.fit(X_train, y_train)# 对测试集进行预测
y_pred = knn_classifier.predict(X_test)# 输出分类准确率
print("KNN 分类器准确率:", accuracy_score(y_test, y_pred))
运行该代码后,会输出模型在测试集上的准确率,说明 KNN 分类器在 Iris 数据集上的表现。
案例 2:KNN 回归(California Housing 数据集)
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsRegressor
from sklearn.metrics import mean_squared_error# 下载 California Housing 数据集
housing = fetch_california_housing(as_frame=True)
X = housing.data
y = housing.target# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)# 构造并训练 KNN 回归器(这里取 k=5)
knn_regressor = KNeighborsRegressor(n_neighbors=5)
knn_regressor.fit(X_train, y_train)# 对测试集进行预测
y_pred = knn_regressor.predict(X_test)# 计算并输出均方误差(MSE)
mse = mean_squared_error(y_test, y_pred)
print("KNN 回归器的均方误差:", mse)
运行该代码后,将输出模型在 California Housing 数据集上预测的均方误差,从而评估回归效果。
以上两个案例分别展示了如何利用在线数据和 scikit-learn 中的 KNN 模型进行分类和回归任务。根据具体问题的特点,可以调整 k 值、数据预处理及评估指标以获得更好的效果。
5.利用KNN算法对鸢尾花分类完整的案例:
整个流程主要包括以下几个步骤:
1. 下载和加载鸢尾花数据集
使用 scikit-learn 内置的鸢尾花数据集,可以快速获取数据,方便后续实验。
2. 案例总体处理流程
• 数据加载与初步探索:通过数据可视化(如散点图、成对关系图)来查看各个特征在不同类别中的分布情况,判断是否存在明显的分类边界。
• 数据预处理:包括数据标准化以及数据集划分。标准化能消除不同特征量纲的影响;划分为训练集和测试集,可以验证模型的泛化能力。
• 模型训练:利用训练集数据训练 KNN 模型。
• 模型评估:通过在测试集上计算准确率来评价模型性能。
• 模型预测:利用训练好的模型对新数据进行分类预测,并输出预测概率。
# 导入必要的库
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score
# 设置绘图风格
sns.set(style="whitegrid")
# 设置 Seaborn 的字体参数
sns.set(font='Heiti TC')# 1. 下载和加载鸢尾花数据集
# scikit-learn 内置的 load_iris() 函数可以直接加载数据
iris = load_iris()
# 将数据转换为 DataFrame,方便可视化探索
df = pd.DataFrame(data=iris.data, columns=iris.feature_names)
df['target'] = iris.target
# 假设 df 的列名是 iris.feature_names + ['target']
df.rename(columns={'sepal length (cm)': '萼片长度','sepal width (cm)': '萼片宽度','petal length (cm)': '花瓣长度','petal width (cm)': '花瓣宽度','target': '类别' # 如果你也想改 target 列名的话
}, inplace=True)
print(df.head())
# 2. 数据初步探索和可视化
# 使用 Seaborn 的 pairplot 展示各特征之间的关系,颜色区分不同类别
sns.pairplot(df, hue='类别', markers=["o", "s", "D"])
plt.suptitle("鸢尾花数据各特征成对关系图", y=1.02)
plt.show()# -----------------------------
# 第3步:数据预处理——标准化 & 数据集划分
# -----------------------------# 3.1 划分训练集和测试集
# 使用 train_test_split 将数据随机拆分为训练集和测试集,这里测试集占 30%
# iris.data 表示所有特征数据,iris.target 表示对应的标签
# test_size=0.3 指定 30% 的数据作为测试集,剩余 70% 作为训练集
# random_state=22 固定随机数种子,保证每次运行结果一致
X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.3, random_state=22
)# 输出数据集数量,帮助理解数据分布
print('数据总数量:', len(iris.data)) # 原始数据的样本总数
print('训练集样本数:', len(X_train)) # 训练集中包含的样本数
print('测试集样本数:', len(X_test)) # 测试集中包含的样本数# 3.2 数据标准化
# 标准化可以消除各特征之间因量纲不同带来的影响,使模型训练更稳定
scaler = StandardScaler() # 创建标准化转换器# 对训练集先拟合 (fit) 再转换 (transform),计算并应用均值、方差等参数
X_train_scaled = scaler.fit_transform(X_train)# 使用训练集计算得到的均值和方差,对测试集做相同的转换 (transform)
# 这里不再对测试集进行 fit,是为了避免数据泄露
X_test_scaled = scaler.transform(X_test)# -----------------------------
# 第4步:机器学习(模型训练)
# -----------------------------# 初始化 KNN 分类器,设置邻居数为3(即寻找距离最近的3个点投票决定分类)
knn = KNeighborsClassifier(n_neighbors=3)# 使用标准化后的训练集和对应的标签进行训练
knn.fit(X_train_scaled, y_train)# -----------------------------
# 第5步:模型评估
# -----------------------------# 利用训练好的模型对测试集进行预测,得到预测标签 y_pred
y_pred = knn.predict(X_test_scaled)# 计算预测结果与真实标签之间的准确率
accuracy = accuracy_score(y_test, y_pred)# 输出模型在测试集上的准确率,格式化为百分比
print("模型在测试集上的准确率:{:.2f}%".format(accuracy * 100))# -----------------------------
# 第6步:模型预测
# -----------------------------# 查看模型识别到的类别标签(这里通常是 [0, 1, 2])
print("模型识别的类别标签:", knn.classes_)# 对新数据进行预测前,必须先进行标准化转换
# 这里以两组新数据为例,每组数据包含4个特征
new_data = [[5.1, 3.5, 1.4, 0.2],[4.6, 3.1, 1.5, 0.2]
]
new_data_scaled = scaler.transform(new_data)
# 预测类别
new_pred = knn.predict(new_data_scaled)
print("新数据预测的类别:\n", new_pred)
# 预测每个类别的概率分布
new_pred_proba = knn.predict_proba(new_data_scaled)
print("新数据预测的概率分布:\n", new_pred_proba)
输出:
输出第一部分: 原始数据展示5条

输出第二部分:数据可视化

sns.pairplot(df, hue='标签', markers=["o", "s", "D"])
plt.suptitle("鸢尾花数据各特征成对关系图", y=1.02)
plt.show()
1. sns.pairplot(df, hue='target', markers=["o", "s", "D"])
• sns.pairplot:这是 Seaborn 提供的一个便捷绘图函数,可以一次性生成多张散点图和分布图,展示多个特征之间的两两关系(pairwise relationships)。
• df:这里传入的是一个包含了特征(sepal length, sepal width, petal length, petal width)以及标签列(target)的 DataFrame。
• hue='target':告诉 Seaborn 根据 target 列对数据点进行着色,这样不同类别(0, 1, 2)就会用不同颜色表示。
• markers=["o", "s", "D"]:除了颜色以外,还会用不同形状(圆圈、方形、菱形)来区分不同类别的数据点,便于区分。
2. plt.suptitle("鸢尾花数据各特征成对关系图", y=1.02)
• plt.suptitle:给整张图添加一个总标题(suptitle 表示“super title”)。
• y=1.02:将标题的位置往上移动一点,让标题不会与图像重叠。
下面我们分三个角度来解释这张成对关系图(Pairplot),帮助你从宏观到细节理解它所表达的含义:
1. Pairplot 的基本结构
1. 网格布局
• 这张图是一个 4×4 的网格,因为鸢尾花数据集有 4 个特征(萼片长度、萼片宽度、花瓣长度、花瓣宽度)。
• 每行和每列都对应一个特征,因此在网格的每个小格子里,横坐标和纵坐标分别代表这两个特征。
2. 对角线(Diagonal)
• 对角线位置的图通常是该特征的分布图(这里是核密度估计图)。
• 例如在 “萼片长度” 那个对角线小格中,你看到的是“萼片长度”在不同类别上的分布情况(曲线越高,说明在该数值范围内出现的样本越多)。
3. 非对角线(Off-diagonal)
• 对角线以外的每个小格都是一个散点图,横坐标是列对应的特征,纵坐标是行对应的特征。
• 不同颜色(以及不同形状)代表不同的类别(这里是 0、1、2 三种鸢尾花)。
2. 图中每个要素的含义
1. 散点图
• 每个点代表一条数据(一个鸢尾花样本)。
• 横纵坐标分别代表该样本在两个特征上的取值。
• 通过散点在坐标系中的分布,可以观察不同类别之间是否有明显的分隔。
2. 颜色和形状
• hue='类别' 将数据根据类别分成三种颜色(并用三种不同形状),帮助你快速区分这三类鸢尾花的分布。
• 如果在某个散点子图中,不同颜色(类别)聚成各自相对分离的簇,说明这两个特征对区分类别有帮助;如果三种颜色的点高度混杂,则这两个特征对分类的区分度不大。
3. 核密度图(对角线)
• 对角线上展示的是单一特征在三个类别上的分布情况。
• 曲线越高表示该特征在这个区间内的样本数量越多。
• 如果三个类别的曲线彼此间隔较大,说明该特征可以很好地区分这三个类别;反之,如果曲线高度重叠,说明这个特征对分类的区分度不高。
3. 如何解读这张图
1. 观察特征对分类的区分度
• 先看对角线:如果某个特征在不同类别间的分布曲线几乎不重叠,比如“花瓣长度”在类别 0(最左侧那条曲线)与 1、2 之间相差较大,就意味着“花瓣长度”对区分类别 0 与其他类别有较好的效果。
• 再看散点图:某些特征组合可能会让不同类别的点分布更明显。例如“花瓣长度”和“花瓣宽度”那一格中,如果你看到三种颜色的点几乎各自成簇,则说明这对特征组合很适合做分类。
2. 判断类别之间的重叠
• 如果在某些特征组合的散点图中,颜色分布混杂,说明这两个特征并不能很好地区分那几个类别。
• 例如,如果类别 1 和类别 2 大部分点都交织在一起,说明这两个类别在该对特征上表现相似,区分度不高。
3. 为后续建模提供参考
• 在实际的机器学习项目中,看到这样的可视化,你会知道哪些特征最能帮助你区分不同类别。
• 如果某对特征在散点图上就能把不同颜色分得很开,往往意味着它们对分类任务特别有用。
小结
• 这张 Pairplot 的作用:让你在一张图中同时观察所有特征两两之间的关系和分布。
• 图中每个格子的意义:对角线显示单一特征的分布,非对角线显示两特征间的散点分布。
• 如何利用:通过颜色(类别)区分,可以直观地看出某些特征(或特征组合)是否能够很好地区分不同类别。
这样,你就能从整体上把握数据特征与类别之间的关系,为后续的特征选择、模型训练和调参提供重要参考。
输出第三部分:训练与预测以及准确率
• knn.classes_ 可以查看模型内部所识别的类别(对于鸢尾花数据集,一般是三种类别 0、1、2)。
• 在实际应用中,可以使用训练好的模型对新样本进行预测,从而得到其类别标签。
通过上述详细注释,你可以清晰地了解每一步的目的、原理以及在整个机器学习流程中所扮演的角色。
6. 总结
KNN算法因其简单直观而在入门机器学习时备受推崇,虽然在大规模数据和高维数据上存在计算和噪声问题,但其易于实现和理解的特点,使其成为很多初学者和实际应用场景中的不错选择。通过本文的介绍,希望大家对KNN算法有了基本的认识,并能在实践中灵活运用。
如果你有任何问题或想进一步讨论,欢迎在评论区留言交流!
希望这篇文章能帮助你快速上手KNN算法,开启机器学习之旅。