使用Dill序列化scikit-learn/statsmodels模型有哪些陷阱?

Nik*_*hil 23 python pickle scikit-learn statsmodels dill

我需要序列化scikit-learn/statsmodels模型,以便将所有依赖项(代码+数据)打包在一个人工制品中,这个人工制品可用于初始化模型并进行预测.使用pickle module不是一个选项,因为这只会处理数据依赖(代码不会被打包).所以,我一直在用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'))
Run Code Online (Sandbox Code Playgroud)

与此相对应,以下是我如何初始化持久化模型(在新会话中)并进行预测.请注意,此代码未明确初始化或了解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)
Run Code Online (Sandbox Code Playgroud)

Dill有没有用过这种方式?我们的想法是让数据科学家为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)
Run Code Online (Sandbox Code Playgroud)

除了安全隐患(任意代码执行)以及scikit-learn必须在服务模型的机器上安装类似模块的要求之外,这种方法还有其他任何缺陷吗?任何意见或建议的话都是最有帮助的.

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

Mik*_*rns 21

我是dill作者. dill是为了完成你正在做的事情......(在数据类实例中保持数值拟合),然后可以将这些对象分发到不同的资源并以令人尴尬的并行方式运行.所以,答案是肯定的 - 我运行的代码与你的一样,使用mystic和/或sklearn.

请注意,许多作者sklearn使用cloudpickle用于启用并行计算sklearn的对象,而不是dill. dill可以腌制更多类型的对象cloudpickle,但是cloudpickle稍微好一些(在此时编写)在酸洗对象上,这些对象将全局字典作为闭包的一部分进行引用 - 默认情况下,dill这是通过引用来实现的,同时cloudpickle物理地存储依赖项.但是,dill有一种"recurse"模式,就像这样cloudpickle,所以使用这种模式时的差异很小.(启用"recurse"模式,执行dill.settings['recurse'] = Truerecurse=True用作标志dill.dump).另一个微小的区别是,cloudpickle包含对scikits.timeseriesand和PIL.Imagewhile 等内容的特殊支持dill 才不是.

从有利的一面,dill通过参考不咸菜类,因此通过酸洗一个类的实例,它系列化类对象本身-这是一个很大的优势,因为它系列化分类,模型和等派生类的实例,从sklearn他们的酸洗时的确切状态...因此,如果您对类对象进行了修改,则实例仍会正确取消.还有其他优点dillcloudpickle,除了从更广泛的范围内的对象(通常更小咸菜) -但是,我这里就不一一列举.你问了陷阱,所以差异不是陷阱.

主要陷阱:

  • 您应该在远程计算机上安装类引用的任何内容,以防万一dill(或cloudpickle)通过引用进行pickle.

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

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


Bas*_*ein 8

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

Dill主要基于pickle,所以他们是非常相似的,你应该考虑/调查一些事情:

  1. 的局限性 Dill

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

  2. cloudpickle 对你的问题也许是一个好主意,它在酸洗对象方面有更好的支持(比泡菜更好,而不是比Dill更好看),你也可以轻松地腌制代码.

一旦目标机器已加载正确的库,(要小心了不同python版本的为好,因为他们可能错误代码),一切都应该很好地工作既Dillcloudpickle,只要你不使用unsuported标准类型.

希望这可以帮助.