数据准备是将原始数据转换为适合建模的格式的过程。
一种朴素的数据准备方法是在评估模型性能之前,将转换应用于整个数据集。这会导致一个称为“数据泄露”的问题,即保留测试集(hold-out test set)的知识泄露到用于训练模型的数据集中。这可能导致在对新数据进行预测时,模型性能的估计不准确。
为了避免数据泄露,需要仔细应用数据准备技术,这取决于所使用的模型评估方案,例如训练-测试集划分或k折交叉验证。
在本教程中,您将学习如何在评估机器学习模型的数据准备过程中避免数据泄露。
完成本教程后,您将了解:
- 将数据准备方法朴素地应用于整个数据集会导致数据泄露,从而导致模型性能估计不准确。
- 为避免数据泄露,数据准备只能在训练集上进行。
- 如何在 Python 中实现数据准备,以避免训练-测试集划分和 k 折交叉验证的数据泄露。
开始您的项目,阅读我的新书《机器学习数据准备》,其中包含分步教程和所有示例的Python源代码文件。
让我们开始吧。

在进行数据准备时如何避免数据泄露
照片由 kuhnmi 拍摄,保留部分权利。
教程概述
本教程分为三个部分;它们是:
- 朴素数据准备的问题
- 带训练集和测试集的数据准备
- 朴素数据准备的训练-测试评估
- 正确数据准备的训练-测试评估
- 带 k 折交叉验证的数据准备
- 朴素数据准备的交叉验证评估
- 正确的交叉验证评估
朴素数据准备的问题
数据准备技术应用方式很重要。
一种常见的方法是先将一个或多个转换应用于整个数据集。然后,将数据集划分为训练集和测试集,或者使用 k 折交叉验证来拟合和评估机器学习模型。
- 1. 准备数据集
- 2. 划分数据
- 3. 评估模型
尽管这是一种常见的方法,但在大多数情况下,它都是危险地不正确的。
在模型评估之前应用数据准备技术的问题在于,它可能导致数据泄露,进而可能导致模型在实际问题上的性能估计不准确。
数据泄露是指模型在训练数据集中可以访问保留数据集(如测试集或验证集)的信息的问题。这种泄露通常很小且微妙,但可能对性能产生显著影响。
…泄露意味着模型可以获取信息,从而获得不切实际的优势来做出更好的预测。这可能发生在测试数据泄露到训练集时,或者未来数据泄露到过去时。当模型在生产环境中实时预测时,如果模型能够访问它不应访问的信息,就存在泄露。
— 第 93 页,机器学习特征工程,2018。
通过将数据准备技术应用于整个数据集,我们会发生数据泄露。
这不是直接类型的数据泄露,我们不会在测试数据集上训练模型。相反,这是一种间接的数据泄露,其中一些关于测试数据集的知识(以汇总统计数据的形式捕获)在训练期间可供模型使用。这使得数据泄露更难发现,尤其是对于初学者。
重采样的一个方面与信息泄露的概念有关,即在训练过程中使用了(直接或间接)测试集数据。这可能导致结果过于乐观,无法在未来的数据点上复制,并且可能以微妙的方式发生。
— 第 55 页,特征工程与选择,2019。
例如,考虑我们要对数据进行归一化的情况,即尺度输入变量到 0-1 的范围。
当我们对输入变量进行归一化时,需要先计算每个变量的最小值和最大值,然后再使用这些值来缩放变量。然后,数据集被划分为训练集和测试集,但训练集中的示例了解测试集中的数据;它们已被全局最小值和最大值缩放,因此它们比应该了解的更多地了解变量的全局分布。
我们通过几乎所有数据准备技术获得了相同的泄露类型;例如,标准化估计域的均值和标准差值以缩放变量;即使是使用模型或汇总统计数据来填充缺失值的模型,也会利用整个数据集来填充训练集中的值。
解决方案很简单.
数据准备只能在训练集上进行拟合。也就是说,为数据准备过程准备的任何系数或模型都只能使用训练集中的数据行。
一旦拟合完成,数据准备算法或模型就可以应用于训练集,然后应用于测试集。
- 1. 划分数据。
- 2. 在训练集上拟合数据准备。
- 3. 将数据准备应用于训练集和测试集。
- 4. 评估模型。
更一般地说,整个建模管道必须仅在训练数据集上进行准备,以避免数据泄露。这可能包括数据转换,但也包括其他技术,如特征选择、降维、特征工程等。这意味着所谓的“模型评估”实际上应该称为“建模管道评估”。
为了使任何重采样方案都能产生泛化到新数据的性能估计,它必须包含在建模过程中可能显著影响模型有效性的所有步骤。
— 第 54-55 页,特征工程与选择,2019。
现在我们熟悉了如何应用数据准备来避免数据泄露,让我们看一些实际示例。
想开始学习数据准备吗?
立即参加我为期7天的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
带训练集和测试集的数据准备
在本节中,我们将使用训练集和测试集,在输入变量已归一化的合成二元分类数据集上评估逻辑回归模型。
首先,让我们定义我们的合成数据集。
我们将使用 make_classification() 函数创建一个包含 1000 行数据和 20 个数值输入特征的数据集。下面的示例创建了数据集,并总结了输入和输出变量数组的形状。
1 2 3 4 5 6 |
# 测试分类数据集 from sklearn.datasets import make_classification # 定义数据集 X, y = make_classification(n_samples=1000, n_features=20, n_informative=15, n_redundant=5, random_state=7) # 汇总数据集 print(X.shape, y.shape) |
运行示例创建了数据集,并确认数据集的输入部分有 1000 行和 20 列(对应 20 个输入变量),而输出变量有 1000 个示例,与 1000 行输入数据匹配,每行一个值。
1 |
(1000, 20) (1000,) |
接下来,我们可以对缩放后的数据集评估我们的模型,首先从朴素或不正确的方法开始。
朴素数据准备的训练-测试评估
朴素方法包括首先应用数据准备方法,然后划分数据,最后评估模型。
我们可以使用 MinMaxScaler 类对输入变量进行归一化,该类首先用默认配置定义,将数据缩放到 0-1 的范围,然后调用 fit_transform() 函数在一个步骤中拟合转换并将其应用于数据集。结果是输入变量的归一化版本,其中数组中的每一列都单独归一化(例如,有自己的最小值和最大值计算)。
1 2 3 4 |
... # 标准化数据集 scaler = MinMaxScaler() X = scaler.fit_transform(X) |
接下来,我们可以使用 train_test_split() 函数将数据集划分为训练集和测试集。我们将使用 67% 作为训练集,33% 作为测试集。
1 2 3 |
... # 拆分为训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1) |
然后,我们可以通过 LogisticRegression 类定义我们的逻辑回归算法,使用默认配置,并在训练数据集上拟合它。
1 2 3 4 |
... # 拟合模型 model = LogisticRegression() model.fit(X_train, y_train) |
拟合后的模型就可以对测试集的输入数据进行预测,我们可以将预测值与期望值进行比较,并计算分类准确率分数。
1 2 3 4 5 6 |
... # 评估模型 yhat = model.predict(X_test) # 评估预测 accuracy = accuracy_score(y_test, yhat) print('Accuracy: %.3f' % (accuracy*100)) |
将这些结合起来,完整的示例列在下面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# 在数据划分和模型评估之前对数据进行归一化的朴素方法 from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split 从 sklearn.预处理 导入 MinMaxScaler from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score # 定义数据集 X, y = make_classification(n_samples=1000, n_features=20, n_informative=15, n_redundant=5, random_state=7) # 标准化数据集 scaler = MinMaxScaler() X = scaler.fit_transform(X) # 拆分为训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1) # 拟合模型 model = LogisticRegression() model.fit(X_train, y_train) # 评估模型 yhat = model.predict(X_test) # 评估预测 accuracy = accuracy_score(y_test, yhat) print('Accuracy: %.3f' % (accuracy*100)) |
运行示例,对数据进行归一化,划分数据为训练集和测试集,然后拟合和评估模型。
注意:由于算法或评估程序的随机性,或者数值精度的差异,您的结果可能有所不同。考虑运行几次示例并比较平均结果。
在这种情况下,我们可以看到模型估计的准确率为 84.848%。
1 |
准确率: 84.848 |
鉴于我们知道存在数据泄露,我们知道这个模型准确率的估计是错误的。
接下来,让我们看看如何正确地准备数据以避免数据泄露。
正确数据准备的训练-测试评估
使用训练-测试集划分进行数据准备的正确方法是:首先在训练集上拟合数据准备,然后将转换应用于训练集和测试集。
这需要我们先将数据划分为训练集和测试集。
1 2 3 |
... # 拆分为训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1) |
然后,我们可以定义 MinMaxScaler 并调用 fit() 函数在训练集上进行拟合,然后将 transform() 函数应用于训练集和测试集,以创建每个数据集的归一化版本。
1 2 3 4 5 6 7 8 9 |
... # 定义缩放器 scaler = MinMaxScaler() # 在训练数据集上拟合 scaler.fit(X_train) # 缩放训练数据集 X_train = scaler.transform(X_train) # 缩放测试数据集 X_test = scaler.transform(X_test) |
这避免了数据泄露,因为每个输入变量的最小值和最大值的计算仅使用训练数据集 (X_train) 而不是整个数据集 (X)。
然后可以像之前一样评估模型。
将这些结合起来,完整的示例列在下面。
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 |
# 在数据划分之后、模型评估之前对数据进行归一化的正确方法 from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split 从 sklearn.预处理 导入 MinMaxScaler from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score # 定义数据集 X, y = make_classification(n_samples=1000, n_features=20, n_informative=15, n_redundant=5, random_state=7) # 拆分为训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1) # 定义缩放器 scaler = MinMaxScaler() # 在训练数据集上拟合 scaler.fit(X_train) # 缩放训练数据集 X_train = scaler.transform(X_train) # 缩放测试数据集 X_test = scaler.transform(X_test) # 拟合模型 model = LogisticRegression() model.fit(X_train, y_train) # 评估模型 yhat = model.predict(X_test) # 评估预测 accuracy = accuracy_score(y_test, yhat) print('Accuracy: %.3f' % (accuracy*100)) |
运行示例,将数据划分为训练集和测试集,正确地进行数据归一化,然后拟合和评估模型。
注意:由于算法或评估程序的随机性,或者数值精度的差异,您的结果可能有所不同。考虑运行几次示例并比较平均结果。
在这种情况下,我们可以看到模型估计的准确率为 85.455%,比上一节中存在数据泄露的准确率 84.848% 更准确。
我们期望数据泄露会导致模型性能估计不准确。我们期望这是具有数据泄露的乐观估计,即性能更好,尽管在这种情况下,我们可以看到数据泄露导致了略微较差的性能。这可能是因为预测任务的难度。
1 |
准确率: 85.455 |
带 k 折交叉验证的数据准备
在本节中,我们将使用 k 折交叉验证,在输入变量已归一化的合成二元分类数据集上评估逻辑回归模型。
您可能还记得,k 折交叉验证涉及将数据集划分为 k 个不重叠的行组。然后,模型在除一个组之外的所有组上进行训练,形成训练数据集,然后对保留的折进行评估。重复此过程,以便每个折都有机会被用作保留的测试集。最后,报告所有评估的平均性能。
k 折交叉验证程序通常比训练-测试集划分提供更可靠的模型性能估计,尽管由于模型重复拟合和评估,它的计算成本更高。
让我们先看看朴素数据准备与 k 折交叉验证。
朴素数据准备的交叉验证评估
带交叉验证的朴素数据准备包括先应用数据转换,然后使用交叉验证程序。
我们将使用上一节中准备的合成数据集并直接归一化数据。
1 2 3 4 |
... # 标准化数据集 scaler = MinMaxScaler() X = scaler.fit_transform(X) |
必须首先定义 k 折交叉验证程序。我们将使用重复分层 10 折交叉验证,这是分类的最佳实践。重复意味着整个交叉验证过程会重复多次,这里是三次。分层意味着每个行组的示例组成与整个数据集的类相对构成相同。我们将使用 k=10,即 10 折交叉验证。
这可以通过 RepeatedStratifiedKFold 来实现,该函数可以配置为重复三次和 10 折,然后使用 cross_val_score() 函数执行该过程,传入定义的模型、交叉验证对象和要计算的指标,这里是准确率。
1 2 3 4 5 |
... # 定义评估过程 cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1) # 使用交叉验证评估模型 scores = cross_val_score(model, X, y, scoring='accuracy', cv=cv, n_jobs=-1) |
然后,我们可以报告所有重复和折叠的平均准确率。
总而言之,使用数据泄露进行数据准备的模型交叉验证评估的完整示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# 用于模型评估和 k 折交叉验证的朴素数据准备 from numpy import mean from numpy import std from sklearn.datasets import make_classification from sklearn.model_selection import cross_val_score from sklearn.model_selection import RepeatedStratifiedKFold 从 sklearn.预处理 导入 MinMaxScaler 从 sklearn.线性模型 导入 LogisticRegression # 定义数据集 X, y = make_classification(n_samples=1000, n_features=20, n_informative=15, n_redundant=5, random_state=7) # 标准化数据集 scaler = MinMaxScaler() X = scaler.fit_transform(X) # 定义模型 model = LogisticRegression() # 定义评估过程 cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1) # 使用交叉验证评估模型 scores = cross_val_score(model, X, y, scoring='accuracy', cv=cv, n_jobs=-1) # 报告表现 print('Accuracy: %.3f (%.3f)' % (mean(scores)*100, std(scores)*100)) |
运行示例,首先对数据进行归一化,然后使用重复分层交叉验证评估模型。
注意:由于算法或评估程序的随机性,或者数值精度的差异,您的结果可能有所不同。考虑运行几次示例并比较平均结果。
在这种情况下,我们可以看到模型估计的准确率约为 85.300%,但我们知道这是不正确的,因为数据准备过程允许数据泄露。
1 |
准确率: 85.300 (3.607) |
接下来,让我们看看如何使用交叉验证评估模型并避免数据泄露。
正确的交叉验证评估
使用交叉验证进行数据准备时避免数据泄露会稍微困难一些。
它要求数据准备方法在训练集上进行准备,并在交叉验证过程中应用于训练集和测试集,例如行组的折叠。
我们可以通过定义一个建模管道来实现这一点,该管道定义了一系列要执行的数据准备步骤,并以要拟合和评估的模型结束。
为了提供一个扎实的方法,我们应该将自己限制在开发预处理技术列表,仅在训练数据点存在的情况下对其进行估算,然后将技术应用于未来的数据(包括测试集)。
— 第 55 页,特征工程与选择,2019。
评估过程从简单地、不正确地仅评估模型变为正确地将数据准备和模型这两个整个流程作为一个原子单元进行评估。
这可以通过 Pipeline 类来实现。
此类接受一个定义管道的步骤列表。列表中的每个步骤都是一个包含两个元素的元组。第一个元素是步骤的名称(字符串),第二个元素是步骤的配置对象,例如转换器或模型。模型仅支持作为最后一步,尽管我们可以在序列中包含任意数量的转换器。
1 2 3 4 5 6 |
... # 定义管道 steps = list() steps.append(('scaler', MinMaxScaler())) steps.append(('model', LogisticRegression())) pipeline = Pipeline(steps=steps) |
然后,我们可以将配置好的对象传递给 cross_val_score() 函数进行评估。
1 2 3 4 5 |
... # 定义评估过程 cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1) # 使用交叉验证评估模型 scores = cross_val_score(pipeline, X, y, scoring='accuracy', cv=cv, n_jobs=-1) |
总而言之,在交叉验证中正确执行数据准备以避免数据泄露的完整示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# 用于模型评估和 k 折交叉验证的正确数据准备 from numpy import mean from numpy import std from sklearn.datasets import make_classification from sklearn.model_selection import cross_val_score from sklearn.model_selection import RepeatedStratifiedKFold 从 sklearn.预处理 导入 MinMaxScaler from sklearn.linear_model import LogisticRegression from sklearn.pipeline import Pipeline # 定义数据集 X, y = make_classification(n_samples=1000, n_features=20, n_informative=15, n_redundant=5, random_state=7) # 定义管道 steps = list() steps.append(('scaler', MinMaxScaler())) steps.append(('model', LogisticRegression())) pipeline = Pipeline(steps=steps) # 定义评估过程 cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1) # 使用交叉验证评估模型 scores = cross_val_score(pipeline, X, y, scoring='accuracy', cv=cv, n_jobs=-1) # 报告表现 print('Accuracy: %.3f (%.3f)' % (mean(scores)*100, std(scores)*100)) |
运行示例,在评估过程的交叉验证折内正确地对数据进行归一化,以避免数据泄露。
注意:由于算法或评估程序的随机性,或者数值精度的差异,您的结果可能有所不同。考虑运行几次示例并比较平均结果。
在这种情况下,我们可以看到模型的估计准确率约为 85.433%,而有数据泄露的方法的准确率约为 85.300%。
与上一节的训练-测试示例一样,移除数据泄露使性能略有提高,而我们的直觉可能认为由于数据泄露通常会导致模型性能的乐观估计,因此会下降。尽管如此,这些示例清楚地表明,数据泄露确实会影响模型性能的估计,以及如何通过在数据划分后正确执行数据准备来纠正数据泄露。
1 |
准确率: 85.433 (3.471) |
进一步阅读
如果您想深入了解,本节提供了更多关于该主题的资源。
教程
书籍
- 特征工程和选择:预测模型的实用方法, 2019.
- 应用预测建模, 2013.
- 《数据挖掘:实用机器学习工具与技术》(Data Mining: Practical Machine Learning Tools and Techniques), 2016.
- 机器学习的特征工程, 2018.
API
- sklearn.datasets.make_classification API.
- sklearn.preprocessing.MinMaxScaler API.
- sklearn.model_selection.train_test_split API.
- sklearn.linear_model.LogisticRegression API.
- sklearn.model_selection.RepeatedStratifiedKFold API.
- sklearn.model_selection.cross_val_score API.
文章
总结
在本教程中,您学习了如何在评估机器学习模型的数据准备过程中避免数据泄露。
具体来说,你学到了:
- 将数据准备方法朴素地应用于整个数据集会导致数据泄露,从而导致模型性能估计不准确。
- 为避免数据泄露,数据准备只能在训练集上进行。
- 如何在 Python 中实现数据准备,以避免训练-测试集划分和 k 折交叉验证的数据泄露。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
Jason 你好,感谢这篇文章。
我想知道,如果我们先分离训练集和测试集,然后分别对它们应用简单的 min-max 或 z-score 归一化,这是否能帮助解决数据泄露问题?
我假设一种情况是,我们根本不使用 MinMaxScaler 或任何其他内置的库缩放器,而只是使用一个我们可以单独应用于训练集和测试集特征的简单函数。
例如,一个简单的 min-max 方法,例如:
def min_max:(x_train): return (x_train – min(x_train)) / (max(x_train) – min(x_train))
在分别应用此函数之前分离数据集,是否可以帮助减少或防止数据泄露?
如果那不起作用的话:
如何在不使用 MinMax() 或 StandardScaler() 库的情况下避免数据泄露?
只要您计算的任何系数仅在训练集上进行准备,或者比两个数据集更广泛的领域知识。
上面的示例确切地展示了如何在训练/测试集划分中避免数据泄露。
Jason,
谢谢您提供这些很棒的资料,我是您工作的忠实粉丝,它极大地促进了我对该领域的学习和知识。
谢谢,上帝保佑
Priscila
Priscila,不客气!
感谢这篇文章。
我有两个问题
当人们报告“正确完成”的准确率时,他们是否会使用任何特定术语来表示这一点?
当我删除不完整数据行时,我是否应该调整我引用的准确率以将其计入?(显然,一个不能处理不完整数据的模型,对任何新的不完整数据都不能做出好的预测。)或者,引用我期望模型在完整数据上达到的准确率是否合理?
嗯。很棒的问题!
在描述数据如何准备以及模型如何评估时,我们应该尽可能详细。
理想情况下,我们会在论文或报告中提供代码,以便任何人都可以重现结果并进行扩展。
嗨,Jason,
一如既往的精彩教程。
如果我们想预测单行数据怎么办?
然后我们该如何缩放它?
我们不能将标准或最小最大缩放器应用于单行。
谢谢。
可以,将其转换为矩阵然后进行转换。
Jason,
感谢您清晰的演示,它说明了这些原理!我同意您,模型性能的精确估计最好以这种方式完成,给定训练集和验证集之间的分割。但是,我认为将数据分割为训练、验证和测试集也是有价值的,其中测试数据在模型优化之前被保留。因此,验证数据可用于优化模型的元参数,并且在我看来也可用于拟合缩放器。
我之所以这样论证,是因为在实际问题中,您不仅想知道模型的性能,还希望它尽可能健壮。假设您将数据分为三部分:训练、验证和测试。我会同时使用训练和验证集来拟合缩放器,并使用测试数据来查看是否有任何意外情况,例如数据与模型拟合不佳。
变量选择的评估也应在此背景下进行。假设您将测量的高程作为输入,并且根据训练数据的范围进行缩放。在这种情况下,不能保证新数据点在此范围内,这可能会严重影响非线性分类或回归方法。
不客气!
是的,您可以将验证集与训练集分开。更多信息请参见此处
https://machinelearning.org.cn/difference-test-validation-datasets/
尊敬的Jason博士,
在上述代码中,用于正确处理交叉验证数据
特别是这一行
如果我们采用朴素方法,那么整个X的标准差是泄漏的原因。相比之下,通过应用管道,是否意味着我们考虑了每个子样本的标准差?
谢谢你,
悉尼的Anthony
管道仅在每个CV分割的训练部分上拟合转换(整个管道)。
再次提前感谢,
还有一个问题,“……仅在每个CV分割的训练部分”。这意味着每个CV分割都有自己的标准差。相比之下,“朴素”方法处理整个数据集,而不考虑特定分割的个体标准差。
因此,管道确保了特征X的全局标准差没有泄露到每个CV分割中?
谢谢你
悉尼的Anthony
当使用管道时,转换不会应用于整个数据集,而是根据需要仅应用于每次模型评估过程的训练数据。
谢谢你
悉尼的Anthony
不客气。
那么,这意味着,如果总折叠数为“n”,则对于每次cv迭代,管道中的“MinMax()”对象是否将fit_transform()应用于n-1(训练)部分,并将transform()应用于剩余的1(验证)部分?
如果这是真的,那么上述相同的流程是否适用于其他特征转换/转换方法,例如LabelEncode()、OneHotEncoder(),当我们使用这些方法来防止数据泄露时?
你好 Lukman……以下是一个很好的资源来帮助澄清
https://machinelearning.org.cn/mcnemars-test-for-machine-learning/
Jason Brownlee 2020年7月1日 上午11:17 #
管道仅在每个CV分割的训练部分上拟合转换(整个管道)。
关于你在这里的回复:管道只应用于CV的训练部分。
但是,在管道中,您还在缩放值,并且只对CV的训练数据应用缩放,那么验证折叠或没有应用数据准备的数据怎么样?或者,管道是否也分别应用于CV中的验证集?
缩放是使用训练数据训练的。例如,您只使用训练数据来确定均值和标准差。然后,在验证时,您使用相同的均值和标准差来无条件地转换数据。
尊敬的Jason博士,
我想比较朴素模型和管道模型的性能。
从您的书中,第29页的列表4.7(398中的46),我已经成功实现了。
我将展示朴素代码的成功实现,然后是我尝试进行管道化。
在您代码的列表4.7,第29页,成功的朴素模型。跳过此处,查看我尝试进行管道化。
我的目标是构建一个管道,并比较管道化模型的准确率分数和交叉验证分数。
我应该在管道中放什么?
这是我想做的。为问题的核心,跳过此代码,
这是问题
问题是我还需要添加哪些步骤才能在您代码的列表4.7中获得管道化模型?
目标是获得
请指引我方向,我会最终解决的。
感谢您的时间和您的电子书。
悉尼的Anthony
抱歉,我没能清楚地看出问题所在,您必须调试错误。
尊敬的Jason博士,
感谢您的回复,
以下是我希望在管道中调用的函数
当我在管道中调用上述函数时,这里是错误信息
我是否遗漏了什么或应该做什么?
谢谢你,
悉尼的Anthony
抱歉,我还不清楚原因,您需要自己调试错误。
尊敬的Jason博士,
以下是关于设置管道的进一步信息
我是否应该在管道中添加更多步骤?
这是为了避免在调用上述错误中描述的错误
谢谢你,
悉尼的Anthony
对我来说,管道看起来很好。
也许确认您的数据已正确加载。
也许尝试使用不同的数据集,看看是否仍然会导致错误。
也许尝试使用不同的模型,看看是否仍然会导致错误。
尊敬的Jason博士,
谢谢,您更改数据集的建议奏效了!
我使用了 pima-indians-diabetes.csv 文件,它奏效了,而合成的 make_classification 模型出现了问题。
我在最后展示程序和结论。
程序
我将展示管道化和朴素模型的代码。
对于管道化模型
总结和结论
对于管道化模型
准确率
0.7755905511811023;#来自准确率分数
mean(scores), std(scores); # 来自交叉验证分数
(0.7704032809295966, 0.04222493685923635)
对于朴素模型
准确率;#来自准确率分数
0.7755905511811023
mean(scores), std(scores); # 来自交叉验证分数
(0.7717076782866258, 0.04318082938105522)
评论
* 准确率 – 使用管道化或朴素模型时,准确率似乎没有区别,均为 0.775591
* 交叉验证分数均值和标准差 – 管道化方法的平均分和标准差分数略低于朴素模型的平均分和标准差分数,分别为 0.770 和 0.042,而朴素模型为 0.772 和 0.043
* 为什么我的模型在 pima-indians 数据上有效,而在 make_classification 合成数据上无效。
总的来说,它是有效的,管道化和朴素模型之间的准确率没有变化。管道化模型的交叉验证平均分和标准差分数略低于朴素模型。但不知道为什么合成生成的 make_classification 值会导致问题。
谢谢你,
来自悉尼的安东尼
干得好!
尊敬的Jason博士,
再补充一点评论和一则问题
* 鉴于朴素模型和管道化模型的准确率相同,我们可以得出结论,朴素模型中的数据泄露可能非常少。
*请问:为什么我使用的 sklearn.datasets import make_classification 的合成数据集 make_classification 产生了错误,而 pima-indians-diabetes 数据集却有效?
谢谢你
悉尼的Anthony
是的,可能确实如此。
我不确定,也许检查一下你的实现中的错误或复制粘贴错误。
尊敬的Jason博士,
谢谢你。
对于第二点,我使用与 pima-indians-diabetes 相同的模型重新运行了使用 make_classification 生成的数据集/合成数据。
使用合成 make_classification 的管道化和朴素模型的测试结果为
管道方法 – 使用数据源合成数据 make_classification
准确率
0.8424242424242424
mean(scores), std(scores)
(0.8153333333333334, 0.04341530708043982)
朴素方法 – 使用数据源合成数据 make_classification
准确率
0.8393939393939394
mean(scores), std(scores)
0.8153333333333334, 0.04341530708043982
总结
使用 make_classification 合成数据,使用管道化方法
准确率比朴素方法高,而管道化和朴素模型中的平均分和标准差分数相同。
感谢您的建议,
悉尼的Anthony
干得好。
嗨,Jason,
感谢您这篇详细的帖子!我似乎在这里有点迷失,可能需要更多的澄清。
在“带正确数据准备的交叉验证评估”部分,在最后一个代码框的第20行,我的理解如下:
– 我们将“整个”数据集馈送给了 cross_val_score 函数。因此,cv 函数将“整个”数据分割为训练集和测试集以进行预处理和建模。
– 然后,管道分别应用于训练集和测试集,在训练集上开发逻辑回归,并在测试集上对其进行评估。
现在我的问题是,交叉验证集在哪里?考虑到我们已经将“整个”数据集馈送给 cross_val_score 函数,并且 cv 函数将数据拆分为两个集合,训练集和测试集,我无法想象交叉验证集是如何在内部创建并在过程中使用的。我是否遗漏了什么?
谢谢你,
Mahdi
谢谢。
cross_val_score 函数不拆分训练集和测试集,它执行交叉验证过程。
你可以在这里了解更多
https://scikit-learn.cn/stable/modules/generated/sklearn.model_selection.cross_val_score.html
感谢 Jason 及时回复。
那么,在您示例中的 cross_val_score 中,我们将整个数据集 X 和 y 以及一个 cv 函数馈送给模型。我对此组合的理解是,cv 函数会迭代地拆分数据,然后在每次迭代中应用管道。这意味着在每次迭代中创建两个子数据集。这两个子集是什么?测试集、训练集还是交叉验证集?
再次感谢!
Mahdi
数据集被拆分为 k 个不重叠的折,其中一个折被保留用于评估模型(测试),其余的用于训练模型并记录分数,然后重复此过程,以便每个折都有机会用作保留集,这意味着我们评估 k 个模型并获得 k 个分数。
这是 k 折交叉验证过程,而不是训练-测试过程——这是不同的。
在此处了解 k 折交叉验证的工作原理
https://machinelearning.org.cn/k-fold-cross-validation/
谢谢你,Jason!
我看到在某些情况下,会(使用 train_test_split)保留一个测试集,然后将训练集馈送给管道和 cross_val_score。这与您所做的(因为您在 cross_vale_score 中使用了整个数据集)有何不同?我们何时使用您的方法,何时使用另一种方法?
再次感谢您的回复!
Mahdi
也许保留集用于对选定的模型进行最终验证。如果数据允许,这是一个好方法。
那样的话,在 CV 中如何避免数据泄露,因为我们做了
1. 拆分训练/测试
2. 在训练集上 fit_transform + 在测试集上 transform
3. 使用已经转换过的训练集来馈送 CV => 数据泄露
如果您这样做,那么是的,会有数据泄露。
您将在 CV 中使用管道来正确地 fit/apply 转换。
嗨,Jason,
在管道在训练集上训练后,如何对保留的测试集进行 fit_transform?
因为 kfold=10 将产生 10 个最小、最大或标准差统计属性。
关于它有两个简单的假设。
1.管道的功能有自己的方法来 fit_tranforms 保留的测试集。那么,这种方法是否适用于 CV 的最佳分数的统计属性?
2.在获得 CV 的最佳超参数后,fit_transform 并重新训练所有训练数据,然后应用于保留的测试集。
你好 xxq…以下内容可能很有趣
https://machinelearning.org.cn/training-validation-test-split-and-cross-validation-done-right/
你好 Jason,
我同意一些预处理方法可能导致数据泄露。
但我不太明白为什么归一化步骤会引起这样的问题。如果你想避免协变量偏移,训练集和测试集应该具有相似的分布。如果是这样,像 min、max 或 std 这样的统计属性对于两个集合来说应该是相似的,并且在训练集或完整数据集上进行归一化不应该如此显著。
您能解释一下我遗漏了什么吗?
非常感谢!
Melec
如果任何数据准备系数(例如 min/max/mean/stdev/…)使用了样本外数据(例如测试集),那就是数据泄露。
感谢分享精彩文章!如果我们对某些受试者有多个行(例如,对某些受试者有一个以上的示例),您在书中是否有任何关于防止数据泄露的建议?
很高兴看到一个关于如何拆分数据的示例:1)在保持训练集和测试集中患病率的同时,2)防止同一受试者出现在训练集和测试集中。
非常好的观点!
是的,同一受试者的数据可能(必须!?)一起保留在同一数据集中,以避免数据泄露。
在执行 train_test_split 后,我如何使用管道实现多个预处理步骤,类似于您为 cv 所展示的方法?
您可以使用管道。
我定义了一个管道,其中包含 imputer、stdscaler 等,如下所示:
pl=Pipeline(….)
然后 fit 它
pl.fit(X_train,y_train)
由于我在任何预处理之前都执行了 train_test_split,所以我的 X_test 包含缺失值。使用 pl.score(X_test,y_test) 不会将管道预处理应用于 X_test 数据集,因为它会显示 NaN 错误。我可以用什么来对 X_test 应用相同的预处理?甚至 pl.predict 也会出错。
dt=DecisionTreeClassifier()
pl=Pipeline(steps=[(‘transform’,col_transform),(‘dt’,dt)])
这就是管道
如果管道包含 imputer,并且测试集与训练集相同,那么管道将处理缺失值。
也许可以使用 fit() 然后 predict(),并手动评估结果。
非常感谢 Jason。Tc
不客气。
你好,
我目前正在处理这个问题,并且我对数据泄露和缩放操作有一个概念性问题。
1. 拆分数据。
2. 使用训练数据拟合缩放器(而不是测试数据)。
3. 使用相同的缩放器转换两组数据。
为什么这是可以的?我们正在使用来自训练集的数据(步骤 2)来转换测试数据?
是的,我们正在使用已知历史数据来拟合模型(训练),以准备新的、之前未见过但未用于训练模型的数据(测试)。
你好,Jason。
感谢这篇文章。
在模型选择期间,对于给定的 X_test、X_validation 和 X_test,我们不能分别拟合和转换它们吗?
问这个问题是因为,当我测试保存的模型在新测试数据上时,我需要拟合转换新数据集。
我不确定我是否理解您的问题,或许您可以重新表述一下?
数据准备在训练集上进行拟合,然后应用于训练集、测试集、验证集,之后数据才提供给模型。
在训练期间,我们标准化训练数据(scaler.fit_transform())。然后根据训练数据,我们标准化验证和测试数据(scaler.transform())。
我的问题是,在模型选择之后,当我们有新的测试数据批次时,我们需要对其进行标准化。那时,我只对新的测试数据进行标准化,即 scaler.fit_transform() 并使用保存的模型进行预测。
这种处理新测试数据的方法是否正确?特别是当我们在一台 Web 应用程序中运行模型并且无法访问历史数据时。
谢谢!
是的,使用在训练数据上拟合的数据准备对象来缩放新数据。
抱歉 Jason。不太明白。在 Web 应用程序的上下文中,我们只有保存的模型,对吗?我该如何获取在训练数据上拟合的数据准备对象?我是否遗漏了什么?那么,只缩放测试数据并将其用于预测是否正确?
您可以保存整个管道(数据准备和模型),也可以分别保存数据准备对象和模型。由您决定。
哦,好的。明白了。谢谢 Jason。
嗨 Jason
一如既往的精彩解释。
您能否指出您的建议是否也适用于分类数据?也就是说,我们是否可以在拆分训练测试之前对分类数据进行编码?
谢谢。
是的。编码应该只在训练数据上进行。您可能希望在编码中使用“领域知识”(例如,您在实践中可能看到的所有可能值),这可能是可以的。
嗨,Jason,
很棒的文章,我有一个关于 FE 过程的问题。我确实理解,为了执行 min/max scaler 或归一化等转换,我们应该先在训练数据上进行 fit_transform,然后在测试数据上进行 transform。
在应用建模之前,我们总是查看特征:尝试找到相关性,填充数据中的缺失值,并设计新特征。
1) 每当我想要处理缺失值时,我该如何处理填补缺失值的问题?据我所知,我不能简单地查看组合的训练和测试集,然后填补缺失值,因为这会泄露关于测试集的数据知识。对此有什么解决方案?
2) 假设我处理连续数值特征,例如年龄。如果我想对该特征进行分箱,那么我会分别在训练集和测试集上进行。但这会造成一个大问题,即由于数据不同,训练集和测试集上的分箱会不同,因此特征也会不同。这将不允许我稍后将数据拟合到模型。
我该如何解决这个问题?
基于整个数据集对数值特征进行分箱可以解决这个问题,但可能会引入数据泄露。
3) log() 转换是否也需要分别应用于训练集和测试集?这只是一个数学运算,似乎不使用任何特定的数据参数(如果我错了,请纠正我)。
您的反馈将非常有价值,
谢谢
您像其他任何数据准备方法一样,在训练数据上拟合插补方法,并将其应用于测试数据。
分箱将在训练数据上进行拟合,并以相同的方式应用于训练和测试数据。
Log 可以随时应用于任何数据,它没有学习到的系数。
那么,当您在这里说在训练集上拟合,然后在训练集和测试集上应用时,您指的是在管道中应用预处理吗?
您有如何在管道中使用分箱的示例吗?
特征工程怎么样?可以在组合数据上完成吗?
不,我们说的是管道能为您做什么。
如果您使用管道,那么您只需调用 fit 然后 predict,它就能为您完成所有操作。
特征工程也是一样的,在训练集上拟合然后应用于训练和测试集,或者您可以让管道为您完成。
我可以看到,在使用管道时,可以轻松避免数据泄露问题。
如果我不想使用管道来设计一个新特征怎么办?而是使用 pandas 来操作数据。
在这种情况下,新特征的创建必须分别在训练集和测试集上完成吗?
在这种情况下,所有数据准备都必须在训练集上进行拟合,并在训练集和测试集上应用。
我可以使用训练集的最小值和最大值来标准化测试集吗?
如果答案是肯定的,这可能会导致负值或大于 1 的值。这有问题吗?
谢谢。
是的,这是标准方法。如果它不适合您的数据,您可能需要根据领域知识来定义最小值和最大值。
你好,
我正在处理一个已经被拆分给我进行训练和测试的数据集。我正在使用管道,但在数据可视化步骤中,我发现测试集中的一个特征有一个缺失值,而在训练集中,该特征的所有值都存在。
1) 我可以根据测试集的平均值来填充缺失值吗?或者我必须将其留空?
2) 如果我想基于现有特征创建新特征,我可以使用 pandas 框架在训练集上进行,然后分别在测试集上进行吗?
非常感谢
是的,尝试插补缺失值,尝试不同的方法并比较性能。
是的,您可以使用 pandas 或 numpy 直接操作,无论您觉得哪种方式容易。
嗨,Jason,
我有一个问题。同时使用 train/test split 和交叉验证策略是否有必要或有意义?我在这里看到您的交叉验证示例,您在 cross_val_score() 中使用了 X 和 y。我很困惑。我认为最好先进行 train/test split,然后只在训练集上使用交叉验证。
谢谢!
交叉验证是在幕后使用 train/test split,使用您提供给函数的任何数据。但是,如果您有一个大型数据集,您的方法也不会造成损害。
你好,
如何在交叉验证中对 K 这样的参数进行超参数调整,并对数据进行归一化?
您有另一篇文章吗?
您认为这回答了您的问题吗:https://machinelearning.org.cn/training-validation-test-split-and-cross-validation-done-right/
Jason,感谢您的解释,但如果我们在此过程中减少训练数据中的特征,是否也可以减少测试数据中的特征,以便它们对应?
是的,如果您在训练数据中减少了特征,您也会以相同的方式减少测试数据中的特征。
我们可以说交叉验证步骤本身会导致数据泄露吗?因为我们最终使用平均分数,该分数是从不同行计算的,其中一个样本在不同行中可以同时分配到训练部分和测试部分。
是的,所以我们必须小心交叉验证的步骤。请参阅 https://machinelearning.org.cn/training-validation-test-split-and-cross-validation-done-right/
Q1) 管道是否负责仅在训练集上拟合预处理转换并存储参数,并在每个折叠中应用相同的预处理到保留集(而不对其进行再次拟合)?
Q2) 同样的做法或解释也可以扩展到过采样技术,如 SMOTE,对吗?
提前感谢!
@jason- 您对标签/独热编码和数据泄露有什么看法,以及如何将其最小化?
嗨 Rohit……最有效的技术之一是 k 折交叉验证
https://machinelearning.org.cn/k-fold-cross-validation/
嗨,Jason,
我有一个非常基本的问题。
我有一个原始数据集。我想应用缺失值插补、归一化和特征选择。我现在做的是,我将原始数据拆分为训练集和测试集。然后,因为我想执行 k 折交叉验证,我使用 rskf = StratifiedKFold(n_splits=n_splits, shuffle = True, random_state = 42) 将训练数据拆分为训练集和验证集。
现在,我是否需要在每个折叠中为训练集和验证集执行插补、归一化和特征选择?
一旦我从交叉验证中获得最佳模型,我还会获得最佳模型、最佳参数和最佳特征集。
然后我可以使用具有最佳特征集和相同预处理步骤的最佳模型在测试数据上评估模型的性能吗?
嗨 Ankush……以下资源可能很有趣
https://machinelearning.org.cn/training-validation-test-split-and-cross-validation-done-right/