基于Python的机器学习系列(11):K-Nearest Neighbors

简介

        K-Nearest Neighbors (KNN) 算法是所有监督学习算法中最简单、直观的之一。其基本思想是通过计算新数据点到所有训练数据点的距离,找到距离最近的 K 个数据点(即 K 个邻居),然后根据这 K 个邻居的多数类别来决定新数据点的类别。

        例如,给定一个红色的交叉点 X,我们只需要获取其周围最近的邻居,并根据这些邻居的多数类别来为 X 分类。

算法实现步骤

  1. 准备数据

    • 获取训练集和测试集,将数据整理成适当的格式。
    • 数据标准化,以加速算法收敛。
    • 分割训练集和测试集。
  2. 计算点之间的距离

    编写函数来计算测试数据与所有训练数据之间的成对距离。
  3. 找到最近的邻居

    对距离进行排序,选取前 K 个最近的邻居。
  4. 根据多数类别进行分类

    统计最近 K 个邻居的类别,并选取出现频率最高的类别作为预测结果。

实践

1. 准备数据

        我们将从一个二维数据集开始,其中包含 4 个不同的类别。首先,我们将数据集分割成训练集和测试集,并对数据进行标准化处理。

from sklearn.datasets import make_blobs
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

# 生成数据
X, y = make_blobs(n_samples=300, centers=4, random_state=0, cluster_std=1.0)

# 分割训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)

# 标准化数据
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

2. 计算点之间的距离

        接下来,我们将编写一个函数来计算测试集和训练集之间的成对距离。

def find_distance(X_train, X_test):
    dist = X_test[:, np.newaxis, :] - X_train[np.newaxis, :, :]
    sq_dist = dist ** 2
    summed_dist = sq_dist.sum(axis=2)
    return np.sqrt(summed_dist)

3. 找到最近的邻居

        我们将利用上一步计算的距离矩阵,找到每个测试数据点的 K 个最近邻居。

def find_neighbors(X_train, X_test, k=3):
    dist = find_distance(X_train, X_test)
    neighbors_ix = np.argsort(dist)[:, 0:k]
    return neighbors_ix

4. 根据多数类别进行分类

        最后,我们统计最近 K 个邻居的类别,并选取出现频率最高的类别作为预测结果。

def get_most_common(y):
    return np.bincount(y).argmax()

def predict(X_train, X_test, y_train, k=3):
    neighbors_ix = find_neighbors(X_train, X_test, k)
    pred = np.zeros(X_test.shape[0])
    for ix, y in enumerate(y_train[neighbors_ix]):
        pred[ix] = get_most_common(y)
    return pred

yhat = predict(X_train, X_test, y_train, k=3)

5. 验证模型性能

        我们将使用准确率、平均精度得分和分类报告来评估 KNN 模型的性能。

from sklearn.metrics import average_precision_score, classification_report
from sklearn.preprocessing import label_binarize

n_classes = len(np.unique(y_test))

print("Accuracy: ", np.sum(yhat == y_test)/len(y_test))

y_test_binarized = label_binarize(y_test, classes=[0, 1, 2, 3])
yhat_binarized = label_binarize(yhat, classes=[0, 1, 2, 3])

for i in range(n_classes):
    class_score = average_precision_score(y_test_binarized[:, i], yhat_binarized[:, i])
    print(f"Class {i} score: ", class_score)

print("Classification report: ")
print(classification_report(y_test, yhat))

使用Scikit-Learn实现KNN

        Scikit-Learn 提供了更为简便的实现方式,通过KNeighborsClassifier 可以轻松地创建和调参 KNN 模型。

from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import StratifiedShuffleSplit, GridSearchCV

model = KNeighborsClassifier()
param_grid = {"n_neighbors": np.arange(2, 10)}

cv = StratifiedShuffleSplit(n_splits=5, test_size=0.2, random_state=42)
grid = GridSearchCV(model, param_grid=param_grid, cv=cv, refit=True)
grid.fit(X_train, y_train)

print(f"The best parameters are {grid.best_params_} with a score of {grid.best_score_:.2f}")

yhat = grid.predict(X_test)

print("Classification report: ")
print(classification_report(y_test, yhat))

何时使用KNN

        KNN 算法的实现相对简单,在一些简单的分类问题上表现良好。然而,它的劣势也十分明显:

  • 随着特征数量的增加,计算开销急剧上升。KNN 需要为每个输入点计算到所有其他点的距离,并排序,这在特征数多时代价极高。
  • 无法处理类别型特征,因为难以为类别型特征制定合适的距离公式。
  • 调整邻居数目(K)的过程耗时较长。

加深理解

  1. 比较 Naive Bayes、Logistic Regression 和 K-Nearest Neighbors 三种算法

    • Naive Bayes:假设特征之间相互独立,适用于高维数据和文本分类任务,计算速度快,但假设过于简单。
    • Logistic Regression:适用于线性可分数据,有明确的概率输出,适合特征数量适中的数据集。
    • K-Nearest Neighbors:直观简单,适合少量特征和数据分布规则的情况,但计算成本高,特征多时效果差。
  2. 欧几里得距离失效的情况

    当特征具有不同的量纲或尺度时,欧几里得距离可能会失效,因为它会受到大尺度特征的主导。此时需要进行特征标准化或使用其他距离度量。
  3. 分类平局时的处理方法

    若出现平局情况,可以选择:
    • 随机选择一个类别
    • 使用权重距离,即离得越近的点权重越大
    • 减少 K 的值
  4. K-Nearest Neighbors 分类计算

    给定测试数据,使用 K=3 计算其属于类 0 或类 1 的可能性,并给出预测类别。

结语

        K-Nearest Neighbors 是一种简单但功能强大的分类算法,特别适合初学者学习机器学习的基本概念。然而,它的计算复杂度较高,随着数据量和特征维度的增加,可能并不是最优选择。因此,在实际应用中,需要根据数据集的具体情况选择合适的算法。

如果你觉得这篇博文对你有帮助,请点赞、收藏、关注我,并且可以打赏支持我!

欢迎关注我的后续博文,我将分享更多关于人工智能、自然语言处理和计算机视觉的精彩内容。

谢谢大家的支持!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

会飞的Anthony

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

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

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

打赏作者

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

抵扣说明:

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

余额充值