专项练习 · 9 分钟阅读

Python k-NN 练习:距离、投票与缩放

k-NN 是很好的第一个分类器,因为每个决定都看得见:距离、邻居、投票和评估都能检查。这些练习让每一步都可以测试。

只写一次距离函数

保留一个距离函数,在排序和评估中复用。小函数比散落在循环里的距离逻辑更容易测试。比较点之前要检查维度是否一致。

def distance(a, b):
    return sum((x - y) ** 2 for x, y in zip(a, b)) ** 0.5

print(distance((0, 0), (3, 4)))

可复用的度量

带着标签排序

按距离排序 (点, 标签) 对,而不是先排点、之后再查标签。这样预测始终和证据绑定,也避免排序后索引错位。

def distance(a, b):
    return sum((x - y) ** 2 for x, y in zip(a, b)) ** 0.5

points = [((1, 1), "a"), ((4, 4), "b"), ((1, 2), "a")]
target = (1, 1.5)
ranked = sorted(points, key=lambda item: distance(target, item[0]))
print(ranked[:2])

标签跟着点走

用确定的平票规则投票

取前 k 个标签并计数。如果两个标签平票,使用明确规则,例如选择距离最近的平票点。随机或依赖哈希顺序的平票规则会让相同输入产生不同预测。

from collections import Counter

labels = ["a", "b", "a", "c"]
winner = Counter(labels).most_common(1)[0][0]
print(winner)

可复现的投票

度量前先缩放

距离把每个单位视为同等重要。收入以千计、年龄以年计时,收入会占主导。用训练统计量标准化特征,再把同一变换应用到验证集和测试集。

def distance(a, b):
    return sum((x - y) ** 2 for x, y in zip(a, b)) ** 0.5

raw = distance((30, 30000), (30, 31000))
scaled = distance((0.3, 0.3), (0.3, 1.0))
print(round(raw, 1), round(scaled, 1))

单位改变答案

用留出点评估

在训练集上预测不是评估。使用训练/测试划分,数据很小时可以用留一法。还要报告基线:多数类预测往往出人意料地强。

data = [((0, 0), "a"), ((1, 0), "a"), ((5, 5), "b")]
correct = 0
for i, (point, label) in enumerate(data):
    train = data[:i] + data[i + 1:]
    nearest = min(train, key=lambda item: (item[0][0] - point[0]) ** 2 + (item[0][1] - point[1]) ** 2)
    correct += nearest[1] == label
print(correct, len(data))

留一法

你的练习清单

常见问题

k 怎么选?

二分类可从小奇数开始,再在验证数据上评估多个值。k 太小噪声大,k 太大会抹平局部结构。

应该标准化还是归一化?

两者都可以。标准化按标准差中心化缩放;最小-最大归一化映射到固定区间。关键是变换只在训练数据上拟合。

k-NN 计算贵吗?

预测时要把新点与所有训练点比较,大数据集需要近似索引或换模型。

练习 k-NN

12 道题,覆盖距离、邻居、投票、缩放和留出评估。

打开练习

继续阅读: