29

我需要序列化 ​​scikit-learn/statsmodels 模型,以便将所有依赖项(代码 + 数据)打包在一个人工制品中,并且该人工制品可用于初始化模型并进行预测。使用 picklemodule不是一个选项,因为这只会处理数据依赖关系(代码不会被打包)。所以,我一直在用Dill进行实验。为了使我的问题更准确,以下是我构建模型并将其持久化的示例。

from sklearn import datasets
from sklearn import svm
from sklearn.preprocessing import Normalizer
import dill

digits = datasets.load_digits()
training_data_X = digits.data[:-5]
training_data_Y = digits.target[:-5]
test_data_X = digits.data[-5:]
test_data_Y = digits.target[-5:]

class Model:
    def __init__(self):
        self.normalizer = Normalizer()
        self.clf = svm.SVC(gamma=0.001, C=100.)
    def train(self, training_data_X, training_data_Y):
        normalised_training_data_X = normalizer.fit_transform(training_data_X)
        self.clf.fit(normalised_training_data_X, training_data_Y)
    def predict(self, test_data_X):
        return self.clf.predict(self.normalizer.fit_transform(test_data_X))  

model = Model()
model.train(training_data_X, training_data_Y)
print model.predict(test_data_X)
dill.dump(model, open("my_model.dill", 'w'))

与此相对应,这是我如何初始化持久模型(在新会话中)并进行预测。请注意,此代码没有显式初始化或了解class Model.

import dill
from sklearn import datasets

digits = datasets.load_digits()
training_data_X = digits.data[:-5]
training_data_Y = digits.target[:-5]
test_data_X = digits.data[-5:]
test_data_Y = digits.target[-5:]

with open("my_model.dill") as model_file:
    model = dill.load(model_file)

print model.predict(test_data_X)

有没有人用过莳萝是这样的?这个想法是让数据科学家为ModelWrapper class他们实现的每个模型扩展一个,然后围绕这个构建基础架构,以保持模型、将模型部署为服务并管理模型的整个生命周期。

class ModelWrapper(object):
    __metaclass__ = abc.ABCMeta
    def __init__(self, model):
        self.model = model
    @abc.abstractmethod
    def predict(self, input):
        return
    def dumps(self):
        return dill.dumps(self)
    def loads(self, model_string):
        self.model = dill.loads(model_string)

除了安全隐患(任意代码执行)和模块scikit-learn必须安装在为模型服务的机器上的要求之外,这种方法是否存在任何其他缺陷?任何意见或建议将是最有帮助的。

我认为YHatDato采取了类似的方法,但出于类似目的推出了自己的 Dill 实现。

4

3 回答 3

39

我是dill作者。 dill旨在完成您正在做的事情......(在类实例中保持数值拟合以进行统计),然后这些对象可以分布到不同的资源并以令人尴尬的并行方式运行。所以,答案是肯定的——我已经运行了像你这样的代码,使用mysticand/or sklearn

请注意,许多作者sklearn使用cloudpickle了对sklearn对象启用并行计算,而不是dill. dill可以腌制更多类型的对象cloudpickle,但是cloudpickle在腌制将全局字典作为闭包的一部分的对象时(在撰写本文时)稍微好一点——默认情况下,dill通过引用来执行此操作,同时cloudpickle物理存储依赖项。但是,dill有一种"recurse"模式,其行为类似于cloudpickle,因此使用此模式时的差异很小。(要启用"recurse"模式,请执行dill.settings['recurse'] = Truerecurse=True用作 中的标志dill.dump)。另一个小的区别是它包含对和cloudpickle之类的特殊支持,而scikits.timeseriesPIL.Imagedill才不是。

从好的方面来说,dill不会通过引用来腌制类,因此通过腌制类实例,它会序列化类对象本身——这是一个很大的优势,因为它会序列化分类器、模型等派生类的实例sklearn。酸洗时的确切状态......所以如果你对类对象进行修改,实例仍然会正确地取消选择。dill除了更广泛的对象(通常是较小的泡菜)之外, over还有其他优点cloudpickle——但是,我不会在这里列出它们。你要求陷阱,所以差异不是陷阱。

主要陷阱:

  • 你应该在远程机器上安装你的类引用的任何东西,以防万一dill(或cloudpickle)通过引用腌制它。

  • 您应该尝试使您的类和类方法尽可能自包含(例如,不要从您的类中引用在全局范围内定义的对象)。

  • sklearn对象可能很大,因此将它们中的许多保存到单个泡菜中并不总是一个好主意……您可能希望使用klepto 具有dict缓存和存档接口的对象,并允许您配置存档接口以存储每个键值对单独(例如每个文件一个条目)。

于 2015-10-03T17:49:10.980 回答
10

好的,在您的示例代码中pickle可以正常工作,我一直使用 pickle 来打包模型并在以后使用它,除非您想将模型直接发送到另一台服务器或保存interpreter state,因为那Dill是擅长的并且pickle做不到。它还取决于您的代码、您使用的类型等,pickle可能会失败,Dill是否更稳定。

Dill主要基于pickle,因此它们非常相似,您应该考虑/研究一些事情:

  1. 的限制Dill

    frame, generator,traceback标准类型不能打包。

  2. cloudpickle对于您的问题也可能是一个好主意,它对酸洗对象有更好的支持(比泡菜,不是比 Dill 更好),而且您也可以轻松地泡菜代码。

一旦目标机器加载了正确的库(对于不同的python版本也要小心,因为它们可能会错误您的代码),只要您不使用不受支持的标准类型,一切都应该可以正常Dill工作cloudpickle

希望这可以帮助。

于 2015-09-29T07:59:51.760 回答
2

scikit-learn使用pickle.

主要原因是因为 GP 需要很长时间来构建并且使用pickle. 因此,在我的代码初始化中,我检查模型的数据文件是否已更新并在必要时重新生成模型,否则只需从pickle!

我会按相应的顺序使用pickle, 。dillcloudpickle

请注意,pickle包含protocol关键字参数和一些值可以显着加快和减少内存使用!最后,如有必要,我会使用 CPython STL 的压缩来包装 pickle 代码。

于 2015-10-03T12:37:14.817 回答