哈喽,我是cos大壮!~
今儿和大家聊一个关于 KNN 的案例:利用KNN进行分类任务。
为了更好地阐述如何利用K近邻算法(K-Nearest Neighbors, KNN)来进行分类任务,我将从理论讲解开始,逐步构建一个基于虚拟数据集的实际案例,使用PyTorch来实现,并通过可视化图形来说明分类过程中的关键步骤。
我们还会深入探讨如何进行算法优化和参数调优。
文末可取本文PDF版本~
1. KNN算法的原理与公式
算法原理
K近邻算法是一种基于实例的学习算法,是监督学习中的一种分类算法。其核心思想是,给定一个待分类样本,通过计算该样本与训练集中每个样本之间的距离,找到距离最近的K个样本(邻居),然后根据这K个样本所属的类别来决定待分类样本的类别。
KNN的分类步骤通常如下:
计算距离:对于每一个待分类样本,计算它与训练集中所有样本的距离。 选择邻居:根据距离选出距离最近的K个训练样本作为邻居。 投票决定类别:对K个邻居中出现频率最多的类别进行投票,并将这个类别作为待分类样本的预测类别。
距离度量
KNN算法中最常见的距离度量方式是欧氏距离。给定两个样本 和 ,它们的欧氏距离可以表示为:
除此之外,还可以使用其他距离度量方法,如曼哈顿距离、切比雪夫距离等。
K值的选择
K值是KNN算法中的超参数,它代表我们选择的邻居数量。K值的选择对于KNN的性能有显著影响:
K较小时,模型会变得更加敏感,容易过拟合,因为它只依赖少量邻居的类别决定结果; K较大时,模型变得更加平滑,但可能会出现欠拟合,因为它可能会包含过多的邻居,并且包括一些不同类别的点。
一般通过交叉验证的方式来确定最优的K值。
2. 数据集生成与预处理
为了展示KNN算法的具体实现,我们将使用虚拟数据集来进行分类任务。接下来使用Python生成一个二维空间的虚拟数据集,并将其划分为两类。
数据集生成
我们使用make_blobs
函数来生成一组二维数据点,其中包含两类数据。每类数据将有不同的中心点,以确保数据点可以合理区分。
import torch
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
from sklearn.model_selection import train_test_split
from torch.utils.data import DataLoader, TensorDataset
# 生成虚拟数据集
X, y = make_blobs(n_samples=2000, centers=2, random_state=42, cluster_std=2.0)
# 可视化生成的数据
plt.scatter(X[:, 0], X[:, 1], c=y, cmap='coolwarm', s=30)
plt.title('Generated Dataset')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.show()
# 转换为PyTorch张量
X_tensor = torch.tensor(X, dtype=torch.float32)
y_tensor = torch.tensor(y, dtype=torch.long)
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X_tensor, y_tensor, test_size=0.2, random_state=42)
# 构建DataLoader
train_dataset = TensorDataset(X_train, y_train)
test_dataset = TensorDataset(X_test, y_test)
train_loader = DataLoader(train_dataset, batch_size=16, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=16, shuffle=False)
上面的代码生成了一个包含500个数据点的二维数据集,每个数据点有两个特征,并分为两类。我们将数据集划分为训练集和测试集,并使用PyTorch的DataLoader来进行批量处理。
通过生成的散点图,我们可以看到两类数据分布在二维平面中的不同区域。
3. 模型训练与测试
这里咱们使用pytorch来实现。由于KNN本身是一种基于距离的简单算法,它不涉及模型的显式训练。因此,我们不需要像其他机器学习算法那样设计复杂的模型结构。在PyTorch中,我们可以通过计算待分类样本与训练集中所有样本的距离,找到最近的K个样本,并进行投票决定分类结果。
距离计算与分类函数
我们将定义一个KNN函数,用于在PyTorch中实现KNN的核心分类逻辑。该函数接收待分类的测试样本,计算其与训练样本的距离,并根据距离找到K个最近邻居,最终返回预测的类别。
def knn_predict(X_train, y_train, X_test, k=3):
y_pred = []
for x in X_test:
# 计算测试样本与训练集的欧氏距离
distances = torch.sqrt(torch.sum((X_train - x) ** 2, dim=1))
# 选择K个最近邻
k_neighbors = y_train[torch.topk(distances, k, largest=False).indices]
# 通过投票决定类别
predicted_label = torch.mode(k_neighbors).values.item()
y_pred.append(predicted_label)
return torch.tensor(y_pred, dtype=torch.long)
# 在测试集上进行预测
y_pred = knn_predict(X_train, y_train, X_test, k=5)
# 计算分类准确率
accuracy = torch.sum(y_pred == y_test).item() / len(y_test)
print(f'Accuracy: {accuracy * 100:.2f}%')
上述代码中,我们实现了一个简单的KNN预测函数。它通过计算测试样本与所有训练样本之间的距离,选出K个最近的邻居,并通过投票决定类别。最后,我们在测试集上进行分类并计算准确率。
4. 结果分析与数据可视化
接下来,我们将通过可视化图形展示KNN分类过程中的一些关键点。
散点图展示分类结果
我们首先通过散点图来展示训练集和测试集中的数据点,并将分类结果以不同颜色区分出来。
# 可视化分类结果
plt.figure(figsize=(10, 6))
plt.scatter(X_test[:, 0], X_test[:, 1], c=y_pred, cmap='coolwarm', marker='o', label='Test Predictions', s=50)
plt.scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap='coolwarm', marker='x', label='Training Data', alpha=0.5)
plt.title('KNN Classification Results')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.legend()
plt.show()
该图展示了测试集中每个数据点的分类结果(以圆点表示)和训练集中的数据(以叉号表示)。颜色代表不同的分类,红色和蓝色分别表示两个不同的类别。通过该图我们可以直观地看到分类效果,并观察KNN在不同区域的决策边界。
K值对分类结果的影响
KNN的分类效果对K值的选择非常敏感。接下来我们展示不同K值下的分类效果。
# 创建2x2子图网格
fig, axs = plt.subplots(2, 2, figsize=(12, 12))
# 定义要测试的k值
k_values = [1, 3, 5, 7]
for i, k in enumerate(k_values):
y_pred = knn_predict(X_train, y_train, X_test, k=k)
ax = axs[i // 2, i % 2] # 使用整除和取余来确定行和列
# 绘制测试数据的预测结果
scatter1 = ax.scatter(X_test[:, 0], X_test[:, 1], c=y_pred, cmap='coolwarm', marker='o', s=50, label=f'Test Predictions (k={k})')
# 绘制训练数据
scatter2 = ax.scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap='coolwarm', marker='x', alpha=0.5, label='Training Data')
# 设置标题和坐标轴标签
ax.set_title(f'KNN Classification with k={k}')
ax.set_xlabel('Feature 1')
ax.set_ylabel('Feature 2')
ax.legend()
plt.tight_layout()
plt.show()
这组图展示了不同K值下的分类结果。我们可以看到,随着K值的变化,决策边界会变得更加平滑或更加敏感。K值过小会导致过拟合,而K值过大则可能引入过多噪声。
混淆矩阵
为了评估分类模型的效果,我们可以绘制混淆矩阵来显示预测类别与真实类别之间的关系。
from sklearn.metrics import confusion_matrix
import seaborn as sns
# 计算混淆矩阵
cm = confusion_matrix(y_test, y_pred)
# 可视化混淆矩阵
plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, fmt="d", cmap="Blues", xticklabels=['Class 0', 'Class 1'], yticklabels=['Class 0', 'Class 1'])
plt.title('Confusion Matrix')
plt.xlabel('Predicted Label')
plt.ylabel('True Label')
plt.show()
混淆矩阵可以帮助我们更好地了解模型的错误分类情况。矩阵的对角线元素表示正确分类的样本数,非对角线元素则表示错误分类的数量。
分类报告与ROC曲线
我们还可以通过分类报告来评估模型的精确率、召回率和F1-score,同时绘制ROC曲线来评估模型的整体性能。
from sklearn.metrics import classification_report, roc_curve, auc
# 打印分类报告
print(classification_report(y_test, y_pred))
# 计算ROC曲线
fpr, tpr, _ = roc_curve(y_test, y_pred)
roc_auc = auc(fpr, tpr)
# 绘制ROC曲线
plt.figure(figsize=(8, 6))
plt.plot(fpr, tpr, color='darkorange', lw=2, label=f'ROC curve (area = {roc_auc:.2f})')
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver Operating Characteristic (ROC) Curve')
plt.legend(loc="lower right")
plt.show()
ROC曲线展示了模型在不同分类阈值下的性能表现,曲线下的面积(AUC)可以衡量分类器的总体性能。
5. 算法优化与参数调优
KNN算法的性能很大程度上依赖于数据集的分布以及超参数K的选择。为了提高分类精度,我们可以从以下几个方面进行优化:
K值选择
通过交叉验证的方法找到最佳的K值。交叉验证通过将数据集划分为多个子集,在多个训练-测试分割上测试模型,从而得到更稳健的K值选择。
from sklearn.model_selection import cross_val_score
# 使用交叉验证选择最佳K值
k_range = range(1, 21)
scores = []
for k in k_range:
y_pred_cv = knn_predict(X_train, y_train, X_test, k=k)
score = torch.sum(y_pred_cv == y_test).item() / len(y_test)
scores.append(score)
# 可视化K值与准确率的关系
plt.plot(k_range, scores, marker='o')
plt.title('K-Value vs Accuracy')
plt.xlabel('K')
plt.ylabel('Accuracy')
plt.show()
通过交叉验证选择最佳K值可以防止模型过拟合或欠拟合。
距离度量优化
除了使用欧氏距离之外,还可以尝试使用其他距离度量方法(如曼哈顿距离、切比雪夫距离等)来改善KNN的性能。
数据标准化
KNN对不同量纲的特征非常敏感,因此在很多情况下,进行特征的标准化处理非常重要。我们可以通过归一化或标准化方法来处理特征值,使得不同特征在相同的尺度上进行比较。
from sklearn.preprocessing import StandardScaler
# 标准化数据
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# 在标准化数据上重新训练KNN
y_pred_scaled = knn_predict(torch.tensor(X_train_scaled), y_train, torch.tensor(X_test_scaled), k=5)
标准化可以显著提升KNN的分类性能,尤其是在特征量纲不一致的情况下。
通过整个的理论部分,和后面的案例部分,我们详细介绍了KNN分类算法的工作原理,并使用PyTorch实现了一个完整的分类任务。从模型训练到数据可视化,我们展示了KNN如何通过计算距离和投票机制来进行分类,并且探讨了K值选择、特征标准化和距离度量等优化方法。KNN是一种简单但有效的分类算法,适用于小规模数据集的分类任务,但在高维数据集或大规模数据集上,可能存在计算复杂度较高、分类效果不佳的问题。
最后的可视化图形,我们可以清晰地看到KNN的分类边界如何随着K值的变化而调整,以及模型在不同决策边界下的性能表现。
最后
大家有问题可以直接在评论区留言即可~
喜欢本文的朋友可以收藏、点赞、转发起来!
推荐阅读
原创、超强、精华合集 100个超强机器学习算法模型汇总 机器学习全路线 机器学习各个算法的优缺点 7大方面,30个最强数据集 6大部分,20 个机器学习算法全面汇总 铁汁,都到这了,别忘记点赞呀~