使用Dill将scikit学习/统计模型系列化有哪些陷阱



我需要串行化scikit学习/统计模型,以便将所有依赖项(代码+数据(打包在一个人工制品中,并且该人工制品可以用于初始化模型并进行预测。使用pickle module不是一个选项,因为这只会处理数据依赖关系(代码不会打包(。所以,我一直在和迪尔做实验。为了使我的问题更加精确,下面是一个我构建模型并将其持久化的例子

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等模块必须安装在为模型服务的机器上的要求之外,这种方法还有其他陷阱吗?任何意见或建议都会很有帮助。

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

我是dill的作者。dill的构建正是为了做你正在做的事情…(在类实例中保持数字拟合以进行统计(,然后这些对象可以分布到不同的资源,并以令人尴尬的并行方式运行。所以,答案是肯定的——我已经运行了类似于您的代码,使用mystic和/或sklearn

注意,sklearn的许多作者使用cloudpickle来启用对sklearn对象的并行计算,而不是dill。与cloudpickle相比,dill可以pickle更多类型的对象,但cloudpickle(在撰写本文时(在pickle作为闭包一部分引用全局字典的对象方面稍好一些——默认情况下,dill通过引用来实现这一点,而cloudpickle则物理存储依赖关系。然而,dill具有与cloudpickle类似的"recurse"模式,因此使用该模式时的差异很小。(要启用"recurse"模式,请执行dill.settings['recurse'] = True,或使用recurse=True作为dill.dump中的标志(。另一个小的区别是cloudpickle包含对scikits.timeseriesPIL.Image之类的东西的特殊支持,而dill没有。

从好的方面来说,dill不会通过引用来pickle类,因此通过pickle一个类实例,它会序列化类对象本身——这是一个很大的优势,因为它会序列化来自sklearn的分类器、模型等派生类的实例,使其处于pickle时的确切状态…因此,如果您对类对象进行修改,实例仍然可以正确地取消pickle。与cloudpickle相比,dill还有其他优点,除了对象范围更广(通常是较小的pickle(之外——不过,我不会在这里列出它们。你要求陷阱,所以差异不是陷阱。

主要陷阱:

  • 您应该在远程机器,以防dill(或cloudpickle(通过参考

  • 您应该尝试将类和类方法设置为尽可能独立(例如,不要引用中定义的对象类的全局作用域(。

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

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

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

  1. Dill 的局限性

    framegeneratortraceback标准类型不能打包。

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

一旦目标机器加载了正确的库(也要小心不同的python版本,因为它们可能会对您的代码造成错误(,只要您不使用未移植的标准类型,Dillcloudpickle都应该可以正常工作。

希望这能有所帮助。

I使用picklescikit-learn封装高斯过程(GP(。

主要原因是GP的构建需要很长时间,并且使用pickle的加载速度要快得多。因此,在我的代码初始化中,我会检查模型的数据文件是否已更新,并在必要时重新生成模型,否则只需从pickle反序列化它!

我会按照各自的顺序使用pickledillcloudpickle

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

相关内容

  • 没有找到相关文章

最新更新