K 最近邻分类器 – 训练测试分割的随机状态导致不同的准确性得分

我使用Python的sklearn模块对乳腺癌数据集进行了一些KNN分类分析。我有以下代码,试图找到目标变量分类的最佳k值。

from sklearn.datasets import load_breast_cancerfrom sklearn.model_selection import train_test_splitfrom sklearn.neighbors import KNeighborsClassifierimport matplotlib.pyplot as pltbreast_cancer_data = load_breast_cancer()training_data, validation_data, training_labels, validation_labels = train_test_split(breast_cancer_data.data, breast_cancer_data.target, test_size = 0.2, random_state = 40)results = []for k in range(1,101):  classifier = KNeighborsClassifier(n_neighbors = k)  classifier.fit(training_data, training_labels)  results.append(classifier.score(validation_data, validation_labels))k_list = range(1,101)plt.plot(k_list, results)plt.ylim(0.85,0.99)plt.xlabel("k")plt.ylabel("Accuracy")plt.title("Breast Cancer Classifier Accuracy")plt.show()

代码从1到100循环,生成100个KNN模型,’k’值从1到100递增。每种模型的性能被保存到一个列表中,并生成一个图表,显示x轴上的’k’值和y轴上的模型性能。

我的问题是,当我更改分割数据到训练和测试部分时的random_state参数时,这会导致完全不同的图表,显示不同的数据集分割对于不同的’k’值的模型性能各不相同。

对我来说,这使得决定哪个’k’是最佳的变得困难,因为算法在使用不同的随机状态时,对于不同的’k’值表现不同。这难道不是意味着,对于这个特定的数据集,’k’是任意的吗?谁能对此提供一些见解?

Random State = 0Random State = 40Random State = 75


回答:

这是完全可以预见的。当你进行训练-测试分割时,你实际上是从原始人群中进行抽样。这意味着当你拟合一个模型时,任何统计数据(如模型参数估计或模型得分)本身就是从某个分布中取样的估计。你真正想要的是这个得分周围的置信区间,而获得这种置信区间的最简单方法是重复抽样并重新测量得分。

但你必须非常小心如何做这件事。以下是一些稳健的选项:

1. 交叉验证

解决这个问题的常见方法是使用k折交叉验证。为了不与knn中的k混淆,我将使用大写的K来表示交叉验证(但请注意,这不是正常的命名法)。这是一种执行上述建议但不泄露目标的方案。不是随机创建许多分割,而是将数据分割成K部分(称为折)。然后你训练K个模型,每次在K-1个折的数据上训练,留出一个不同的折作为测试集。现在每个模型都是独立的且没有目标泄露。事实证明,这些K个模型在它们各自的K个独立测试集上的成功得分的平均值是对使用那些超参数在整个数据集上训练模型的性能的一个良好估计。因此,现在你应该为你的不同k值(knn中的小k)得到一个更稳定的得分,你可以用这种方式选择最终的k值。

一些额外的说明:

  • 准确性不是评估分类性能的最佳度量。看看像精确度与召回率或AUROC或f1这样的得分。
  • 不要尝试自己编写CV,使用sklearn的GridSearchCV
  • 如果你对数据进行任何预处理,计算某种使用数据的状态,那需要在每个折的训练数据上进行。例如,如果你正在缩放数据,你不能在缩放时包含测试数据。你需要在训练数据上拟合(并转换)缩放器,然后使用相同的缩放器在测试数据上转换(不要再次拟合)。为了在CV中实现这一点,你需要使用sklearn的Pipeline。这非常重要,确保你理解它。
  • 如果你基于输出类对训练-测试分割进行分层,你可能会获得更多的稳定性。查看train_test_split中的stratify参数。

请注意,CV是行业标准,这是你应该做的,但还有其他选项:

2. 自助法

你可以在统计学习导论第5.2节(第187页)中详细了解这一点,并在第5.3.4节中看到示例。

这个想法是从你的训练集中抽取一个随机样本,有放回地抽取。这意味着你最终会得到一些重复的记录。你采用这个新的训练集,训练模型,然后在没有进入自助样本的记录上(通常称为袋外样本)评分。你重复这个过程多次。现在你可以得到你的得分(例如准确性)的分布,你可以用它来选择你的超参数,而不是像之前那样仅仅使用点估计。

3. 确保你的测试集代表你的验证集

Jeremy Howard 在 如何校准你的验证集以便成为你的测试集的良好代表方面有一个非常有趣的建议。你只需要从链接开始的地方观看大约5分钟。这个想法是将数据分割成三个集合(你应该无论如何都要这样做来选择像k这样的超参数),在你的训练集上训练一堆非常不同但简单快速的模型,然后在你的验证集和测试集上对它们进行评分。在这里使用测试集是可以的,因为这些不是会影响你最终模型的真实模型。然后绘制验证得分与测试得分的图表。它们应该大致落在一条直线上(y=x线)。如果是,那么这意味着验证集和测试集都很好或都很差,即验证集中的性能代表了测试集中的性能。如果它们不在这条直线上,这意味着你从验证集中得到的模型得分不代表你在未见数据上的得分,因此你不能使用这个分割来训练一个合理的模型。

4. 获取更大的数据集

这显然对你的情况不太实用,但我认为为了完整性我应该提到这一点。随着你的样本量增加,你的标准误差会下降(即你可以得到更紧密的置信区间)。但你需要更多的训练和测试数据。虽然你在这里可能无法访问这些数据,但值得记住,对于现实世界的情况,你可以评估收集新数据的成本与评估模型性能所需的准确性之间的权衡(可能还有性能本身)。

Related Posts

使用LSTM在Python中预测未来值

这段代码可以预测指定股票的当前日期之前的值,但不能预测…

如何在gensim的word2vec模型中查找双词组的相似性

我有一个word2vec模型,假设我使用的是googl…

dask_xgboost.predict 可以工作但无法显示 – 数据必须是一维的

我试图使用 XGBoost 创建模型。 看起来我成功地…

ML Tuning – Cross Validation in Spark

我在https://spark.apache.org/…

如何在React JS中使用fetch从REST API获取预测

我正在开发一个应用程序,其中Flask REST AP…

如何分析ML.NET中多类分类预测得分数组?

我在ML.NET中创建了一个多类分类项目。该项目可以对…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注