训练测试集是一种过拟合,它会创建一个模型,该模型故意在给定的测试集上表现良好,但代价是增加了泛化误差。
这是一种常见的机器学习竞赛中的过拟合类型,在竞赛中提供完整的训练数据集,并且只提供测试集的输入部分。一种“训练测试集”的方法包括构建一个与测试集最相似的训练集,然后将其作为训练模型的基础。期望模型在测试集上表现更好,但在训练数据集和未来任何新数据上的表现很可能更差。
尽管过拟合测试集并不可取,但作为思想实验来探索它很有趣,并且能提供对机器学习竞赛和避免过拟合的更深入的见解。
在本教程中,您将学习如何为分类和回归问题专门训练测试集。
完成本教程后,您将了解:
- 训练测试集是可能在机器学习竞赛中发生的数据泄露的一种类型。
- 训练测试集的一种方法是创建一个与提供的测试集最相似的训练数据集。
- 如何使用KNN模型构建训练数据集并使用真实数据集训练测试集。
通过我的新书《数据准备机器学习》快速启动您的项目,其中包含分步教程和所有示例的Python源代码文件。
让我们开始吧。

如何在机器学习中针对测试集进行训练
照片作者:ND Strupler,部分权利保留。
教程概述
本教程分为三个部分;它们是:
- 训练测试集
- 分类训练测试集
- 回归训练测试集
训练测试集
在应用机器学习中,我们寻求一个模型,该模型使用训练数据集学习输入变量和输出变量之间的关系。
希望和目标是学习一个关系,该关系可以泛化到训练数据集之外的新示例。这个目标促使我们使用重采样技术,如k折交叉验证,来估计模型在对未在训练期间使用的数据进行预测时的性能。
对于像Kaggle这样的机器学习竞赛,我们能够访问完整的训练数据集和测试数据集的输入,并需要为测试数据集做出预测。
这可能导致我们意外地或选择性地训练模型到测试集。也就是说,通过调整模型行为以在测试数据集上获得最佳性能,而不是使用k折交叉验证等技术来开发一个总体上表现良好的模型。
另一种更明显的泄露信息的方式,有时会在机器学习竞赛中出现,即训练集和测试集数据同时提供。
— 第56页,特征工程与选择:预测模型的实践方法,2019。
训练测试集通常是个坏主意.
这是一种明确的数据泄露。尽管如此,它仍然是一个有趣的思想实验。
训练测试集的一种方法是设计一个与测试集最相似的训练数据集。例如,我们可以删除训练集中与测试集差异过大的所有行,只在与测试集行最大程度相似的训练集行上进行训练。
虽然测试集数据通常会隐藏结果数据,但可以通过仅使用与测试集数据最相似的训练集样本来“训练测试集”。这很可能会提高模型在特定测试集上的性能得分,但可能会损害模型在更广泛数据集上的预测能力。
— 第56页,特征工程与选择:预测模型的实践方法,2019。
我们预期模型会过拟合测试集,但这正是这个思想实验的要点。
在本教程中,我们将探讨这种训练测试集的方法。
我们可以使用k近邻模型来选择训练集中与测试集最相似的实例。 KNeighborsRegressor和KNeighborsClassifier都提供了kneighbors()函数,该函数将返回训练数据集中与给定数据(如测试集)最相似的行的索引。
1 2 3 4 |
... # 获取测试集中每个点的最相似邻居 neighbor_ix = knn.kneighbors(X_test, 2, return_distance=False) ix = neighbor_ix[:,0] |
我们可能需要尝试删除选定行索引中的重复项。
1 2 3 |
... # 删除重复行 ix = unique(ix) |
然后,我们可以使用这些行索引来构建自定义训练数据集并拟合模型。
1 2 3 |
... # 从选定的实例创建训练数据集 X_train_neigh, y_train_neigh = X_train[ix], y_train[ix] |
鉴于我们使用KNN模型从测试集中构建训练集,我们也使用相同类型的模型来预测测试集。这不是必需的,但它使示例更简单。
通过这种方法,我们现在可以尝试为分类和回归数据集训练测试集。
想开始学习数据准备吗?
立即参加我为期7天的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
分类训练测试集
我们将使用糖尿病数据集作为基础,探索用于分类问题的训练测试集。
每条记录描述了一位女性的医疗细节,预测是在未来五年内患糖尿病的发生率。
该数据集有八个输入变量和768行数据;输入变量全部是数字,目标有两个类别标签,例如,这是一个二元分类任务。
下面提供了该数据集前五行的样本。
1 2 3 4 5 6 |
6,148,72,35,0,33.6,0.627,50,1 1,85,66,29,0,26.6,0.351,31,0 8,183,64,0,0,23.3,0.672,32,1 1,89,66,23,94,28.1,0.167,21,0 0,137,40,35,168,43.1,2.288,33,1 ... |
首先,我们可以直接从URL加载数据集,将其分成输入和输出元素,然后将数据集分成训练集和测试集,预留30%作为测试集。然后,我们可以通过在训练集上训练KNN模型并对测试集进行预测来评估它,以默认模型超参数。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# 评估糖尿病分类数据集上KNN模型的示例 from pandas import read_csv from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score from sklearn.neighbors import KNeighborsClassifier # 加载数据集 url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.csv' df = read_csv(url, header=None) data = df.values X, y = data[:, :-1], data[:, -1] print(X.shape, y.shape) # 分割数据集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1) print(X_train.shape, X_test.shape, y_train.shape, y_test.shape) # 定义模型 model = KNeighborsClassifier() # 拟合模型 model.fit(X_train, y_train) # 进行预测 yhat = model.predict(X_test) # 评估预测 accuracy = accuracy_score(y_test, yhat) print('Accuracy: %.3f' % (accuracy * 100)) |
运行示例后,我们首先加载数据集并汇总行数和列数,这与我们的预期相符。然后报告训练集和测试集的形状,显示测试集大约有230行。
注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。
最后,模型分类准确率为77.056%。
1 2 3 |
(768, 8) (768,) (537, 8) (231, 8) (537,) (231,) 准确率:77.056 |
现在,让我们看看是否可以通过准备一个直接为此训练的模型来提高在测试集上的性能。
首先,我们将使用训练集中更简单的示例为测试集中的每一行构建训练数据集。
1 2 3 4 5 6 7 8 9 10 |
... # 选择与测试集最相似的示例 knn = KNeighborsClassifier() knn.fit(X_train, y_train) # 获取测试集中每个点的最相似邻居 neighbor_ix = knn.kneighbors(X_test, 1, return_distance=False) ix = neighbor_ix[:,0] # 从选定的实例创建训练数据集 X_train_neigh, y_train_neigh = X_train[ix], y_train[ix] print(X_train_neigh.shape, y_train_neigh.shape) |
接下来,我们将在此新数据集上训练模型,并像以前一样在测试集上对其进行评估。
1 2 3 4 5 |
... # 定义模型 model = KNeighborsClassifier() # 拟合模型 model.fit(X_train_neigh, y_train_neigh) |
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# 糖尿病数据集训练测试集的示例 from pandas import read_csv from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score from sklearn.neighbors import KNeighborsClassifier # 加载数据集 url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.csv' df = read_csv(url, header=None) data = df.values X, y = data[:, :-1], data[:, -1] print(X.shape, y.shape) # 分割数据集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1) print(X_train.shape, X_test.shape, y_train.shape, y_test.shape) # 选择与测试集最相似的示例 knn = KNeighborsClassifier() knn.fit(X_train, y_train) # 获取测试集中每个点的最相似邻居 neighbor_ix = knn.kneighbors(X_test, 1, return_distance=False) ix = neighbor_ix[:,0] # 从选定的实例创建训练数据集 X_train_neigh, y_train_neigh = X_train[ix], y_train[ix] print(X_train_neigh.shape, y_train_neigh.shape) # 定义模型 model = KNeighborsClassifier() # 拟合模型 model.fit(X_train_neigh, y_train_neigh) # 进行预测 yhat = model.predict(X_test) # 评估预测 accuracy = accuracy_score(y_test, yhat) print('Accuracy: %.3f' % (accuracy * 100)) |
运行示例,我们可以看到新训练数据集的大小与测试集相同,正如我们预期的那样。
注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。
我们可以看到,通过训练测试集,我们在测试集上的性能有所提高,而使用整个训练集训练模型时,性能为77.056%。在此案例中,我们达到了79.654%的分类准确率。
1 2 3 4 |
(768, 8) (768,) (537, 8) (231, 8) (537,) (231,) (231, 8) (231,) 准确率:79.654 |
您可能想尝试选择训练集中不同的邻居数量,以便为测试集中的每个示例获得更好的性能。
此外,您可能想尝试保留训练集中的唯一行索引,看看这是否会产生影响。
最后,保留一个最终的验证数据集并比较不同的“训练测试集”技术如何影响持出数据集上的性能会很有趣。例如,看看训练测试集如何影响泛化误差。
请在下面的评论中报告您的发现。
现在我们知道了如何为分类问题训练测试集,让我们来看一个回归的例子。
回归训练测试集
我们将使用房屋数据集作为基础,探索用于回归问题的训练测试集。
房屋数据集涉及根据房屋及其周边环境的详细信息预测房屋价格(以千美元为单位)。
这是一个回归问题,意味着我们在预测一个数值。有506个观测值,13个输入变量和一个输出变量。
下面列出了前五行的样本。
1 2 3 4 5 6 |
0.00632,18.00,2.310,0,0.5380,6.5750,65.20,4.0900,1,296.0,15.30,396.90,4.98,24.00 0.02731,0.00,7.070,0,0.4690,6.4210,78.90,4.9671,2,242.0,17.80,396.90,9.14,21.60 0.02729,0.00,7.070,0,0.4690,7.1850,61.10,4.9671,2,242.0,17.80,392.83,4.03,34.70 0.03237,0.00,2.180,0,0.4580,6.9980,45.80,6.0622,3,222.0,18.70,394.63,2.94,33.40 0.06905,0.00,2.180,0,0.4580,7.1470,54.20,6.0622,3,222.0,18.70,396.90,5.33,36.20 ... |
首先,我们可以加载数据集,对其进行拆分,然后直接使用整个训练数据集对KNN模型进行评估。我们将使用平均绝对误差(MAE)来报告该回归类的性能。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# 评估房屋回归数据集上KNN模型的示例 from pandas import read_csv from sklearn.model_selection import train_test_split from sklearn.metrics import mean_absolute_error from sklearn.neighbors import KNeighborsRegressor # 加载数据集 url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/housing.csv' df = read_csv(url, header=None) data = df.values X, y = data[:, :-1], data[:, -1] print(X.shape, y.shape) # 分割数据集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1) print(X_train.shape, X_test.shape, y_train.shape, y_test.shape) # 定义模型 model = KNeighborsRegressor() # 拟合模型 model.fit(X_train, y_train) # 进行预测 yhat = model.predict(X_test) # 评估预测 mae = mean_absolute_error(y_test, yhat) print('MAE: %.3f' % mae) |
运行示例后,我们首先加载数据集并汇总行数和列数,这与我们的预期相符。然后报告训练集和测试集的形状,显示测试集大约有150行。
注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。
最后,模型的MAE约为4.488。
1 2 3 |
(506, 13) (506,) (354, 13) (152, 13) (354,) (152,) MAE:4.488 |
现在,让我们看看是否可以通过准备一个为此训练的模型来提高在测试集上的性能。
首先,我们将使用训练集中更简单的示例为测试集中的每一行构建训练数据集。
1 2 3 4 5 6 7 8 9 10 |
... # 选择与测试集最相似的示例 knn = KNeighborsClassifier() knn.fit(X_train, y_train) # 获取测试集中每个点的最相似邻居 neighbor_ix = knn.kneighbors(X_test, 1, return_distance=False) ix = neighbor_ix[:,0] # 从选定的实例创建训练数据集 X_train_neigh, y_train_neigh = X_train[ix], y_train[ix] print(X_train_neigh.shape, y_train_neigh.shape) |
接下来,我们将在此新数据集上训练模型,并像以前一样在测试集上对其进行评估。
1 2 3 4 5 |
... # 定义模型 model = KNeighborsClassifier() # 拟合模型 model.fit(X_train_neigh, y_train_neigh) |
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# 房屋数据集训练测试集的示例 from pandas import read_csv from sklearn.model_selection import train_test_split from sklearn.metrics import mean_absolute_error from sklearn.neighbors import KNeighborsRegressor # 加载数据集 url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/housing.csv' df = read_csv(url, header=None) data = df.values X, y = data[:, :-1], data[:, -1] print(X.shape, y.shape) # 分割数据集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1) print(X_train.shape, X_test.shape, y_train.shape, y_test.shape) # 选择与测试集最相似的示例 knn = KNeighborsRegressor() knn.fit(X_train, y_train) # 获取测试集中每个点的最相似邻居 neighbor_ix = knn.kneighbors(X_test, 1, return_distance=False) ix = neighbor_ix[:,0] # 从选定的实例创建训练数据集 X_train_neigh, y_train_neigh = X_train[ix], y_train[ix] print(X_train_neigh.shape, y_train_neigh.shape) # 定义模型 model = KNeighborsRegressor() # 拟合模型 model.fit(X_train_neigh, y_train_neigh) # 进行预测 yhat = model.predict(X_test) # 评估预测 mae = mean_absolute_error(y_test, yhat) print('MAE: %.3f' % mae) |
运行示例,我们可以看到新训练数据集的大小与测试集相同,正如我们预期的那样。
注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行示例几次并比较平均结果。
我们可以看到,通过训练测试集,我们在测试集上的性能有所提高,而使用整个训练集训练模型时,MAE为4.488。在此案例中,我们达到了约4.433的MAE。
同样,您可能想探索在构建新训练集时使用不同数量的邻居,看看保留训练数据集中的唯一行是否有区别。请在下面的评论中报告您的发现。
1 2 3 4 |
(506, 13) (506,) (354, 13) (152, 13) (354,) (152,) (152, 13) (152,) MAE:4.433 |
进一步阅读
如果您想深入了解,本节提供了更多关于该主题的资源。
书籍
- 特征工程和选择:预测模型的实用方法, 2019.
API
总结
在本教程中,您学习了如何为分类和回归问题专门训练测试集。
具体来说,你学到了:
- 训练测试集是可能在机器学习竞赛中发生的数据泄露的一种类型。
- 训练测试集的一种方法是创建一个与提供的测试集最相似的训练数据集。
- 如何使用KNN模型构建训练数据集并使用真实数据集训练测试集。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
Jason,您好。我喜欢这个有趣的教程,就像其他所有教程一样。
我现实生活中的无标签数据与我的训练数据性质差异很大。使用我的训练数据的10折交叉验证,我获得了90%的准确率,而在现实生活中是70%。我使用训练数据检查了过拟合和欠拟合,并调整了超参数。
您认为这种方法有用吗?
谢谢!
不。以上教程中的方法可能适用于竞赛(或不适用),或者用于演示有意或无意地训练测试集的危险。
您在“回归训练测试集”中似乎有一个错别字:
…
# 定义模型
model = KNeighborsClassifier()
# 拟合模型
model.fit(X_train_neigh, y_train_neigh)
错别字在哪里?
感谢Jason Brownlee分享这个精彩的教程。您从第一天起就激励了我。我非常感激。请继续写作。🙂
不客气。
好吧,毫无疑问,阅读所有这些内容需要时间,但这是一项伟大的工作。感谢分享。
谢谢!
尊敬的Jason博士,
在您建议的“训练测试集分类”部分中,您提到“…您可能想尝试使用不同的邻居数量…”,我没有发现任何区别。
您的意思是改变以下参数1为各种值吗?
如果是这样,我有代码显示将参数1更改为其他值没有任何效果
输出
那么,您的意思是“不同的邻居数量”是将以下参数1更改为各种值吗?结果是相同的。
谢谢你,
悉尼的Anthony
好发现,感谢分享。
你好 Jason,
我对多变量时间序列(例如EEG、ECG)的机器学习感兴趣。我正在寻找用于处理和分析这类信号的Python课程和教程。
我期待您的回复,并感谢您的帮助。
谢谢
穆罕默德
请看这本关于时间序列预测的深度学习书籍
https://machinelearning.org.cn/deep-learning-for-time-series-forecasting/
感谢回复
不客气。
嗨,Jason,
非常感谢您对此主题的精彩解释和示例。我一直是您博客的粉丝,并从您的出版物中学到了很多东西。
当训练集和测试集是分离的时,我对监督学习方法有一些疑问。例如,训练集包含标签1、标签2、标签3,而测试集包含标签1到5。在这种情况下,标签4和5在训练集中并未出现,因此训练好的模型从未见过这些类别。
在这种情况下,我的问题是:
i) 是否有任何技术能够区分已训练和未训练的(即标签1-3 vs 标签4-5)?
ii) 要从训练好的模型中预测未训练的标签,我们如何为它们分配(或创建)标签?
我假设您可能可以引用任何无监督方法来解决这个问题,但在这种情况下,我只想使用监督学习。
任何想法或建议都将不胜感激。
谢谢,
戴夫
不客气。
并非如此——通常假设训练数据能够代表问题/所有数据。也许您可以训练一个模型来确定数据是否看起来像训练数据,并且仅在数据看起来像训练数据时才使用该模型。
是的,聚类可能有助于梳理数据中的自然结构,而这些结构可能与标签相关,也可能无关。
感谢您的回复,Jason。
使用二元分类器的想法需要已训练/未训练这两个类别的标签,而我仍然不知道如何有效/准确地为未训练类别创建数据……
也许,我们可以使用预测的软决策,但我发现这种方法很难泛化。
再次感谢您的回复。
-D。
我们使用具有已知标签的数据集来训练和评估模型/配置。
我们使用结果来选择模型和配置。
然后,我们在所有数据上训练模型,并使用它来预测未知标签的新数据上的标签。这就是选择模型的目标。
这有帮助吗?
嗨,Jason,
感谢您一如既往的精彩文章。
请允许我先简单介绍一下我的问题。我正在对结构化数据训练神经网络模型。这些数据每周都在增加,所以我每次都会在当前和增加的数据集(合并)上训练我的模型。我对我收到的数据进行内部拆分:训练集占70%,其余30%用于验证集和测试集(各占15%)。这是在生产环境中进行的。
我的问题
– 您认为,“在生产环境中”,使用所有(所有数据拆分)数据来训练模型是否更有价值?
我担心在这种情况下模型会过拟合(尤其是我目前使用早期停止技术,该技术会监控我验证数据上的验证损失)。
– 或者您认为使用最近邻算法创建代表性的验证/测试集更好?并在生产中继续这种训练过程(不使用验证集进行训练)。
这一直是一个困惑。我期待您的回答。
谢谢 🙂