我需要串行化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.timeseries
和PIL.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
,因此它们非常相似,有些事情你应该考虑/研究:
-
Dill
的局限性frame
、generator
、traceback
标准类型不能打包。 -
对于您的问题,
cloudpickle
可能也是一个好主意,它在pickle对象中有更好的支持(比pickle更好,而不是比Dill更好(,并且您也可以轻松地pickle代码。
一旦目标机器加载了正确的库(也要小心不同的python
版本,因为它们可能会对您的代码造成错误(,只要您不使用未移植的标准类型,Dill
和cloudpickle
都应该可以正常工作。
希望这能有所帮助。
I使用pickle
从scikit-learn
封装高斯过程(GP(。
主要原因是GP的构建需要很长时间,并且使用pickle
的加载速度要快得多。因此,在我的代码初始化中,我会检查模型的数据文件是否已更新,并在必要时重新生成模型,否则只需从pickle
反序列化它!
我会按照各自的顺序使用pickle
、dill
、cloudpickle
。
请注意,pickle
包含protocol
关键字参数,某些值可以显著加快和减少内存使用!最后,如果需要的话,我用CPython STL的压缩来包装pickle代码。