torch.package¶
torch.package 添加了对创建包含任意
PyTorch 代码的密封包的支持。这些包可以保存、共享,并用于在以后的时间或不同的机器上加载和执行模型,甚至可以使用
torch::deploy 部署到生产环境。
本文档包含教程、操作指南、解释以及API参考,这些内容将帮助您更深入地了解torch.package及其使用方法。
警告
此模块依赖于pickle模块,该模块不安全。仅解包您信任的数据。
有可能构造恶意的pickle数据,这些数据在反序列化期间会执行任意代码。 请勿解包可能来自不可信来源或被篡改的数据。
有关更多信息,请查看 文档 中的 pickle 模块。
如何做……¶
查看包内有什么?¶
将该软件包视为ZIP存档¶
该容器的格式为torch.package,因此任何处理标准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 and PackageExporter 提供了一个 file_structure() 方法,该方法将返回一个可打印
且可查询的 Folder 对象。 Folder 对象是一个简单的目录结构,你可以使用它来探索
当前 torch.package 的内容。
The Folder object itself is directly printable and will print out a file tree representation. To filter what is returned,
use the glob-style include and exclude filtering arguments.
with PackageExporter('my_package.pt') as pe:
pe.save_pickle('models', 'model_1.pkl', mod)
# can limit printed items with include/exclude args
print(pe.file_structure(include=["**/utils.py", "**/*.pkl"], exclude="**/*.storages"))
importer = PackageImporter('my_package.pt')
print(importer.file_structure()) # will print out all files
Output:
# filtered with glob pattern:
# include=["**/utils.py", "**/*.pkl"], exclude="**/*.storages"
─── 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
You can also query Folder objects with the has_file() method.
exporter_file_structure = exporter.file_structure()
found: bool = exporter_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/tens.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__。
Steps:
在目标类上定义方法
__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)
print(pe.file_structure())
pi = PackageImporter('foo_package.pt')
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
在我的源代码中测试它是否在包内执行?¶
A 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_pacakge).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")
API importlib.resources 允许从打包代码内部访问资源。
# 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!", equivalient 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 文件是一个通常使用 .pt 扩展名的 ZIP 归档文件。在 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:class:`PackageImporter`. ``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格式的更多信息,请参阅 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")或通配符(如 "foo.**")。您使用PackageImporter的方法将模式与操作关联,例如
my_exporter.intern("torchvision.**")
my_exporter.extern("numpy")
如果一个模块匹配了某个模式,则对该模块应用相应的操作。对于给定的模块,按定义的顺序检查模式,并执行第一个操作。
intern¶
如果一个模块被 intern-化,它将被放入包中。
此操作是您的模型代码,或任何您想要打包的相关代码。例如,如果您正在尝试打包来自torchvision的ResNet,
您需要intern模块torchvision.models.resnet。
在导入包时,当您的打包代码尝试导入一个intern-ed模块时,PackageImporter将在您的包内查找该模块。
如果找不到该模块,则会引发错误。这确保了每个PackageImporter与其加载环境隔离——即使您在包和加载环境中都有my_interned_module可用,
PackageImporter也只会使用包中的版本。
注意:只有Python源模块可以被intern-化。其他类型的模块,比如C扩展模块和字节码模块,在尝试对其进行intern时会引发错误。这些类型的模块需要被mock-化或extern-化。
extern¶
如果一个模块被 extern-ed,它将不会被打包。相反,它将被添加到此包的外部依赖列表中。您可以在 package_exporter.extern_modules 上找到这个列表。
在导入包时,当时间打包的代码尝试导入一个 extern-ed 模块时,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*)。通配符匹配任何字符串,包括空字符串。两个通配符 (
**)。这可以匹配零个或多个完整段落。
Examples:
torch.**: 匹配torch及其所有子模块,例如torch.nn和torch.nn.functional。torch.*: 匹配torch.nn或torch.functional,但不是torch.nn.functional或torchtorch*.**: 匹配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 导入器相同的 core 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。
名称混淆有助于避免在不同包之间意外地重用模块名称,并通过使堆栈跟踪和打印语句更清晰地显示它们是否引用了打包代码来帮助您进行调试。有关名称混淆的开发者详细信息,请参阅mangling.md中的torch/package/。
API 参考¶
-
class
torch.package.PackagingError(dependency_graph)[source]¶ 此异常在导出包时出现问题时抛出。
PackageExporter将尝试收集所有错误并一次性呈现给您。
-
class
torch.package.PackageExporter(f, importer=<torch.package.importer._SysImporter object>)[source]¶ 导出器允许您将代码包、序列化的 Python 数据以及任意的二进制和文本资源写入一个独立的包中。
导入可以以一种封闭的方式加载此代码,使得代码是从包中加载而不是通过正常的 Python 导入系统加载。这允许打包 PyTorch 模型代码和数据,以便可以在服务器上运行或将来用于迁移学习。
包中包含的代码在创建时会按文件逐一复制自原始来源,文件格式是一个特别组织的压缩文件。未来的用户可以解压该包,并编辑代码以进行自定义修改。
包导入程序确保模块中的代码只能从包内加载,除非明确列出使用
extern()作为外部模块。 压缩文件中的extern_modules列出了该包所依赖的所有外部模块。 这可以防止因导入本地安装的包而导致在本地运行正常但在复制到另一台机器时失败的“隐式”依赖。当源代码被添加到包中时,导出器可以选择性地扫描它 以查找进一步的代码依赖项 (
dependencies=True)。它会查找导入语句, 解析相对引用为完整的模块名称,并执行用户指定的操作 (参见:extern(),mock()和intern())。-
__init__(f, importer=<torch.package.importer._SysImporter object>)[source]¶ 创建一个导出器。
- Parameters
f – 导出的目标位置。可以是包含文件名的
string/Path对象 或一个二进制 I/O 对象。导入器 – 如果传递了一个单独的导入器,则使用该导入器来搜索模块。 如果传递了一组导入器,将从中构造一个
OrderedImporter。
-
all_paths(src, dst)[source]¶ - Return a dot representation of the subgraph
具有从 src 到 dst 的所有路径。
- Returns
包含从源到目标的所有路径的点表示。 (https://graphviz.org/doc/info/lang.html)
-
close()[source]¶ 将包写入文件系统。任何在
close()之后的调用现在无效。 建议使用资源保护语法:with PackageExporter("file.zip") as e: ...
-
deny(include, *, exclude=())[source]¶ 从可以导入的模块列表中排除与给定通配符模式匹配名称的模块。 如果发现对任何匹配包的依赖关系,则会引发一个
PackagingError异常。
-
extern(include, *, exclude=(), allow_empty=True)[source]¶ 在可以导入的外部模块列表中包含
module。 这将防止依赖项发现将其保存到包中。导入器将直接从标准导入系统加载外部模块。 外部模块的代码也必须存在于加载包的过程中。- Parameters
包含 (Union[List[str], str]) – 一个字符串,例如
"my_package.my_subpackage",或者字符串列表 用于指定要外部化的模块名称。这也可以是一个类似glob的模式,如mock()中所述。allow_empty (布尔值) – 一个可选标志,指定此调用中指定的外部模块是否必须在打包期间匹配到某个模块
extern方法。如果使用allow_empty=False添加了外部模块通配符模式,并且在任何模块匹配该模式之前调用了close()(无论是显式调用还是通过__exit__调用),则会抛出异常。如果为allow_empty=True,则不会抛出此类异常。
-
intern(include, *, exclude=(), allow_empty=True)[source]¶ 指定要打包的模块。一个模块必须匹配某些
intern模式,才能被包含在包中,并递归处理其依赖项。- Parameters
-
mock(include, *, exclude=(), allow_empty=True)[source]¶ 用一个模拟实现替换一些所需的模块。模拟模块将返回一个假对象,用于访问其任何属性。因为我们是逐文件复制的,依赖解析有时会找到由模型文件导入但从未使用的文件(例如自定义序列化代码或训练助手)。 使用此函数来模拟这些功能,而无需修改原始代码。
- Parameters
例如一个字符串
"my_package.my_subpackage",或者字符串列表 用于指定要模拟的模块名称。字符串也可以是一个通配符样式模式 字符串,可能匹配多个模块。任何符合此模式字符串的必需依赖项将被自动模拟。- Examples :
'torch.**'– 匹配torch以及 torch 的所有子模块,例如'torch.nn'和'torch.nn.functional''torch.*'– 匹配'torch.nn'或'torch.functional',但不是'torch.nn.functional'
排除 (Union[List[str], str]) – 可选的模式,用于排除与包含字符串匹配的一些模式。 例如:
include='torch.**', exclude='torch.foo'将模拟所有 torch 包,除了'torch.foo', 默认值为[]。allow_empty (布尔值) – 一个可选标志,用于指定此调用中指定的模拟实现是否必须在打包时匹配到某个模块
mock()方法。如果以allow_empty=False添加了一个模拟,并且调用了close()(无论是显式调用还是通过__exit__调用),并且该模拟未匹配到导出包中使用的模块,则会抛出异常。 如果为allow_empty=True,则不会抛出此类异常。
-
register_extern_hook(hook)[source]¶ 在导出器上注册一个外部钩子。
每次模块与一个
extern()模式匹配时,都会调用此钩子。 它应该具有以下签名:hook(exporter: PackageExporter, module_name: str) -> None
钩子将按注册顺序被调用。
- Returns
一个可以用来通过调用
handle.remove()删除添加钩的句柄。- Return type
torch.utils.hooks.RemovableHandle
-
register_intern_hook(hook)[source]¶ 在导出器上注册一个内部钩子。
每次模块与一个
intern()模式匹配时,都会调用此钩子。 它应该具有以下签名:hook(exporter: PackageExporter, module_name: str) -> None
钩子将按注册顺序被调用。
- Returns
一个可以用来通过调用
handle.remove()删除添加钩的句柄。- Return type
torch.utils.hooks.RemovableHandle
-
register_mock_hook(hook)[source]¶ 在导出器上注册一个模拟钩子。
每次模块匹配到
mock()模式时,钩子将被调用。 它应该具有以下签名:hook(exporter: PackageExporter, module_name: str) -> None
钩子将按注册顺序被调用。
- Returns
一个可以用来通过调用
handle.remove()删除添加钩的句柄。- Return type
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为真,此方法还将扫描需要用于重建这些对象的模块,并保存相关的代码。为了能够保存一个对象,其中
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的代码。- Parameters
模块名称 (字符串) – 例如
"my_package.my_subpackage", 代码将被保存以提供此包的代码。文件或目录 (str) – 代码的文件或目录路径。当为目录时,该目录下的所有Python文件会被递归复制使用
save_source_file()。如果文件名为"/__init__.py",则将其视为一个包。依赖项 (布尔值, 可选) – 如果
True,我们将扫描源以查找依赖项。
-
-
class
torch.package.PackageImporter(file_or_buffer, module_allowed=<function PackageImporter.<lambda>>)[source]¶ 导入器允许您加载为
PackageExporter包编写的代码。 代码以封闭方式加载,使用包中的文件而不是正常的Python导入系统。这使得打包PyTorch模型代码和数据成为可能,以便可以在服务器上运行或将来的迁移学习中使用。包导入器确保模块中的代码只能从包内加载,除非在导出时明确列出为外部模块。 压缩文件中的
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中出现。- Parameters
- Returns
已加载(可能已经加载)的模块。
- Return type
-
load_pickle(package, resource, map_location=None)[source]¶ 从包中反序列化资源,加载构造对象所需的任何模块 使用
import_module()。
-