你需要知道你的算法在新数据上的表现如何。
评估算法性能的最佳方法是预测你已知答案的新数据。次佳方法是使用统计学中称为重采样方法的巧妙技术,它允许你准确估计算法在新数据上的表现。
在这篇文章中,你将了解如何使用 Python 和 scikit-learn 中的重采样方法来估计机器学习算法的准确性。
通过我的新书《使用 Python 进行机器学习精通》**启动你的项目**,其中包括**分步教程**和所有示例的 **Python 源代码文件**。
让我们开始吧。
- 2017年1月更新:更新以反映 scikit-learn 0.18 版本中的 API 变化。
- **2017 年 10 月更新**:更新了打印语句以与 Python 3 兼容。
- **2018 年 3 月更新**:添加了下载数据集的备用链接,因为原始链接似乎已被删除。

在 Python 中使用重采样评估机器学习算法的性能
图片由 Doug Waldron 拍摄,部分权利保留。
关于这些示例
本文使用 Python 中的小段代码示例演示了重采样方法。
每个示例都设计为独立的,你可以将其复制粘贴到你的项目中并立即使用。
每个示例都使用了 Pima 印第安人糖尿病发作数据集。这是一个二元分类问题,其中所有输入变量都是数值。在每个示例中,它都直接下载。你可以根据需要用自己的数据集替换它。
评估你的机器学习算法
为什么不能在你的数据集上训练机器学习算法,然后使用该相同数据集的预测来评估机器学习算法呢?
简单的答案是过拟合。
想象一个记住它所看到的每个观测值的算法。如果你在用于训练算法的相同数据集上评估你的机器学习算法,那么像这样的算法在训练数据集上将获得完美的分数。但它在新数据上的预测结果将非常糟糕。
我们必须在未用于训练算法的数据上评估我们的机器学习算法。
评估是一种估计,我们可以用它来讨论我们认为算法在实践中可能表现得有多好。它不能保证性能。
一旦我们估计了算法的性能,我们就可以在整个训练数据集上重新训练最终算法,并使其准备好投入使用。
接下来我们将探讨四种不同的技术,我们可以用它们来分割我们的训练数据集,并为我们的机器学习算法创建有用的性能估计
- 训练集和测试集。
- K 折交叉验证。
- 留一法交叉验证。
- 重复随机测试-训练分割。
我们将从最简单的方法“训练集和测试集”开始。
需要 Python 机器学习方面的帮助吗?
参加我为期 2 周的免费电子邮件课程,探索数据准备、算法等等(附带代码)。
立即点击注册,还将免费获得本课程的 PDF 电子书版本。
1. 分割成训练集和测试集
我们可以用来评估机器学习算法性能的最简单方法是使用不同的训练和测试数据集。
我们可以将原始数据集分成两部分。在第一部分上训练算法,在第二部分上进行预测,并根据预期结果评估预测。
分割的大小取决于你的数据集的大小和具体情况,尽管通常将 67% 的数据用于训练,其余 33% 用于测试。
这种算法评估技术非常快。它非常适合大型数据集(数百万条记录),并且有充分证据表明数据的两次分割都代表了底层问题。由于速度快,当您研究的算法训练缓慢时,使用这种方法很有用。
这种技术的一个缺点是它可能具有高方差。这意味着训练集和测试集之间的差异可能导致准确性估计出现显著差异。
在下面的示例中,我们将 Pima 印第安人数据集分成 67%/33% 的训练集和测试集,并评估逻辑回归模型的准确性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# 使用训练集和测试集进行评估 import pandas from sklearn import model_selection from sklearn.linear_model import LogisticRegression url = "https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.csv" names = ['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age', 'class'] dataframe = pandas.read_csv(url, names=names) array = dataframe.values X = array[:,0:8] Y = array[:,8] test_size = 0.33 seed = 7 X_train, X_test, Y_train, Y_test = model_selection.train_test_split(X, Y, test_size=test_size, random_state=seed) model = LogisticRegression() model.fit(X_train, Y_train) result = model.score(X_test, Y_test) print("Accuracy: %.3f%%" % (result*100.0)) |
我们可以看到,模型的估计准确率约为 75%。请注意,除了指定分割大小之外,我们还指定了随机种子。由于数据的分割是随机的,我们希望确保结果是可重现的。通过指定随机种子,我们确保每次运行代码时都能获得相同的随机数。
注意:考虑到算法或评估过程的随机性,或数值精度的差异,您的结果可能会有所不同。请考虑多次运行示例并比较平均结果。
如果我们要将这个结果与另一个机器学习算法的估计准确度或相同算法的不同配置进行比较,这一点很重要。为了确保比较是公平的,我们必须确保它们在相同的数据上进行训练和测试。
1 |
准确率:75.591% |
2. K 折交叉验证
交叉验证是一种方法,你可以用它来估计机器学习算法的性能,其方差小于单个训练-测试集分割。
它的工作原理是将数据集分成 k 份(例如 k=5 或 k=10)。数据的每个分割称为一个折叠。算法在 k-1 个折叠上训练,其中一个保留下来,并在保留的折叠上进行测试。重复此过程,以便数据集的每个折叠都有机会成为保留的测试集。
运行交叉验证后,你将获得 k 个不同的性能分数,你可以使用平均值和标准差对其进行汇总。
结果是对算法在新数据上给定你的测试数据的性能的更可靠的估计。它更准确,因为算法在不同的数据上多次训练和评估。
k 的选择必须允许每个测试分区的足够大,足以成为问题的合理样本,同时允许算法的训练-测试评估有足够的重复次数,以提供对算法在新数据上性能的公平估计。对于数千或数万条记录的适度大小数据集,k 值 3、5 和 10 很常见。
在下面的示例中,我们使用 10 折交叉验证。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# 使用交叉验证进行评估 import pandas from sklearn import model_selection from sklearn.linear_model import LogisticRegression url = "https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.csv" names = ['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age', 'class'] dataframe = pandas.read_csv(url, names=names) array = dataframe.values X = array[:,0:8] Y = array[:,8] num_instances = len(X) seed = 7 kfold = model_selection.KFold(n_splits=10, random_state=seed) model = LogisticRegression() results = model_selection.cross_val_score(model, X, Y, cv=kfold) print("Accuracy: %.3f%% (%.3f%%)" % (results.mean()*100.0, results.std()*100.0)) |
您可以看到我们报告了性能度量的均值和标准差。
注意:考虑到算法或评估过程的随机性,或数值精度的差异,您的结果可能会有所不同。请考虑多次运行示例并比较平均结果。
在总结性能指标时,最好总结指标的分布,在本例中假设性能呈高斯分布(一个非常合理的假设),并记录均值和标准差。
1 |
准确率:76.951% (4.841%) |
3. 留一法交叉验证
您可以将交叉验证配置为使折叠大小为 1(k 设置为数据集中观测值的数量)。这种交叉验证的变体称为留一法交叉验证。
结果是大量的性能度量,可以将其汇总以期对模型在新数据上的准确性提供更合理的估计。缺点是它可能比 k 折交叉验证在计算上更昂贵。
在下面的示例中,我们使用留一法交叉验证。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# 使用留一法交叉验证进行评估 import pandas from sklearn import model_selection from sklearn.linear_model import LogisticRegression url = "https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.csv" names = ['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age', 'class'] dataframe = pandas.read_csv(url, names=names) array = dataframe.values X = array[:,0:8] Y = array[:,8] num_folds = 10 num_instances = len(X) loocv = model_selection.LeaveOneOut() model = LogisticRegression() results = model_selection.cross_val_score(model, X, Y, cv=loocv) print("Accuracy: %.3f%% (%.3f%%)" % (results.mean()*100.0, results.std()*100.0)) |
注意:考虑到算法或评估过程的随机性,或数值精度的差异,您的结果可能会有所不同。请考虑多次运行示例并比较平均结果。
你可以看到,标准差表明该分数的方差比上面描述的 k 折交叉验证结果更大。
1 |
准确率:76.823% (42.196%) |
4. 重复随机测试-训练分割
K 折交叉验证的另一种变体是创建像上面描述的训练/测试分割一样的随机数据分割,但像交叉验证一样重复分割和算法评估的过程多次。
这兼具使用训练/测试分割的速度和 K 折交叉验证中估计性能的方差降低。你也可以根据需要重复该过程更多次。缺点是重复可能在每次运行中包含训练或测试分割中的许多相同数据,从而在评估中引入冗余。
下面的例子将数据分成 67%/33% 的训练/测试分割,并重复该过程 10 次。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# 使用洗牌分割交叉验证进行评估 import pandas from sklearn import model_selection from sklearn.linear_model import LogisticRegression url = "https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.csv" names = ['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age', 'class'] dataframe = pandas.read_csv(url, names=names) array = dataframe.values X = array[:,0:8] Y = array[:,8] num_samples = 10 test_size = 0.33 num_instances = len(X) seed = 7 kfold = model_selection.ShuffleSplit(n_splits=10, test_size=test_size, random_state=seed) model = LogisticRegression() results = model_selection.cross_val_score(model, X, Y, cv=kfold) print("Accuracy: %.3f%% (%.3f%%)" % (results.mean()*100.0, results.std()*100.0)) |
注意:考虑到算法或评估过程的随机性,或数值精度的差异,您的结果可能会有所不同。请考虑多次运行示例并比较平均结果。
我们可以看到性能度量的分布与上面的 k 折交叉验证相当。
1 |
准确率:76.496% (1.698%) |
何时使用何种技术
- 通常,K 折交叉验证是评估机器学习算法在新数据上性能的黄金标准,其中 k 设置为 3、5 或 10。
- 当使用缓慢的算法时,使用训练/测试分割对于速度很有利,并且在使用大型数据集时产生具有较低偏差的性能估计。
- 留一法交叉验证和重复随机分割等技术在尝试平衡估计性能的方差、模型训练速度和数据集大小时可能是有用的中间方法。
最好的建议是进行实验,为你的问题找到一种既快速又能产生合理性能估计的技术,以便你做出决策。如果拿不准,请使用 10 折交叉验证。
总结
在这篇文章中,你了解了可用于估计机器学习算法性能的统计技术,称为重采样。
具体来说,您学习了
- 训练集和测试集。
- 交叉验证。
- 留一法交叉验证。
- 重复随机测试-训练分割。
您对重采样方法或此帖子有任何疑问吗?请在评论中提出您的问题,我将尽力回答。
布朗利先生,感谢您精彩的解释。我只建议您验证 Scikit Learn 模块在新版本中的一些修改。例如,考虑将“from sklearn import cross_validation”替换为“from sklearn.model_selection import cross_val_score, KFold”,并关联第四部分代码的第 15 行和第 17 行的更改。
“
感谢您的建议,Vinicius,我将尽快安排更改。
感谢您提供如此详细的主题。我正在从零开始学习数据科学,发现它非常有帮助。您能告诉我,如何找到上述技术的均方误差吗?我已评估了测试训练的 RMSE,但如何为 k 折和 LOOC 这样做。谢谢。
你好。将整个数据集进行分区和分割,并进行 10 折交叉验证后,再将其用于测试是否可以?与随机分割相比,结果只有轻微变化。
用于评估技能(测试)的数据不能用于训练模型(训练)。
如果是,估计会有偏差。
谢谢你的回复。
嗨,Jason。我使用 10 折交叉验证处理了一个包含 223,585 个样本的数据集。我还尝试了 80:20 的随机分割。结果没有变化。除了 SVM 和 LR 之外,没有其他模型在此数据集上表现良好。准确率为 0.97。召回率为 0.99。我是否可以认为我的模型运行良好?
听起来不错。做得好。
嗨 Jason
当我们对回归模型应用 k 折交叉验证时。像准确率这样的术语意义不大,那么在回归问题下,k 折验证是否使用了不同的函数?
您将估计模型误差,例如 MSE、RMSE 或 MAE。
嗨,Jason,好文章(和网站!)。
我的问题是关于交叉验证的。根据我的经验,我们通常将数据集分成训练集和测试集,然后将训练集进一步分成训练集和验证集,其中验证集用于调整机器学习超参数(例如 lasso 惩罚系数,或决策树上的最小样本叶)。
我的问题是,在交叉验证期间,我们应该从一次 train_test_split 中确定一组超参数,还是每次都重新调整超参数?在后一种情况下,我们可能会面临模型超参数在不同折叠之间不同的风险……这是一个问题,还是可以接受,尽管计算成本很高?
谢谢,
好问题,这真的取决于你。
如果你有资源,每次调整模型、记录所选参数并在调整最终模型时使用参数的平均值会很好。
嗨,Jason,
您的网站是机器学习爱好者的最佳选择,我想获得更多关于我们计算准确性时计算的均值和方差的解释,它们告诉我们结果什么?
当然
https://machinelearning.org.cn/statistical-data-distributions/
在 K 折部分,您正在查找准确度,这是测试数据、训练数据还是整个数据集的准确度?实际上,我理解了训练-测试分割的实现概念,但在使用 K 折时如何拟合数据有点疑问??
results = model_selection.cross_val_score(model, X, Y, cv=kfold)
print(“Accuracy: %.3f%% (%.3f%%)” % (results.mean()*100.0, results.std()*100.0))
两者都不是,它使用 k 折交叉验证。您可以在这里了解更多信息
https://machinelearning.org.cn/k-fold-cross-validation/
嗨,Jason Brownlee,我有一个数据集手动分类部分,并根据训练数据训练算法预测其他部分,哪种方法更适合我评估机器学习算法的性能,
您可以在这里了解有关如何评估算法的更多信息
https://machinelearning.org.cn/faq/single-faq/how-do-i-evaluate-a-machine-learning-algorithm
嗨。
这对我这样的初学者来说是个好例子。但是,如何生成从 k 折交叉验证获得的混淆矩阵呢?
您无法从多次运行(例如,通过交叉验证)获得混淆矩阵。
您可以从单次运行(例如,在训练/测试分割上)获得混淆矩阵。
这篇文章会告诉你怎么做
https://machinelearning.org.cn/confusion-matrix-machine-learning/
嗨,Jason 先生,
如果我想使用召回率、精确率和 F1 分数等其他指标来处理多类别标签,该怎么办?
没问题,您可以使用 scikit-learn 的实现
https://scikit-learn.cn/stable/modules/classes.html#module-sklearn.metrics
我的理解是 cross_val_score 实际上不拟合模型。如果我使用重复的 k 折交叉验证,我如何在此分割上拟合模型?然后使用此模型进行预测?重复的 k 折很好,因为它可以一步完成调整和训练,我不需要拟合“最终模型”。请看 sklearn 网页上的以下示例
import numpy as np
from sklearn.model_selection import RepeatedKFold
X = np.array([[1, 2], [3, 4], [1, 2], [3, 4]])
y = np.array([0, 0, 1, 1])
rkf = RepeatedKFold(n_splits=2, n_repeats=2, random_state=2652124)
for train_index, test_index in rkf.split(X)
print(“TRAIN:”, train_index, “TEST:”, test_index)
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
我想在这些分割上拟合模型。
下面的代码是否可行?
logit = LogisticRegression()
rkf = RepeatedKFold(n_splits=2, n_repeats=2, random_state=2652124)
for train_index, test_index in rkf.split(X)
print(“TRAIN:”, train_index, “TEST:”, test_index)
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
model_fit = logit.fit(X_train, Y_train)
交叉验证仅用于评估模型。
一旦您选择了一个模型/配置,您可以拟合一个最终模型并将其用于进行预测,更多详细信息请点击此处
https://machinelearning.org.cn/train-final-machine-learning-model/
感谢 Jason 您的有用帖子,我有一个问题
我使用是否更好
model = RandomForestClassifier()
而不是
model = LogisticRegression()
我通常建议针对一个问题测试一系列不同的模型。
谢谢你,杰森,但我还有另一个问题
我使用随机森林算法构建了一个模型,我可以使用上面的交叉验证代码进行评估吗?
请问,在估计和评估步骤之后,我何时拟合我的模型?
是的。
评估后,将模型拟合到所有可用数据上,并用它来做出预测。更多详细信息请点击此处
https://machinelearning.org.cn/train-final-machine-learning-model/
杰森,您好!
我注意到 k 折交叉验证的方差比重复随机测试-训练分割的方差更大。
这是否意味着交叉验证中的方差/随机性来源比重复随机测试-训练分割中的更多?
1) 由于算法是非确定性的导致的方差
2) 由于 k 个折叠选择导致的方差
这有道理吗?
也许你选择的 k 值对你的数据集来说过于严格了?
我很感激能从我非常尊敬的人那里得到快速回复,但您的回答缺少了关键点。也许我的蹩脚英语与此有关。我很抱歉。
但是我的意思是,您上面例子中的方差,交叉验证 +/-4.841%,多次运行 +/-1.698%。几乎是 3 倍。
此外,总的来说,k 折交叉验证的方差是否比在相同数据集上重复 k 次随机测试-训练分割的方差更大?为什么?我曾认为交叉验证有更多的方差来源,但我不确定我的直觉。所以我想听听像您这样受人尊敬的专家的意见。请您花时间思考一下。
我们重复 CV 以减少对平均性能估计的方差。
即,均值应该是对未知底层总体均值更准确的估计。
反过来,您可能会测量均值分布或分数分布的更大方差,但这只是因为您更好地近似了数据集上模型性能的底层分布。
这有帮助吗?
谢谢!这次你真的帮到我了。抱歉显得粗鲁,我不是那种人。我真的想得到帮助,并且渴望理解。
我想您刚刚给了我一个关于交叉验证方差(上面的 2. K 折交叉验证)与单次运行执行(上面的 1. 分割成训练和测试集情况)的很好的解释。
现在,k 折交叉验证(上面的 2. K 折交叉验证)与在相同数据集上 k 次重复随机测试-训练分割(上面的 4. 重复随机测试-训练分割)的方差差异如何?前者显示出三倍的方差。
我的问题现在更清楚了吗?
完全没有冒犯,我在这里尽力帮助。
它们不能直接比较,因为它们是不同的采样策略。
如果我们忽略这个事实,假设它们都在寻求模型性能分布的估计(它们确实如此),那么重复的训练/测试过于乐观,因为相同的数据出现在测试集中。
较小的方差不是真实的,域样本(准确度分数或其他)是有偏差的——它在测试集中有相同的数据。
交叉验证解决了这个问题,使得每个测试集都不同。
这就是为什么我评论说交叉验证比训练/测试和重复训练/测试的乐观偏差更小。
这有帮助吗?
我现在更清楚了。因此,测试集的有限大小和重复引入了乐观偏差,这在看似较小的方差中体现出来。我怎么没想到呢?
我确实坚持了一会儿,但我明白您工作繁忙,还需要帮助许多其他人。
非常感谢您提供的启发性解释。
不客气。
嗨,Jason,
运行以下代码时出现此错误
# 使用训练集和测试集进行评估
“C:\Users\harvey.PROFEEDS\AppData\Local\Continuum\anaconda3\lib\site-packages\sklearn\linear_model\logistic.py:432: FutureWarning: Default solver will be changed to ‘lbfgs’ in 0.22. Specify a solver to silence this warning.
FutureWarning)”
所以我将一个求解器放入代码中,如下所示
model = LogisticRegression(solver=”lbfgs”, max_iter=1000)
但是准确率现在从 75.591% 上升到 78.740%。
为什么会这样?我正在尝试复制您的结果。
谢谢
M. 哈维
这是一个警告,您可以安全地忽略它。
每次运行算法时,您都可能得到不同的结果,请参阅此内容
https://machinelearning.org.cn/faq/single-faq/why-do-i-get-different-results-each-time-i-run-the-code
嗨,Jason。我能问一下为什么留一法的方差比 k 折交叉验证的方差高吗?是不是因为它们的训练折叠重叠度很高,每次创建的模型都会非常相似,因此方差更高?谢谢
很好的问题。
我没有一个自信的即时答案,抱歉。直觉认为应该反过来。
我还有另一个关于您用于重采样的代码的问题,希望您能帮我解决。我看到您将 model = LogisticRegrssion()。这是否意味着您预先假设这些模型都是逻辑回归?如果我们不知道应该使用哪个模型来处理数据,这是否意味着我们不能使用这些重采样方法?
一般来说,我建议测试一系列不同的模型,以发现最适合您的特定数据集的模型。
我明白了。我的理解是,在我们决定使用哪个模型之前,我们首先使用重采样方法分割数据,然后将其应用于模型,对吗?根据您所说的,我感觉您是先预设模型,然后使用该模型对数据进行重采样?(顺便说一句,我只想说非常感谢!我很高兴找到这个网站,您人真好!非常感谢您的帮助!)
不太对。
“模型”实际上是一个建模流程,由数据转换、特征选择和进行预测的学习算法组成。
嗨,Jason,我有一个基本问题。当我的权重在 K-1 折叠上训练并在 1 折叠上测试时,我是否也应该平均这些权重以确定在原始(25%)测试集上测试的最佳模型?如果我在每次迭代(每个新折叠)时都将权重重新初始化为零,那么验证如何使我的模型更好?在这种情况下,我应该在哪些参数上训练测试集?
我不太明白。
模型被评估了 k 次,我们报告平均性能。然后所有模型都被丢弃。一旦我们选择了要使用的模型,我们就会在所有数据上拟合它并开始进行预测。
这有帮助吗?
我可以用什么代码运行 10 折交叉验证 30 次?
使用这个
https://scikit-learn.cn/stable/modules/generated/sklearn.model_selection.RepeatedKFold.html
嗨,Jason Brownlee 先生,您能介绍一篇解释这些方法的文章吗?我买不到您的书。
也许从这里开始
https://machinelearning.org.cn/k-fold-cross-validation/
亲爱的 Jason,
感谢您的精彩页面。它非常有帮助。我能问两个关于 LOOCV 的问题吗?
1. 逻辑回归的 DV 是否需要是数据电子表格中的最后一列?我看到您的示例数据预测的是变量号 9。类别变量(0 或 1)。您的数组设置为 0:8。因此,我假设当我在我的数据上运行此脚本时,我的 DV 需要是最后一列。我有 4 个 IV 和 1 个 DV。因此,我将把 DV 放在第 5 列,并将数组写成 0:4。这正确吗?
2. 我假设,就像在逻辑回归中一样,当所有 IV 都是二元时,此脚本也能正常工作。这正确吗?您的示例数据中有所有数字 IV。
我只是想确保我正确使用了您出色的脚本。再次感谢!
汤姆
不客气。
目标变量不必是最后一列,但这是一种惯例。
如果切片数组对您来说是新事物,您可以在这里了解更多信息
https://machinelearning.org.cn/index-slice-reshape-numpy-arrays-machine-learning-python/
大多数模型假设/要求数字输入。包括逻辑回归。
谢谢。如果可能,还有两件事
1. 我的带有离散 IVs 的逻辑回归得到了我委员会中数据科学家的批准。我查阅的文献也说这是可能的。我希望 LOOCV 在这种情况下能正常工作,是吗?
2. LOOCV 结果证实了我的回归结果。对于三个分析,我得到了 85%、92% 和 94% 的准确率。把这三个都称为“高度准确”可以吗?
再次感谢您。
准确性是相对的,请将结果与朴素模型甚至其他有能力的模型进行比较。
感谢您提供的所有帮助。不胜感激。
不客气。