Spec-Zone.ru › PyTorch 2.14

torch.package

Создано: 10 июня 2025 г. | Последнее обновление: 13 апреля 2026 г.

torch.package добавляет поддержку создания пакетов, содержащих артефакты и произвольный код PyTorch. Эти пакеты можно сохранять, передавать другим, использовать для загрузки и запуска моделей позднее или на другом компьютере, а также развертывать в рабочей среде с помощью torch::deploy.

В этом документе представлены руководства, инструкции, пояснения и справочник по API, которые помогут вам узнать больше о torch.package и о том, как им пользоваться.

Предупреждение

Этот модуль зависит от модуля pickle, который небезопасен. Распаковывайте только те данные, которым доверяете.

Можно создать вредоносные данные pickle, которые будут выполнять произвольный код при десериализации. Никогда не распаковывайте данные, которые могли поступить из ненадежного источника или быть изменены.

Дополнительную информацию см. в документации по модулю pickle.

  • Руководства

    • Упаковка первой модели
  • Как мне…

    • Посмотреть содержимое пакета?
    • Узнать, почему тот или иной модуль включен в качестве зависимости?
    • Включить в пакет произвольные ресурсы и получить к ним доступ позднее?
    • Настроить способ упаковки класса?
    • Проверить в исходном коде, выполняется ли он внутри пакета?
    • Добавить исправления кода в пакет?
    • Получить доступ к содержимому пакета из упакованного кода?
    • Различать упакованный и неупакованный код?
    • Повторно экспортировать импортированный объект?
  • Пояснения

    • Обзор формата torch.package
    • Как torch.package находит зависимости вашего кода
    • Управление зависимостями
    • Подводные камни torch.package
    • Как torch.package изолирует пакеты друг от друга
  • Справочник по 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.

Действия:

  1. Определите метод __reduce_package__(self, exporter: PackageExporter) в целевом классе. Этот метод должен сохранять экземпляр класса в пакете и возвращать кортеж с соответствующей функцией распаковки и аргументами, необходимыми для ее вызова. Этот метод вызывается PackageExporter при обнаружении экземпляра целевого класса.
  2. Определите функцию распаковки для класса. Эта функция должна восстановить и вернуть экземпляр класса. Первым параметром сигнатуры функции должен быть экземпляр 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 создаёт независимую среду для своего содержимого. Это удобно: можно загружать несколько пакетов и гарантировать их изоляцию друг от друга. Но если модули написаны в расчёте на совместное изменяемое глобальное состояние, такое поведение может привести к ошибкам, которые трудно отлаживать.

Типы не являются общими для пакетов и среды загрузки

Любой класс, импортированный из PackageImporter, будет версией класса, уникальной для этого импортёра. Например:

from foo import MyClass

my_class_instance = MyClass()

with PackageExporter(f) as exporter:
    exporter.save_module("foo")

importer = PackageImporter(f)
imported_MyClass = importer.import_module("foo").MyClass

assert isinstance(my_class_instance, MyClass)  # works
assert isinstance(my_class_instance, imported_MyClass)  # ERROR!

В этом примере MyClass и imported_MyClass — не один и тот же тип. В этом конкретном примере MyClass и imported_MyClass имеют совершенно одинаковую реализацию, поэтому может показаться, что их можно считать одним и тем же классом. Но представьте, что imported_MyClass взят из старого пакета с совершенно другой реализацией MyClass — в этом случае считать их одним и тем же классом небезопасно.

Внутри каждый импортёр имеет префикс, позволяющий однозначно идентифицировать классы:

print(MyClass.__name__)  # prints "foo.MyClass"
print(imported_MyClass.__name__)  # prints <torch_package_0>.foo.MyClass

Это означает, что проверки isinstance не будут работать, если один из аргументов получен из пакета, а другой — нет. Если вам нужна такая функциональность, рассмотрите следующие варианты:

  • Использовать утиную типизацию (просто использовать класс, не проверяя явно, что объект принадлежит заданному типу).
  • Сделать отношение типов явной частью контракта класса. Например, можно добавить атрибут-маркер self.handler = "handle_me_this_way" и попросить клиентский код проверять значение handler вместо непосредственной проверки типа.

Как 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.
add_dependency(module_name, dependencies=True) [исходный код]

Добавить модуль в граф зависимостей согласно шаблонам, заданным пользователем.

all_paths(src, dst) [исходный код]
Вернуть представление подграфа в формате dot

со всеми путями от src до dst.

Возвращает:

Представление в формате dot со всеми путями от src до dst. (https://graphviz.org/doc/info/lang.html)

Тип возвращаемого значения:

str

close() [исходный код]

Записать пакет в файловую систему. После вызова close() любые дальнейшие вызовы недопустимы. Предпочтительно использовать синтаксис менеджера ресурсов:

with PackageExporter("file.zip") as e:
    ...
denied_modules() [исходный код]

Вернуть все модули, которые в данный момент запрещены.

Возвращает:

Список с именами модулей, импорт которых в этом пакете будет запрещен.

Тип возвращаемого значения:

list[str]

deny(include, *, exclude=()) [исходный код]

Добавить в черный список модули, имена которых соответствуют заданным шаблонам glob, исключив их из списка модулей, которые может импортировать пакет. Если обнаружена зависимость от любого соответствующего шаблону пакета, возникает исключение PackagingError.

Параметры:
  • include (list[str] | str) – Строка, например "my_package.my_subpackage", или список строк с именами модулей, которые следует сделать внешними. Также можно указать шаблон glob, как описано в mock().
  • exclude (list[str] | str) – Необязательный шаблон, исключающий некоторые шаблоны, соответствующие строке include.
dependency_graph_string() [исходный код]

Возвращает строковое представление ориентированного графа зависимостей пакета.

Возвращает:

Строковое представление зависимостей пакета.

Тип возвращаемого значения:

str

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, такое исключение не возникает.
externed_modules() [исходный код]

Вернуть все модули, которые в данный момент являются внешними.

Возвращает:

Список с именами модулей, которые будут внешними в этом пакете.

Тип возвращаемого значения:

list[str]

get_rdeps(module_name) [исходный код]

Вернуть список всех модулей, зависящих от модуля module_name.

Возвращает:

Список с именами модулей, зависящих от module_name.

Тип возвращаемого значения:

list[str]

get_unique_id() [исходный код]

Получить идентификатор. Гарантируется, что этот идентификатор будет выдан для данного пакета только один раз.

Тип возвращаемого значения:

str

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, такое исключение не возникает.
interned_modules() [исходный код]

Вернуть все модули, которые в данный момент включены в пакет.

Возвращает:

Список с именами модулей, которые будут включены в этот пакет.

Тип возвращаемого значения:

list[str]

mock(include, *, exclude=(), allow_empty=True) [исходный код]

Заменить некоторые необходимые модули имитационной реализацией. Имитируемые модули будут возвращать фиктивный объект при обращении к любому их атрибуту. Поскольку мы копируем файлы по отдельности, при разрешении зависимостей иногда обнаруживаются файлы, импортируемые файлами модели, но функциональность которых никогда не используется (например, пользовательский код сериализации или вспомогательные средства обучения). Используйте эту функцию, чтобы имитировать такую функциональность, не изменяя исходный код.

Параметры:
  • include (list[str] | str) –

    Строка, например "my_package.my_subpackage", или список строк с именами модулей, для которых нужно создать имитацию. Строки также могут содержать шаблон glob, соответствующий нескольким модулям. Все необходимые зависимости, соответствующие этому шаблону, будут автоматически имитированы.

    Примеры:

    'torch.**' – соответствует torch и всем подмодулям torch, например 'torch.nn' и 'torch.nn.functional'

    'torch.*' – соответствует 'torch.nn' или 'torch.functional', но не 'torch.nn.functional'

  • exclude (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() [исходный код]

Вернуть все модули, для которых в данный момент созданы имитации.

Возвращает:

Список с именами модулей, для которых будут созданы имитации в этом пакете.

Тип возвращаемого значения:

list[str]

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) [исходный код]

Сохранить необработанные байты в пакет.

Параметры:
  • package (str) – Имя пакета модулей, в который следует поместить этот ресурс (например, "my_package.my_subpackage").
  • resource (str) – Уникальное имя ресурса, используемое для его идентификации при загрузке.
  • binary (str) – Данные для сохранения.
save_module(module_name, dependencies=True) [исходный код]

Сохранить код для module в пакет. Код модуля определяется с помощью пути importers для поиска объекта модуля, а затем с помощью его атрибута __file__ для поиска исходного кода.

Параметры:
  • module_name (str) – например, my_package.my_subpackage; код будет сохранен для предоставления кода этого пакета.
  • dependencies (bool, optional) – Если True, мы сканируем исходный код на предмет зависимостей.
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, мы сканируем исходный код на предмет зависимостей.
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, выполняется поиск зависимостей в исходном коде.
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, выполняется поиск зависимостей в исходном коде.
save_text(package, resource, text) [исходный код]

Сохраняет текстовые данные в пакет.

Параметры:
  • package (str) – имя пакета модуля, в который следует поместить этот ресурс (например, "my_package.my_subpackage").
  • resource (str) – уникальное имя ресурса, используемое для его идентификации при загрузке.
  • text (str) – содержимое, которое нужно сохранить.
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) – метод, определяющий, следует ли разрешить модуль, предоставленный извне. Его можно использовать, чтобы гарантировать, что загружаемые пакеты не зависят от модулей, которые не поддерживаются сервером. По умолчанию разрешены любые модули.
Исключения:

ImportError – если пакет будет использовать запрещённый модуль.

file_structure(*, include='**', exclude=()) [исходный код]

Возвращает представление структуры файлов ZIP-файла пакета.

Параметры:
  • include (list[str] | str) – необязательная строка, например "my_package.my_subpackage", или необязательный список строк с именами файлов, которые нужно включить в представление ZIP-файла. Это также может быть шаблон в стиле glob, описанный в PackageExporter.mock()
  • exclude (list[str] | str) – необязательный шаблон, исключающий файлы, имена которых ему соответствуют.
Возвращает:

Directory

Тип возвращаемого значения:

Directory

id() [исходный код]

Возвращает внутренний идентификатор, который torch.package использует для различения экземпляров PackageImporter. Выглядит так:

<torch_package_0>
import_module(name, package=None) [исходный код]

Загружает модуль из пакета, если он ещё не был загружен, а затем возвращает его. Модули загружаются в область видимости импортёра и появляются в self.modules, а не в sys.modules.

Параметры:
  • name (str) – полное имя загружаемого модуля.
  • package ([type], optional) – не используется, но присутствует для соответствия сигнатуре importlib.import_module. По умолчанию — None.
Возвращает:

Загруженный модуль (возможно, уже загруженный ранее).

Тип возвращаемого значения:

types.ModuleType

load_binary(package, resource) [исходный код]

Загружает необработанные байты.

Параметры:
  • package (str) – имя пакета модуля (например, "my_package.my_subpackage").
  • resource (str) – уникальное имя ресурса.
Возвращает:

Загруженные данные.

Тип возвращаемого значения:

bytes

load_pickle(package, resource, map_location=None) [исходный код]

Десериализует ресурс из пакета, загружая все модули, необходимые для создания объектов, с помощью import_module().

Параметры:
  • package (str) – имя пакета модуля (например, "my_package.my_subpackage").
  • resource (str) – уникальное имя ресурса.
  • map_location – передаётся в torch.load для определения способа сопоставления тензоров устройствам. По умолчанию — None.
Возвращает:

Десериализованный объект.

Тип возвращаемого значения:

Any

load_text(package, resource, encoding='utf-8', errors='strict') [исходный код]

Загружает строку.

Параметры:
  • package (str) – имя пакета модуля (например, "my_package.my_subpackage").
  • resource (str) – уникальное имя ресурса.
  • encoding (str, optional) – передаётся в decode. По умолчанию — 'utf-8'.
  • errors (str, optional) – передаётся в decode. По умолчанию — 'strict'.
Возвращает:

Загруженный текст.

Тип возвращаемого значения:

str

python_version() [исходный код]

Возвращает версию Python, использованную для создания этого пакета.

Примечание: эта функция является экспериментальной и не обеспечивает прямую совместимость. Планируется позднее перенести её в файл блокировки.

Возвращает:

str | None версия Python, например 3.8.9, или None, если для этого пакета версия не была сохранена

class torch.package.Directory(name, is_dir) [исходный код]

Представление структуры файлов. Организовано в виде узлов Directory, содержащих списки дочерних узлов Directory. Каталоги пакета создаются вызовом PackageImporter.file_structure().

has_file(filename) [исходный код]

Проверяет, присутствует ли файл в Directory.

Параметры:

filename (str) – путь к файлу, который нужно найти.

Возвращает:

Содержит ли Directory указанный файл.

Тип возвращаемого значения:

bool

Утилиты анализа

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

Тип возвращаемого значения:

dict[str, list[str]]

Возвращает: словарь, сопоставляющий имена неисправных модулей со списками имён модулей в пути.

torch.package.analyze.is_from_package.is_from_package(obj) [исходный код]

Возвращает, был ли объект загружен из пакета.

Примечание: для упакованных объектов из внешних модулей будет возвращено False.

Тип возвращаемого значения:

bool

torch.package.analyze.trace_dependencies.trace_dependencies(callable, inputs) [исходный код]

Отслеживает выполнение вызываемого объекта, чтобы определить, какие модули он использует.

Параметры:
  • callable (Callable[[Any], Any]) – вызываемый объект, выполнение которого нужно отслеживать.
  • inputs (Iterable[tuple[Any, ...]]) – входные данные, используемые при отслеживании. Модули, используемые вызываемым объектом при вызове для каждого набора входных данных, объединяются, чтобы определить все модули, используемые этим объектом при упаковке.
Тип возвращаемого значения:

list[str]

Возвращает: список имён всех модулей, использованных во время выполнения вызываемого объекта.

© 2026, PyTorch Contributors
PyTorch has a BSD-style license, as found in the LICENSE file.
https://docs.pytorch.org/docs/2.14/package.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API