Scikit-learn KNeighborsClassifier 实战:5 行代码解决鸢尾花分类与 3 个关键参数调优
鸢尾花分类问题是机器学习领域的经典案例,而 k 近邻(KNN)算法凭借其直观性和易用性,成为解决这类问题的利器。本文将带你快速掌握 Scikit-learn 中 KNeighborsClassifier 的核心用法,通过 5 行代码完成基础分类,并深入解析n_neighbors、weights和metric三个关键参数的调优技巧。
1. 环境准备与数据加载
在开始之前,我们需要导入必要的库并加载鸢尾花数据集。Scikit-learn 提供了现成的数据集接口,极大简化了数据准备过程:
from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split # 加载数据集 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)鸢尾花数据集包含 150 个样本,每个样本有 4 个特征:
- 花萼长度(sepal length)
- 花萼宽度(sepal width)
- 花瓣长度(petal length)
- 花瓣宽度(petal width)
目标变量是 3 种鸢尾花的类别:
- 0: Setosa
- 1: Versicolor
- 2: Virginica
2. 基础实现:5 行代码完成分类
KNN 算法的核心思想非常简单:一个新样本的类别由其最近的 k 个邻居的多数投票决定。使用 Scikit-learn 实现基础版本仅需 5 行代码:
from sklearn.neighbors import KNeighborsClassifier # 创建模型实例(默认k=5) knn = KNeighborsClassifier() # 训练模型 knn.fit(X_train, y_train) # 预测测试集 y_pred = knn.predict(X_test) # 评估准确率 accuracy = knn.score(X_test, y_test) print(f"基础模型准确率: {accuracy:.2f}")这段代码使用了 KNeighborsClassifier 的默认参数:
n_neighbors=5:考虑最近的 5 个邻居weights='uniform':所有邻居投票权重相同metric='minkowski'withp=2:欧氏距离
3. 核心参数深度解析
3.1 n_neighbors:平衡偏差与方差
n_neighbors是 KNN 算法中最重要的参数,它直接影响模型的复杂度和泛化能力:
小 k 值(如 k=1):
- 模型更复杂,决策边界更崎岖
- 容易过拟合,对噪声敏感
- 训练误差低但测试误差高
大 k 值(如 k=50):
- 模型更简单,决策边界更平滑
- 可能欠拟合,忽略局部特征
- 训练误差和测试误差都较高
我们可以通过网格搜索找到最优 k 值:
from sklearn.model_selection import GridSearchCV param_grid = {'n_neighbors': range(1, 30)} grid = GridSearchCV(KNeighborsClassifier(), param_grid, cv=5) grid.fit(X_train, y_train) print(f"最优k值: {grid.best_params_['n_neighbors']}") print(f"交叉验证最佳得分: {grid.best_score_:.2f}")下表展示了不同 k 值对模型性能的影响:
| k值 | 训练准确率 | 测试准确率 | 决策边界特点 |
|---|---|---|---|
| 1 | 1.00 | 0.95 | 非常复杂,过拟合 |
| 5 | 0.98 | 0.98 | 适度平滑 |
| 15 | 0.96 | 0.97 | 非常平滑 |
| 30 | 0.93 | 0.93 | 过于简单,欠拟合 |
3.2 weights:距离加权投票
除了简单的多数投票,KNN 还支持距离加权投票,通过weights参数控制:
'uniform':所有邻居权重相同'distance':权重与距离成反比
距离加权特别适用于数据分布不均匀的情况:
knn_distance = KNeighborsClassifier(n_neighbors=10, weights='distance') knn_distance.fit(X_train, y_train) acc_distance = knn_distance.score(X_test, y_test) print(f"距离加权准确率: {acc_distance:.2f}")提示:当特征尺度差异较大时,距离加权效果更明显。建议先对数据进行标准化处理。
3.3 metric:距离度量选择
距离度量决定了如何计算样本间的相似度,常用选项包括:
'euclidean':欧氏距离(默认)'manhattan':曼哈顿距离'chebyshev':切比雪夫距离'minkowski':闵可夫斯基距离(可通过 p 参数调整)
不同距离度量的特点:
metrics = ['euclidean', 'manhattan', 'chebyshev'] for m in metrics: knn = KNeighborsClassifier(metric=m).fit(X_train, y_train) print(f"{m}距离准确率: {knn.score(X_test, y_test):.2f}")对于鸢尾花数据集,由于特征都是连续数值且尺度相近,欧氏距离通常表现最佳。但在文本分类等高维稀疏数据中,余弦相似度可能更合适。
4. 高级调优技巧
4.1 网格搜索结合交叉验证
将多个参数组合起来进行系统搜索:
from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler pipe = Pipeline([ ('scaler', StandardScaler()), ('knn', KNeighborsClassifier()) ]) param_grid = { 'knn__n_neighbors': [3, 5, 7, 10], 'knn__weights': ['uniform', 'distance'], 'knn__metric': ['euclidean', 'manhattan'] } grid = GridSearchCV(pipe, param_grid, cv=5, scoring='accuracy') grid.fit(X_train, y_train) print("最优参数组合:", grid.best_params_) print("最佳交叉验证得分:", grid.best_score_)4.2 特征工程对 KNN 的影响
KNN 对特征尺度非常敏感,标准化处理能显著提升性能:
from sklearn.preprocessing import MinMaxScaler # 未标准化 knn_raw = KNeighborsClassifier().fit(X_train, y_train) acc_raw = knn_raw.score(X_test, y_test) # MinMax标准化 scaler = MinMaxScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) knn_scaled = KNeighborsClassifier().fit(X_train_scaled, y_train) acc_scaled = knn_scaled.score(X_test_scaled, y_test) print(f"未标准化准确率: {acc_raw:.2f}") print(f"标准化后准确率: {acc_scaled:.2f}")4.3 维度灾难与特征选择
KNN 在高维空间中表现会下降,这就是所谓的"维度灾难"。解决方法包括:
- 特征选择:选择最相关的特征子集
- 降维:使用 PCA 等降维技术
- 调整距离度量:使用更适合高维数据的度量方式
from sklearn.decomposition import PCA # 降维到2维 pca = PCA(n_components=2) X_train_pca = pca.fit_transform(X_train) X_test_pca = pca.transform(X_test) knn_pca = KNeighborsClassifier().fit(X_train_pca, y_train) acc_pca = knn_pca.score(X_test_pca, y_test) print(f"PCA降维后准确率: {acc_pca:.2f}")5. 实战案例:参数调优全流程
让我们通过一个完整的案例演示如何系统优化 KNN 参数:
# 数据预处理 from sklearn.preprocessing import StandardScaler scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) # 参数网格 param_grid = { 'n_neighbors': range(1, 20), 'weights': ['uniform', 'distance'], 'metric': ['euclidean', 'manhattan', 'minkowski'], 'p': [1, 2, 3] # 仅当metric='minkowski'时有效 } # 网格搜索 grid = GridSearchCV( KNeighborsClassifier(), param_grid, cv=5, scoring='accuracy', n_jobs=-1 # 使用所有CPU核心 ) grid.fit(X_train_scaled, y_train) # 评估最佳模型 best_knn = grid.best_estimator_ test_acc = best_knn.score(X_test_scaled, y_test) print(f"测试集准确率: {test_acc:.2f}") print("最优参数组合:", grid.best_params_) # 特征重要性分析(基于距离加权) import numpy as np feature_importance = np.mean([ np.argsort(np.abs(best_knn.kneighbors(X_test_scaled)[1]))[:, -1] ], axis=0) print("特征重要性排序:", iris.feature_names[np.argsort(feature_importance)])这个流程展示了从数据预处理到参数优化的完整步骤。在实际项目中,你可能还需要考虑:
- 更复杂的交叉验证策略
- 自定义距离度量函数
- 处理类别不平衡问题
- 模型解释与可视化
KNN 虽然简单,但在许多实际问题中仍然表现出色。理解其核心参数的作用,结合适当的特征工程,可以构建出高效可靠的分类模型。