gpt4 book ai didi

python - 为什么这段代码可以在不导入 sklearn 的情况下使用 sklearn 函数?

转载 作者:行者123 更新时间:2023-12-04 11:54:41 30 4
gpt4 key购买 nike

所以刚看了一个教程,作者不需要import sklearn使用时 predict anaconda 环境中pickled 模型的功能(安装了sklearn)。
我试图在 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)
我意识到我仍然需要安装 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)
错误:
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'
那么它是怎样工作的?据我了解 pickle 不依赖于 scikit-learn。序列化模型做吗 import sklearn ? 为什么我可以使用 predict在第一个代码中没有导入scikit学习的功能?

最佳答案

这里有几个问题被问到,所以让我们一一分析它们:

So, how does it work? as far as I understand pickle doesn't depend on 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()
到目前为止,我所做的是在一个新的 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
尽管是进口的,但是, numpy仍然不是一个定义的变量名。 Python 中的导入是全局的,但导入只会更新实际执行导入的模块的命名空间。如果我们想访问 numpy我们仍然需要写 import numpy ,但由于 Numpy 已经在过程中的其他地方导入,因此不会重新运行 Numpy 的模块初始化代码。相反,它会创建一个 numpy我们模块的全局字典中的变量,并使其成为对预先存在的 Numpy 模块对象的引用,并且可以通过 sys.modules['numpy'] 访问.
那么 pickle 在这里做什么呢?它嵌入了有关哪个模块用于定义 pickle 中的任何内容的信息。然后,当它 unp​​ickle 某些东西时,它使用该信息导入模块,以便它可以使用类的 unpickle 方法。我们可以查看 Pickle 模块的源代码,我们可以看到正在发生的事情:
_Pickler 我们看到 save 方法使用 save_global 方法。这反过来使用 whichmodule 函数来获取模块名称( 'scikit-learn' ,在您的情况下),然后将其保存在 pickle 中。
_UnPickler 我们看到了 find_class 方法使用 __import__ 使用存储的模块名称导入模块。 find_class方法用于少数 load_*方法,例如 load_inst ,这将用于加载类的实例,例如您的模型实例:
def load_inst(self):
module = self.readline()[:-1].decode("ascii")
name = self.readline()[:-1].decode("ascii")
klass = self.find_class(module, name)
self._instantiate(klass, self.pop_mark())
The documentation for Unpickler.find_class explains :

Import module if necessary and return the object called name from it, where the module and name arguments are str objects.


The docs also explain how you can restrict this behaviour :

[You] may want to control what gets unpickled by customizing Unpickler.find_class(). Unlike its name suggests, Unpickler.find_class() is called whenever a global (i.e., a class or a function) is requested. Thus it is possible to either completely forbid globals or restrict them to a safe subset.


虽然这通常只在取消不可信数据时才相关,但这里似乎并非如此。

Does the serialized model do import sklearn?


严格来说,序列化模型本身不做任何事情。如上所述,这一切都由 Pickle 模块处理。

Why can I use predict function without import scikit learn in the first code?


因为 sklearn 在解压数据时由 Pickle 模块导入,从而为您提供一个完全实现的模型对象。这就像某个其他模块导入了 sklearn,创建了模型对象,然后将其作为函数的参数传递到您的代码中。

由于所有这些,为了取消您的模型,您需要安装 sklearn - 理想情况下与用于创建 pickle 的版本相同。通常,Pickle 模块存储任何所需模块的完全限定路径,因此对对象进行处理的 Python 进程和对对象进行解压的进程必须具有所有 [1] 所需的模块,并且具有相同的完全限定名称。

[1] 需要注意的是,Pickle 模块可以自动调整/修复特定模块/类的某些导入,这些模块/类在 Python 2 和 3 之间具有不同的完全限定名称。来自 the docs :

If fix_imports is true, pickle will try to map the old Python 2 names to the new names used in Python 3.

关于python - 为什么这段代码可以在不导入 sklearn 的情况下使用 sklearn 函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69262618/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com