对训练数据集执行的任何数据准备也必须在未来的新数据集上执行,这一点至关重要。
这可能包括在评估模型时使用的测试数据集,或者在使用模型进行预测时来自领域的新数据。
通常,在训练数据集上拟合的模型会保存起来供以后使用。将来为模型准备新数据的正确解决方案是,将任何数据准备对象(例如数据缩放方法)与模型一起保存到文件中。
在本教程中,您将学习如何将模型和数据准备对象保存到文件中以供以后使用。
完成本教程后,您将了解:
- 为机器学习模型正确准备测试数据和新数据的挑战。
- 将模型和数据准备对象保存到文件以供以后使用的解决方案。
- 如何保存、稍后加载和使用机器学习模型和数据准备模型处理新数据。
通过我的新书《机器学习数据准备》**启动您的项目**,其中包括**分步教程**和所有示例的**Python源代码**文件。
让我们开始吧。
- **2020年1月更新**:已针对 scikit-learn v0.22 API 的变更进行更新。
- **2020年5月更新**:改进了代码示例和打印输出。

如何在 Scikit-Learn 中保存和加载模型和数据准备以供以后使用
图片由Dennis Jarvis提供,保留部分权利。
教程概述
本教程分为三个部分;它们是:
- 为模型准备新数据的挑战
- 保存数据准备对象
- 如何保存和稍后使用数据准备对象
为模型准备新数据的挑战
数据集中每个输入变量的单位可能不同。
例如,一个变量可能以英寸为单位,另一个以英里为单位,另一个以天为单位,等等。
因此,在拟合模型之前缩放数据通常很重要。
这对于使用输入的加权和或距离度量(如逻辑回归、神经网络和k-近邻)的模型尤其重要。这是因为具有较大值或范围的变量可能会主导或冲淡具有较小值或范围的变量的影响。
缩放技术,例如归一化或标准化,可以使每个输入变量的分布相同,例如在归一化情况下具有相同的最小值和最大值,或者在标准化情况下具有相同的均值和标准差。
缩放技术必须经过拟合,这意味着它需要从数据中计算系数,例如观察到的最小值和最大值,或者观察到的均值和标准差。这些值也可以由领域专家设置。
使用缩放技术评估模型的最佳实践是:在训练数据集上拟合它们,然后将其应用于训练和测试数据集。
或者,在使用最终模型时,在训练数据集上拟合缩放方法,并将转换应用于训练数据集以及将来任何新数据集。
对训练数据集应用的任何数据准备或转换也必须在将来应用于测试或其他数据集,这一点至关重要。
当所有数据和模型都在内存中时,这很简单。
当模型被保存并在以后使用时,这具有挑战性。
当保存拟合模型以供以后使用(例如最终模型)时,缩放数据的最佳实践是什么?
想开始学习数据准备吗?
立即参加我为期7天的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
保存数据准备对象
解决方案是将数据准备对象与模型一起保存到文件中。
例如,通常使用 pickle 框架(Python 内置)来保存机器学习模型以供以后使用,例如保存最终模型。
此框架也可用于保存用于数据准备的对象。
稍后,模型和数据准备对象可以被加载和使用。
将整个对象(例如模型对象和数据准备对象)保存到文件中很方便。然而,专家可能更喜欢只将模型参数保存到文件中,然后稍后加载它们并将其设置到新的模型对象中。这种方法也可以用于缩放数据所用的系数,例如每个变量的最小值和最大值,或每个变量的均值和标准差。
选择哪种方法适合您的项目由您决定,但我建议直接将模型和数据准备对象(或多个对象)保存到文件中以供以后使用。
为了使保存对象和数据转换对象到文件的想法具体化,我们来看一个示例。
如何保存和稍后使用数据准备对象
在本节中,我们将演示如何准备数据集、在数据集上拟合模型、将模型和数据转换对象保存到文件,以及稍后加载模型和转换并在新数据上使用它们。
1. 定义数据集
首先,我们需要一个数据集。
我们将使用 scikit-learn 数据集中的一个测试数据集,具体是一个通过 make_blobs() 函数随机创建的具有两个输入变量的二元分类问题。
下面的示例创建了一个包含 100 个样本、两个输入特征和两个类别标签(0 和 1)的测试数据集。然后将数据集拆分为训练集和测试集,并报告每个变量的最小值和最大值。
重要的是,在创建数据集和拆分数据时都设置了 _random_state_,这样每次运行代码时都会创建相同的数据集并执行相同的数据拆分。
1 2 3 4 5 6 7 8 9 10 11 12 |
# 创建测试数据集并将其拆分为训练集和测试集的示例 from sklearn.datasets import make_blobs from sklearn.model_selection import train_test_split # 准备数据集 X, y = make_blobs(n_samples=100, centers=2, n_features=2, random_state=1) # 将数据拆分为训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1) # 总结每个输入变量的规模 for i in range(X_test.shape[1]): print('>%d, train: min=%.3f, max=%.3f, test: min=%.3f, max=%.3f' % (i, X_train[:, i].min(), X_train[:, i].max(), X_test[:, i].min(), X_test[:, i].max())) |
运行示例报告了训练集和测试集中每个变量的最小值和最大值。
我们可以看到,每个变量的规模都不同,而且训练集和测试集之间的规模也不同。这是一种我们可能在真实数据集中遇到的现实情况。
1 2 |
>0, train: min=-11.856, max=0.526, test: min=-11.270, max=0.085 >1, train: min=-6.388, max=6.507, test: min=-5.581, max=5.926 |
2. 缩放数据集
接下来,我们可以缩放数据集。
我们将使用 MinMaxScaler 将每个输入变量缩放到 [0, 1] 范围。此缩放器的最佳实践用法是在训练数据集上拟合它,然后将转换应用于训练数据集和所有其他数据集:在本例中是测试数据集。
下面列出了缩放数据和总结效果的完整示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# 缩放数据集的示例 from sklearn.datasets import make_blobs from sklearn.model_selection import train_test_split from sklearn.preprocessing import MinMaxScaler # 准备数据集 X, y = make_blobs(n_samples=100, centers=2, n_features=2, random_state=1) # 将数据拆分为训练集和测试集 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_scaled = scaler.transform(X_train) X_test_scaled = scaler.transform(X_test) # 总结每个输入变量的规模 for i in range(X_test.shape[1]): print('>%d, train: min=%.3f, max=%.3f, test: min=%.3f, max=%.3f' % (i, X_train_scaled[:, i].min(), X_train_scaled[:, i].max(), X_test_scaled[:, i].min(), X_test_scaled[:, i].max())) |
运行示例会打印缩放数据的影响,显示训练集和测试集中每个变量的最小值和最大值。
我们可以看到,两个数据集中的所有变量现在的值都在所需的 0 到 1 范围内。
1 2 |
>0, train: min=0.000, max=1.000, test: min=0.047, max=0.964 >1, train: min=0.000, max=1.000, test: min=0.063, max=0.955 |
3. 保存模型和数据缩放器
接下来,我们可以在训练数据集上拟合模型,并将模型和缩放器对象都保存到文件中。
我们将使用 LogisticRegression 模型,因为该问题是一个简单的二元分类任务。
训练数据集像以前一样进行缩放,在这种情况下,我们假设测试数据集当前不可用。缩放后,数据集用于拟合逻辑回归模型。
我们将使用 pickle 框架将 LogisticRegression 模型保存到一个文件,将 MinMaxScaler 保存到另一个文件。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# 在缩放数据集上拟合模型的示例 from sklearn.datasets import make_blobs from sklearn.model_selection import train_test_split 从 sklearn.预处理 导入 MinMaxScaler from sklearn.linear_model import LogisticRegression from pickle import dump # 准备数据集 X, y = make_blobs(n_samples=100, centers=2, n_features=2, random_state=1) # 将数据拆分为训练集和测试集 X_train, _, y_train, _ = train_test_split(X, y, test_size=0.33, random_state=1) # 定义缩放器 scaler = MinMaxScaler() # 在训练数据集上拟合缩放器 scaler.fit(X_train) # 转换训练数据集 X_train_scaled = scaler.transform(X_train) # 定义模型 model = LogisticRegression(solver='lbfgs') model.fit(X_train_scaled, y_train) # 保存模型 dump(model, open('model.pkl', 'wb')) # 保存缩放器 dump(scaler, open('scaler.pkl', 'wb')) |
运行示例会缩放数据、拟合模型,并使用 pickle 将模型和缩放器保存到文件中。
您应该在当前工作目录中看到两个文件:
- model.pkl
- scaler.pkl
4. 加载模型和数据缩放器
最后,我们可以加载模型和缩放器对象并使用它们。
在这种情况下,我们将假设训练数据集不可用,并且只有新数据或测试数据集可用。
我们将加载模型和缩放器,然后使用缩放器准备新数据,并使用模型进行预测。由于是测试数据集,我们有预期的目标值,因此我们将预测与预期的目标值进行比较并计算模型的准确性。
完整的示例如下所示。
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 |
# 加载模型和缩放器并在新数据上进行预测 from sklearn.datasets import make_blobs from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score from pickle import load # 准备数据集 X, y = make_blobs(n_samples=100, centers=2, n_features=2, random_state=1) # 将数据拆分为训练集和测试集 _, X_test, _, y_test = train_test_split(X, y, test_size=0.33, random_state=1) # 加载模型 model = load(open('model.pkl', 'rb')) # 加载缩放器 scaler = load(open('scaler.pkl', 'rb')) # 检查缩放前测试集的范围 print('原始测试集范围') for i in range(X_test.shape[1]): print('>%d, min=%.3f, max=%.3f' % (i, X_test[:, i].min(), X_test[:, i].max())) # 转换测试数据集 X_test_scaled = scaler.transform(X_test) print('缩放后测试集范围') for i in range(X_test_scaled.shape[1]): print('>%d, min=%.3f, max=%.3f' % (i, X_test_scaled[:, i].min(), X_test_scaled[:, i].max())) # 对测试集进行预测 yhat = model.predict(X_test_scaled) # 评估准确性 acc = accuracy_score(y_test, yhat) print('测试准确度:', acc) |
运行此示例将加载模型和缩放器,然后使用缩放器正确地为模型准备测试数据集,从而满足模型训练时的期望。
为了确认缩放器产生了预期的效果,我们报告了在应用缩放之前和之后每个输入特征的最小值和最大值。然后,模型对测试集中的示例进行预测,并计算分类准确度。
在这种情况下,正如预期的那样,数据集正确地归一化,模型在测试集上实现了 100% 的准确率,因为测试问题是微不足道的。
1 2 3 4 5 6 7 8 9 |
原始测试集范围 >0, min=-11.270, max=0.085 >1, min=-5.581, max=5.926 缩放后测试集范围 >0, min=0.047, max=0.964 >1, min=0.063, max=0.955 测试准确度:1.0 |
这提供了一个模板,您可以使用它将模型和缩放器对象(或多个对象)保存到文件中,以便您自己的项目使用。
进一步阅读
如果您想深入了解,本节提供了更多关于该主题的资源。
文章
API
- sklearn.datasets.make_blobs API.
- sklearn.model_selection.train_test_split API.
- sklearn.preprocessing.MinMaxScaler API.
- sklearn.metrics.accuracy_score API.
- sklearn.linear_model.LogisticRegression API.
- pickle API.
总结
在本教程中,您学习了如何将模型和数据准备对象保存到文件中以供以后使用。
具体来说,你学到了:
- 为机器学习模型正确准备测试数据和新数据的挑战。
- 将模型和数据准备对象保存到文件以供以后使用的解决方案。
- 如何保存、稍后加载和使用机器学习模型和数据准备模型处理新数据。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
感谢您提供关于数据准备和数据集解释的宝贵信息。
不客气。
嗨,Jason,
将缩放器和模型包装在 sklearn 管道对象中并对管道进行 pickle 操作不是更合适吗?
祝好,
Elie
是的,同意!
在这种情况下,我试图传达保存缩放器对象的想法。管道将是一个更简单的实现。
大家好。
这是一个有趣的帖子,但是……这些过程如何投入生产呢?
加载(打开…加载?转储(模型,打开…?)到“本地”文件系统?这还不够。您同意吗?
许多组织在最后一步(投入生产)失败,成功需要应用软件工程技术。
仅仅对一堆对象进行 pickle 并没有帮助。
此致。
好观点。
是的,保存/加载作为第一步很好。更好的方法可能是将用于缩放的系数存储在配置中,然后将其加载到应用程序中,并根据需要将其应用于新数据,可以使用自定义数据准备代码或将系数插入 sklearn 对象。
这有帮助吗?
感谢 Jason 的精彩帖子!
您能给一个使用配置方法的小例子或者指出一个有用的链接吗?
提前感谢你
是的,从上面的教程开始。
嗨 Jason
您提到“更好的方法可能是将用于缩放的系数存储在配置中并将其加载到应用程序中。”
我遇到了 sklearn.impute 导入 KNNImputer 的这个问题。我用完整的数据集拟合并保存了 imputer。在生产时我将其加载回来。但是当我得到一个新样本时,插补值不正确。您提到了保存系数。我不知道如何从 imputer 保存系数。我是不是漏掉了什么?
谢谢。
也许您可以尝试使用不同的插补器,它要么需要更少的开销,要么在新数据上表现更好。
谢谢,这是一个很棒的帖子,非常感谢,帮了我很多。信息量很大。
不客气。
嗨,Jason,
我建立了一个聚类模型来分割客户账户。它有以下步骤:
– Min Max 缩放器
– PCA 用于降维,然后
– K 均值模型以获得约 5 个聚类
我使用 pickle 保存了整个管道,现在我必须使用最新月份的数据刷新模型。当我刷新模型时,与上个月相比,聚类发生了剧烈变化。我知道它们是随机的,但没想到账户会移动约 40%。
这是因为算法的选择,我是说 K 均值吗?考虑到我每个月都必须刷新模型,最好的模型刷新策略是什么?
我敢肯定,如果变动这么大,企业主不会高兴。
好问题!
你可能需要用所有旧数据和新数据重新拟合模型。我不认为 sklearn 模型支持增量更新。
非常非常感谢!正是我需要的
不客气。
在“4. 加载模型和数据缩放器”部分,代码第26行:
acc = accuracy_score(y_test, yhat)
,其中y_test
是未缩放数据,而yhat
是缩放数据。我期望在同一个空间中比较这两个向量,要么都缩放,要么都未缩放。在这个例子中,这没有发生,这让我很困惑。
现在更清楚了吗,Jason?
PS:抱歉现在才回复,但我没有收到任何关于您回复的电子邮件通知。
你好,非常感谢这个很棒的教程。你节省了我很多时间。我一直担心如何将缩放后的输入提供给模型(从我之前训练的模型的.h5文件加载),以获得相同的缩放后的输入数据。
非常感谢。
加载数据,缩放它,加载模型,将数据传递给加载的模型。
嗨 Jason,
这很有帮助。感谢这篇文章。
你能不能也解释一下,假设我有一些预处理步骤,我使用自定义逻辑来填充缺失值。例如,如果年龄缺失,我找到每个组的年龄众数并相应地分配给任何缺失年龄的记录。因此,如果用户在表单中输入所有详细信息,除了年龄,那么预测时我是否会调用自定义预处理?
谢谢你。
是的,你需要保存自定义数据准备所使用的任何统计数据。
感谢您的文章,非常有用。我有一个问题。我有一个未标记的数据集,我已经对其进行了聚类分析,以便将聚类的标签用作监督分类任务中的目标变量。因此,我训练了我的模型并使用pickle保存了。如果我想用新数据使用我的模型,我需要有监督数据吗?换句话说,pickle对象不保留标签信息吗?
提前感谢
监督学习模型可用于对新数据(仅输入)进行预测。
模型这种用法是监督学习的目标——预测新数据的标签。
谢谢,这很有帮助!
如果新(或测试)数据集包含的特征的最小值/最大值低于/高于训练数据集中的值,则可能会出现问题。我假设缩放器仍然可以处理它,但转换后的高值会高于1吗?对于在训练集中未表示的分类特征,情况可能会更糟。您知道这种情况的解决方案吗?
提前感谢你
你可能需要先手动将新值裁剪到已知范围。
对于分类,你可以设置一个参数来忽略训练期间未见过的新标签,例如映射到全零。
太好了,谢谢你的建议!我不知道还有参数可以忽略新标签。
是的,设置:
你好,感谢你的博客。它是我机器学习问题和答案的首选。我有一个关于在你的代码的这些步骤中为模型分配拟合的问题
# 定义模型
model = LogisticRegression(solver=’lbfgs’)
model.fit(X_train_scaled, y_train)
例如,在最后一行,model.fit(X_train_scaled, y_train),我期望看到类似 fitted = model.fit(X_train_scaled, y_train) 的东西,然后你会用 pickle 保存“fitted”。你如何在不将其分配给新属性的情况下保存训练好的模型?或者你真的只是用 pickle 保存了前一行代码中未训练的模型:model = LogisticRegression(solver=’lbfgs’)?
不客气。
我们通过调用 fit() 来拟合模型,然后保存它。记住 model 是一个对象,它包含模型所需的系数。
嗨,Jason,
感谢这篇精彩的文章。
我有一个关于目标变量缩放的问题
1) 目标变量的缩放方法是否需要与输入特征相同?
2) 我需要保存两个缩放对象吗?一个用于特征,一个用于目标变量?
3) 如果我将目标变量的缩放对象从训练阶段传递到预测阶段,这是否会导致数据从训练阶段泄漏到生产阶段?
不客气。
不,你可以在不同的变量上使用不同的缩放。
是的,每个对象都需要保存。
不,数据泄露是指在模型训练过程中使用测试集中的信息。
非常感谢,你总是帮我很多,上帝保佑你
不客气!
请问,我有一个问题。
如果我有30个特征,想把其中6个特征减少成一个特征(30个变成25个),如果我对训练数据使用PCA并保存模型,我以后可以在另一个测试数据上使用它吗?
当然可以。
那么拟合缩放器之后呢?
model.scaler = scaler
然后用pickle保存模型
加载时只需使用 model.scaler 对测试文件进行缩放
您可以将数据预处理 + 模型保存到管道中(保存管道对象)。这是我的建议。
你好。为什么我们假设测试或训练数据集不可用?这种情况什么时候会发生?
如果你保存了模型,为什么还需要再次访问训练数据集?
你好,Anil……一旦模型经过训练和测试,就可以进行验证了。验证数据是网络从未见过的数据,实际上可能代表模型在实际使用或“真实世界”中会遇到的情况。因此,当在真实世界中使用时,模型将不再进行训练和测试,除非它进行根本性的重新设计或重大修改。
-此致,
你好,感谢你的帖子。我有一个问题:我已经预处理了数据(将其重塑为向量用于KNN分类),并且没有对其进行缩放。如何保存不缩放的数据?在你的帖子中,你缩放了数据并保存了缩放器。但我没有缩放我的数据,这是不必要的。
谢谢。
你好,Daniel……你可以简单地省略缩放数据的步骤。
我有一个深度学习lstm模型,我有两个问题
1. 我需要以.h5格式保存缩放器吗?
2. 如何以.h5格式保存缩放器?
你好,Ali……以下资源可能对你有用
https://naadispeaks.wordpress.com/2021/06/30/using-hierarchical-data-format-hdf5-in-machine-learning/
感谢这篇好文章。问题:第4节,你获取了
y_test
(第9行),然后将其与yhat
在acc = accuracy_score(y_test, yhat)
中进行比较,其中yhat
是对缩放数据执行的预测。那么这是正确的吗?我的意思是,yhat
是否会属于缩放空间,而y_test
属于原始(未缩放)空间?例如,根据我的直觉,我有一个数据集在[-100, 100]之间。我将其缩放到[0, 1]。有新数据进来,准备进行预测,例如99。真实值是100。
我对其进行缩放,假设它变成0.98。
我将其传递给模型,我将假设它会预测接近[0, 1]的值,可能略大于1,例如1.05。
现在我认为我应该将1.05反向缩放(某种程度上使用 inverse_transform),使其映射回原始空间,例如变成105。
所以我会说我的预测值是105,真实值是100。你能帮我解决这个问题吗?
你好,gsamaras……请重新措辞或澄清你的问题,以便我们更好地帮助你。
在“4. 加载模型和数据缩放器”部分,代码第26行:acc = accuracy_score(y_test, yhat),其中y_test是未缩放数据,而yhat是缩放数据。
我期望在同一个空间中比较这两个向量,要么都缩放,要么都未缩放。在这个例子中,这没有发生,这让我很困惑。
现在更清楚了吗,Jason?
PS:抱歉现在才回复,但我没有收到任何关于您回复的电子邮件通知。
gsamaras,有点晚了,但你是对的——需要yhat_scaler才能进行逆变换,以回到原始空间。