KNN的调参方法

发布于:2025-02-10 ⋅ 阅读:(48) ⋅ 点赞:(0)


在使用KNN(K-Nearest Neighbors)算法时,最重要的超参数(Hyperparameters)主要包括下面几个方面:

  1. K 值(邻居数)
  2. 距离度量方式
  3. 权重策略(weights)
  4. 特征预处理
  5. 特征选择或降维(在高维数据中尤其重要)

下面将一一介绍,以及如何利用交叉验证来进行KNN的调参。


1. K 值(邻居数 K)的选择

K 值是决定 KNN 算法效果最关键的因素之一。

  • K 值过小

    • 好处:能够更贴近局部数据特性。
    • 坏处:对异常点和噪声非常敏感,容易过拟合。
  • K 值过大

    • 好处:具有较强的平滑性,受噪声影响相对较小。
    • 坏处:容易造成欠拟合,因为大量不太相似的点也被纳入投票或回归的计算之中。

通常的做法是选择一个区间(例如从1到20,或1到30),在这个区间内使用交叉验证(Cross Validation)或 留一交叉验证(Leave-One-Out Cross Validation)来选择使得模型在验证集上表现最优的 K 值。

scikit-learn 中,可以使用 GridSearchCV 或者 RandomizedSearchCV 来自动选择最佳 K。例如:

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

# 定义要搜索的 K 的范围
param_grid = {'n_neighbors': list(range(1, 31))}

knn = KNeighborsClassifier()
grid_search = GridSearchCV(knn, param_grid, cv=5, scoring='accuracy')
grid_search.fit(X_train, y_train)

print("最佳参数:", grid_search.best_params_)
print("最佳得分:", grid_search.best_score_)

2. 距离度量方式

2.1 常见距离度量

  1. 欧氏距离(Euclidean Distance)
    d ( x , y ) = ∑ i = 1 n ( x i − y i ) 2 d(x, y) = \sqrt{\sum_{i=1}^n (x_i - y_i)^2} d(x,y)=i=1n(xiyi)2
    这是KNN中最常用的一种距离度量,适合数值型特征。

  2. 曼哈顿距离(Manhattan Distance)
    d ( x , y ) = ∑ i = 1 n ∣ x i − y i ∣ d(x, y) = \sum_{i=1}^n |x_i - y_i| d(x,y)=i=1nxiyi
    当特征是稀疏分布或带有明显坐标解释(如网格路径)时,曼哈顿距离也常被使用。

  3. 闵可夫斯基距离(Minkowski Distance)
    d ( x , y ) = ( ∑ i = 1 n ∣ x i − y i ∣ p ) 1 p d(x, y) = \left(\sum_{i=1}^n |x_i - y_i|^p \right)^{\frac{1}{p}} d(x,y)=(i=1nxiyip)p1
    当 (p=2) 时等价于欧氏距离,(p=1) 时等价于曼哈顿距离。

  4. 切比雪夫距离(Chebyshev Distance)
    d ( x , y ) = max ⁡ i ( ∣ x i − y i ∣ ) d(x, y) = \max_i(|x_i - y_i|) d(x,y)=imax(xiyi)

2.2 如何选择距离度量

  • 对于连续型的数值特征,欧氏距离和曼哈顿距离最常见。
  • 对于离散非线性分布的特征,可以考虑其它更合适的距离度量方式。

scikit-learn 中,通过 metric 参数来指定距离度量方式:

knn = KNeighborsClassifier(n_neighbors=5, metric='euclidean')
knn = KNeighborsClassifier(n_neighbors=5, metric='manhattan')
knn = KNeighborsClassifier(n_neighbors=5, metric='minkowski', p=2)  # 与欧氏距离相同

同样可以使用交叉验证或网格搜索,对不同距离度量方式进行评估,并选择最优的。


3. 权重策略(weights)

KNN 在投票(或做回归)时,对于距离较近的邻居可以赋予更高的权重,以增加对更近邻居的重视。scikit-learn 中有两个常见策略:

  1. uniform:所有邻居权重相同,即普通投票。
  2. distance:距离越近的邻居权重越高,一般用 1 d i s t a n c e \frac{1}{distance} distance1 之类的函数来表示权重。

scikit-learn 中,可以通过 weights 参数来进行设置:

knn = KNeighborsClassifier(n_neighbors=5, weights='uniform')
knn = KNeighborsClassifier(n_neighbors=5, weights='distance')

同样可以通过交叉验证自动选择哪一种权重策略更好。


4. 特征预处理

4.1 特征缩放(标准化/归一化)

KNN 十分依赖距离计算,特征之间的数量级差异会严重影响距离结果,因此特征缩放往往必不可少:

  • 标准化(StandardScaler):将特征转换为均值 0,标准差 1
  • 归一化(MinMaxScaler):将特征缩放到 [0, 1] 区间

常见做法是在管道(Pipeline)中先对特征进行缩放,再训练 KNN。例如:

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler

pipeline = Pipeline([
    ('scaler', StandardScaler()),
    ('knn', KNeighborsClassifier())
])

# 然后再用 GridSearchCV
param_grid = {'knn__n_neighbors': list(range(1,31)),
              'knn__metric': ['euclidean', 'manhattan']}

grid_search = GridSearchCV(pipeline, param_grid, cv=5)
grid_search.fit(X_train, y_train)

4.2 处理异常值

如果数据中有异常值(Outliers),这些异常值对距离会产生较大影响,需要结合业务场景,决定是否过滤或平滑处理异常值。


5. 特征选择或降维

在高维数据(特征数量多)下,KNN 可能会遭遇维度灾难(Curse of Dimensionality),导致在高维空间中所有点都变得“差不多远”,从而降低KNN性能。常见解决方法:

  1. 特征选择

    • 根据业务经验或统计方法(如相关系数、Chi-square等)筛选对分类/回归更重要的特征。
    • 可以尝试基于树模型(如随机森林)得到特征重要性,再做特征选择。
  2. 降维

    • PCA(主成分分析):将数据投影到一个低维的子空间。
    • LDA(线性判别分析):如果是有监督的场景,可以使用 LDA 来降维。

在做完降维或特征选择后,再使用 KNN 进行训练和预测。


6. 使用交叉验证来综合调参

6.1 为什么需要交叉验证

为了找到最优的超参数组合(例如:K 值、距离度量方式、权重策略、是否降维等),我们需要对训练集做充分的评估,防止只在固定的测试集上调参而过拟合。

6.2 GridSearchCV/RandomizedSearchCV

  • GridSearchCV:在给定的超参数网格上做穷举搜索。
  • RandomizedSearchCV:在给定的超参数分布上做随机搜索,更适合搜索空间特别大的情况。

示例(综合调参):

from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler

# 使用 Pipeline
pipeline = Pipeline([
    ('scaler', StandardScaler()),
    ('knn', KNeighborsClassifier())
])

param_grid = {
    'knn__n_neighbors': [3, 5, 7, 9, 11],
    'knn__weights': ['uniform', 'distance'],
    'knn__metric': ['euclidean', 'manhattan']
}

grid_search = GridSearchCV(pipeline, param_grid, cv=5, scoring='accuracy')
grid_search.fit(X_train, y_train)

print("最佳参数:", grid_search.best_params_)
print("最佳交叉验证得分:", grid_search.best_score_)

# 使用最佳参数在测试集上评估
best_model = grid_search.best_estimator_
test_score = best_model.score(X_test, y_test)
print("测试集准确率:", test_score)

这里我们通过 param_grid 同时搜索以下超参数:

  • n_neighbors:候选值为 [3, 5, 7, 9, 11]
  • weights:候选值为 [uniform, distance]
  • metric:候选值为 [euclidean, manhattan]

并在 5 折交叉验证cv=5)上进行评估,选出得分最高的组合。


7. 总结

  • K 值:可通过交叉验证从一个合理的范围内挑选最优值。
  • 距离度量:根据数据特性选择合适的度量方式,如欧氏距离、曼哈顿距离等;同样可通过交叉验证挑选。
  • 权重策略:决定是使用统一权重还是基于距离衰减的权重;也可通过交叉验证确定。
  • 特征预处理:包括特征缩放和对异常值的处理,对 KNN 性能极其重要。
  • 特征选择/降维:在高维数据中,先做特征选择或降维再进行 KNN,往往能显著提升效果。
  • 交叉验证:是调参的关键工具,能够帮助选择最优超参数并避免过拟合。

通过上述方法综合调参,可以让 KNN 模型在不同数据集下达到更好的表现。


网站公告

今日签到

点亮在社区的每一天
去签到