翻译小组 关注
手机版

深度学习小技巧(二):如何保存和恢复scikit-learn训练的模型

  1. 云栖社区>
  2. 翻译小组>
  3. 博客>
  4. 正文

深度学习小技巧(二):如何保存和恢复scikit-learn训练的模型

【方向】 2017-10-30 16:03:59 浏览1525 评论0

摘要: 深度学习小技巧掌握:作者通过一个简单的例子详细介绍了如何将训练过程中的深度学习模型保存,然后如何加载。有了这个小技巧,再也不用担心在训练模型中出错了。

更多深度文章,请关注云计算频道:https://yq.aliyun.com/cloud


深度学习小技巧(一):如何保存和恢复TensorFlow训练的模型

在许多情况下,在使用scikit学习库的同时,你需要将预测模型保存到文件中,然后在使用它们的时候还原它们,以便重复使用以前的工作。比如在新数据上测试模型,比较多个模型的优劣。这种保存过程也称为对象序列化——表示具有字节流的对象,以便将其存储在磁盘上,它可以通过网络发送或保存到数据库,而其恢复的过程被称为反序列化。在本文中,我们将在Python和scikit学习中看到三种可能的方法,而且每种都有其优点和缺点。

1.保存和恢复模型的工具

我们第一个介绍的工具是Pickle,用于对象(de)序列化的标准Python工具。之后,我们会介绍Joblib库,它提供了容易(de)序列化方法,其中包含了大数据数组的对象,最后我们会介绍一种手动方法来保存和恢复JSON对象(JavaScript Object Notation)。这些方法都不能代表最佳解决方案,但是可以根据项目的需要选择合适的方案。

2.模型初始化

首先,我们要创建一个scikit学习模型。在我们的例子中,我们将使用Logistic回归模型和Iris数据集。我们导入所需的库,并且加载数据,并将其拆分为训练集和测试集。


from sklearn.linear_model import LogisticRegression  
from sklearn.datasets import load_iris  
from sklearn.model_selection import train_test_split

# Load and split data
data = load_iris()  
Xtrain, Xtest, Ytrain, Ytest = train_test_split(data.data, data.target, test_size=0.3, random_state=4)  

现在让我们用一些非默认参数来创建模型,并用训练数据来“喂养”它。我们假设你先前已经找到了模型的最优参数,即产生最高估计精度的参数。

# Create a model
model = LogisticRegression(C=0.1,  
                           max_iter=20, 
                           fit_intercept=True, 
                           n_jobs=3, 
                           solver='liblinear')
model.fit(Xtrain, Ytrain)  

这是我们产生的模型:

LogisticRegression(C=0.1, class_weight=None, dual=False, fit_intercept=True,  
    intercept_scaling=1, max_iter=20, multi_class='ovr', n_jobs=3,
    penalty='l2', random_state=None, solver='liblinear', tol=0.0001,
    verbose=0, warm_start=False)

使用该fit方法,模型已经学习了存储在其中的系数model.coef_。目标是将模型的参数和系数保存到文件中,因此你不需要再次对新数据重复模型训练和参数优化的步骤。

3.Pickle模块

在以下几行代码中,我们将上一步中创建的模型保存到文件中,然后作为一个新对象加载pickled_model。然后使用加载的模型计算准确度分数,并对新的未见(测试)数据进行预测结果。


import pickle
#
# Create your model here (same as above)
#
# Save to file in the current working directory
pkl_filename = "pickle_model.pkl"  
with open(pkl_filename, 'wb') as file:  
    pickle.dump(model, file)
# Load from file
with open(pkl_filename, 'rb') as file:  
    pickle_model = pickle.load(file)
# Calculate the accuracy score and predict target values
score = pickle_model.score(Xtest, Ytest)  
print("Test score: {0:.2f} %".format(100 * score))  
Ypredict = pickle_model.predict(Xtest)  

运行此代码应该会产生你的预测分数,并通过Pickle保存模型:

$ python save_model_pickle.py
Test score: 91.11 %  

使用Pickle来保存和恢复学习模型的好处在于它很快,并且你可以用两行代码完成。如果你已经对训练数据上的模型参数进行了优化,那么这是非常有用的,因此你不需要重复此步骤。不管如何,它都不保存测试结果和任何数据。但仍然可以保存多个对象的元组或列表(并记住哪个对象在哪里),如下所示:

tuple_objects = (model, Xtrain, Ytrain, score)
# Save tuple
pickle.dump(tuple_objects, open("tuple_model.pkl", 'wb'))
# Restore tuple
pickled_model, pickled_Xtrain, pickled_Ytrain, pickled_score = pickle.load(open("tuple_model.pkl", 'rb'))  

3.Joblib模块

Joblib库它的目的是替代Pickle,用于包含大数据的对象。我们将重复与Pickle一样的保存和恢复过程。


from sklearn.externals import joblib
# Save to file in the current working directory
joblib_file = "joblib_model.pkl"  
joblib.dump(model, joblib_file)
# Load from file
joblib_model = joblib.load(joblib_file)
# Calculate the accuracy and predictions
score = joblib_model.score(Xtest, Ytest)  
print("Test score: {0:.2f} %".format(100 * score))  
Ypredict = pickle_model.predict(Xtest)  

$ python save_model_joblib.py
Test score: 91.11 %  

从示例中可以看出,与Pickle相比,Joblib库提供了一个简单的工作流程。虽然Pickle要求将文件对象作为参数传递,但是Joblib可与文件对象和字符串文件名一起使用。如果你的模型包含大量数据,则每个数组将存储在单独的文件中,但整体的保存和恢复过程将保持不变。Joblib还允许使用不同的压缩方法,如“zlib”,“gzip”,“bz2”和不同的压缩级别。

4.手动保存并还原到JSON

根据你的项目,很多时候你会发现Pickle和Joblib都不是合适的解决方案。其中一些原因将在兼容性问题部分中稍后讨论。无论何时要想完全控制保存和恢复过程,最好的方法是手动构建自己的功能。

以下显示了使用JSON手动保存和恢复对象的示例。这种方法允许我们选择需要保存的数据,例如模型参数,系数,训练数据以及我们需要的任何其他数据。

由于我们想将所有这些数据保存在一个对象中,所以一个可能的方法是创建一个继承我们的示例中的模型类的新类LogisticRegression。这个新类被MyLogReg调用,然后分别实现save_jsonload_json方法以保存和恢复JSON文件。

为简单起见,我们将只保存三个模型参数和训练数据。我们可以用这种方法存储一些额外的数据,例如训练集上的交叉验证分数,测试数据,测试数据的准确度等等。


import json  
import numpy as np
class MyLogReg(LogisticRegression):
    # Override the class constructor
    def __init__(self, C=1.0, solver='liblinear', max_iter=100, X_train=None, Y_train=None):
        LogisticRegression.__init__(self, C=C, solver=solver, max_iter=max_iter)
        self.X_train = X_train
        self.Y_train = Y_train
    # A method for saving object data to JSON file
    def save_json(self, filepath):
        dict_ = {}
        dict_['C'] = self.C
        dict_['max_iter'] = self.max_iter
        dict_['solver'] = self.solver
        dict_['X_train'] = self.X_train.tolist() if self.X_train is not None else 'None'
        dict_['Y_train'] = self.Y_train.tolist() if self.Y_train is not None else 'None'
        # Creat json and save to file
        json_txt = json.dumps(dict_, indent=4)
        with open(filepath, 'w') as file:
            file.write(json_txt)
    # A method for loading data from JSON file
    def load_json(self, filepath):
        with open(filepath, 'r') as file:
            dict_ = json.load(file)
        self.C = dict_['C']
        self.max_iter = dict_['max_iter']
        self.solver = dict_['solver']
        self.X_train = np.asarray(dict_['X_train']) if dict_['X_train'] != 'None' else None
        self.Y_train = np.asarray(dict_['Y_train']) if dict_['Y_train'] != 'None' else None

现在我们来试一试MyLogReg。首先我们创建一个对象mylogreg,将训练数据传递给它,并将其保存到文件中。然后我们创建一个新对象json_mylogreg并调用该load_json方法从文件加载数据。


filepath = "mylogreg.json"
# Create a model and train it
mylogreg = MyLogReg(X_train=Xtrain, Y_train=Ytrain)  
mylogreg.save_json(filepath)
# Create a new object and load its data from JSON file
json_mylogreg = MyLogReg()  
json_mylogreg.load_json(filepath)  
json_mylogreg  

打印出新的对象,我们可以根据需要来查看我们的参数和训练数据。

MyLogReg(C=1.0,  
     X_train=array([[ 4.3,  3. ,  1.1,  0.1],
       [ 5.7,  4.4,  1.5,  0.4],
       ...,
       [ 7.2,  3. ,  5.8,  1.6],
       [ 7.7,  2.8,  6.7,  2. ]]),
     Y_train=array([0, 0, ..., 2, 2]), class_weight=None, dual=False,
     fit_intercept=True, intercept_scaling=1, max_iter=100,
     multi_class='ovr', n_jobs=1, penalty='l2', random_state=None,
     solver='liblinear', tol=0.0001, verbose=0, warm_start=False)

由于使用JSON的数据序列化实际上是将对象保存为字符串格式,而不是字节流,所以'mylogreg.json'文件可以使用文本编辑器打开和修改。虽然这种方法对开发人员来说很方便,但是由于入侵者可以查看和修改JSON文件的内容,因此安全性较低。此外,这种方法更适合于具有少量实例变量的对象,例如scikit-learn模型,因为任何添加新变量都需要在保存和恢复方法中进行更改。

5.兼容性问题

尽管到目前为止,每个工具的优点和缺点已被介绍,但Pickle和Joblib工具的最大缺点可能是其与不同型号的Python版本的兼容性。

5.1:Python版本的兼容性——两种工具的文档都指出,不建议(de)在不同的Python版本之间对对象进行序列化,尽管它可能在低级的版本更改中起作用。

5.2:模型兼容性——最常见的错误之一是使用Pickle或Joblib保存模型,然后在尝试从文件还原之前更改模型。模型的内部结构需要在保存和重新加载之间保持不变。

Pickle和Joblib的最后一个问题与安全性有关。这两种工具都可能包含恶意代码,因此不建议从不受信任或未经身份验证的源代码。

6.结论

在这篇文章中,我们描述了三种保存和恢复scikit学习模型的工具。Pickle和Joblib库可以快速方便地使用,但是在不同的Python版本和学习模型的变化中存在兼容性问题。另一方面,手动方法更难实现,需要在模型结构发生任何变化中进行修改,但在另一方面,它可以轻松地适应各种需求,并且没有任何兼容性问题。

作者信息

1d0a417647e2b22e534a83c43b1b726531e8f4b5

作者:Mihajlo Pavloski数据科学与机器学习的爱好者,博士生。

本文由阿里云云社区组织翻译。

文章原标题《TensorFlow : Save and Restore Models

作者:Mihajlo Pavloski 译者:虎说八道,审阅:

文章为简译,更为详细的内容,请查看原文

用云栖社区APP,舒服~

【云栖快讯】云栖社区技术交流群汇总,阿里巴巴技术专家及云栖社区专家等你加入互动,老铁,了解一下?  详情请点击

网友评论