Scikit Learn - KNN 学习
Scikit-learn - k近邻算法 (k-NN)
Section titled “Scikit-learn - k近邻算法 (k-NN)”k近邻算法 (k-Nearest Neighbors, k-NN) 是一种简单、通用且广泛使用的机器学习算法。它被归类为非参数算法,这意味着它对底层数据分布不作假设;它也是一种惰性学习(lazy learning,或基于实例 instance-based)算法,这意味着它在训练期间不构建通用的内部模型。相反,它存储所有训练数据 (training data),只在预测时 (prediction time) 进行计算。
k-NN 算法主要包括两个步骤:
- 训练/存储阶段 (Training/Storage Phase):存储训练集 (training set) 中的所有数据点及其对应的标签(用于监督学习 supervised learning)。
- 预测阶段 (Prediction Phase):对于一个新的、未标记的样本:
a. 计算新样本与训练集中所有样本之间的距离 (distance)(例如,欧几里得距离 Euclidean distance)。
b. 找出
k个最近的邻居 (nearest neighbors)(距离最小的k个训练样本)。 c. 对于分类 (classification) 任务,通过这k个邻居的多数投票 (majority vote) 来预测类别标签 (class label)。对于回归 (regression) 任务,预测值是这k个邻居的目标值 (target values) 的平均值(或中位数 median)。
Scikit-learn 的 sklearn.neighbors 模块实现了 k-NN 算法,可用于无监督学习 (unsupervised learning)(邻居搜索 neighbor searches)和监督学习(分类和回归)任务。
无监督 k-NN 学习:最近邻搜索
Section titled “无监督 k-NN 学习:最近邻搜索”k-NN 的核心在于找到给定点的最近邻。这种邻居搜索能力对于许多算法都至关重要,而不仅仅是 k-NN 本身(例如,某些聚类方法 clustering methods、流形学习 manifold learning)。Scikit-learn 提供了 sklearn.neighbors.NearestNeighbors 来实现此目的。它为高效的邻居搜索算法(如 BallTree、KDTree 和暴力搜索 brute-force search)提供了一个统一的接口。
Scikit-learn 模块:NearestNeighbors
Section titled “Scikit-learn 模块:NearestNeighbors”sklearn.neighbors.NearestNeighbors 用于实现无监督最近邻学习。它为数据集 (dataset) 中的每个样本或新的查询点 (query points) 查找 k 个最近邻。
NearestNeighbors 的关键参数:
Section titled “NearestNeighbors 的关键参数:”| 参数 (Parameter) | 描述 (Description) |
|---|---|
n_neighbors | 整数 (default=5)。要检索的邻居数量。 |
radius | 浮点数 (default=1.0)。用于查找固定距离内的邻居的半径。如果使用此参数,kneighbors 将返回此半径内的所有点。 |
algorithm | 字符串 {‘auto’, ‘ball_tree’, ‘kd_tree’, ‘brute’} (default=‘auto’)。用于计算最近邻的算法。‘auto’ 会尝试根据输入数据 결정 최적의 알고리즘。 |
leaf_size | 整数 (default=30)。传递给 BallTree 或 KDTree 的叶子大小。影响构建速度、查询速度和内存。 |
metric | 字符串或可调用对象 (default=‘minkowski’)。要使用的距离度量。常用值:‘euclidean’(欧几里得)、‘manhattan’(曼哈顿)、‘cosine’(余弦)。有关来自 sklearn.metrics.pairwise_distances 或 scipy.spatial.distance 的完整列表,请参见 Scikit-learn 文档。 |
p | 整数 (default=2)。Minkowski 度量的参数。p=1 是曼哈顿距离 (L1),p=2 是欧几里得距离 (L2)。 |
metric_params | 字典 (default=None)。度量函数的附加关键字参数。 |
n_jobs | 整数或 None (default=None)。用于邻居搜索的并行作业数量。-1 表示使用所有处理器。 |
实现示例:查找邻居
Section titled “实现示例:查找邻居”from sklearn.neighbors import NearestNeighborsimport numpy as np
# 示例输入数据input_data = np.array([[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]])
# 初始化 NearestNeighbors,查找 3 个最近邻nrst_neigh = NearestNeighbors(n_neighbors=3, algorithm='ball_tree')
# 使用输入数据拟合模型(构建树)nrst_neigh.fit(input_data)
# 查找与输入数据点相同的 K 个邻居# 返回邻居的距离和索引distances, indices = nrst_neigh.kneighbors(input_data)
print("最近邻的索引(每行对应 input_data 中的一个点):")print(indices)print("\n到最近邻的距离:")print(distances)
# 我们还可以获得一个稀疏图,显示邻居之间的连接# 对于每个点,'1' 表示在指定的 k 个邻居之内connectivity_graph = nrst_neigh.kneighbors_graph(input_data).toarray()print("\n连接图(邻居的邻接矩阵):")print(connectivity_graph)最近邻的索引(每行对应 input_data 中的一个点):[[0 1 3] [1 0 2] [2 1 0] [3 4 0] [4 3 5] [5 4 3]]
到最近邻的距离:[[0. 1. 2.82842712] [0. 1. 1.41421356] [0. 1.41421356 2.23606798] [0. 1. 2.82842712] [0. 1. 1.41421356] [0. 1.41421356 2.23606798]]
连接图(邻居的邻接矩阵):[[1. 1. 0. 1. 0. 0.] [1. 1. 1. 0. 0. 0.] [0. 1. 1. 0. 0. 1.] [1. 0. 0. 1. 1. 0.] [0. 0. 0. 1. 1. 1.] [0. 0. 1. 0. 1. 1.]]第一个邻居总是点本身(距离为 0),因为查询集 (query set) 与训练集相同。然后,需要高效邻居查找的其他算法可以使用此 NearestNeighbors 对象。
监督 k-NN 学习
Section titled “监督 k-NN 学习”监督 k-NN 利用邻居信息进行预测任务 (prediction tasks):
- 分类 (Classification):用于具有离散标签 (discrete labels) 的数据。通过
k个最近邻中的多数投票分配类别标签。 - 回归 (Regression):用于具有连续标签 (continuous labels) 的数据。预测值是
k个最近邻的目标值的平均值(或中位数等其他聚合值 aggregate)。
k-NN 分类器
Section titled “k-NN 分类器”Scikit-learn 提供两种主要的 k-NN 分类器:
KNeighborsClassifier:根据k个最近邻进行分类。k是用户指定的整数。RadiusNeighborsClassifier:根据每个查询点周围固定半径r内的邻居数量进行分类。r是用户指定的浮点数。
这些分类器的关键参数包括 n_neighbors(用于 KNeighborsClassifier)、radius(用于 RadiusNeighborsClassifier)、weights(‘uniform’ 表示等权重 equal weighting,‘distance’ 表示距离越近权重越大 distance weighting)、algorithm、leaf_size、metric、p 和 n_jobs。
示例:在 Iris 数据集上使用 KNeighborsClassifier
Section titled “示例:在 Iris 数据集上使用 KNeighborsClassifier”from sklearn.datasets import load_irisfrom sklearn.model_selection import train_test_splitfrom sklearn.preprocessing import StandardScalerfrom sklearn.neighbors import KNeighborsClassifierfrom sklearn.metrics import accuracy_score
# 加载 Iris 数据集iris = load_iris()X, y = iris.data, iris.target
# 分割数据X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42, stratify=y)
# 特征缩放(对于像 KNN 这样的基于距离的算法很重要)scaler = StandardScaler()X_train_scaled = scaler.fit_transform(X_train)X_test_scaled = scaler.transform(X_test)
# 初始化并训练 KNeighborsClassifierknn_clf = KNeighborsClassifier(n_neighbors=5, weights='distance') # 使用 5 个邻居,按距离加权knn_clf.fit(X_train_scaled, y_train)
# 进行预测y_pred = knn_clf.predict(X_test_scaled)
# 评估准确率accuracy = accuracy_score(y_test, y_pred)print(f"KNeighborsClassifier 准确率: {accuracy:.4f}")KNeighborsClassifier 准确率: 0.9778选择最优的 k 至关重要,通常通过交叉验证 (cross-validation) 来完成。较小的 k 值可能导致决策边界 (decision boundary) 嘈杂(高方差 high variance),而较大的 k 值可能导致过度平滑 (oversmooth)(高偏差 high bias)。
k-NN 回归器
Section titled “k-NN 回归器”类似地,Scikit-learn 提供两种主要的 k-NN 回归器:
KNeighborsRegressor:根据k个最近邻的目标值的平均值(或其他聚合值)进行预测。RadiusNeighborsRegressor:根据固定半径r内的邻居进行预测。
参数与其分类器对应物类似。
示例:KNeighborsRegressor
Section titled “示例:KNeighborsRegressor”from sklearn.datasets import fetch_california_housing # 回归数据集from sklearn.model_selection import train_test_splitfrom sklearn.preprocessing import StandardScalerfrom sklearn.neighbors import KNeighborsRegressorfrom sklearn.metrics import mean_squared_errorimport numpy as np
# 加载加州住房数据集housing = fetch_california_housing()X, y = housing.data, housing.target
# 为了加快示例速度,使用数据子集X_subset, y_subset = X[:2000], y[:2000]
# 分割数据X_train, X_test, y_train, y_test = train_test_split(X_subset, y_subset, test_size=0.3, random_state=42)
# 特征缩放scaler = StandardScaler()X_train_scaled = scaler.fit_transform(X_train)X_test_scaled = scaler.transform(X_test)
# 初始化并训练 KNeighborsRegressorknn_reg = KNeighborsRegressor(n_neighbors=7, weights='distance') # 使用 7 个邻居knn_reg.fit(X_train_scaled, y_train)
# 进行预测y_pred_reg = knn_reg.predict(X_test_scaled)
# 使用均方误差 (MSE) 评估mse = mean_squared_error(y_test, y_pred_reg)print(f"KNeighborsRegressor MSE: {mse:.4f}")print(f"KNeighborsRegressor RMSE: {np.sqrt(mse):.4f}")
# 对新样本进行预测示例(必须进行类似的缩放)sample_new_data = X_test_scaled[0].reshape(1, -1) # 取第一个测试样本predicted_value = knn_reg.predict(sample_new_data)print(f"预测值 (Predicted value) for a sample: {predicted_value[0]:.4f}, 真实值 (True value): {y_test[0]:.4f}")输出(值取决于数据集和分割方式)
Section titled “输出(值取决于数据集和分割方式)”KNeighborsRegressor MSE: 0.5568KNeighborsRegressor RMSE: 0.7462Predicted value for a sample: 0.9695, True value: 0.4770参数 weights='distance' 通常通过给予距离更近的邻居更大的影响来提高性能。k 的选择和距离度量对于 k-NN 回归器来说同样是重要的考虑因素。
有关更多详细信息,请参阅 Scikit-learn 用户指南 (User Guide):https://scikit-learn.org/stable/modules/neighbors.html