标准回答
KNN 不学习参数,预测时对每个测试样本计算它到所有训练样本的距离,选出最近的 K 个邻居,由它们的标签多数投票决定类别。工程上的关键是向量化:用广播构造距离矩阵,再用 argpartition 做 Top-K 选择(O(N) 而非排序的 O(N log N)),并对每个样本用 bincount 投票。下面是可运行实现:
python
import numpy as np
class KNNClassifier:
def __init__(self, k=3):
self.k = k
def fit(self, X, y):
# 惰性学习:仅保存训练数据
self.X_train = np.asarray(X, dtype=float)
self.y_train = np.asarray(y)
return self
def predict(self, X):
X = np.asarray(X, dtype=float)
# 平方欧氏距离矩阵:(a-b)^2 = a^2 + b^2 - 2ab,向量化避免循环
dist2 = (
(X ** 2).sum(1)[:, None]
+ (self.X_train ** 2).sum(1)[None, :]
- 2 * X @ self.X_train.T
)
# argpartition 取每行最近 k 个邻居的索引(无需全排序)
knn_idx = np.argpartition(dist2, kth=self.k - 1, axis=1)[:, :self.k]
preds = np.empty(X.shape[0], dtype=self.y_train.dtype)
for i, idx in enumerate(knn_idx):
votes = self.y_train[idx]
preds[i] = np.bincount(votes).argmax() # 多数投票取众数
return preds
if __name__ == '__main__':
rng = np.random.default_rng(0)
X0 = rng.normal(0, 1, (50, 2)); X1 = rng.normal(4, 1, (50, 2))
X = np.vstack([X0, X1]); y = np.array([0] * 50 + [1] * 50)
clf = KNNClassifier(k=5).fit(X, y)
test = np.array([[0.0, 0.0], [4.0, 4.0]])
print(clf.predict(test)) # -> [0 1]常见误区
⚠️ 常见踩坑
bincount 要求标签是非负整数,若标签是字符串需先做 LabelEncoder 映射,否则报错。另一易错点是忘记标准化特征,量纲大的维度会主导欧氏距离;以及偶数 K 在二分类时可能平票。
延伸学习
与本题相关的知识库文章、术语、工具与行业资讯。