完全突破KNN,利用KNN进行分类 !!

文摘   2024-10-25 15:36   北京  

哈喽,我是cos大壮!~

今儿和大家聊一个关于 KNN 的案例:利用KNN进行分类任务。

为了更好地阐述如何利用K近邻算法(K-Nearest Neighbors, KNN)来进行分类任务,我将从理论讲解开始,逐步构建一个基于虚拟数据集的实际案例,使用PyTorch来实现,并通过可视化图形来说明分类过程中的关键步骤。

我们还会深入探讨如何进行算法优化和参数调优。

老规矩如果大家伙觉得近期文章还不错!欢迎大家点个赞、转个发,文末赠送《机器学习学习小册》

文末可取本文PDF版本~

1. KNN算法的原理与公式

算法原理

K近邻算法是一种基于实例的学习算法,是监督学习中的一种分类算法。其核心思想是,给定一个待分类样本,通过计算该样本与训练集中每个样本之间的距离,找到距离最近的K个样本(邻居),然后根据这K个样本所属的类别来决定待分类样本的类别。

KNN的分类步骤通常如下:

  1. 计算距离:对于每一个待分类样本,计算它与训练集中所有样本的距离。
  2. 选择邻居:根据距离选出距离最近的K个训练样本作为邻居。
  3. 投票决定类别:对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=(106))
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(22, figsize=(1212))

# 定义要测试的k值
k_values = [1357]

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=(86))
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=(86))
plt.plot(fpr, tpr, color='darkorange', lw=2, label=f'ROC curve (area = {roc_auc:.2f})')
plt.plot([01], [01], color='navy', lw=2, linestyle='--')
plt.xlim([0.01.0])
plt.ylim([0.01.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(121)
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值的变化而调整,以及模型在不同决策边界下的性能表现。

最后

大家有问题可以直接在评论区留言即可~

喜欢本文的朋友可收藏、点赞、转发起来!

需要本文PDF的同学,扫码备注「案例汇总」即可~ 
关注本号,带来更多算法干货实例,提升工作学习效率!
最后,给大家准备了《机器学习学习小册》PDF版本16大块的内容,124个问题总结
100个超强算法模型,大家如果觉得有用,可以点击查看~

推荐阅读

原创、超强、精华合集
100个超强机器学习算法模型汇总
机器学习全路线
机器学习各个算法的优缺点
7大方面,30个最强数据集
6大部分,20 个机器学习算法全面汇总
铁汁,都到这了,别忘记点赞呀~

深夜努力写Python
Python、机器学习算法
 最新文章