选择 k 后 KNN 准确度会变差

数据挖掘 机器学习 scikit-学习 k-nn
2021-09-24 18:07:43

这是我的第一个 KNN 实现。我应该使用(最初不缩放数据)线性回归和 KNN 模型来预测贷款状态(Y/N)给定一系列参数,如收入、教育状况等。

我设法建立了 LR 模型,它运行得相当好。对于KNN模型,我选择了最基本的求k值的方法:将k初始化为3,然后遍历(1,40)中k的各种值,绘制出错误率与k的关系图。最终应根据图表选择最小化误差的 k 值,以获得预测。

代码的 KNN 部分:

from sklearn.neighbors import KNeighborsClassifier


# initialize k as 3

knn = KNeighborsClassifier(n_neighbors=3)
knn.fit(x_train,y_train.ravel())#.ravel() converts the column vector into a row vector (1d array). warning without this.

#Predict the values using test dataset, for k=3
pred = knn.predict(x_test)

#Print the classification report and confusion matrix(checking accuracy for k=3 value)

from sklearn.metrics import classification_report,confusion_matrix
print(confusion_matrix(y_test,pred))
print(classification_report(y_test,pred))

#now, we vary k from 1 to 40 and see which value minimizes the error rate

error_rate = []

for i in range(1,40): #also,k value should be odd
    
    knn = KNeighborsClassifier(n_neighbors=i)
    knn.fit(x_train,y_train.ravel()) #.ravel() converts the column vector into a row vector (1d array). warning without this and takes a lot of time. 
    pred_i = knn.predict(x_test)
    error_rate.append(np.mean(pred_i != y_test))
    
plt.figure(figsize=(10,6))
plt.plot(range(1,40),error_rate,color='blue', linestyle='dashed', marker='o',
         markerfacecolor='red', markersize=10)
plt.title('Error Rate vs. K Value')
plt.xlabel('K')
plt.ylabel('Error Rate')
plt.show()


#k value which minimizes the error rate: 39

knn = KNeighborsClassifier(n_neighbors=39)
knn.fit(x_train,y_train.ravel())
pred=knn.predict(x_test)

from sklearn.metrics import classification_report, confusion_matrix
print(confusion_matrix(y_test,pred))
print(classification_report(y_test,pred))

from sklearn.metrics import r2_score
from sklearn.metrics import mean_squared_error
r2score_knn= r2_score(y_test,pred)
MSE_knn= mean_squared_error(y_test,pred)
print("r2 score,non normalized knn: ", r2score_knn)
print("MSE , non normalised knn: ", MSE_knn)

根据此图选择了 39 的 k 值: 在此处输入图像描述

然而,输出是相当令人费解的。k=39(0.65) 的准确度得分比 k=3(0.74) 差,尽管图表显示 k=3 的错误率远高于 39 的错误率。

[[14 21]
 [ 4 57]]
              precision    recall  f1-score   support

           0       0.78      0.40      0.53        35
           1       0.73      0.93      0.82        61

    accuracy                           0.74        96
   macro avg       0.75      0.67      0.67        96
weighted avg       0.75      0.74      0.71        96

[[ 1 34]
 [ 0 61]]
              precision    recall  f1-score   support

           0       1.00      0.03      0.06        35
           1       0.64      1.00      0.78        61

    accuracy                           0.65        96
   macro avg       0.82      0.51      0.42        96
weighted avg       0.77      0.65      0.52        96

r2 score,non normalized knn:  -0.5288056206088991
MSE, non normalised knn:  0.3541666666666667

这可能是什么原因?那么我究竟如何推导出最佳k值呢?

查看图表,我假设这可能与 k=3 是局部最小值(某种程度)这一事实有关,而 k=39 不是……我尝试了 k=25 的模型(其他局部最小值),并且准确度得分确实增加了(0.70),但仍然小于 k=3。

但是,唯一相关的信息应该只是错误率......那么这里到底发生了什么?

2个回答

我认为有几个问题,解开它们有点困难。以下是一些观察:

  • 首先,因为您要选择超参数ķ根据数据,您应该使用单独的验证集。这是因为选择ķ类似于训练,因此目前您正在使用测试集进行训练和测试。正确的方法是分成三组:训练、验证、测试:首先使用验证集检查每个的性能ķ,然后选择最好的ķ并仅应用选定的ķ在测试集上获得真实性能。
  • 结果不一致:显然,如果训练/测试数据相同,则只能有一个性能结果ķ=39. 一定有什么不同。
  • 还有一些奇怪的事情是随着误差增加而减少很多ķ. 一般来说,这没有多大意义:越高ķ,在预测一个实例的目标时考虑的不同实例越多,因此出错的风险就越高。最终,系统将始终预测多数类,这正是基于第二个混淆矩阵发生的情况:多数类几乎总是在 39 个实例的子集中有更多实例。
  • 现在为什么增加时误差会减少ķ? 当然是因为特征信息不够丰富,所以“模型”依靠多数类达到了更好的性能:简单地预测(几乎)总是多数类就足以有大约 66% 的准确率,而更高的ķ它越系统地预测多数类别。

我的两个主要评论是:

  1. KNN 是一种基于距离的算法,缩放是必须的!否则距离会被最大的特征值扭曲,而小的特征值不会被正确考虑。您应该尝试正确缩放或编码所有功能。

  2. 你能告诉你编码前后有多少特征吗?您可能需要特征工程/选择来获得合理数量的它们,并保留最有用的信息。

其他备注:

  1. 超参数 k 的评估应该使用特定的验证数据集来完成。您可以使用 scikit-learn 的 GridSearchCV 来测试和评估几个 k 值。

  2. 混淆矩阵只显示了 96 个数据,这可能有点太少而无法获得好的结果!您可能需要更多数据。你的整个数据集有多大?而且错误率似乎很大。您应该将它与使用其他算法获得的错误率进行比较?

  3. 您谈论线性回归,而不是逻辑回归,因为您的目标是二元的?