mal*_*oro 7 python pickle scikit-learn
import sklearn所以我只是看了一个教程,作者在anaconda环境(安装了sklearn)中使用predictpickled模型的功能时不需要看。
我尝试在 Google Colab 中重现它的最小版本。如果您有 pickled-sklearn-model,下面的代码可以在 Colab 中运行(已安装 sklearn):
import pickle
model = pickle.load(open("model.pkl", "rb"), encoding="bytes")
out = model.predict([[20, 0, 1, 1, 0]])
print(out)
Run Code Online (Sandbox Code Playgroud)
我意识到我仍然需要安装 sklearn 软件包。如果我卸载 sklearn,该predict功能现在不起作用:
!pip uninstall scikit-learn
import pickle
model = pickle.load(open("model.pkl", "rb"), encoding="bytes")
out = model.predict([[20, 0, 1, 1, 0]])
print(out)
Run Code Online (Sandbox Code Playgroud)
错误:
WARNING: Skipping scikit-learn as it is not installed.
---------------------------------------------------------------------------
ModuleNotFoundError Traceback (most recent call last)
<ipython-input-1-dec96951ae29> in <module>()
1 get_ipython().system('pip uninstall scikit-learn')
2 import pickle
----> 3 model = pickle.load(open("model.pkl", "rb"), encoding="bytes")
4 out = model.predict([[20, 0, 1, 1, 0]])
5 print(out)
ModuleNotFoundError: No module named 'sklearn'
Run Code Online (Sandbox Code Playgroud)
那么它是怎样工作的?据我了解pickle不依赖于scikit-learn。序列化模型可以吗import sklearn?为什么我可以在第一个代码中使用没有 import scikit learn 的函数?predict
Wil*_*lva 11
这里提出了几个问题,所以让我们一一解答:
那么它是怎样工作的?据我了解pickle不依赖于scikit-learn。
这里没有什么特别需要 scikit-learn 进行的事情。Pickle 将对任何模块表现出这种行为。这是一个使用 Numpy 的示例:
will@will-desktop ~ $ python
Python 3.9.6 (default, Aug 24 2021, 18:12:51)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> 'numpy' in sys.modules
False
>>> import numpy
>>> 'numpy' in sys.modules
True
>>> pickle.dumps(numpy.array([1, 2, 3]))
b'\x80\x04\x95\xa0\x00\x00\x00\x00\x00\x00\x00\x8c\x15numpy.core.multiarray\x94\x8c\x0c_reconstruct\x94\x93\x94\x8c\x05numpy\x94\x8c\x07ndarray\x94\x93\x94K\x00\x85\x94C\x01b\x94\x87\x94R\x94(K\x01K\x03\x85\x94h\x03\x8c\x05dtype\x94\x93\x94\x8c\x02i8\x94\x89\x88\x87\x94R\x94(K\x03\x8c\x01<\x94NNNJ\xff\xff\xff\xffJ\xff\xff\xff\xffK\x00t\x94b\x89C\x18\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x94t\x94b.'
>>> exit()
Run Code Online (Sandbox Code Playgroud)
到目前为止,我所做的就是表明,在一个新的Python进程中'numpy'不存在sys.modules(导入模块的字典)。然后我们导入 Numpy,并 pickle 一个 Numpy 数组。
然后在如下所示的新 Python 过程中,我们看到在 unpickle 之前,Numpy 还没有被导入,但是在我们有 Numpy 之后已经被导入了。
will@will-desktop ~ $ python
Python 3.9.6 (default, Aug 24 2021, 18:12:51)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pickle
>>> import sys
>>> 'numpy' in sys.modules
False
>>> pickle.loads(b'\x80\x04\x95\xa0\x00\x00\x00\x00\x00\x00\x00\x8c\x15numpy.core.multiarray\x94\x8c\x0c_reconstruct\x94\x93\x94\x8c\x05numpy\x94\x8c\x07ndarray\x94\x93\x94K\x00\x85\x94C\x01b\x94\x87\x94R\x94(K\x01K\x03\x85\x94h\x03\x8c\x05dtype\x94\x93\x94\x8c\x02i8\x94\x89\x88\x87\x94R\x94(K\x03\x8c\x01<\x94NNNJ\xff\xff\xff\xffJ\xff\xff\xff\xffK\x00t\x94b\x89C\x18\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x94t\x94b.')
array([1, 2, 3])
>>> 'numpy' in sys.modules
True
>>> numpy
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'numpy' is not defined
Run Code Online (Sandbox Code Playgroud)
然而,尽管已导入,但numpy仍然没有定义变量名。Python 中的导入是全局的,但导入只会更新实际执行导入的模块的命名空间。如果我们想访问,numpy我们仍然需要编写import numpy,但由于 Numpy 已经在进程中的其他地方导入,因此不会重新运行 Numpy 的模块初始化代码。相反,它将numpy在我们模块的全局字典中创建一个变量,并使其成为对先前存在的 Numpy 模块对象的引用,并且可以通过sys.modules['numpy'].
那么Pickle在这里做什么呢?它嵌入了有关使用哪个模块来定义 pickle 中正在腌制的内容的信息。然后,当它取消某些内容时,它使用该信息来导入模块,以便它可以使用该类的 unpickle 方法。我们可以查看 Pickle 模块的源代码,我们可以看到正在发生的事情:
在_Pickler我们看到的save方法中使用了save_global方法。这反过来使用该whichmodule函数来获取模块名称('scikit-learn'在您的情况下),然后将其保存在 pickle 中。
在 中,_UnPickler我们看到该find_class方法用于__import__使用存储的模块名称导入模块。该find_class方法在一些方法中使用load_*,例如load_inst,它用于加载类的实例,例如模型实例:
will@will-desktop ~ $ python
Python 3.9.6 (default, Aug 24 2021, 18:12:51)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> 'numpy' in sys.modules
False
>>> import numpy
>>> 'numpy' in sys.modules
True
>>> pickle.dumps(numpy.array([1, 2, 3]))
b'\x80\x04\x95\xa0\x00\x00\x00\x00\x00\x00\x00\x8c\x15numpy.core.multiarray\x94\x8c\x0c_reconstruct\x94\x93\x94\x8c\x05numpy\x94\x8c\x07ndarray\x94\x93\x94K\x00\x85\x94C\x01b\x94\x87\x94R\x94(K\x01K\x03\x85\x94h\x03\x8c\x05dtype\x94\x93\x94\x8c\x02i8\x94\x89\x88\x87\x94R\x94(K\x03\x8c\x01<\x94NNNJ\xff\xff\xff\xffJ\xff\xff\xff\xffK\x00t\x94b\x89C\x18\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x94t\x94b.'
>>> exit()
Run Code Online (Sandbox Code Playgroud)
如果需要,导入 module 并从中返回名为 name 的对象,其中 module 和 name 参数是 str 对象。
[您]可能想通过自定义 Unpickler.find_class() 来控制 unpickled 的内容。与它的名字所暗示的不同,每当请求全局(即类或函数)时,就会调用 Unpickler.find_class()。因此,可以完全禁止全局变量或将它们限制为安全子集。
尽管这通常仅在取消不受信任的数据时才相关,但这里的情况似乎并非如此。
序列化模型是否导入sklearn?
严格来说,序列化模型本身不执行任何操作。如上所述,这一切都由 Pickle 模块处理。
为什么我可以在第一个代码中使用预测函数而无需 import scikit learn ?
因为sklearn是由Pickle模块在unpickle数据时导入的,从而为您提供了一个完全实现的模型对象。这就像其他模块导入 sklearn、创建模型对象,然后将其作为函数参数传递到代码中一样。
因此,为了解开模型,您需要安装 sklearn - 最好与用于创建 pickle 的版本相同。一般来说,Pickle 模块存储任何所需模块的完全限定路径,因此 pickle 对象的 Python 进程和 unpickle 对象的进程必须具有所有 [1] 所需模块,并且具有相同的完全限定名称。
[1] 需要注意的是,Pickle 模块可以自动调整/修复在 Python 2 和 3 之间具有不同完全限定名称的特定模块/类的某些导入。来自文档:
如果 fix_imports 为 true,pickle 将尝试将旧的 Python 2 名称映射到 Python 3 中使用的新名称。
| 归档时间: |
|
| 查看次数: |
1248 次 |
| 最近记录: |