K 最近邻分类器 - 火车测试拆分的随机状态导致不同的准确度分数

K Nearest Neighbour Classifier - random state for train test split leads to different accuracy scores

我对数据分析和机器学习还很陌生。我一直在 python 的 sklearn 模块中对乳腺癌数据集进行一些 KNN 分类分析。我有以下代码试图找到目标变量分类的最佳 k。

from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
import matplotlib.pyplot as plt

breast_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' 是任意的?任何人都可以帮助阐明这一点吗?

感谢期待

这种“行为”是意料之中的。当然,当训练和测试分开时,您会得到不同的结果。

您可以通过用新的 train-validation-splits 重复每个 'k' 几次来统计地解决问题。然后取每个 k 的中值性能。或者更好:查看性能分布和中位数。给定 'k' 的窄性能分布也是 'k' 选择得当的好兆头。 之后你可以使用测试集来测试你的模型

这完全在意料之中。当您执行 train-test-split 时,您实际上是从原始人群中 抽样 。这意味着当您拟合模型时,任何统计数据(例如模型参数估计值或模型分数)本身都是从某个分布中提取的样本估计值。您真正想要的是围绕该分数的置信区间,获得该分数的最简单方法是重复采样并重新测量分数。

但是您必须非常小心如何做到这一点。这里有一些可靠的选项:

1。交叉验证

此问题最常见的解决方案是使用 k-fold cross-validation。为了不把这个 k 和 knn 中的 k 混淆,我打算用大写字母表示 cross-validation(但请记住,这不是正常的命名法)这是执行上述建议但没有目标的方案泄露。您不是随机创建多个拆分,而是将数据拆分为 K 个部分(称为折叠)。然后,您每次在 K-1 折叠数据上训练 K 模型,每次都留下不同的折叠作为您的测试集。现在每个模型都是独立的,没有目标泄漏。事实证明,无论你从这 K 个模型在它们的 K 个独立测试集上使用的任何成功分数的平均值,都可以很好地估计在整个集上使用这些超参数训练模型的性能。所以现在你应该为你的每个不同的k值(knn的小k)得到一个更稳定的分数,你可以这样选择一个最终的k。

一些额外的注意事项:

  • Accuracy is a bad measure for classification performance。查看精度与召回率或 AUROC 或 f1 等分数。
  • 不要自己尝试程序 CV,使用 sklearns GridSearchCV
  • 如果您正在对数据进行任何预处理以使用数据计算某种状态,则需要在每个折叠中 训练数据上完成。例如,如果您正在缩放数据,则在缩放时不能包含测试数据。您需要在训练数据上拟合(和转换)缩放器,然后使用相同的缩放器在您的测试数据上进行转换(不要再次拟合)。为了让它在 CV 中工作,你需要使用 sklearn Pipelines。这个很重要,一定要看懂。
  • 如果根据输出 class 对 train-test-split 进行分层,您可能会更加稳定。请参阅 train_test_split 上的 stratify 参数。

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

2。自举

您可以在 introduction to statistical learning 第 5.2 节(第 187 页)中详细阅读,并在第 5.3.4 节中阅读示例。

我们的想法是带你训练集并从中抽取一个随机样本替换。这意味着您最终会得到一些重复的记录。您采用这个新的训练集、训练和模型,然后在没有进入引导样本(通常称为 out-of-bag 样本)的记录上对其进行评分。你多次重复这个过程。您现在可以获得您的分数分布(例如准确度),您可以使用它来选择您的 hyper-parameter 而不仅仅是您之前使用的点估计。

3。确保您的测试集代表您的验证集

Jeremy Howard 关于 how to calibrate your validation set 有一个非常有趣的建议,可以很好地代表您的测试集。您只需要从 link 开始的地方观看大约 5 分钟。这个想法是分成三组(无论如何你都应该这样做来选择像 k 这样的超参数),在你的训练集上训练一堆非常不同但简单的快速模型,然后在你的验证集和测试集上对它们进行评分。可以在此处使用测试集,因为这些不是会影响最终模型的真实模型。然后绘制验证分数与测试分数。它们应该大致落在一条直线上(y=x 线)。如果是,这意味着验证集和测试集要么好要么坏,即验证集的性能代表测试集的性能。如果它们不落在这条直线上,则意味着您从验证集中获得的模型分数并不表示您将在看不见的数据上获得的分数,因此您不能使用该拆分来训练合理的模型。

4。获取更大的数据集

这对于您的情况显然不是很实用,但我想为了完整起见我会提到它。随着样本量的增加,您的标准误差会下降(即您可以在置信区间上获得更严格的界限)。但是您需要更多的培训和更多的测试数据。虽然您可能无法在此处访问它,但在现实世界的情况下值得牢记,您可以评估收集新数据的成本 trade-off 与评估模型性能(以及可能的性能)所需的准确性本身也是)。