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或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 匯入器相同的核心 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.org/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()載入構造物件所需的任何模組。