torch.package#
创建时间:2025 年 6 月 10 日 | 最后更新时间:2025 年 7 月 15 日
torch.package
增加了对创建包含 PyTorch 工件和任意 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
之间的所有依赖路径。
如果您只想查看 :class: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 标准。但是,也可以从打包代码中访问父 :class: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)
解释#
torch.package
格式概述#
torch.package
文件是一个 ZIP 存档,通常使用 .pt
扩展名。在 ZIP 存档内,有两种文件:
框架文件,放在
.data/
中。用户文件,即其他所有文件。
例如,这是完全打包的 ResNet 模型来自 torchvision
的样子:
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 字节码。
在 pickle 中,对象会与一个 GLOBAL
操作码一起保存,该操作码描述了在哪里找到对象类型的实现,例如:
GLOBAL 'torchvision.models.resnet Resnet`
依赖项解析器将收集所有 GLOBAL
操作码,并将它们标记为已序列化对象的依赖项。有关序列化和 pickle 格式的更多信息,请参阅 Python 文档。
分析模块的依赖项#
当识别出一个 Python 模块作为依赖项时,torch.package
会遍历该模块的 Python AST 表示,并查找具有完全支持标准形式的导入语句:from x import y
、import z
、from w import v as u
等。当遇到其中一个导入语句时,torch.package
会将导入的模块注册为依赖项,然后这些依赖项本身将以相同的方式通过 AST 遍历进行解析。
注意:AST 解析对 __import__(...)
语法支持有限,并且不支持 importlib.import_module
调用。总的来说,您不应期望 torch.package
会检测到动态导入。
依赖项管理#
torch.package
会自动查找您的代码和对象所依赖的 Python 模块。这个过程称为依赖项解析。对于依赖项解析器找到的每个模块,您必须指定一个要执行的*操作*。
允许的操作包括:
intern
:将此模块放入包中。extern
:将此模块声明为包的外部依赖项。mock
:模拟此模块。deny
:依赖于此模块将在包导出期间引发错误。
最后,还有一个重要的操作严格来说不是 torch.package
的一部分:
重构:删除或更改代码中的依赖项。
请注意,操作仅对整个 Python 模块定义。无法只打包模块中的“某个”函数或类而排除其余部分。这是有意的。Python 不提供模块中定义的类之间的清晰边界。唯一的定义单位是模块,因此 torch.package
使用它。
使用模式将操作应用于模块。模式可以是模块名称("foo.bar"
)或 glob(如 "foo.**"
)。您可以使用 PackageExporter
上的方法将模式与操作关联,例如:
my_exporter.intern("torchvision.**")
my_exporter.extern("numpy")
如果模块匹配模式,则对其应用相应操作。对于给定的模块,将按定义的顺序检查模式,并采取第一个匹配的操作。
intern
#
如果一个模块被 intern
,它将被放入包中。
此操作是您的模型代码,或您想要打包的任何相关代码。例如,如果您试图打包 torchvision
中的 ResNet,您将需要 intern
模块 torchvision.models.resnet。
在包导入时,当您的打包代码尝试导入一个 intern
模块时,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.EmptyMatchError[source]#
当 mock 或 extern 被标记为
allow_empty=False
并且在打包过程中没有与任何模块匹配时,会抛出此异常。
- class torch.package.PackageExporter(f, importer=<torch.package.importer._SysImporter object>, debug=False)[source]#
Exporter 允许您将代码包、序列化的 Python 数据以及任意二进制和文本资源写入一个独立的包。
导入可以以密封的方式加载此代码,使得代码从包中加载而不是从普通的 Python 导入系统中加载。这使得 PyTorch 模型代码和数据可以被打包,以便在服务器上运行或将来用于迁移学习。
包中包含的代码在创建时是逐个文件从原始源复制的,并且文件格式是专门组织的 zip 文件。包的未来用户可以解压包,并编辑代码以执行自定义修改。
包的导入器确保模块中的代码只能从包内部加载,除了通过
extern()
明确列为外部的模块。zip 存档中的extern_modules
文件列出了包外部依赖的所有模块。这可以防止“隐式”依赖,即包在本地运行,因为它导入了本地安装的包,但当包复制到另一台机器时就会失败。当源代码被添加到包中时,导出器可以选择扫描它以查找更多代码依赖项(
dependencies=True
)。它查找导入语句,解析相对引用到限定模块名,并执行用户指定的动作(请参阅:extern()
、mock()
和intern()
)。- all_paths(src, dst)[source]#
- 返回子图的 dot 表示
包含从 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
方法调用指定的内部模块是否必须在打包过程中与某些模块匹配。如果使用allow_empty=False
添加了内部模块 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.nn'
和'torch.nn.functional'
'torch.*'
– 匹配'torch.nn'
或'torch.functional'
,但不匹配'torch.nn.functional'
exclude (Union[List[str], str]) – 一个可选模式,用于排除匹配 include 字符串的一些模式。例如
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,此方法还将扫描 pickled 对象以确定重构它们所需的模块,并保存相关代码。要能够保存一个对象,其中
type(obj).__name__
是my_module.MyObject
,my_module.MyObject
必须根据importer
顺序解析为对象的类。保存先前已打包的对象时,需要importer
列表包含importer
的import_module
方法才能正常工作。
- 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]#
Importer 允许您加载由
PackageExporter
写入包的代码。代码以隔离的方式加载,使用包中的文件而不是常规的 Python import 系统。这允许打包 PyTorch 模型代码和数据,以便可以在服务器上运行,或者将来用于迁移学习。包的 Importer 确保模块中的代码只能从包内加载,除非在导出时显式列为外部的模块。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]#
如果模块尚未加载,则从包中加载该模块,然后返回该模块。模块在 Importer 本地加载,并且将出现在
self.modules
中,而不是sys.modules
中。- 参数
- 返回
已(可能已)加载的模块。
- 返回类型
- load_pickle(package, resource, map_location=None)[source]#
从包中反序列化资源,使用
import_module()
加载构造对象所需的任何模块。