如何加载之前使用笔记本保存的自定义类的 joblib 文件?

问题描述 投票:0回答:2

在 jupyter 笔记本中,如果我定义一个类,实例化它并使用 joblib 保存对象,我可以将它加载回来:

import joblib

class Duck():
    def quack(self):
        print("Quack!")

my_duck = Duck()
joblib.dump(my_duck, "my_duck.joblib")
loaded_duck = joblib.load("my_duck.joblib")
loaded_duck.quack()

输出:

Quack!

但是如果我尝试加载new笔记本(甚至是常规的.py脚本),我将无法:

import joblib

loaded_duck = joblib.load("my_duck.joblib")
loaded_duck.quack()

输出:

AttributeError: module '__main__' has no attribute 'Duck'

如何解决这个问题?

python jupyter-notebook joblib
2个回答
2
投票

尽管这个问题很旧并且现有的答案是正确的,但我想扩大讨论,因为我花了很多时间来理解这个问题。

joblib
是一个类似于
pickle
的允许对象持久化的包。这些对象是类的实例,例如通常,数据帧是
pandas.DataFrame
类的实例。如果序列化该对象,即
joblib.dump(“my_df.joblib”)
,该对象将存储在一个二进制文件中,该文件由其类名标记。

如果您反序列化文件以取回对象,即

joblib.load(“my_df.joblib”)
,Python 必须搜索类定义才能实例化它。为了跟上我们的数据框示例,这将对应于
pandas.DataFrame
。因此,如果在当前上下文(不同的脚本、不同的笔记本等)中,您没有安装
pandas
,您将得到著名的
ModuleNotFoundError
,因为 Python 不知道如何实例化您的数据框。

现在您必须将这种直觉转移到您的自定义类中:如果您创建它时它的定义位于主模块中,那么在您加载它时,相同的定义也必须位于主模块中。在您的情况下,这是

__main__.Duck
,这意味着您需要将类定义复制粘贴到新笔记本中。然而,这不是一个非常实用的方法。所以我建议你创建一个额外的模块,例如名为
utils
或类似的文件夹,您可以在其中放置包含自定义类的所有脚本。结构可能如下所示:

yourProject/
│
├── notebook.ipynb
├── utils/
|   ├── __init__.py
|   └── animals.py
|
└── my_duck.joblib

并且在

animals.py
内:

class Duck():
  def quack(self):
    print("Quack!")

如果您现在导入自定义类,您将使用:

from utils.animals import Duck

像这样,

joblib
相应地标记对象,当您在其他地方加载对象时,您可以只使用自定义模块(例如复制粘贴 utils 文件夹)。只需确保相对路径与导入
./utils/animals.py
时 Python 在
Duck
中搜索的路径完全相同即可。

编辑: 遵循上述逻辑,最优雅的解决方案是创建您自己的 python 包,其中包含所有自定义类/方法,然后在需要的地方

import
它。


1
投票

不知道你是否还像1个月前一样遇到这个问题;但是,如果其他人也遇到同样的问题:

您收到该错误是因为,在新笔记本(您在其中导入对象)中,您没有导入类的定义。

首先尝试在新笔记本中导入类

Duck

from *script_duck import Duck

import joblib

loaded_duck = joblib.load("my_duck.joblib")
loaded_duck.quack()
© www.soinside.com 2019 - 2024. All rights reserved.