KNN and K-means 监督与非监督学习

什么是 KNN 算法? 🤔

KNN (K-Nearest Neighbors,K近邻算法) 是一种非参数监督学习算法,既可用于分类,也可用于回归。它是一种惰性学习(Lazy Learning)算法,这意味着它在训练阶段几乎不做任何计算,而是将所有训练数据存储起来,直到需要做出预测时才开始计算。

它的工作原理是什么? 💡

KNN 的核心思想是:一个样本的类别由其最近的 K 个邻居的类别决定。

1. K-近邻分类 (K-Nearest Neighbors for Classification)

对于一个新的数据点,KNN 算法执行以下步骤来预测其类别:

  1. 确定 K 值: 选择一个正整数 K(通常较小,例如 K=3 或 K=5)。
  2. 计算距离: 计算新数据点与训练集中所有数据点之间的距离(最常见的是欧氏距离)。
  3. 找到 K 个邻居: 找出距离最近的 K 个训练样本点,这些点就是新数据点的 K 个“邻居”。
  4. 投票决定类别: 统计这 K 个邻居中,哪一类别出现的次数最多。将出现次数最多的那个类别作为新数据点的预测类别。
    在这里插入图片描述

2. K-近邻回归 (K-Nearest Neighbors for Regression)

如果用于回归,步骤类似,但在最后一步有所不同:

  1. 找到最近的 K 个邻居。
  2. 将这 K 个邻居的目标值(即输出值)的平均值(或加权平均值)作为新数据点的预测值。

关键要素 🔑

  • K 值: K 的选择对结果至关重要。K 值太小(例如 K=1)容易受到异常值的影响,模型可能过于复杂(过拟合)。K 值太大会使模型过于简单,导致欠拟合,并且会模糊不同类别之间的边界。通常通过交叉验证来确定最佳 K 值。
  • 距离度量: 用于衡量样本之间相似性的方法。最常用的是欧氏距离(Euclidean Distance)。其他包括曼哈顿距离(Manhattan Distance)或闵可夫斯基距离(Minkowski Distance)。
  • 特征缩放: 由于 KNN 依赖于距离计算,因此所有特征应具有相似的尺度。在使用 KNN 之前,通常需要进行特征标准化归一化

优缺点 👍👎

在这里插入图片描述

什么是 K-Means 算法?

K-Means 算法是一种经典的无监督学习算法,主要用于聚类 (Clustering)。它的目标是将一个数据集中的 N 个数据点划分为预先设定的 K 个簇 (Cluster),使得每个数据点都属于离它最近的聚类中心 (Centroid) 所代表的簇。

在这里插入图片描述

K-Means 的工作原理(迭代过程)

K-Means 算法通过迭代来寻找最佳的聚类划分:

  1. 初始化 (Initialization):
  • 首先,确定要划分的簇的数量 K。
  • 随机选择 K 个数据点作为初始聚类中心(或质心,Centroid)。
  1. 分配 (Assignment):
  • 对于数据集中的每个数据点,计算它到 K 个聚类中心的距离(通常使用欧氏距离)。
  • 将该数据点分配给距离它最近的那个聚类中心所在的簇。
  1. 更新 (Update):
  • 在所有数据点都分配完毕后,重新计算每个簇的新聚类中心。新中心是该簇内所有数据点的平均值(均值,Mean)。
  1. 重复 (Repeat):
  • 重复执行“分配”和“更新”步骤,直到聚类中心的位置不再发生显著变化,或者达到预设的最大迭代次数为止。

K-Means 与 KNN 的联系和区别 🔗

K-Means (K-Means Clustering) 和 KNN (K-Nearest Neighbors) 仅在名称和都使用距离度量(如欧氏距离)方面有相似之处,但在基本原理、目的和应用场景上存在本质区别。

📊 核心区别

在这里插入图片描述

🤝 联系

  • 都基于距离度量: 两个算法的核心操作都是计算样本点之间的距离(最常见的是欧氏距离)来衡量相似性。

  • 名称相似: 都包含参数 K,尽管 K 的意义完全不同。

  • 都依赖最近邻思想: K-Means 在分配阶段是基于数据点到最近中心的距离;KNN 在预测阶段是基于数据点到最近邻居的距离。
    总而言之:

  • 如果你想将无标签的数据分成 K 个有意义的组,你应该使用 K-Means (聚类)

  • 如果你想根据有标签的训练数据来预测新数据的类别或数值,你应该使用 KNN (分类/回归)

最佳实践数据集推荐:鸢尾花 (Iris) 数据集 🌸

鸢尾花数据集 (Iris Dataset) 是机器学习领域最著名、使用最广泛的数据集之一。它非常适合初学者,因为它体积小、干净,并且数据自带标签,这使得它既可以用于有监督学习(KNN)也可以用于无监督学习(K-Means 的评估)。

数据集概览

目标类别 (Labels):

  1. Iris Setosa (山鸢尾)
  2. Iris Versicolour (杂色鸢尾)
  3. Iris Virginica (维吉尼亚鸢尾)

实践代码

# 数据获取
import numpy as np
from collections import Counter
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

# --- 欧氏距离函数 (用于两个模型) ---
def euclidean_distance(point1, point2):
    """计算两个多维点之间的欧氏距离"""
    return np.sqrt(np.sum((point1 - point2) ** 2))

# 1. 加载 Iris 数据
iris = load_iris()
X = iris.data  # 特征 (萼片/花瓣长度和宽度)
y = iris.target # 标签 (0, 1, 2 分别代表三种鸢尾花)

# 2. 特征缩放 (对距离敏感的算法是必要的)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)


# 定义 KNN 算法
class CustomKNN:
    def __init__(self, k=3):
        self.k = k

    def fit(self, X_train, y_train):
        self.X_train = X_train
        self.y_train = y_train

    def predict(self, X_test):
        predictions = [self._predict(x) for x in X_test]
        return np.array(predictions)

    def _predict(self, x):
        distances = [euclidean_distance(x, x_train) for x_train in self.X_train]
        k_indices = np.argsort(distances)[:self.k]
        # print("1",k_indices)
        k_nearest_labels = [self.y_train[i] for i in k_indices]
        # print("2",k_nearest_labels)
        most_common = Counter(k_nearest_labels).most_common(1)
        return most_common[0][0]

#实践 KNN
# 1. 分割数据集
X_train, X_test, y_train, y_test = train_test_split(
    X_scaled, y, test_size=0.3, random_state=42
)

# 2. 初始化并训练模型 (使用 K=5)
knn_model = CustomKNN(k=5)
knn_model.fit(X_train, y_train)

# 3. 预测
y_pred = knn_model.predict(X_test)

# 4. 评估 (计算准确率)
accuracy = np.sum(y_pred == y_test) / len(y_test)
print("--- KNN 分类结果 ---")
print(f"使用的 K 值: {knn_model.k}")
print(f"测试集准确率: {accuracy:.4f}")

# 定义 K-means 算法
class CustomKMeans:
    def __init__(self, n_clusters=3, max_iter=100):
        self.n_clusters = n_clusters
        self.max_iter = max_iter
        self.centroids = None
        self.labels = None

    def _initialize_centroids(self, X):
        random_indices = np.random.choice(X.shape[0], self.n_clusters, replace=False)
        self.centroids = X[random_indices]

    def _create_clusters(self, X):
        clusters = [[] for _ in range(self.n_clusters)]
        self.labels = np.zeros(X.shape[0])

        for idx, sample in enumerate(X):
            distances = [euclidean_distance(sample, centroid) for centroid in self.centroids]
            closest_centroid_idx = np.argmin(distances)

            clusters[closest_centroid_idx].append(idx)
            self.labels[idx] = closest_centroid_idx

        return clusters

    def _update_centroids(self, X, clusters):
        new_centroids = np.zeros((self.n_clusters, X.shape[1]))
        for cluster_idx, cluster in enumerate(clusters):
            if cluster:
                cluster_points = X[cluster]
                new_centroids[cluster_idx] = np.mean(cluster_points, axis=0)
            else:
                # 处理空簇
                new_centroids[cluster_idx] = self.centroids[cluster_idx]
        return new_centroids

    def fit(self, X):
        self._initialize_centroids(X)

        for _ in range(self.max_iter):
            clusters = self._create_clusters(X)
            old_centroids = self.centroids
            self.centroids = self._update_centroids(X, clusters)

            # 检查收敛
            if np.allclose(old_centroids, self.centroids):
                break

        return self.labels
# 实践 K-means
# K-Means 在训练时不需要标签,所以使用 X_scaled
kmeans_model = CustomKMeans(n_clusters=3)
predicted_clusters = kmeans_model.fit(X_scaled)

# 评估 K-Means (与真实标签进行比较)
# 注意:由于 K-Means 的簇标签 (0, 1, 2) 顺序不一定与真实标签顺序一致,
# 简单的准确率计算不适用。我们需要使用聚类指标。

# 为了简单展示,我们打印前 10 个点的聚类结果和真实标签进行对比:
print("\n--- K-Means 聚类结果 (K=3) ---")
print("前 10 个点的预测簇标签 (K-Means):", predicted_clusters[:10].astype(int))
print("前 10 个点的真实类别标签 (Iris):", y[:10])

# 我们可以看到 K-Means 自动将数据分成了 3 组,并且与真实标签有很高的相似性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

xwhking

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

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

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

打赏作者

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

抵扣说明:

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

余额充值