torch.package¶
torch.package 添加了对创建包含工件和任意 PyTorch 代码的包的支持。这些包可以保存、共享、用于在以后或在不同的机器上加载和执行模型,甚至可以使用 torch::deploy 部署到生产环境中。
本文档包含教程、操作指南、解释和 API 参考,将帮助您了解 torch.package 及其使用方法。
警告
此模块依赖于 pickle 模块,该模块不安全。仅解包您信任的数据。
可以构造恶意 pickle 数据,这些数据将在 **解包期间执行任意代码**。切勿解包可能来自不受信任来源或可能被篡改的数据。
有关更多信息,请查看 pickle 模块的 文档。
如何…¶
查看包内包含的内容?¶
将包视为 ZIP 存档¶
torch.package 的容器格式为 ZIP,因此任何可与标准 ZIP 文件一起使用的工具都应该适用于探索其内容。一些与 ZIP 文件交互的常见方法
- unzip my_package.pt将解压缩- torch.package存档到磁盘,您可以自由查看其内容。
$ unzip my_package.pt && tree my_package
my_package
├── .data
│   ├── 94304870911616.storage
│   ├── 94304900784016.storage
│   ├── extern_modules
│   └── version
├── models
│   └── model_1.pkl
└── torchvision
    └── models
        ├── resnet.py
        └── utils.py
~ cd my_package && cat torchvision/models/resnet.py
...
- Python 的 - zipfile模块提供了一种标准方法来读取和写入 ZIP 存档内容。
from zipfile import ZipFile
with ZipFile("my_package.pt") as myzip:
    file_bytes = myzip.read("torchvision/models/resnet.py")
    # edit file_bytes in some way
    myzip.writestr("torchvision/models/resnet.py", new_file_bytes)
- vim 具有原生读取 ZIP 存档的能力。您甚至可以编辑文件并使用 : - write将它们写回存档!
# add this to your .vimrc to treat `*.pt` files as zip files
au BufReadCmd *.pt call zip#Browse(expand("<amatch>"))
~ vi my_package.pt
使用 file_structure() API¶
PackageImporter 提供了一个 file_structure() 方法,该方法将返回一个可打印和可查询的 Directory 对象。该 Directory 对象是一个简单的目录结构,您可以使用它来探索 torch.package 的当前内容。
该 Directory 对象本身可以直接打印,并将打印出文件树表示。要过滤返回的内容,请使用 glob 风格的 include 和 exclude 过滤参数。
with PackageExporter('my_package.pt') as pe:
    pe.save_pickle('models', 'model_1.pkl', mod)
importer = PackageImporter('my_package.pt')
# can limit printed items with include/exclude args
print(importer.file_structure(include=["**/utils.py", "**/*.pkl"], exclude="**/*.storage"))
print(importer.file_structure()) # will print out all files
输出
# filtered with glob pattern:
#    include=["**/utils.py", "**/*.pkl"], exclude="**/*.storage"
─── my_package.pt
    ├── models
    │   └── model_1.pkl
    └── torchvision
        └── models
            └── utils.py
# all files
─── my_package.pt
    ├── .data
    │   ├── 94304870911616.storage
    │   ├── 94304900784016.storage
    │   ├── extern_modules
    │   └── version
    ├── models
    │   └── model_1.pkl
    └── torchvision
        └── models
            ├── resnet.py
            └── utils.py
您还可以使用 has_file() 方法查询 Directory 对象。
importer_file_structure = importer.file_structure()
found: bool = importer_file_structure.has_file("package_a/subpackage.py")
查看为什么某个模块被包含为依赖项?¶
假设有一个名为 foo 的模块,您想知道为什么您的 PackageExporter 将 foo 作为依赖项引入。
PackageExporter.get_rdeps() 将返回所有直接依赖于 foo 的模块。
如果您想查看某个模块 src 如何依赖于 foo,PackageExporter.all_paths() 方法将返回一个 DOT 格式的图形,显示 src 和 foo 之间的依赖路径。
如果您只想查看您的 PackageExporter 的整个依赖关系图,可以使用 PackageExporter.dependency_graph_string()。
将任意资源包含在我的包中并在以后访问它们?¶
PackageExporter 公开了三个方法,save_pickle、save_text 和 save_binary,允许您将 Python 对象、文本和二进制数据保存到包中。
with torch.PackageExporter("package.pt") as exporter:
    # Pickles the object and saves to `my_resources/tensor.pkl` in the archive.
    exporter.save_pickle("my_resources", "tensor.pkl", torch.randn(4))
    exporter.save_text("config_stuff", "words.txt", "a sample string")
    exporter.save_binary("raw_data", "binary", my_bytes)
PackageImporter 公开了名为 load_pickle、load_text 和 load_binary 的互补方法,允许您从包中加载 Python 对象、文本和二进制数据。
importer = torch.PackageImporter("package.pt")
my_tensor = importer.load_pickle("my_resources", "tensor.pkl")
text = importer.load_text("config_stuff", "words.txt")
binary = importer.load_binary("raw_data", "binary")
如何自定义类的打包方式?¶
torch.package 允许自定义类的打包方式。这种行为可以通过在类上定义方法 __reduce_package__ 以及定义相应的解包函数来实现。这类似于为 Python 的正常序列化过程定义 __reduce__。
步骤
- 在目标类上定义方法 - __reduce_package__(self, exporter: PackageExporter)。此方法应完成将类实例保存到包中的工作,并应返回一个元组,其中包含相应的解包函数及其调用解包函数所需的参数。此方法由- PackageExporter在遇到目标类的实例时调用。
- 为该类定义一个解包函数。此解包函数应完成重建和返回类实例的工作。函数签名的第一个参数应为 - PackageImporter实例,其余参数由用户定义。
# foo.py [Example of customizing how class Foo is packaged]
from torch.package import PackageExporter, PackageImporter
import time
class Foo:
    def __init__(self, my_string: str):
        super().__init__()
        self.my_string = my_string
        self.time_imported = 0
        self.time_exported = 0
    def __reduce_package__(self, exporter: PackageExporter):
        """
        Called by ``torch.package.PackageExporter``'s Pickler's ``persistent_id`` when
        saving an instance of this object. This method should do the work to save this
        object inside of the ``torch.package`` archive.
        Returns function w/ arguments to load the object from a
        ``torch.package.PackageImporter``'s Pickler's ``persistent_load`` function.
        """
        # use this pattern to ensure no naming conflicts with normal dependencies,
        # anything saved under this module name shouldn't conflict with other
        # items in the package
        generated_module_name = f"foo-generated._{exporter.get_unique_id()}"
        exporter.save_text(
            generated_module_name,
            "foo.txt",
            self.my_string + ", with exporter modification!",
        )
        time_exported = time.clock_gettime(1)
        # returns de-packaging function w/ arguments to invoke with
        return (unpackage_foo, (generated_module_name, time_exported,))
def unpackage_foo(
    importer: PackageImporter, generated_module_name: str, time_exported: float
) -> Foo:
    """
    Called by ``torch.package.PackageImporter``'s Pickler's ``persistent_load`` function
    when depickling a Foo object.
    Performs work of loading and returning a Foo instance from a ``torch.package`` archive.
    """
    time_imported = time.clock_gettime(1)
    foo = Foo(importer.load_text(generated_module_name, "foo.txt"))
    foo.time_imported = time_imported
    foo.time_exported = time_exported
    return foo
# example of saving instances of class Foo
import torch
from torch.package import PackageImporter, PackageExporter
import foo
foo_1 = foo.Foo("foo_1 initial string")
foo_2 = foo.Foo("foo_2 initial string")
with PackageExporter('foo_package.pt') as pe:
    # save as normal, no extra work necessary
    pe.save_pickle('foo_collection', 'foo1.pkl', foo_1)
    pe.save_pickle('foo_collection', 'foo2.pkl', foo_2)
pi = PackageImporter('foo_package.pt')
print(pi.file_structure())
imported_foo = pi.load_pickle('foo_collection', 'foo1.pkl')
print(f"foo_1 string: '{imported_foo.my_string}'")
print(f"foo_1 export time: {imported_foo.time_exported}")
print(f"foo_1 import time: {imported_foo.time_imported}")
# output of running above script
─── foo_package
    ├── foo-generated
    │   ├── _0
    │   │   └── foo.txt
    │   └── _1
    │       └── foo.txt
    ├── foo_collection
    │   ├── foo1.pkl
    │   └── foo2.pkl
    └── foo.py
foo_1 string: 'foo_1 initial string, with reduction modification!'
foo_1 export time: 9857706.650140837
foo_1 import time: 9857706.652698385
在源代码中测试它是否在包内执行?¶
PackageImporter 将向其初始化的每个模块添加属性 __torch_package__。您的代码可以检查此属性是否存在以确定它是否在打包的上下文中执行。
# In foo/bar.py:
if "__torch_package__" in dir():  # true if the code is being loaded from a package
    def is_in_package():
        return True
    UserException = Exception
else:
    def is_in_package():
        return False
    UserException = UnpackageableException
现在,代码将根据它是通过您的 Python 环境正常导入还是从 torch.package 导入而表现出不同的行为。
from foo.bar import is_in_package
print(is_in_package())  # False
loaded_module = PackageImporter(my_package).import_module("foo.bar")
loaded_module.is_in_package()  # True
警告:一般来说,让代码根据其是否打包而表现出不同的行为是一种不好的做法。这会导致难以调试的问题,这些问题对您导入代码的方式很敏感。如果您的包旨在被大量使用,请考虑重构您的代码,使其无论如何加载都表现相同。
将代码修补到包中?¶
PackageExporter 提供了一个 save_source_string() 方法,允许您将任意 Python 源代码保存到您选择的模块中。
with PackageExporter(f) as exporter:
    # Save the my_module.foo available in your current Python environment.
    exporter.save_module("my_module.foo")
    # This saves the provided string to my_module/foo.py in the package archive.
    # It will override the my_module.foo that was previously saved.
    exporter.save_source_string("my_module.foo", textwrap.dedent(
        """\
        def my_function():
            print('hello world')
        """
    ))
    # If you want to treat my_module.bar as a package
    # (e.g. save to `my_module/bar/__init__.py` instead of `my_module/bar.py)
    # pass is_package=True,
    exporter.save_source_string("my_module.bar",
                                "def foo(): print('hello')\n",
                                is_package=True)
importer = PackageImporter(f)
importer.import_module("my_module.foo").my_function()  # prints 'hello world'
从打包的代码访问包内容?¶
PackageImporter 实现 importlib.resources API,用于访问包内部的资源。
with PackageExporter(f) as exporter:
    # saves text to my_resource/a.txt in the archive
    exporter.save_text("my_resource", "a.txt", "hello world!")
    # saves the tensor to my_pickle/obj.pkl
    exporter.save_pickle("my_pickle", "obj.pkl", torch.ones(2, 2))
    # see below for module contents
    exporter.save_module("foo")
    exporter.save_module("bar")
importlib.resources API 允许访问打包代码中的资源。
# foo.py:
import importlib.resources
import my_resource
# returns "hello world!"
def get_my_resource():
    return importlib.resources.read_text(my_resource, "a.txt")
使用 importlib.resources 是从打包代码中访问包内容的推荐方法,因为它符合 Python 标准。但是,也可以从打包代码中访问父 PackageImporter 实例本身。
# bar.py:
import torch_package_importer # this is the PackageImporter that imported this module.
# Prints "hello world!", equivalent to importlib.resources.read_text
def get_my_resource():
    return torch_package_importer.load_text("my_resource", "a.txt")
# You also do things that the importlib.resources API does not support, like loading
# a pickled object from the package.
def get_my_pickle():
    return torch_package_importer.load_pickle("my_pickle", "obj.pkl")
区分打包代码和非打包代码?¶
要判断一个对象的代码是否来自 torch.package,请使用 torch.package.is_from_package() 函数。注意:如果一个对象来自一个包,但其定义来自一个标记为 extern 的模块或来自 stdlib,则此检查将返回 False。
importer = PackageImporter(f)
mod = importer.import_module('foo')
obj = importer.load_pickle('model', 'model.pkl')
txt = importer.load_text('text', 'my_test.txt')
assert is_from_package(mod)
assert is_from_package(obj)
assert not is_from_package(txt) # str is from stdlib, so this will return False
重新导出导入的对象?¶
要重新导出之前由 PackageImporter 导入的对象,必须让新的 PackageExporter 了解原始的 PackageImporter,以便它可以找到对象依赖项的源代码。
importer = PackageImporter(f)
obj = importer.load_pickle("model", "model.pkl")
# re-export obj in a new package
with PackageExporter(f2, importer=(importer, sys_importer)) as exporter:
    exporter.save_pickle("model", "model.pkl", obj)
打包 TorchScript 模块?¶
要打包 TorchScript 模型,请使用与其他任何对象相同的 save_pickle 和 load_pickle API。保存作为属性或子模块的 TorchScript 对象也支持,无需额外操作。
# save TorchScript just like any other object
with PackageExporter(file_name) as e:
    e.save_pickle("res", "script_model.pkl", scripted_model)
    e.save_pickle("res", "mixed_model.pkl", python_model_with_scripted_submodule)
# load as normal
importer = PackageImporter(file_name)
loaded_script = importer.load_pickle("res", "script_model.pkl")
loaded_mixed = importer.load_pickle("res", "mixed_model.pkl"
解释¶
torch.package 格式概述¶
一个 torch.package 文件是一个 ZIP 压缩文件,通常使用 .pt 扩展名。在 ZIP 压缩文件内部,有两种类型的文件
- 框架文件,放置在 - .data/中。
- 用户文件,即所有其他文件。 
例如,这是一个来自 torchvision 的完整打包 ResNet 模型的样子
resnet
├── .data  # All framework-specific data is stored here.
│   │      # It's named to avoid conflicts with user-serialized code.
│   ├── 94286146172688.storage  # tensor data
│   ├── 94286146172784.storage
│   ├── extern_modules  # text file with names of extern modules (e.g. 'torch')
│   ├── version         # version metadata
│   ├── ...
├── model  # the pickled model
│   └── model.pkl
└── torchvision  # all code dependencies are captured as source files
    └── models
        ├── resnet.py
        └── utils.py
框架文件¶
目录 .data/ 由 torch.package 拥有,其内容被视为私有实现细节。 torch.package 格式不对 .data/ 的内容做出任何保证,但任何更改都将向后兼容(也就是说,更新版本的 PyTorch 将始终能够加载旧的 torch.packages)。
目前,目录 .data/ 包含以下项目
- version:序列化格式的版本号,以便- torch.package导入基础设施知道如何加载此包。
- extern_modules:被视为- extern的模块列表。- extern模块将使用加载环境的系统导入器导入。
- *.storage:序列化张量数据。
.data
├── 94286146172688.storage
├── 94286146172784.storage
├── extern_modules
├── version
├── ...
用户文件¶
存档中的所有其他文件都是由用户放置的。布局与 Python 常规包 相同。要深入了解 Python 打包的工作原理,请参阅 这篇论文(它略微过时,因此请使用 Python 参考文档 仔细检查实现细节)。
<package root>
├── model  # the pickled model
│   └── model.pkl
├── another_package
│   ├── __init__.py
│   ├── foo.txt         # a resource file , see importlib.resources
│   └── ...
└── torchvision
    └── models
        ├── resnet.py   # torchvision.models.resnet
        └── utils.py    # torchvision.models.utils
 torch.package 如何查找代码的依赖项¶
分析对象的依赖项¶
当您发出 save_pickle(obj, ...) 调用时, PackageExporter 将正常地将对象腌制。然后,它使用 pickletools 标准库模块来解析腌制字节码。
在 pickle 中,对象会与一个 GLOBAL 操作码一起保存,该操作码描述了在何处可以找到对象类型的实现,例如
GLOBAL 'torchvision.models.resnet Resnet`
依赖关系解析器将收集所有 GLOBAL 操作码,并将它们标记为 pickle 对象的依赖项。有关 pickling 和 pickle 格式的更多信息,请参阅 Python 文档。
分析模块的依赖关系¶
当一个 Python 模块被识别为依赖项时,torch.package 会遍历模块的 Python AST 表示,并查找导入语句,完全支持标准形式:from x import y,import z,from w import v as u 等。当遇到这些导入语句之一时,torch.package 会将导入的模块注册为依赖项,然后以相同的方式解析这些依赖项。
注意:AST 解析对 __import__(...) 语法支持有限,不支持 importlib.import_module 调用。一般来说,你不应该期望 torch.package 检测到动态导入。
依赖关系管理¶
torch.package 会自动查找代码和对象所依赖的 Python 模块。这个过程称为依赖关系解析。对于依赖关系解析器找到的每个模块,您必须指定要采取的操作。
允许的操作有
- intern:将此模块放入包中。
- extern:将此模块声明为包的外部依赖项。
- mock:模拟此模块。
- deny:依赖于此模块将在包导出期间引发错误。
最后,还有一个重要的操作,它在技术上并不属于 torch.package
- 重构:删除或更改代码中的依赖项。 
请注意,操作仅定义在整个 Python 模块上。无法仅打包模块中的一个函数或类,而将其他部分排除在外。这是设计使然。Python 并未在模块中定义的对象之间提供清晰的边界。唯一定义的依赖项组织单元是模块,因此 torch.package 使用了它。
操作使用模式应用于模块。模式可以是模块名称("foo.bar")或通配符(如 "foo.**")。您可以使用 PackageExporter 上的方法将模式与操作关联起来,例如:
my_exporter.intern("torchvision.**")
my_exporter.extern("numpy")
如果模块与模式匹配,则将相应的操作应用于它。对于给定的模块,将按定义顺序检查模式,并将执行第一个操作。
intern¶
如果模块被 intern-ed,它将被放置到包中。
此操作是您的模型代码,或您要打包的任何相关代码。例如,如果您尝试打包来自 torchvision 的 ResNet,您需要 intern 模块 torchvision.models.resnet。
在包导入时,当打包的代码尝试导入 intern-ed 模块时,PackageImporter 将在您的包中查找该模块。如果找不到该模块,将引发错误。这确保了每个 PackageImporter 与加载环境隔离 - 即使您在包和加载环境中都拥有 my_interned_module,PackageImporter 仅使用包中的版本。
注意:只有 Python 源代码模块可以被 intern。其他类型的模块,例如 C 扩展模块和字节码模块,如果尝试 intern 它们,将会引发错误。这些类型的模块需要被 mock 或 extern。
extern¶
如果一个模块被 extern,它将不会被打包。相反,它将被添加到此包的外部依赖项列表中。您可以在 package_exporter.extern_modules 中找到此列表。
在包导入时,当打包的代码尝试导入一个 extern 模块时,PackageImporter 将使用默认的 Python 导入器来查找该模块,就像您执行 importlib.import_module("my_externed_module") 一样。如果它找不到该模块,将引发错误。
通过这种方式,您可以从包中依赖于第三方库,例如 numpy 和 scipy,而无需将它们也打包。
警告:如果任何外部库以向后不兼容的方式更改,您的包可能无法加载。如果您需要为您的包提供长期可重复性,请尝试限制您对 extern 的使用。
mock¶
如果一个模块被mock,它将不会被打包。取而代之的是,一个存根模块将被打包到它的位置。存根模块将允许你从它中检索对象(这样from my_mocked_module import foo不会报错),但任何对该对象的调用都会抛出NotImplementedError。
mock应该用于你“知道”在加载的包中不需要的代码,但你仍然希望在非打包的内容中可用。例如,初始化/配置代码,或仅用于调试/训练的代码。
警告:一般来说,mock应该作为最后的手段使用。它在打包代码和非打包代码之间引入了行为差异,这可能会导致以后的混淆。最好重构你的代码以删除不需要的依赖项。
重构¶
管理依赖项的最佳方法是根本没有依赖项!通常,代码可以被重构以删除不必要的依赖项。以下是一些编写具有干净依赖项的代码的指南(这些指南通常也是良好的实践!)。
只包含你使用的部分。不要在你的代码中留下未使用的导入。依赖项解析器不够智能,无法判断它们是否确实未使用,并且会尝试处理它们。
限定你的导入。例如,不要写import foo,然后使用foo.bar.baz,最好写from foo.bar import baz。这更精确地指定了你的真实依赖项(foo.bar),并让依赖项解析器知道你不需要所有的foo。
将具有不相关功能的大文件拆分成更小的文件。如果你的utils模块包含各种不相关功能,那么任何依赖于utils的模块都需要引入大量不相关的依赖项,即使你只需要其中的一小部分。最好定义单一用途的模块,这些模块可以独立于彼此打包。
模式¶
模式允许您使用方便的语法指定模块组。模式的语法和行为遵循 Bazel/Buck glob()。
我们尝试与模式匹配的模块称为候选模块。候选模块由用分隔符字符串分隔的段列表组成,例如 foo.bar.baz。
模式包含一个或多个段。段可以是
- 文字字符串(例如 - foo),完全匹配。
- 包含通配符的字符串(例如 - torch或- foo*baz*)。通配符匹配任何字符串,包括空字符串。
- 双通配符 ( - **)。这匹配零个或多个完整段。
示例
- torch.**:匹配- torch及其所有子模块,例如- torch.nn和- torch.nn.functional。
- torch.*:匹配- torch.nn或- torch.functional,但不匹配- torch.nn.functional或- torch
- torch*.**:匹配- torch、- torchvision及其所有子模块
在指定操作时,您可以传递多个模式,例如:
exporter.intern(["torchvision.models.**", "torchvision.utils.**"])
如果模块与任何模式匹配,则它将与该操作匹配。
您还可以指定要排除的模式,例如:
exporter.mock("**", exclude=["torchvision.**"])
如果模块与任何排除模式匹配,则它将不会与该操作匹配。在此示例中,我们模拟除 torchvision 及其子模块之外的所有模块。
当一个模块可能与多个操作匹配时,将执行第一个定义的操作。
torch.package 尖锐边缘¶
避免在模块中使用全局状态¶
Python 使得在模块级作用域绑定对象和运行代码变得非常容易。这通常是可以的——毕竟,函数和类也是以这种方式绑定到名称的。但是,当您在模块作用域定义一个对象并打算对其进行修改时,事情就会变得更加复杂,从而引入了可变的全局状态。
可变的全局状态非常有用——它可以减少样板代码,允许在表中进行开放式注册等等。但是,除非非常小心地使用,否则它在与 torch.package 一起使用时会导致复杂问题。
每个 PackageImporter 为其内容创建一个独立的环境。这很好,因为它意味着我们可以加载多个包并确保它们彼此隔离,但是当模块以一种假设共享可变全局状态的方式编写时,这种行为会导致难以调试的错误。
如何 torch.package 保持包彼此隔离¶
每个 PackageImporter 实例为其模块和对象创建独立的隔离环境。包中的模块只能导入其他打包的模块,或标记为 extern 的模块。如果您使用多个 PackageImporter 实例来加载单个包,您将获得多个不交互的独立环境。
这是通过使用自定义导入器扩展 Python 的导入基础设施来实现的。 PackageImporter 提供与 importlib 导入器相同的核心 API;即,它实现了 import_module 和 __import__ 方法。
当您调用 PackageImporter.import_module() 时, PackageImporter 将构造并返回一个新模块,就像系统导入器一样。但是, PackageImporter 会修补返回的模块以使用 self(即该 PackageImporter 实例)来满足未来的导入请求,方法是在包中查找而不是搜索用户的 Python 环境。
混淆¶
为了避免混淆(“这个 foo.bar 对象是我的包中的,还是我的 Python 环境中的?”),PackageImporter 会对所有导入的模块的 __name__ 和 __file__ 进行混淆,方法是在它们前面添加一个 *混淆前缀*。
对于 __name__,类似 torchvision.models.resnet18 的名称将变为 <torch_package_0>.torchvision.models.resnet18。
对于 __file__,类似 torchvision/models/resnet18.py 的名称将变为 <torch_package_0>.torchvision/modules/resnet18.py。
名称混淆有助于避免不同包之间模块名称的意外冲突,并通过使堆栈跟踪和打印语句更清晰地显示它们是引用打包代码还是非打包代码来帮助您进行调试。有关混淆的开发者面向细节,请参阅 torch/package/ 中的 mangling.md。
API 参考¶
- class torch.package.PackagingError(dependency_graph, debug=False)[source]¶
- 当导出包时出现问题时,会引发此异常。 - PackageExporter将尝试收集所有错误并一次性向您展示它们。
- class torch.package.PackageExporter(f, importer=<torch.package.importer._SysImporter object>, debug=False)[source]¶
- 导出器允许您将代码包、腌制的 Python 数据以及任意二进制和文本资源写入一个自包含的包中。 - 导入器可以以隔离的方式加载此代码,这样代码将从包中加载,而不是从正常的 Python 导入系统加载。这允许打包 PyTorch 模型代码和数据,以便在服务器上运行或在将来用于迁移学习。 - 包中包含的代码在创建时会从原始源逐个文件复制,文件格式是一个经过特殊组织的 zip 文件。包的未来用户可以解压缩包,并编辑代码以对其进行自定义修改。 - 包的导入器确保模块中的代码只能从包中加载,除了使用 - extern()明确列为外部的模块。zip 存档中的- extern_modules文件列出了包外部依赖的所有模块。这可以防止“隐式”依赖关系,即包在本地运行是因为它正在导入一个本地安装的包,但在将包复制到另一台机器时失败。- 当源代码被添加到包中时,导出器可以选择性地扫描它以查找进一步的代码依赖关系 ( - dependencies=True)。它查找导入语句,解析对限定模块名的相对引用,并执行用户指定的动作(参见:- extern()、- mock()和- intern())。- all_paths(src, dst)[source]¶
- 返回子图的点表示
- 它包含从 src 到 dst 的所有路径。 
 - 返回值
- 包含从 src 到 dst 的所有路径的点表示。 (https://graphviz.cn/doc/info/lang.html) 
- 返回类型
 
 - close()[source]¶
- 将包写入文件系统。任何在 - close()之后调用的方法现在都是无效的。最好使用资源保护语法。- with PackageExporter("file.zip") as e: ... 
 - deny(include, *, exclude=())[source]¶
- 从包可以导入的模块列表中,阻止名称与给定 glob 模式匹配的模块。如果发现对任何匹配包的依赖关系,则会引发 - PackagingError。
 - extern(include, *, exclude=(), allow_empty=True)[source]¶
- 将 - module包含在包可以导入的外部模块列表中。这将阻止依赖项发现将其保存到包中。导入器将直接从标准导入系统加载外部模块。外部模块的代码也必须存在于加载包的进程中。- 参数
- include (Union[List[str], str]) – 字符串,例如 - "my_package.my_subpackage",或要外部化的模块名称的字符串列表。这也可以是 glob 风格的模式,如- mock()中所述。
- exclude (Union[List[str], str]) – 一个可选模式,用于排除与 include 字符串匹配的某些模式。 
- allow_empty (bool) – 一个可选标志,指定通过对 - extern方法的调用指定的外部模块是否必须在打包期间与某些模块匹配。如果使用- allow_empty=False添加了外部模块 glob 模式,并且在任何模块匹配该模式之前调用了- close()(显式或通过- __exit__),则会抛出异常。如果- allow_empty=True,则不会抛出此类异常。
 
 
 - intern(include, *, exclude=(), allow_empty=True)[source]¶
- 指定要打包的模块。要包含在包中并递归处理其依赖项,模块必须与某些 - intern模式匹配。- 参数
- include (Union[List[str], str]) – 一个字符串,例如“my_package.my_subpackage”,或要外部化的模块名称的字符串列表。这也可以是一个 glob 风格的模式,如 - mock()中所述。
- exclude (Union[List[str], str]) – 一个可选模式,用于排除与 include 字符串匹配的某些模式。 
- allow_empty (bool) – 一个可选标志,指定此调用 - intern方法指定的 intern 模块是否必须在打包期间与某个模块匹配。如果使用- allow_empty=False添加了- intern模块 glob 模式,并且在任何模块匹配该模式之前调用了- close()(显式或通过- __exit__),则会抛出异常。如果- allow_empty=True,则不会抛出此类异常。
 
 
 - mock(include, *, exclude=(), allow_empty=True)[source]¶
- 用模拟实现替换一些必需的模块。模拟的模块将为从其访问的任何属性返回一个伪对象。因为我们逐文件复制,所以依赖关系解析有时会找到模型文件导入但其功能从未使用过的文件(例如自定义序列化代码或训练助手)。使用此函数模拟此功能,而无需修改原始代码。 - 参数
- include (Union[List[str], str]) – - 一个字符串,例如 - "my_package.my_subpackage",或要模拟的模块名称的字符串列表。字符串也可以是 glob 样式的模式字符串,它可能匹配多个模块。任何匹配此模式字符串的必需依赖项将被自动模拟。- 示例
- 'torch.**'– 匹配- 'torch'和 torch 的所有子模块,例如- 'torch.nn'和- 'torch.nn.functional'- 'torch.*'– 匹配- 'torch.nn'或- 'torch.functional',但不匹配- 'torch.nn.functional'
 
- exclude (Union[List[str], str]) – 一个可选模式,用于排除与包含字符串匹配的某些模式。例如, - include='torch.**', exclude='torch.foo'将模拟所有 torch 包,除了- 'torch.foo',默认值为- []。
- allow_empty (bool) – 一个可选标志,用于指定此调用对 - mock()方法指定的模拟实现是否必须与打包期间的某些模块匹配。如果使用- allow_empty=False添加模拟,并且调用了- close()(显式或通过- __exit__),并且模拟尚未与正在导出的包使用的模块匹配,则会抛出异常。如果- allow_empty=True,则不会抛出此类异常。
 
 
 - register_extern_hook(hook)[source]¶
- 在导出器上注册外部钩子。 - 每次模块与 - extern()模式匹配时,都会调用该钩子。它应该具有以下签名- hook(exporter: PackageExporter, module_name: str) -> None - 钩子将按注册顺序调用。 - 返回值
- 一个句柄,可用于通过调用 - handle.remove()来删除添加的钩子。
- 返回类型
- torch.utils.hooks.RemovableHandle
 
 - register_intern_hook(hook)[source]¶
- 在导出器上注册一个实习钩子。 - 每次模块与 - intern()模式匹配时,都会调用该钩子。它应该具有以下签名- hook(exporter: PackageExporter, module_name: str) -> None - 钩子将按注册顺序调用。 - 返回值
- 一个句柄,可用于通过调用 - handle.remove()来删除添加的钩子。
- 返回类型
- torch.utils.hooks.RemovableHandle
 
 - register_mock_hook(hook)[source]¶
- 在导出器上注册一个模拟钩子。 - 每次模块与 - mock()模式匹配时,都会调用该钩子。它应该具有以下签名- hook(exporter: PackageExporter, module_name: str) -> None - 钩子将按注册顺序调用。 - 返回值
- 一个句柄,可用于通过调用 - handle.remove()来删除添加的钩子。
- 返回类型
- torch.utils.hooks.RemovableHandle
 
 - save_module(module_name, dependencies=True)[source]¶
- 将 - module的代码保存到包中。模块的代码使用- importers路径来查找模块对象,然后使用其- __file__属性来查找源代码。
 - save_pickle(package, resource, obj, dependencies=True, pickle_protocol=3)[source]¶
- 使用 pickle 将 Python 对象保存到存档中。等效于 - torch.save(),但保存到存档而不是独立文件。标准 pickle 不会保存代码,只会保存对象。如果- dependencies为 true,此方法还会扫描腌制对象以查找哪些模块是重建它们所必需的,并保存相关代码。- 为了能够保存一个对象,其中 - type(obj).__name__为- my_module.MyObject,- my_module.MyObject必须根据- importer顺序解析为对象的类。在保存先前已打包的对象时,导入器的- import_module方法需要出现在- importer列表中才能使此方法起作用。
 - save_source_file(module_name, file_or_directory, dependencies=True)[source]¶
- 将本地文件系统 - file_or_directory添加到源包中,以提供- module_name的代码。- 参数
- module_name (str) – 例如 - "my_package.my_subpackage",代码将被保存以提供此包的代码。
- file_or_directory (str) – 代码文件或目录的路径。当为目录时,目录中的所有 Python 文件将使用 - save_source_file()递归地复制。如果文件名为- "/__init__.py",则代码将被视为一个包。
- dependencies (bool, optional) – 如果 - True,我们将扫描源代码以查找依赖项。
 
 
 
- class torch.package.PackageImporter(file_or_buffer, module_allowed=<function PackageImporter.<lambda>>)[source]¶
- 导入器允许您加载由 - PackageExporter编写的包中的代码。代码以隔离的方式加载,使用包中的文件而不是正常的 Python 导入系统。这允许打包 PyTorch 模型代码和数据,以便它可以在服务器上运行或将来用于迁移学习。- 包的导入器确保模块中的代码只能从包内部加载,除了在导出期间明确列出的外部模块。zip 存档中的 - extern_modules文件列出了包外部依赖的所有模块。这可以防止“隐式”依赖关系,即包在本地运行是因为它正在导入本地安装的包,但在将包复制到另一台机器时失败。- __init__(file_or_buffer, module_allowed=<function PackageImporter.<lambda>>)[source]¶
- 打开 - file_or_buffer以进行导入。这将检查导入的包是否仅需要- module_allowed允许的模块。
 - id()[source]¶
- 返回 torch.package 用于区分 - PackageImporter实例的内部标识符。看起来像- <torch_package_0> 
 - import_module(name, package=None)[source]¶
- 如果模块尚未加载,则从包中加载模块,然后返回模块。模块在导入器本地加载,并将出现在 - self.modules而不是- sys.modules中。- 参数
- 返回值
- (可能已经)加载的模块。 
- 返回类型
 
 - load_pickle(package, resource, map_location=None)[source]¶
- 从包中解开资源,使用 - import_module()加载构建对象所需的任何模块。