使用 Dill 序列化 scikit-learn/statsmodels 模型有哪些陷阱?
What are the pitfalls of using Dill to serialise scikit-learn/statsmodels models?
我需要序列化 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'))
与此相对应,这是我如何初始化持久模型(在新会话中)并进行预测。请注意,此代码未明确初始化或了解 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
这样的模块必须安装在为模型服务的机器上的要求之外,这种方法还有其他缺陷吗?任何评论或建议都会很有帮助。
好的开始,在你的示例代码中 pickle
可以正常工作,我一直使用 pickle 来打包模型并在以后使用它,除非你想直接将模型发送到另一个服务器或把interpreter state
留着,因为那是Dill
擅长而pickle
做不到的。它还取决于您的代码、您使用的类型等,pickle
可能会失败,Dill
更稳定。
Dill
主要基于 pickle
,因此它们非常相似,有些事情您应该考虑/研究:
Dill
的限制
frame
、generator
、traceback
标准类型无法打包。
cloudpickle
也可能是解决您的问题的好主意,它在酸洗对象方面有更好的支持(比泡菜,并不比 Dill 更好)并且您可以轻松地酸洗代码还有。
一旦目标机器加载了正确的库,(也要小心不同的 python
版本,因为它们可能会导致代码出错),Dill
和 cloudpickle
,只要你不使用不受支持的标准类型。
希望这对您有所帮助。
我使用 pickle
从 scikit-learn
打包高斯过程 (GP)。
主要原因是因为使用 pickle
构建 GP 需要很长时间来构建和加载速度更快。因此,在我的代码初始化中,我检查模型的数据文件是否已更新并在必要时重新生成模型,否则只需从 pickle
!
反序列化它
我会按照各自的顺序使用pickle
、dill
、cloudpickle
。
请注意 pickle
包括 protocol
关键字参数,一些值可以显着加快和减少内存使用!
最后,如有必要,我使用来自 CPython STL 的压缩包 pickle 代码。
我是 dill
作者。 dill
的构建完全是为了做你正在做的事情......(在 class 实例中保持数值适合以进行统计)然后可以将这些对象分发到不同的资源并且 运行 以令人尴尬的并行方式时尚。所以,答案是肯定的——我有和你一样的 运行 代码,使用 mystic
and/or sklearn
.
请注意,sklearn
的许多作者使用 cloudpickle
来启用 sklearn
对象的并行计算,而不是 dill
。 dill
可以 pickle 比 cloudpickle
更多类型的对象,但是 cloudpickle
在 pickle 将引用全局字典作为闭包的一部分的对象方面稍微好一点(在撰写本文时)- - 默认情况下,dill
通过引用执行此操作,而 cloudpickle
物理存储依赖项。但是,dill
有一个 "recurse"
模式,其作用类似于 cloudpickle
,因此使用此模式时的差异很小。 (要启用 "recurse"
模式,请执行 dill.settings['recurse'] = True
,或使用 recurse=True
作为 dill.dump
中的标志)。另一个细微差别是 cloudpickle
包含对 scikits.timeseries
和 PIL.Image
等内容的特殊支持,而 dill
则没有。
从好的方面来说,dill
不会通过引用 pickle classes,因此通过 pickle class 实例,它会序列化 class 对象本身 --这是一个很大的优势,因为它序列化了 class 派生的 classes 的实例 classifiers,models, and etc from sklearn
in their exact state at the time of pickling ... 所以如果你对 class 对象的修改,实例仍然正确地取消选择。 dill
与 cloudpickle
相比还有其他优势,除了更广泛的对象(通常是更小的 pickle)之外——但是,我不会在这里列出它们。你问的是陷阱,差异不是陷阱
主要陷阱:
您应该将 class 所指的任何东西安装在
远程机器,以防万一 dill
(或 cloudpickle
)通过
参考.
您应该尝试使您的 classes 和 class 方法成为
尽可能自包含(例如,不要引用定义在
来自 classes 的全局范围)。
sklearn
对象可以很大,所以把很多对象保存到一个
泡菜并不总是一个好主意......你可能想使用 klepto
它有一个用于缓存和归档的 dict
接口,并使您能够配置归档接口以单独存储每个键值对(例如每个文件一个条目)。
我需要序列化 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'))
与此相对应,这是我如何初始化持久模型(在新会话中)并进行预测。请注意,此代码未明确初始化或了解 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
这样的模块必须安装在为模型服务的机器上的要求之外,这种方法还有其他缺陷吗?任何评论或建议都会很有帮助。
好的开始,在你的示例代码中 pickle
可以正常工作,我一直使用 pickle 来打包模型并在以后使用它,除非你想直接将模型发送到另一个服务器或把interpreter state
留着,因为那是Dill
擅长而pickle
做不到的。它还取决于您的代码、您使用的类型等,pickle
可能会失败,Dill
更稳定。
Dill
主要基于 pickle
,因此它们非常相似,有些事情您应该考虑/研究:
的限制Dill
frame
、generator
、traceback
标准类型无法打包。cloudpickle
也可能是解决您的问题的好主意,它在酸洗对象方面有更好的支持(比泡菜,并不比 Dill 更好)并且您可以轻松地酸洗代码还有。
一旦目标机器加载了正确的库,(也要小心不同的 python
版本,因为它们可能会导致代码出错),Dill
和 cloudpickle
,只要你不使用不受支持的标准类型。
希望这对您有所帮助。
我使用 pickle
从 scikit-learn
打包高斯过程 (GP)。
主要原因是因为使用 pickle
构建 GP 需要很长时间来构建和加载速度更快。因此,在我的代码初始化中,我检查模型的数据文件是否已更新并在必要时重新生成模型,否则只需从 pickle
!
我会按照各自的顺序使用pickle
、dill
、cloudpickle
。
请注意 pickle
包括 protocol
关键字参数,一些值可以显着加快和减少内存使用!
最后,如有必要,我使用来自 CPython STL 的压缩包 pickle 代码。
我是 dill
作者。 dill
的构建完全是为了做你正在做的事情......(在 class 实例中保持数值适合以进行统计)然后可以将这些对象分发到不同的资源并且 运行 以令人尴尬的并行方式时尚。所以,答案是肯定的——我有和你一样的 运行 代码,使用 mystic
and/or sklearn
.
请注意,sklearn
的许多作者使用 cloudpickle
来启用 sklearn
对象的并行计算,而不是 dill
。 dill
可以 pickle 比 cloudpickle
更多类型的对象,但是 cloudpickle
在 pickle 将引用全局字典作为闭包的一部分的对象方面稍微好一点(在撰写本文时)- - 默认情况下,dill
通过引用执行此操作,而 cloudpickle
物理存储依赖项。但是,dill
有一个 "recurse"
模式,其作用类似于 cloudpickle
,因此使用此模式时的差异很小。 (要启用 "recurse"
模式,请执行 dill.settings['recurse'] = True
,或使用 recurse=True
作为 dill.dump
中的标志)。另一个细微差别是 cloudpickle
包含对 scikits.timeseries
和 PIL.Image
等内容的特殊支持,而 dill
则没有。
从好的方面来说,dill
不会通过引用 pickle classes,因此通过 pickle class 实例,它会序列化 class 对象本身 --这是一个很大的优势,因为它序列化了 class 派生的 classes 的实例 classifiers,models, and etc from sklearn
in their exact state at the time of pickling ... 所以如果你对 class 对象的修改,实例仍然正确地取消选择。 dill
与 cloudpickle
相比还有其他优势,除了更广泛的对象(通常是更小的 pickle)之外——但是,我不会在这里列出它们。你问的是陷阱,差异不是陷阱
主要陷阱:
您应该将 class 所指的任何东西安装在 远程机器,以防万一
dill
(或cloudpickle
)通过 参考.您应该尝试使您的 classes 和 class 方法成为 尽可能自包含(例如,不要引用定义在 来自 classes 的全局范围)。
sklearn
对象可以很大,所以把很多对象保存到一个 泡菜并不总是一个好主意......你可能想使用klepto
它有一个用于缓存和归档的dict
接口,并使您能够配置归档接口以单独存储每个键值对(例如每个文件一个条目)。