torch.package
Создано: 10 июня 2025 г. | Последнее обновление: 13 апреля 2026 г.
torch.package добавляет поддержку создания пакетов, содержащих артефакты и произвольный код PyTorch. Эти пакеты можно сохранять, передавать другим, использовать для загрузки и запуска моделей позднее или на другом компьютере, а также развертывать в рабочей среде с помощью torch::deploy.
В этом документе представлены руководства, инструкции, пояснения и справочник по API, которые помогут вам узнать больше о torch.package и о том, как им пользоваться.
Предупреждение
Этот модуль зависит от модуля pickle, который небезопасен. Распаковывайте только те данные, которым доверяете.
Можно создать вредоносные данные pickle, которые будут выполнять произвольный код при десериализации. Никогда не распаковывайте данные, которые могли поступить из ненадежного источника или быть изменены.
Дополнительную информацию см. в документации по модулю pickle.
-
- Посмотреть содержимое пакета?
- Узнать, почему тот или иной модуль включен в качестве зависимости?
- Включить в пакет произвольные ресурсы и получить к ним доступ позднее?
- Настроить способ упаковки класса?
- Проверить в исходном коде, выполняется ли он внутри пакета?
- Добавить исправления кода в пакет?
- Получить доступ к содержимому пакета из упакованного кода?
- Различать упакованный и неупакованный код?
- Повторно экспортировать импортированный объект?
- Справочник по API
- Утилиты анализа
Руководства
Упаковка первой модели
Руководство по упаковке и распаковке простой модели доступно в Colab. Выполнив это упражнение, вы познакомитесь с базовым API для создания пакетов Torch и работы с ними.
Как мне…
Посмотреть содержимое пакета?
Работать с пакетом как с 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
Использовать API file_structure()
PackageImporter предоставляет метод file_structure(), который возвращает доступный для печати и запросов объект Directory. Объект Directory представляет собой простую структуру каталогов, с помощью которой можно изучить текущее содержимое torch.package.
Сам объект Directory можно вывести на печать: он отображает дерево файлов. Чтобы отфильтровать возвращаемые данные, используйте аргументы фильтрации include и exclude, поддерживающие шаблоны glob.
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
Кроме того, к объектам Directory можно отправлять запросы с помощью метода has_file().
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__, а также функцию распаковки. Это похоже на определение __reduce__ для стандартного процесса сериализации Python с помощью pickle.
Действия:
- Определите метод
__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 реализует API importlib.resources для доступа к ресурсам внутри пакета.
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. Однако из упакованного кода также можно получить доступ к самому родительскому экземпляру :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 сериализует объект с помощью pickle обычным образом. Затем он использует стандартный модуль библиотеки pickletools для разбора байткода pickle.
При сериализации pickle объект сохраняется вместе с инструкцией GLOBAL, которая указывает, где найти реализацию типа объекта, например:
GLOBAL 'torchvision.models.resnet Resnet`
Механизм разрешения зависимостей соберёт все инструкции GLOBAL и пометит их как зависимости сериализованного объекта. Дополнительные сведения о сериализации pickle и формате pickle см. в документации Python.
Анализ зависимостей модуля
Когда модуль Python определяется как зависимость, torch.package обходит представление AST модуля Python и ищет инструкции импорта, полностью поддерживая стандартные формы: 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, он будет добавлен в пакет.
Это действие предназначено для кода вашей модели или любого связанного кода, который вы хотите включить в пакет. Например, если вы пытаетесь упаковать ResNet из torchvision, вам нужно будет поместить модуль torchvision.models.resnet в intern.
При импорте пакета, когда ваш упакованный код пытается импортировать модуль, помещённый в intern, PackageImporter будет искать этот модуль внутри пакета. Если модуль не удастся найти, возникнет ошибка. Это гарантирует, что каждый PackageImporter изолирован от среды загрузки: даже если my_interned_module доступен и в вашем пакете, и в среде загрузки, PackageImporter будет использовать только версию из пакета.
Примечание: в intern можно помещать только исходные модули Python. Другие типы модулей, например модули расширений 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, должен будет включить множество несвязанных зависимостей, даже если вам нужна только небольшая его часть. Лучше создавать модули, предназначенные для одной задачи, которые можно упаковывать независимо друг от друга.
Шаблоны
Шаблоны позволяют удобно задавать группы модулей. Синтаксис и поведение шаблонов соответствуют функции glob() из Bazel/Buck.
Модуль, который мы пытаемся сопоставить с шаблоном, называется кандидатом. Кандидат состоит из списка сегментов, разделённых строкой-разделителем, например 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 предоставляет тот же основной API, что и импортёр importlib, а именно реализует методы 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, debug=False)[исходный код] -
Это исключение возникает при проблемах с экспортом пакета.
PackageExporterпопытается собрать все ошибки и показать их вам одновременно.
-
class torch.package.EmptyMatchError[исходный код] -
Это исключение возникает, когда заглушка или внешняя зависимость помечена как
allow_empty=False, но при упаковке ей не соответствует ни один модуль.
-
class torch.package.PackageExporter(f, importer=<torch.package.importer._SysImporter object>, debug=False)[исходный код] -
Экспортеры позволяют записывать пакеты кода, сериализованные данные Python, а также произвольные бинарные и текстовые ресурсы в автономный пакет.
Импортеры могут загружать этот код герметичным способом, при котором код загружается из пакета, а не через обычную систему импорта Python. Это позволяет упаковывать код и данные модели PyTorch, чтобы запускать её на сервере или использовать в будущем для переноса обучения.
При создании код в пакетах копируется из исходного кода файл за файлом, а формат файла представляет собой специально организованный zip-архив. В дальнейшем пользователи пакета могут распаковать его и отредактировать код для внесения собственных изменений.
Импортер пакетов гарантирует, что код в модуле можно загрузить только из пакета, за исключением модулей, явно указанных как внешние с помощью
extern(). Файлextern_modulesв zip-архиве содержит список всех модулей, от которых пакет зависит извне. Это предотвращает «неявные» зависимости: пакет может работать локально, поскольку импортирует установленный локально пакет, но перестать работать после копирования на другую машину.При добавлении исходного кода в пакет экспортер может дополнительно просканировать его на предмет зависимостей от другого кода (
dependencies=True). Он ищет инструкции импорта, разрешает относительные ссылки в полные имена модулей и выполняет действие, заданное пользователем (см.:extern(),mock()иintern()).-
__init__(f, importer=<torch.package.importer._SysImporter object>, debug=False)[исходный код] -
Создать экспортер.
- Параметры:
-
-
f (str | PathLike[str] | IO[bytes]) – Место, куда будет выполнен экспорт. Это может быть объект
string/Path, содержащий имя файла, или объект двоичного ввода-вывода. -
importer (Importer | Sequence[Importer]) – Если передан один импортер, он используется для поиска модулей. Если передана последовательность импортеров, из них будет создан
OrderedImporter. - debug (bool) – Если установлено значение True, добавлять пути к неисправным модулям в PackagingErrors.
-
f (str | PathLike[str] | IO[bytes]) – Место, куда будет выполнен экспорт. Это может быть объект
-
add_dependency(module_name, dependencies=True)[исходный код] -
Добавить модуль в граф зависимостей согласно шаблонам, заданным пользователем.
-
all_paths(src, dst)[исходный код] -
- Вернуть представление подграфа в формате dot
-
со всеми путями от src до dst.
- Возвращает:
-
Представление в формате dot со всеми путями от src до dst. (https://graphviz.org/doc/info/lang.html)
- Тип возвращаемого значения:
-
close()[исходный код] -
Записать пакет в файловую систему. После вызова
close()любые дальнейшие вызовы недопустимы. Предпочтительно использовать синтаксис менеджера ресурсов:with PackageExporter("file.zip") as e: ...
-
denied_modules()[исходный код] -
Вернуть все модули, которые в данный момент запрещены.
-
deny(include, *, exclude=())[исходный код] -
Добавить в черный список модули, имена которых соответствуют заданным шаблонам glob, исключив их из списка модулей, которые может импортировать пакет. Если обнаружена зависимость от любого соответствующего шаблону пакета, возникает исключение
PackagingError.- Параметры:
-
-
include (list[str] | str) – Строка, например
"my_package.my_subpackage", или список строк с именами модулей, которые следует сделать внешними. Также можно указать шаблон glob, как описано вmock(). - exclude (list[str] | str) – Необязательный шаблон, исключающий некоторые шаблоны, соответствующие строке include.
-
include (list[str] | str) – Строка, например
-
dependency_graph_string()[исходный код] -
Возвращает строковое представление ориентированного графа зависимостей пакета.
- Возвращает:
-
Строковое представление зависимостей пакета.
- Тип возвращаемого значения:
-
extern(include, *, exclude=(), allow_empty=True)[исходный код] -
Добавить
moduleв список внешних модулей, которые может импортировать пакет. Это не позволит обнаружению зависимостей сохранять его в пакете. Импортер будет загружать внешний модуль напрямую через стандартную систему импорта. Код внешних модулей также должен присутствовать в процессе, загружающем пакет.- Параметры:
-
-
include (list[str] | str) – Строка, например
"my_package.my_subpackage", или список строк с именами модулей, которые следует сделать внешними. Также можно указать шаблон glob, как описано вmock(). - exclude (list[str] | str) – Необязательный шаблон, исключающий некоторые шаблоны, соответствующие строке include.
-
allow_empty (bool) – Необязательный флаг, определяющий, должен ли хотя бы один модуль соответствовать внешним модулям, заданным этим вызовом метода
externво время упаковки. Если с помощьюallow_empty=Falseдобавлен шаблон glob для внешнего модуля, аclose()вызван (явно или через__exit__) до того, как этому шаблону будет соответствовать какой-либо модуль, возникает исключение. Еслиallow_empty=True, такое исключение не возникает.
-
include (list[str] | str) – Строка, например
-
externed_modules()[исходный код] -
Вернуть все модули, которые в данный момент являются внешними.
-
get_rdeps(module_name)[исходный код] -
Вернуть список всех модулей, зависящих от модуля
module_name.
-
get_unique_id()[исходный код] -
Получить идентификатор. Гарантируется, что этот идентификатор будет выдан для данного пакета только один раз.
- Тип возвращаемого значения:
-
intern(include, *, exclude=(), allow_empty=True)[исходный код] -
Указать модули, которые следует включить в пакет. Чтобы модуль попал в пакет и его зависимости обрабатывались рекурсивно, он должен соответствовать какому-либо шаблону
intern.- Параметры:
-
-
include (list[str] | str) – Строка, например “my_package.my_subpackage”, или список строк с именами модулей, которые следует сделать внешними. Также можно указать шаблон glob, как описано в
mock(). - exclude (list[str] | str) – Необязательный шаблон, исключающий некоторые шаблоны, соответствующие строке include.
-
allow_empty (bool) – Необязательный флаг, определяющий, должен ли хотя бы один модуль соответствовать внутренним модулям, заданным этим вызовом метода
internво время упаковки. Если с помощьюallow_empty=Falseдобавлен шаблон glob для модуляintern, аclose()вызван (явно или через__exit__) до того, как этому шаблону будет соответствовать какой-либо модуль, возникает исключение. Еслиallow_empty=True, такое исключение не возникает.
-
include (list[str] | str) – Строка, например “my_package.my_subpackage”, или список строк с именами модулей, которые следует сделать внешними. Также можно указать шаблон glob, как описано в
-
interned_modules()[исходный код] -
Вернуть все модули, которые в данный момент включены в пакет.
-
mock(include, *, exclude=(), allow_empty=True)[исходный код] -
Заменить некоторые необходимые модули имитационной реализацией. Имитируемые модули будут возвращать фиктивный объект при обращении к любому их атрибуту. Поскольку мы копируем файлы по отдельности, при разрешении зависимостей иногда обнаруживаются файлы, импортируемые файлами модели, но функциональность которых никогда не используется (например, пользовательский код сериализации или вспомогательные средства обучения). Используйте эту функцию, чтобы имитировать такую функциональность, не изменяя исходный код.
- Параметры:
-
-
Строка, например
"my_package.my_subpackage", или список строк с именами модулей, для которых нужно создать имитацию. Строки также могут содержать шаблон glob, соответствующий нескольким модулям. Все необходимые зависимости, соответствующие этому шаблону, будут автоматически имитированы.- Примеры:
-
'torch.**'– соответствуетtorchи всем подмодулям torch, например'torch.nn'и'torch.nn.functional''torch.*'– соответствует'torch.nn'или'torch.functional', но не'torch.nn.functional'
-
exclude (list[str] | str) – Необязательный шаблон, исключающий некоторые шаблоны, соответствующие строке include. Например,
include='torch.**', exclude='torch.foo'создаст имитации для всех пакетов torch, кроме'torch.foo'. Значение по умолчанию:[]. -
allow_empty (bool) – Необязательный флаг, определяющий, должна ли хотя бы одна имитируемая реализация, заданная этим вызовом метода
mock(), соответствовать какому-либо модулю во время упаковки. Если имитация добавлена с помощьюallow_empty=False, аclose()вызван (явно или через__exit__) до того, как имитация будет применена к модулю, используемому экспортируемым пакетом, возникает исключение. Еслиallow_empty=True, такое исключение не возникает.
-
-
mocked_modules()[исходный код] -
Вернуть все модули, для которых в данный момент созданы имитации.
-
register_extern_hook(hook)[исходный код] -
Регистрирует внешний хук в экспортере.
Хук будет вызываться каждый раз, когда модуль соответствует шаблону
extern(). Он должен иметь следующую сигнатуру:hook(exporter: PackageExporter, module_name: str) -> None
Хуки вызываются в порядке регистрации.
- Возвращает:
-
Дескриптор, который можно использовать для удаления добавленного хука, вызвав
handle.remove(). - Тип возвращаемого значения:
-
torch.utils.hooks.RemovableHandle
-
register_intern_hook(hook)[исходный код] -
Регистрирует хук включения модулей в экспортере.
Хук будет вызываться каждый раз, когда модуль соответствует шаблону
intern(). Он должен иметь следующую сигнатуру:hook(exporter: PackageExporter, module_name: str) -> None
Хуки вызываются в порядке регистрации.
- Возвращает:
-
Дескриптор, который можно использовать для удаления добавленного хука, вызвав
handle.remove(). - Тип возвращаемого значения:
-
torch.utils.hooks.RemovableHandle
-
register_mock_hook(hook)[исходный код] -
Регистрирует хук имитации в экспортере.
Хук будет вызываться каждый раз, когда модуль соответствует шаблону
mock(). Он должен иметь следующую сигнатуру:hook(exporter: PackageExporter, module_name: str) -> None
Хуки вызываются в порядке регистрации.
- Возвращает:
-
Дескриптор, который можно использовать для удаления добавленного хука, вызвав
handle.remove(). - Тип возвращаемого значения:
-
torch.utils.hooks.RemovableHandle
-
save_binary(package, resource, binary)[исходный код] -
Сохранить необработанные байты в пакет.
-
save_module(module_name, dependencies=True)[исходный код] -
Сохранить код для
moduleв пакет. Код модуля определяется с помощью путиimportersдля поиска объекта модуля, а затем с помощью его атрибута__file__для поиска исходного кода.
-
save_pickle(package, resource, obj, dependencies=True, pickle_protocol=3)[исходный код] -
Сохранить объект Python в архив с помощью pickle. Эквивалентно
torch.save(), но сохраняет объект в архив, а не в отдельный файл. Стандартный pickle сохраняет только объекты, но не код. Еслиdependenciesимеет значение true, этот метод также просканирует сериализованные объекты, чтобы определить необходимые для их восстановления модули, и сохранит соответствующий код.Чтобы сохранить объект, для которого
type(obj).__name__имеет значениеmy_module.MyObject,my_module.MyObjectдолжен разрешаться в класс объекта согласно порядкуimporter. При сохранении ранее упакованных объектов для успешного выполнения этой операции методimport_moduleимпортера должен присутствовать в спискеimporter.- Параметры:
-
-
package (str) – Имя пакета модулей, в который следует поместить этот ресурс (например,
"my_package.my_subpackage"). - resource (str) – Уникальное имя ресурса, используемое для его идентификации при загрузке.
- obj (Any) – Сохраняемый объект; он должен поддерживать сериализацию с помощью pickle.
-
dependencies (bool, optional) – Если
True, мы сканируем исходный код на предмет зависимостей.
-
package (str) – Имя пакета модулей, в который следует поместить этот ресурс (например,
-
-
save_source_file(module_name, file_or_directory, dependencies=True)[исходный код] -
Добавляет локальную файловую систему
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, выполняется поиск зависимостей в исходном коде.
-
module_name (str) – например,
-
save_source_string(module_name, src, is_package=False, dependencies=True)[исходный код] -
Добавляет
srcв качестве исходного кода дляmodule_nameв экспортируемый пакет.- Параметры:
-
-
module_name (str) – например,
my_package.my_subpackage; код будет сохранён для предоставления кода этому пакету. - src (str) – исходный код Python, который нужно сохранить для этого пакета.
-
is_package (bool, optional) – Если
True, этот модуль рассматривается как пакет. У пакетов могут быть подмодули (например,my_package.my_subpackage.my_subsubpackage), а внутри них можно сохранять ресурсы. По умолчанию —False. -
dependencies (bool, optional) – Если
True, выполняется поиск зависимостей в исходном коде.
-
module_name (str) – например,
-
save_text(package, resource, text)[исходный код] -
Сохраняет текстовые данные в пакет.
-
-
class torch.package.PackageImporter(file_or_buffer, module_allowed=<function PackageImporter.<lambda>>)[исходный код] -
Импортёры позволяют загружать код, помещённый в пакеты с помощью
PackageExporter. Код загружается в изолированной среде: используются файлы из пакета, а не обычная система импорта Python. Это позволяет упаковывать код и данные моделей PyTorch, чтобы запускать их на сервере или использовать в будущем для переноса обучения.Импортёр пакетов гарантирует, что код модуля можно загрузить только из пакета, за исключением модулей, явно указанных как внешние при экспорте. Файл
extern_modulesв ZIP-архиве содержит список всех модулей, от которых пакет зависит извне. Это предотвращает появление «неявных» зависимостей, когда пакет работает локально, поскольку импортирует установленный в системе пакет, но перестаёт работать после копирования на другой компьютер.-
__init__(file_or_buffer, module_allowed=<function PackageImporter.<lambda>>)[исходный код] -
Открывает
file_or_bufferдля импорта. Проверяет, что импортируемому пакету требуются только модули, разрешённые с помощьюmodule_allowed- Параметры:
-
-
file_or_buffer (str | PathLike[str] | IO[bytes] | PyTorchFileReader) – файловый объект (должен реализовывать
read(),readline(),tell()иseek()), строка или объектos.PathLike, содержащий имя файла. - module_allowed (Callable[[str], bool], optional) – метод, определяющий, следует ли разрешить модуль, предоставленный извне. Его можно использовать, чтобы гарантировать, что загружаемые пакеты не зависят от модулей, которые не поддерживаются сервером. По умолчанию разрешены любые модули.
-
file_or_buffer (str | PathLike[str] | IO[bytes] | PyTorchFileReader) – файловый объект (должен реализовывать
- Исключения:
-
ImportError – если пакет будет использовать запрещённый модуль.
-
file_structure(*, include='**', exclude=())[исходный код] -
Возвращает представление структуры файлов ZIP-файла пакета.
- Параметры:
-
-
include (list[str] | str) – необязательная строка, например
"my_package.my_subpackage", или необязательный список строк с именами файлов, которые нужно включить в представление ZIP-файла. Это также может быть шаблон в стиле glob, описанный вPackageExporter.mock() - exclude (list[str] | str) – необязательный шаблон, исключающий файлы, имена которых ему соответствуют.
-
include (list[str] | str) – необязательная строка, например
- Возвращает:
- Тип возвращаемого значения:
-
id()[исходный код] -
Возвращает внутренний идентификатор, который torch.package использует для различения экземпляров
PackageImporter. Выглядит так:<torch_package_0>
-
import_module(name, package=None)[исходный код] -
Загружает модуль из пакета, если он ещё не был загружен, а затем возвращает его. Модули загружаются в область видимости импортёра и появляются в
self.modules, а не вsys.modules.- Параметры:
- Возвращает:
-
Загруженный модуль (возможно, уже загруженный ранее).
- Тип возвращаемого значения:
-
load_binary(package, resource)[исходный код] -
Загружает необработанные байты.
-
load_pickle(package, resource, map_location=None)[исходный код] -
Десериализует ресурс из пакета, загружая все модули, необходимые для создания объектов, с помощью
import_module().- Параметры:
- Возвращает:
-
Десериализованный объект.
- Тип возвращаемого значения:
-
Any
-
load_text(package, resource, encoding='utf-8', errors='strict')[исходный код] -
Загружает строку.
- Параметры:
- Возвращает:
-
Загруженный текст.
- Тип возвращаемого значения:
-
python_version()[исходный код] -
Возвращает версию Python, использованную для создания этого пакета.
Примечание: эта функция является экспериментальной и не обеспечивает прямую совместимость. Планируется позднее перенести её в файл блокировки.
- Возвращает:
-
str | Noneверсия Python, например 3.8.9, или None, если для этого пакета версия не была сохранена
-
-
class torch.package.Directory(name, is_dir)[исходный код] -
Представление структуры файлов. Организовано в виде узлов Directory, содержащих списки дочерних узлов Directory. Каталоги пакета создаются вызовом
PackageImporter.file_structure().
Утилиты анализа
-
torch.package.analyze.find_first_use_of_broken_modules.find_first_use_of_broken_modules(exc)[исходный код] -
Находит все неисправные модули в PackagingError и для каждого возвращает путь зависимостей, по которому модуль встретился впервые.
Например, неисправный модуль m.n.o был добавлен в граф зависимостей при обработке a.b.c, а затем снова встретился при обработке d.e.f. Этот метод вернул бы {‘m.n.o’: [‘a’, ‘b’, ‘c’]}
- Параметры:
-
exc (PackagingError) – ошибка PackagingError
- Тип возвращаемого значения:
Возвращает: словарь, сопоставляющий имена неисправных модулей со списками имён модулей в пути.
-
torch.package.analyze.is_from_package.is_from_package(obj)[исходный код] -
Возвращает, был ли объект загружен из пакета.
Примечание: для упакованных объектов из внешних модулей будет возвращено
False.- Тип возвращаемого значения:
-
torch.package.analyze.trace_dependencies.trace_dependencies(callable, inputs)[исходный код] -
Отслеживает выполнение вызываемого объекта, чтобы определить, какие модули он использует.
- Параметры:
-
- callable (Callable[[Any], Any]) – вызываемый объект, выполнение которого нужно отслеживать.
- inputs (Iterable[tuple[Any, ...]]) – входные данные, используемые при отслеживании. Модули, используемые вызываемым объектом при вызове для каждого набора входных данных, объединяются, чтобы определить все модули, используемые этим объектом при упаковке.
- Тип возвращаемого значения:
Возвращает: список имён всех модулей, использованных во время выполнения вызываемого объекта.
© 2026, PyTorch Contributors
PyTorch has a BSD-style license, as found in the LICENSE file.
https://docs.pytorch.org/docs/2.14/package.html