Spec-Zone.ru › PyTorch 2.14

torch.sparse

Создано: 26 апр. 2017 | Последнее обновление: 12 мая 2026

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

API PyTorch для разреженных тензоров находится в стадии бета-тестирования и в ближайшем будущем может измениться. Мы будем рады запросам на новые функции, сообщениям об ошибках и общим предложениям в виде задач на GitHub.

Зачем и когда использовать разреженность

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

Некоторые пользователи могут решить представлять такие данные, как матрицы смежности графов, прореженные веса или облака точек, с помощью тензоров, большинство элементов которых равны нулю. Мы понимаем, что это важные сценарии применения, и стремимся обеспечить оптимизацию производительности для них с помощью форматов разреженного хранения.

За прошедшие годы были разработаны различные форматы разреженного хранения, такие как COO, CSR/CSC, полуструктурированный формат, LIL и другие. Хотя точные схемы хранения различаются, все они сжимают данные за счёт эффективного представления элементов, равных нулю. Несжатые значения мы называем заданными, в отличие от незаданных сжатых элементов.

Сжимая повторяющиеся нули, форматы разреженного хранения позволяют экономить память и вычислительные ресурсы на различных CPU и GPU. Это может существенно повысить производительность, особенно при высокой степени разреженности или строго структурированной разреженности. Таким образом, форматы разреженного хранения можно рассматривать как способ оптимизации производительности.

Как и многие другие способы оптимизации производительности, форматы разреженного хранения не всегда дают преимущество. При проверке разреженных форматов в своём сценарии использования вы можете обнаружить, что время выполнения увеличилось, а не уменьшилось.

Если вы теоретически ожидали значительного прироста производительности, но вместо этого измерили её снижение, пожалуйста, создайте задачу на GitHub. Это поможет нам расставить приоритеты при реализации эффективных ядер и более широких оптимизаций производительности.

Мы упрощаем проверку различных схем разреженности и преобразование между ними, не навязывая мнение о том, какая из них лучше подходит для вашего приложения.

Обзор функциональности

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

В следующем примере мы преобразуем двумерный тензор с плотной схемой хранения по умолчанию (strided) в двумерный тензор со схемой хранения COO. В этом случае сохраняются только значения и индексы ненулевых элементов.

>>> a = torch.tensor([[0, 2.], [3, 0]])
>>> a.to_sparse()
tensor(indices=tensor([[0, 1],
                       [1, 0]]),
       values=tensor([2., 3.]),
       size=(2, 2), nnz=2, layout=torch.sparse_coo)

В настоящее время PyTorch поддерживает COO, CSR, CSC, BSR и BSC.

У нас также есть прототип реализации поддержки полуструктурированной разреженности. Дополнительные сведения см. в справочной документации.

Обратите внимание, что мы предоставляем несколько обобщённых вариантов этих форматов.

Пакетная обработка: для оптимальной производительности таким устройствам, как GPU, необходима пакетная обработка, поэтому мы поддерживаем пакетные измерения.

Сейчас мы предлагаем очень простой вариант пакетной обработки, в котором каждый компонент самого разреженного формата обрабатывается пакетно. При этом для каждой записи пакета требуется одинаковое число заданных элементов. В этом примере мы создаём трёхмерный (пакетный) тензор CSR из трёхмерного плотного тензора.

>>> t = torch.tensor([[[1., 0], [2., 3.]], [[4., 0], [5., 6.]]])
>>> t.dim()
3
>>> t.to_sparse_csr()
tensor(crow_indices=tensor([[0, 1, 3],
                            [0, 1, 3]]),
       col_indices=tensor([[0, 0, 1],
                           [0, 0, 1]]),
       values=tensor([[1., 2., 3.],
                      [4., 5., 6.]]), size=(2, 2, 2), nnz=3,
       layout=torch.sparse_csr)

Плотные измерения: с другой стороны, некоторые данные, например эмбеддинги графов, лучше представлять в виде разреженных наборов векторов, а не скаляров.

В этом примере мы создаём трёхмерный гибридный тензор COO с двумя разреженными и одним плотным измерением из трёхмерного тензора со схемой хранения strided. Если вся строка в трёхмерном тензоре со схемой хранения strided состоит из нулей, она не сохраняется. Однако если хотя бы одно значение в строке ненулевое, сохраняется вся строка. Это уменьшает количество индексов, поскольку для каждой строки нужен один индекс, а не отдельный индекс для каждого элемента. Но при этом увеличивается объём памяти, занимаемый значениями. Можно пропустить только строки, полностью состоящие из нулей: наличие хотя бы одного ненулевого значения приводит к сохранению всей строки.

>>> t = torch.tensor([[[0., 0], [1., 2.]], [[0., 0], [3., 4.]]])
>>> t.to_sparse(sparse_dim=2)
tensor(indices=tensor([[0, 1],
                       [1, 1]]),
       values=tensor([[1., 2.],
                      [3., 4.]]),
       size=(2, 2, 2), nnz=2, layout=torch.sparse_coo)

Обзор операторов

В целом операции над тензорами с форматами разреженного хранения работают так же, как операции над тензорами с форматом хранения strided (или другими форматами). Особенности хранения, то есть физическое расположение данных, влияют на производительность операции, но не должны влиять на её семантику.

Мы активно расширяем набор поддерживаемых операторов для разреженных тензоров. Пока пользователям не следует ожидать такого же уровня поддержки, как для плотных тензоров. Список операторов приведён в документации по операторам.

>>> b = torch.tensor([[0, 0, 1, 2, 3, 0], [4, 5, 0, 6, 0, 0]])
>>> b_s = b.to_sparse_csr()
>>> b_s.cos()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: unsupported tensor layout: SparseCsr
>>> b_s.sin()
tensor(crow_indices=tensor([0, 3, 6]),
       col_indices=tensor([2, 3, 4, 0, 1, 3]),
       values=tensor([ 0.8415,  0.9093,  0.1411, -0.7568, -0.9589, -0.2794]),
       size=(2, 6), nnz=6, layout=torch.sparse_csr)

Как показано в примере выше, мы не поддерживаем унарные операторы, не сохраняющие нули, например cos. Результат унарной операции, не сохраняющей нули, не сможет в той же степени, что и входные данные, использовать преимущества форматов разреженного хранения, что потенциально может привести к катастрофическому увеличению объёма памяти. Вместо этого мы полагаемся на то, что пользователь сначала явно преобразует тензор в плотный, а затем выполнит операцию.

>>> b_s.to_dense().cos()
tensor([[ 1.0000, -0.4161],
        [-0.9900,  1.0000]])

Мы знаем, что некоторые пользователи хотят игнорировать сжатые нули при таких операциях, как cos, вместо того чтобы сохранять точную семантику операции. В этом случае можно воспользоваться torch.masked и его MaskedTensor, который также основан на форматах разреженного хранения и соответствующих ядрах.

Также обратите внимание, что пока пользователь не может выбрать схему хранения выходных данных. Например, сложение разреженного тензора с обычным тензором со схемой хранения strided даёт тензор со схемой хранения strided. Некоторые пользователи могут предпочесть, чтобы результат оставался разреженным, поскольку они знают, что он всё ещё будет достаточно разреженным.

>>> a + b.to_sparse()
tensor([[0., 3.],
        [3., 0.]])

Мы признаём, что доступ к ядрам, способным эффективно создавать выходные данные с различными схемами хранения, может быть очень полезен. Следующая операция может значительно выиграть от получения данных с определённой схемой хранения. Мы работаем над API для управления схемой хранения результата и понимаем, что эта важная функция позволит планировать более оптимальный путь выполнения для любой заданной модели.

Полуструктурированные разреженные тензоры

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

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

Полуструктурированная разреженность — это схема хранения разреженных данных, впервые представленная в архитектуре NVIDIA Ampere. Её также называют мелкозернистой структурированной разреженностью или структурированной разреженностью 2:4.

При использовании этой схемы хранения среди каждых 2n элементов сохраняются n элементов, а значение n определяется шириной типа данных тензора (dtype). Чаще всего используется тип данных float16, для которого n=2, отсюда и название «структурированная разреженность 2:4».

Более подробное объяснение полуструктурированной разреженности приведено в этой публикации в блоге NVIDIA.

В PyTorch полуструктурированная разреженность реализована с помощью подкласса Tensor. Создав подкласс, мы можем переопределить __torch_dispatch__, чтобы использовать более быстрые разреженные ядра при умножении матриц. Мы также можем хранить тензор в сжатом виде внутри подкласса, чтобы уменьшить накладные расходы на память.

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

Примечание

Заданные элементы и маска метаданных полуструктурированного разреженного тензора хранятся вместе в одном плоском сжатом тензоре. Они объединяются, образуя непрерывный участок памяти.

сжатый тензор = [ заданные элементы исходного тензора | маска метаданных ]

Для исходного тензора размера (r, c) мы ожидаем, что первые m * k // 2 элементов будут сохранёнными элементами, а оставшаяся часть тензора будет содержать метаданные.

Чтобы пользователю было проще просматривать заданные элементы и маску, можно использовать .indices() и .values() для доступа к маске и заданным элементам соответственно.

  • .values() возвращает заданные элементы в тензоре размера (r, c//2) с тем же типом данных, что и у плотной матрицы.
  • .indices() возвращает маску метаданных в тензоре размера (r, c//2 ) с типом элементов torch.int16, если dtype — torch.float16 или torch.bfloat16, и типом элементов torch.int32, если dtype — torch.int8.

Для разреженных тензоров 2:4 накладные расходы на метаданные незначительны — всего 2 бита на заданный элемент.

Примечание

Важно отметить, что torch.float32 поддерживается только для разреженности 1:2. Поэтому для него не действует та же формула, что приведена выше.

Ниже мы разберём, как вычислить коэффициент сжатия (размер плотного тензора / размер разреженного тензора) для разреженного тензора 2:4.

Пусть (r, c) = tensor.shape и e = bitwidth(tensor.dtype), тогда e = 16 для torch.float16 и torch.bfloat16, а также e = 8 для torch.int8.

Mdense=r×c×eMsparse=Mspecified+Mmetadata=r×c2×e+r×c2×2=rce2+rc=rce(12+1e)M_{dense} = r \times c \times e \\ M_{sparse} = M_{specified} + M_{metadata} = r \times \frac{c}{2} \times e + r \times \frac{c}{2} \times 2 = \frac{rce}{2} + rc =rce(\frac{1}{2} +\frac{1}{e})

Используя эти вычисления, мы можем определить общий объём памяти для исходного плотного и нового разреженного представления.

Это даёт простую формулу коэффициента сжатия, который зависит только от разрядности типа данных тензора.

C=MsparseMdense=12+1eC = \frac{M_{sparse}}{M_{dense}} = \frac{1}{2} + \frac{1}{e}

Согласно этой формуле, коэффициент сжатия составляет 56,25% для torch.float16 или torch.bfloat16 и 62,5% для torch.int8.

Создание полуструктурированных разреженных тензоров

Преобразовать плотный тензор в полуструктурированный разреженный тензор можно просто с помощью функции torch.to_sparse_semi_structured.

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

Для полуструктурированной разреженности поддерживаются следующие типы данных. Обратите внимание, что для каждого типа данных действуют собственные ограничения на форму и коэффициент сжатия.

Тип данных PyTorch

Ограничения на форму

Коэффициент сжатия

Шаблон разреженности

torch.float16

Тензор должен быть двумерным, а значения (r, c) должны быть положительными числами, кратными 64

9/16

2:4

torch.bfloat16

Тензор должен быть двумерным, а значения (r, c) должны быть положительными числами, кратными 64

9/16

2:4

torch.int8

Тензор должен быть двумерным, а значения (r, c) должны быть положительными числами, кратными 128

10/16

2:4

Чтобы создать полуструктурированный разреженный тензор, сначала создайте обычный плотный тензор, соответствующий формату разреженности 2:4 (или полуструктурированному формату). Для этого мы повторяем небольшой фрагмент размером 1x4, создавая плотный тензор float16 размером 16x16. Затем можно вызвать функцию to_sparse_semi_structured, чтобы сжать его и ускорить инференс.

>>> from torch.sparse import to_sparse_semi_structured
>>> A = torch.Tensor([0, 0, 1, 1]).tile((128, 32)).half().cuda()
tensor([[0., 0., 1.,  ..., 0., 1., 1.],
        [0., 0., 1.,  ..., 0., 1., 1.],
        [0., 0., 1.,  ..., 0., 1., 1.],
        ...,
        [0., 0., 1.,  ..., 0., 1., 1.],
        [0., 0., 1.,  ..., 0., 1., 1.],
        [0., 0., 1.,  ..., 0., 1., 1.]], device='cuda:0', dtype=torch.float16)
>>> A_sparse = to_sparse_semi_structured(A)
SparseSemiStructuredTensor(shape=torch.Size([128, 128]), transposed=False, values=tensor([[1., 1., 1.,  ..., 1., 1., 1.],
        [1., 1., 1.,  ..., 1., 1., 1.],
        [1., 1., 1.,  ..., 1., 1., 1.],
        ...,
        [1., 1., 1.,  ..., 1., 1., 1.],
        [1., 1., 1.,  ..., 1., 1., 1.],
        [1., 1., 1.,  ..., 1., 1., 1.]], device='cuda:0', dtype=torch.float16), metadata=tensor([[-4370, -4370, -4370,  ..., -4370, -4370, -4370],
        [-4370, -4370, -4370,  ..., -4370, -4370, -4370],
        [-4370, -4370, -4370,  ..., -4370, -4370, -4370],
        ...,
        [-4370, -4370, -4370,  ..., -4370, -4370, -4370],
        [-4370, -4370, -4370,  ..., -4370, -4370, -4370],
        [-4370, -4370, -4370,  ..., -4370, -4370, -4370]], device='cuda:0',
dtype=torch.int16))

Операции с полуструктурированными разреженными тензорами

В настоящее время для полуструктурированных разреженных тензоров поддерживаются следующие операции:

  • torch.addmm(bias, dense, sparse.t())
  • torch.mm(dense, sparse)
  • torch.mm(sparse, dense)
  • aten.linear.default(dense, sparse, bias)
  • aten.t.default(sparse)
  • aten.t.detach(sparse)

Чтобы использовать эти операции, просто передайте результат to_sparse_semi_structured(tensor) вместо использования tensor, когда в вашем тензоре уже есть нули в полуструктурированном разреженном формате, например:

>>> a = torch.Tensor([0, 0, 1, 1]).tile((64, 16)).half().cuda()
>>> b = torch.rand(64, 64).half().cuda()
>>> c = torch.mm(a, b)
>>> a_sparse = to_sparse_semi_structured(a)
>>> torch.allclose(c, torch.mm(a_sparse, b))
True

Ускорение nn.Linear с помощью полуструктурированной разреженности

Если веса уже имеют полуструктурированную разреженность, можно ускорить линейные слои модели всего несколькими строками кода:

>>> input = torch.rand(64, 64).half().cuda()
>>> mask = torch.Tensor([0, 0, 1, 1]).tile((64, 16)).cuda().bool()
>>> linear = nn.Linear(64, 64).half().cuda()
>>> linear.weight = nn.Parameter(to_sparse_semi_structured(linear.weight.masked_fill(~mask, 0)))
torch.sparse.semi_structured.to_sparse_semi_structured(original_tensor, transposed=False, alg_id=0) [исходный код]

Эта функция преобразует плотный тензор в полуструктурированный разреженный тензор. Она возвращает SparseSemiStructuredTensor — подкласс torch.Tensor.

Эта функция проверяет, что у плотного тензора правильные тип данных, размер, размерность и устройство. В настоящее время мы поддерживаем полуструктурированные разреженные тензоры только для двумерных тензоров CUDA. Кроме того, размер вашего тензора должен быть положительным числом, кратным минимальному размеру разреженного блока, указанному в _DTYPE_TO_SHAPE_CONSTRAINTS для каждого типа данных (float32, float16, bfloat16, int8).

Параметры:
  • original_tensor (Tensor) – плотный тензор для преобразования
  • transposed (bool, optional) – устаревший аргумент, который будет удалён в одном из следующих выпусков. Не используйте его.
  • alg_id (int, optional) – идентификатор алгоритма для умножения матриц cuSPARSELt. По умолчанию равен 0. Его можно получить с помощью torch._cslt_sparse_mm_search.
Возвращает:

Разреженный полуструктурированный тензор, созданный из заданного original_tensor

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

SparseSemiStructuredTensor

Исключения:

None –

Пример

>>> A = torch.Tensor([0, 0, 1, 1]).tile((128, 32)).half().cuda()
tensor([[0., 0., 1.,  ..., 0., 1., 1.],
        [0., 0., 1.,  ..., 0., 1., 1.],
        [0., 0., 1.,  ..., 0., 1., 1.],
        ...,
        [0., 0., 1.,  ..., 0., 1., 1.],
        [0., 0., 1.,  ..., 0., 1., 1.],
        [0., 0., 1.,  ..., 0., 1., 1.]], device='cuda:0', dtype=torch.float16)
>>> A_sparse = to_sparse_semi_structured(A)
SparseSemiStructuredTensor(shape=torch.Size([128, 128]))
>>> A_sparse.values()
tensor([[1., 1., 1.,  ..., 1., 1., 1.],
        [1., 1., 1.,  ..., 1., 1., 1.],
        [1., 1., 1.,  ..., 1., 1., 1.],
        ...,
        [1., 1., 1.,  ..., 1., 1., 1.],
        [1., 1., 1.,  ..., 1., 1., 1.],
        [1., 1., 1.,  ..., 1., 1., 1.]], device='cuda:0', dtype=torch.float16),
>>> A_sparse.indices()
tensor([[-4370, -4370, -4370,  ..., -4370, -4370, -4370],
        [-4370, -4370, -4370,  ..., -4370, -4370, -4370],
        [-4370, -4370, -4370,  ..., -4370, -4370, -4370],
        ...,
        [-4370, -4370, -4370,  ..., -4370, -4370, -4370],
        [-4370, -4370, -4370,  ..., -4370, -4370, -4370],
        [-4370, -4370, -4370,  ..., -4370, -4370, -4370]], device='cuda:0', dtype=torch.int16))
SparseSemiStructuredTensorCUSPARSELT

Бэкенд cuSPARSELt ожидает, что заданные элементы и метаданные будут храниться в одном тензоре: packed = [ заданные элементы исходного тензора | метаданные ]. Для исходного тензора размера (m, k) мы ожидаем, что первые m * k // 2 элементов будут сохранёнными элементами. Остальная часть тензора содержит метаданные.

SparseSemiStructuredTensorCUTLASS

Этот класс реализует полуструктурированную разреженность для бэкенда CUTLASS.

Разреженные тензоры COO

PyTorch реализует так называемый координатный формат, или формат COO, как один из форматов хранения разреженных тензоров. В формате COO заданные элементы хранятся в виде кортежей индексов элементов и соответствующих значений. В частности,

  • индексы заданных элементов собираются в тензор indices размера (ndim, nse) с типом элементов torch.int64,
  • соответствующие значения собираются в тензор values размера (nse,) с произвольным целочисленным типом или типом с плавающей точкой,

где ndim — размерность тензора, а nse — количество заданных элементов.

Примечание

Объём памяти, занимаемый разреженным тензором COO, составляет не менее (ndim * 8 + <size of element type in bytes>) * nse байт (не считая постоянных накладных расходов на хранение других данных тензора).

Объём памяти, занимаемый тензором со схемой хранения strided, составляет не менее product(<tensor shape>) * <size of element type in bytes>.

Например, тензор размером 10 000 x 10 000 со 100 000 ненулевых 32-разрядных чисел с плавающей точкой занимает не менее (2 * 8 + 4) * 100 000 = 2 000 000 байт при использовании схемы хранения COO и 10 000 * 10 000 * 4 = 400 000 000 байт при использовании стандартной схемы хранения strided. Обратите внимание: формат хранения COO позволяет сэкономить память в 200 раз.

Создание

Разреженный тензор COO можно создать, передав два тензора индексов и значений, а также размер разреженного тензора (если его нельзя определить по тензорам индексов и значений) функции torch.sparse_coo_tensor().

Предположим, мы хотим определить разреженный тензор со значением 3 в позиции (0, 2), значением 4 в позиции (1, 0) и значением 5 в позиции (1, 2). Предполагается, что незаданные элементы имеют одно и то же значение заполнения, которое по умолчанию равно нулю. Тогда мы напишем:

>>> i = [[0, 1, 1], [2, 0, 2]]
>>> v =  [3, 4, 5]
>>> s = torch.sparse_coo_tensor(i, v, (2, 3))
>>> s
tensor(indices=tensor([[0, 1, 1],
                       [2, 0, 2]]),
       values=tensor([3, 4, 5]),
       size=(2, 3), nnz=3, layout=torch.sparse_coo)
>>> s.to_dense()
tensor([[0, 0, 3],
        [4, 0, 5]])

Обратите внимание, что входной i — это НЕ список кортежей индексов. Если вы хотите задать индексы таким образом, перед передачей конструктору разреженного тензора транспонируйте их:

>>> i = [[0, 2], [1, 0], [1, 2]]
>>> v =  [3,      4,      5    ]
>>> s = torch.sparse_coo_tensor(list(zip(*i)), v, (2, 3))
>>> # Or another equivalent formulation to get s
>>> s = torch.sparse_coo_tensor(torch.tensor(i).t(), v, (2, 3))
>>> torch.sparse_coo_tensor(i.t(), v, torch.Size([2,3])).to_dense()
tensor([[0, 0, 3],
        [4, 0, 5]])

Пустой разреженный тензор COO можно создать, указав только его размер:

>>> torch.sparse_coo_tensor(size=(2, 3))
tensor(indices=tensor([], size=(2, 0)),
       values=tensor([], size=(0,)),
       size=(2, 3), nnz=0, layout=torch.sparse_coo)

Гибридные разреженные тензоры COO

PyTorch расширяет концепцию разреженных тензоров со скалярными значениями на разреженные тензоры со значениями в виде (непрерывно расположенных) тензоров. Такие тензоры называются гибридными.

Гибридный тензор COO в PyTorch расширяет разреженный тензор COO, позволяя тензору values быть многомерным. Таким образом, мы получаем:

  • индексы заданных элементов собираются в тензор indices размера (sparse_dims, nse) с типом элементов torch.int64,
  • соответствующие значения (в виде тензоров) собираются в тензор values размера (nse, dense_dims) с произвольным целочисленным типом или типом с плавающей точкой.

Примечание

Для обозначения N-мерного разреженного гибридного тензора мы используем запись «тензор размерности (M + K)», где M и K — количество разреженных и плотных измерений соответственно, причём выполняется равенство M + K == N.

Предположим, мы хотим создать тензор размерности (2 + 1) со значением [3, 4] в позиции (0, 2), значением [5, 6] в позиции (1, 0) и значением [7, 8] в позиции (1, 2). Мы напишем:

>>> i = [[0, 1, 1],
         [2, 0, 2]]
>>> v =  [[3, 4], [5, 6], [7, 8]]
>>> s = torch.sparse_coo_tensor(i, v, (2, 3, 2))
>>> s
tensor(indices=tensor([[0, 1, 1],
                       [2, 0, 2]]),
       values=tensor([[3, 4],
                      [5, 6],
                      [7, 8]]),
       size=(2, 3, 2), nnz=3, layout=torch.sparse_coo)

>>> s.to_dense()
tensor([[[0, 0],
         [0, 0],
         [3, 4]],
        [[5, 6],
         [0, 0],
         [7, 8]]])

В общем случае, если s — разреженный тензор COO и M = s.sparse_dim(), K = s.dense_dim(), выполняются следующие инварианты:

  • M + K == len(s.shape) == s.ndim — размерность тензора равна сумме количества разреженных и плотных измерений;
  • s.indices().shape == (M, nse) — разреженные индексы хранятся явно;
  • s.values().shape == (nse,) + s.shape[M : M + K] — значения гибридного тензора представляют собой K-мерные тензоры;
  • s.values().layout == torch.strided — значения хранятся в виде тензоров со схемой хранения strided.

Примечание

Плотные измерения всегда следуют за разреженными, то есть смешивание плотных и разреженных измерений не поддерживается.

Примечание

Чтобы убедиться, что у созданного разреженного тензора согласованы индексы, значения и размер, проверки инвариантов можно включить при создании каждого тензора с помощью именованного аргумента check_invariants=True или глобально, используя экземпляр контекстного менеджера torch.sparse.check_sparse_tensor_invariants. По умолчанию проверки инвариантов разреженных тензоров отключены.

Несобранные разреженные тензоры COO

Формат разреженных тензоров COO в PyTorch допускает разреженные несобранные тензоры, в индексах которых могут повторяться координаты. В этом случае значением по такому индексу считается сумма всех значений с повторяющимися индексами. Например, можно указать несколько значений — 3 и 4 — для одного индекса 1, что приводит к созданию одномерного несобранного тензора:

>>> i = [[1, 1]]
>>> v =  [3, 4]
>>> s=torch.sparse_coo_tensor(i, v, (3,))
>>> s
tensor(indices=tensor([[1, 1]]),
       values=tensor(  [3, 4]),
       size=(3,), nnz=2, layout=torch.sparse_coo)

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

>>> s.coalesce()
tensor(indices=tensor([[1]]),
       values=tensor([7]),
       size=(3,), nnz=1, layout=torch.sparse_coo)

В общем случае результат метода torch.Tensor.coalesce() — разреженный тензор со следующими свойствами:

  • индексы заданных элементов тензора уникальны;
  • индексы отсортированы в лексикографическом порядке;
  • torch.Tensor.is_coalesced() возвращает True.

Примечание

В большинстве случаев вам не нужно беспокоиться о том, собран разреженный тензор или нет, поскольку большинство операций работают одинаково как с собранным, так и с несобранным разреженным тензором.

Однако некоторые операции эффективнее выполняются над несобранными тензорами, а другие — над собранными.

Например, сложение разреженных тензоров COO реализовано простым объединением тензоров индексов и значений:

>>> a = torch.sparse_coo_tensor([[1, 1]], [5, 6], (2,))
>>> b = torch.sparse_coo_tensor([[0, 0]], [7, 8], (2,))
>>> a + b
tensor(indices=tensor([[0, 0, 1, 1]]),
       values=tensor([7, 8, 5, 6]),
       size=(2,), nnz=4, layout=torch.sparse_coo)

Если вы многократно выполняете операцию, которая может создавать повторяющиеся записи (например, torch.Tensor.add()), следует время от времени собирать разреженные тензоры, чтобы они не становились слишком большими.

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

Работа с разреженными тензорами COO

Рассмотрим следующий пример:

>>> i = [[0, 1, 1],
         [2, 0, 2]]
>>> v =  [[3, 4], [5, 6], [7, 8]]
>>> s = torch.sparse_coo_tensor(i, v, (2, 3, 2))

Как упоминалось выше, разреженный тензор COO является экземпляром torch.Tensor. Чтобы отличить его от экземпляров Tensor с другими схемами хранения, можно использовать свойства torch.Tensor.is_sparse или torch.Tensor.layout:

>>> isinstance(s, torch.Tensor)
True
>>> s.is_sparse
True
>>> s.layout == torch.sparse_coo
True

Количество разреженных и плотных измерений можно получить с помощью методов torch.Tensor.sparse_dim() и torch.Tensor.dense_dim() соответственно. Например:

>>> s.sparse_dim(), s.dense_dim()
(2, 1)

Если s — разреженный тензор COO, его данные в формате COO можно получить с помощью методов torch.Tensor.indices() и torch.Tensor.values().

Примечание

В настоящее время получить данные в формате COO можно только для собранного экземпляра тензора:

>>> s.indices()
RuntimeError: Cannot get indices on an uncoalesced tensor, please call .coalesce() first

Чтобы получить данные в формате COO для несобранного тензора, используйте torch.Tensor._values() и torch.Tensor._indices():

>>> s._indices()
tensor([[0, 1, 1],
        [2, 0, 2]])

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

Вызов torch.Tensor._values() вернёт отсоединённый тензор. Для отслеживания градиентов следует использовать torch.Tensor.coalesce().values().

Создание нового разреженного тензора COO приводит к получению несобранного тензора:

>>> s.is_coalesced()
False

однако можно создать собранную копию разреженного тензора COO с помощью метода torch.Tensor.coalesce():

>>> s2 = s.coalesce()
>>> s2.indices()
tensor([[0, 1, 1],
       [2, 0, 2]])

При работе с несобранными разреженными тензорами COO необходимо учитывать аддитивную природу несобранных данных: значения с одинаковыми индексами являются слагаемыми, сумма которых даёт значение соответствующего элемента тензора. Например, умножение несобранного разреженного тензора на скаляр можно реализовать умножением всех несобранных значений на скаляр, поскольку выполняется c * (a + b) == c * a + c * b. Однако нелинейную операцию, например извлечение квадратного корня, нельзя реализовать применением операции к несобранным данным, поскольку в общем случае sqrt(a + b) == sqrt(a) + sqrt(b) не выполняется.

Срезы (с положительным шагом) разреженного тензора COO поддерживаются только для плотных измерений. Индексация поддерживается как для разреженных, так и для плотных измерений:

>>> s[1]
tensor(indices=tensor([[0, 2]]),
       values=tensor([[5, 6],
                      [7, 8]]),
       size=(3, 2), nnz=2, layout=torch.sparse_coo)
>>> s[1, 0, 1]
tensor(6)
>>> s[1, 0, 1:]
tensor([6])

В PyTorch нельзя явно задать значение заполнения разреженного тензора; обычно предполагается, что оно равно нулю. Однако некоторые операции могут интерпретировать значение заполнения иначе. Например, torch.sparse.softmax() вычисляет softmax, предполагая, что значение заполнения равно минус бесконечности.

Сжатые разреженные тензоры

Сжатые разреженные тензоры представляют собой класс разреженных тензоров, для которых характерно сжатие индексов определённого измерения с помощью кодирования, позволяющего оптимизировать ядра линейной алгебры для сжатых разреженных тензоров. Это кодирование основано на формате Compressed Sparse Row (CSR), расширенном в разреженных сжатых тензорах PyTorch поддержкой пакетов разреженных тензоров, многомерных значений тензоров и хранением значений разреженных тензоров в плотных блоках.

Примечание

Для обозначения N-мерного гибридного сжатого разреженного тензора мы используем тензор размерности (B + M + K), где B, M и K — числа пакетных, разреженных и плотных измерений соответственно, так что выполняется B + M + K == N. Число разреженных измерений у сжатых разреженных тензоров всегда равно двум: M == 2.

Примечание

Говорят, что тензор индексов compressed_indices использует кодирование CSR, если выполняются следующие инварианты:

  • compressed_indices — это непрерывный тензор с шагами, содержащий целые числа размером 32 или 64 бита
  • форма compressed_indices имеет вид (*batchsize, compressed_dim_size + 1), где compressed_dim_size — число сжатых измерений (например, строк или столбцов)
  • compressed_indices[..., 0] == 0, где ... обозначает пакетные индексы
  • compressed_indices[..., compressed_dim_size] == nse, где nse — число заданных элементов
  • 0 <= compressed_indices[..., i] - compressed_indices[..., i - 1] <= plain_dim_size для i=1, ..., compressed_dim_size, где plain_dim_size — число обычных измерений (ортогональных сжатым измерениям, например столбцов или строк).

Чтобы убедиться в согласованности индексов, значений и размера создаваемого разреженного тензора, проверки инвариантов можно включить при создании каждого тензора с помощью именованного аргумента check_invariants=True или глобально, используя экземпляр контекстного менеджера torch.sparse.check_sparse_tensor_invariants. По умолчанию проверки инвариантов разреженных тензоров отключены.

Примечание

Обобщение сжатых разреженных форматов на N-мерные тензоры может привести к путанице при подсчёте заданных элементов. Если сжатый разреженный тензор содержит пакетные измерения, число заданных элементов соответствует числу таких элементов в каждом пакете. Если сжатый разреженный тензор содержит плотные измерения, элементом считается K-мерный массив. Кроме того, в блочных сжатых разреженных форматах заданным элементом считается двумерный блок. Рассмотрим в качестве примера трёхмерный блочный разреженный тензор с одним пакетным измерением длины b и размером блока p, q. Если в этом тензоре задано n элементов, то фактически в каждом пакете задано n блоков. У этого тензора будет values с формой (b, n, p, q). Такое толкование числа заданных элементов обусловлено тем, что все сжатые разреженные форматы являются результатом сжатия двумерной матрицы. Пакетные измерения рассматриваются как стопка разреженных матриц, а плотные измерения меняют смысл элемента: вместо простого скалярного значения он становится массивом с собственными измерениями.

Разреженный тензор CSR

Главное преимущество формата CSR перед форматом COO — более эффективное использование памяти и значительно более высокая скорость вычислений, таких как умножение разреженной матрицы на вектор с использованием бэкендов MKL и MAGMA.

В простейшем случае двумерный разреженный тензор CSR (0 + 2 + 0) состоит из трёх одномерных тензоров: crow_indices, col_indices и values:

  • Тензор crow_indices содержит сжатые индексы строк. Это одномерный тензор размера nrows + 1 (число строк плюс 1). Последний элемент crow_indices равен числу заданных элементов, nse. Этот тензор кодирует индекс в values и col_indices в зависимости от того, где начинается заданная строка. Разность каждого последующего числа в тензоре и предыдущего числа обозначает число элементов в соответствующей строке.
  • Тензор col_indices содержит индексы столбцов каждого элемента. Это одномерный тензор размера nse.
  • Тензор values содержит значения элементов тензора CSR. Это одномерный тензор размера nse.

Примечание

Тип элементов тензоров индексов crow_indices и col_indices должен быть torch.int64 (по умолчанию) или torch.int32. Если вы хотите использовать операции с матрицами на базе MKL, используйте torch.int32. Это связано с тем, что PyTorch по умолчанию компонуется с MKL LP64, где для индексации используются 32-битные целые числа.

В общем случае N-мерный разреженный тензор CSR (B + 2 + K) состоит из двух тензоров индексов размерности (B + 1) — crow_indices и col_indices — и тензора values размерности (1 + K), таких что

  • crow_indices.shape == (*batchsize, nrows + 1)
  • col_indices.shape == (*batchsize, nse)
  • values.shape == (nse, *densesize)

при этом форма разреженного тензора CSR равна (*batchsize, nrows, ncols, *densesize), где len(batchsize) == B и len(densesize) == K.

Примечание

Пакеты разреженных тензоров CSR зависят друг от друга: число заданных элементов должно быть одинаковым во всех пакетах. Это несколько искусственное ограничение позволяет эффективно хранить индексы разных пакетов CSR.

Примечание

Число разреженных и плотных измерений можно получить с помощью методов torch.Tensor.sparse_dim() и torch.Tensor.dense_dim(). Пакетные измерения можно вычислить по форме тензора: batchsize = tensor.shape[:-tensor.sparse_dim() - tensor.dense_dim()].

Примечание

Объём памяти, занимаемый разреженным тензором CSR, составляет не менее (nrows * 8 + (8 + <size of element type in bytes> * prod(densesize)) * nse) * prod(batchsize) байт (не считая постоянных накладных расходов на хранение других данных тензора).

Для тех же примерных данных, что и в примечании во введении к формату разреженных тензоров COO, объём памяти для тензора размером 10 000 x 10 000 со 100 000 ненулевых 32-битных чисел с плавающей точкой составляет не менее (10000 * 8 + (8 + 4 * 1) * 100 000) * 1 = 1 280 000 байт при использовании формата тензора CSR. Обратите внимание, что по сравнению с форматами хранения COO и strided формат CSR позволяет сократить расход памяти соответственно в 1,6 и 310 раз.

Создание тензоров CSR

Разреженные тензоры CSR можно создать напрямую с помощью функции torch.sparse_csr_tensor(). Пользователь должен отдельно передать тензоры индексов строк и столбцов, а также значений; при этом индексы строк должны быть заданы с использованием кодирования сжатия CSR. Аргумент size является необязательным: если он не задан, его значение будет выведено из crow_indices и col_indices.

>>> crow_indices = torch.tensor([0, 2, 4])
>>> col_indices = torch.tensor([0, 1, 0, 1])
>>> values = torch.tensor([1, 2, 3, 4])
>>> csr = torch.sparse_csr_tensor(crow_indices, col_indices, values, dtype=torch.float64)
>>> csr
tensor(crow_indices=tensor([0, 2, 4]),
       col_indices=tensor([0, 1, 0, 1]),
       values=tensor([1., 2., 3., 4.]), size=(2, 2), nnz=4,
       dtype=torch.float64)
>>> csr.to_dense()
tensor([[1., 2.],
        [3., 4.]], dtype=torch.float64)

Примечание

Значения разреженных измерений в выведенном size вычисляются по размеру crow_indices и максимальному значению индекса в col_indices. Если число столбцов должно быть больше, чем в выведенном size, необходимо явно указать аргумент size.

Проще всего создать двумерный разреженный тензор CSR из тензора strided или разреженного тензора COO с помощью метода torch.Tensor.to_sparse_csr(). Все нули в тензоре (strided) будут интерпретироваться как отсутствующие значения в разреженном тензоре:

>>> a = torch.tensor([[0, 0, 1, 0], [1, 2, 0, 0], [0, 0, 0, 0]], dtype=torch.float64)
>>> sp = a.to_sparse_csr()
>>> sp
tensor(crow_indices=tensor([0, 1, 3, 3]),
      col_indices=tensor([2, 0, 1]),
      values=tensor([1., 1., 2.]), size=(3, 4), nnz=3, dtype=torch.float64)

Операции с тензорами CSR

Умножение разреженной матрицы на вектор можно выполнить с помощью метода tensor.matmul(). В настоящее время это единственная математическая операция, поддерживаемая для тензоров CSR.

>>> vec = torch.randn(4, 1, dtype=torch.float64)
>>> sp.matmul(vec)
tensor([[0.9078],
        [1.3180],
        [0.0000]], dtype=torch.float64)

Разреженный тензор CSC

Формат разреженного тензора CSC (Compressed Sparse Column, сжатый разреженный столбец) реализует формат CSC для хранения двумерных тензоров и дополнен поддержкой пакетов разреженных тензоров CSC и многомерных тензоров в качестве значений.

Примечание

Разреженный тензор CSC по сути является транспонированным разреженным тензором CSR, если при транспонировании меняются местами разреженные измерения.

Как и разреженные тензоры CSR, разреженный тензор CSC состоит из трёх тензоров: ccol_indices, row_indices и values:

  • Тензор ccol_indices содержит сжатые индексы столбцов. Это тензор размерности (B + 1) с формой (*batchsize, ncols + 1). Последний элемент равен числу заданных элементов, nse. Этот тензор кодирует индекс в values и row_indices в зависимости от того, где начинается заданный столбец. Разность каждого последующего числа в тензоре и предыдущего числа обозначает число элементов в соответствующем столбце.
  • Тензор row_indices содержит индексы строк каждого элемента. Это тензор размерности (B + 1) с формой (*batchsize, nse).
  • Тензор values содержит значения элементов тензора CSC. Это тензор размерности (1 + K) с формой (nse, *densesize).

Создание тензоров CSC

Разреженные тензоры CSC можно создать напрямую с помощью функции torch.sparse_csc_tensor(). Пользователь должен отдельно передать тензоры индексов строк и столбцов, а также значений; при этом индексы столбцов должны быть заданы с использованием кодирования сжатия CSR. Аргумент size является необязательным: если он не задан, его значение будет выведено из тензоров row_indices и ccol_indices.

>>> ccol_indices = torch.tensor([0, 2, 4])
>>> row_indices = torch.tensor([0, 1, 0, 1])
>>> values = torch.tensor([1, 2, 3, 4])
>>> csc = torch.sparse_csc_tensor(ccol_indices, row_indices, values, dtype=torch.float64)
>>> csc
tensor(ccol_indices=tensor([0, 2, 4]),
       row_indices=tensor([0, 1, 0, 1]),
       values=tensor([1., 2., 3., 4.]), size=(2, 2), nnz=4,
       dtype=torch.float64, layout=torch.sparse_csc)
>>> csc.to_dense()
tensor([[1., 3.],
        [2., 4.]], dtype=torch.float64)

Примечание

В функции-конструкторе разреженного тензора CSC аргумент сжатыx индексов столбцов расположен перед аргументом индексов строк.

Разреженные двумерные тензоры CSC (0 + 2 + 0) можно создать из любого двумерного тензора с помощью метода torch.Tensor.to_sparse_csc(). Все нули в тензоре (strided) будут интерпретироваться как отсутствующие значения в разреженном тензоре:

>>> a = torch.tensor([[0, 0, 1, 0], [1, 2, 0, 0], [0, 0, 0, 0]], dtype=torch.float64)
>>> sp = a.to_sparse_csc()
>>> sp
tensor(ccol_indices=tensor([0, 1, 2, 3, 3]),
       row_indices=tensor([1, 1, 0]),
       values=tensor([1., 2., 1.]), size=(3, 4), nnz=3, dtype=torch.float64,
       layout=torch.sparse_csc)

Разреженный тензор BSR

Формат разреженного тензора BSR (Block compressed Sparse Row, блочный сжатый разреженный ряд) реализует формат BSR для хранения двумерных тензоров и дополнен поддержкой пакетов разреженных тензоров BSR и блоков многомерных тензоров в качестве значений.

Разреженный тензор BSR состоит из трёх тензоров: crow_indices, col_indices и values:

  • Тензор crow_indices содержит сжатые индексы строк. Это тензор размерности (B + 1) с формой (*batchsize, nrowblocks + 1). Последний элемент равен числу заданных блоков, nse. Этот тензор кодирует индекс в values и col_indices в зависимости от того, где начинается заданный блок столбцов. Разность каждого последующего числа в тензоре и предыдущего числа обозначает число блоков в соответствующей строке.
  • Тензор col_indices содержит индексы блоков столбцов каждого элемента. Это тензор размерности (B + 1) с формой (*batchsize, nse).
  • Тензор values содержит значения элементов разреженного тензора BSR, сгруппированные в двумерные блоки. Это тензор размерности (1 + 2 + K) с формой (nse, nrowblocks, ncolblocks, *densesize).

Создание тензоров BSR

Разреженные тензоры BSR можно создать напрямую с помощью функции torch.sparse_bsr_tensor(). Пользователь должен отдельно передать тензоры индексов блоков строк и столбцов, а также значений; при этом индексы блоков строк должны быть заданы с использованием кодирования сжатия CSR. Аргумент size является необязательным: если он не задан, его значение будет выведено из тензоров crow_indices и col_indices.

>>> crow_indices = torch.tensor([0, 2, 4])
>>> col_indices = torch.tensor([0, 1, 0, 1])
>>> values = torch.tensor([[[0, 1, 2], [6, 7, 8]],
...                        [[3, 4, 5], [9, 10, 11]],
...                        [[12, 13, 14], [18, 19, 20]],
...                        [[15, 16, 17], [21, 22, 23]]])
>>> bsr = torch.sparse_bsr_tensor(crow_indices, col_indices, values, dtype=torch.float64)
>>> bsr
tensor(crow_indices=tensor([0, 2, 4]),
       col_indices=tensor([0, 1, 0, 1]),
       values=tensor([[[ 0.,  1.,  2.],
                       [ 6.,  7.,  8.]],
                      [[ 3.,  4.,  5.],
                       [ 9., 10., 11.]],
                      [[12., 13., 14.],
                       [18., 19., 20.]],
                      [[15., 16., 17.],
                       [21., 22., 23.]]]),
       size=(4, 6), nnz=4, dtype=torch.float64, layout=torch.sparse_bsr)
>>> bsr.to_dense()
tensor([[ 0.,  1.,  2.,  3.,  4.,  5.],
        [ 6.,  7.,  8.,  9., 10., 11.],
        [12., 13., 14., 15., 16., 17.],
        [18., 19., 20., 21., 22., 23.]], dtype=torch.float64)

Разреженные двумерные тензоры BSR (0 + 2 + 0) можно создать из любого двумерного тензора с помощью метода torch.Tensor.to_sparse_bsr(), для которого также необходимо указать размер блока значений:

>>> dense = torch.tensor([[0, 1, 2, 3, 4, 5],
...                       [6, 7, 8, 9, 10, 11],
...                       [12, 13, 14, 15, 16, 17],
...                       [18, 19, 20, 21, 22, 23]])
>>> bsr = dense.to_sparse_bsr(blocksize=(2, 3))
>>> bsr
tensor(crow_indices=tensor([0, 2, 4]),
       col_indices=tensor([0, 1, 0, 1]),
       values=tensor([[[ 0,  1,  2],
                       [ 6,  7,  8]],
                      [[ 3,  4,  5],
                       [ 9, 10, 11]],
                      [[12, 13, 14],
                       [18, 19, 20]],
                      [[15, 16, 17],
                       [21, 22, 23]]]), size=(4, 6), nnz=4,
       layout=torch.sparse_bsr)

Разреженный тензор BSC

Формат разреженного тензора BSC (Block compressed Sparse Column, блочный сжатый разреженный столбец) реализует формат BSC для хранения двумерных тензоров и дополнен поддержкой пакетов разреженных тензоров BSC и блоков многомерных тензоров в качестве значений.

Разреженный тензор BSC состоит из трёх тензоров: ccol_indices, row_indices и values:

  • Тензор ccol_indices содержит сжатые индексы столбцов. Это тензор размерности (B + 1) с формой (*batchsize, ncolblocks + 1). Последний элемент равен числу заданных блоков, nse. Этот тензор кодирует индекс в values и row_indices в зависимости от того, где начинается заданный блок строк. Разность каждого последующего числа в тензоре и предыдущего числа обозначает число блоков в соответствующем столбце.
  • Тензор row_indices содержит индексы блоков строк каждого элемента. Это тензор размерности (B + 1) с формой (*batchsize, nse).
  • Тензор values содержит значения элементов разреженного тензора BSC, сгруппированные в двумерные блоки. Это тензор размерности (1 + 2 + K) с формой (nse, nrowblocks, ncolblocks, *densesize).

Создание тензоров BSC

Разреженные тензоры BSC можно создать напрямую с помощью функции torch.sparse_bsc_tensor(). Пользователь должен отдельно передать тензоры индексов блоков строк и столбцов, а также значений; при этом индексы блоков столбцов должны быть заданы с использованием кодирования сжатия CSR. Аргумент size является необязательным: если он не задан, его значение будет выведено из тензоров ccol_indices и row_indices.

>>> ccol_indices = torch.tensor([0, 2, 4])
>>> row_indices = torch.tensor([0, 1, 0, 1])
>>> values = torch.tensor([[[0, 1, 2], [6, 7, 8]],
...                        [[3, 4, 5], [9, 10, 11]],
...                        [[12, 13, 14], [18, 19, 20]],
...                        [[15, 16, 17], [21, 22, 23]]])
>>> bsc = torch.sparse_bsc_tensor(ccol_indices, row_indices, values, dtype=torch.float64)
>>> bsc
tensor(ccol_indices=tensor([0, 2, 4]),
       row_indices=tensor([0, 1, 0, 1]),
       values=tensor([[[ 0.,  1.,  2.],
                       [ 6.,  7.,  8.]],
                      [[ 3.,  4.,  5.],
                       [ 9., 10., 11.]],
                      [[12., 13., 14.],
                       [18., 19., 20.]],
                      [[15., 16., 17.],
                       [21., 22., 23.]]]), size=(4, 6), nnz=4,
       dtype=torch.float64, layout=torch.sparse_bsc)

Инструменты для работы со сжатыми разреженными тензорами

Все сжатые разреженные тензоры — CSR, CSC, BSR и BSC — концептуально очень похожи: данные их индексов разделены на две части: так называемые сжатые индексы, использующие кодирование CSR, и так называемые обычные индексы, ортогональные сжатым индексам. Благодаря этому для работы с такими тензорами можно использовать общие инструменты с реализациями, параметризованными форматом тензора.

Создание сжатых разреженных тензоров

Разреженные тензоры CSR, CSC, BSR и CSC можно создать с помощью функции torch.sparse_compressed_tensor(), интерфейс которой совпадает с интерфейсами рассмотренных выше функций-конструкторов torch.sparse_csr_tensor(), torch.sparse_csc_tensor(), torch.sparse_bsr_tensor() и torch.sparse_bsc_tensor() соответственно, но требует дополнительного аргумента layout. В следующем примере показано, как создать тензоры CSR и CSC из одних и тех же входных данных, указав соответствующий параметр формата в функции torch.sparse_compressed_tensor():

>>> compressed_indices = torch.tensor([0, 2, 4])
>>> plain_indices = torch.tensor([0, 1, 0, 1])
>>> values = torch.tensor([1, 2, 3, 4])
>>> csr = torch.sparse_compressed_tensor(compressed_indices, plain_indices, values, layout=torch.sparse_csr)
>>> csr
tensor(crow_indices=tensor([0, 2, 4]),
       col_indices=tensor([0, 1, 0, 1]),
       values=tensor([1, 2, 3, 4]), size=(2, 2), nnz=4,
       layout=torch.sparse_csr)
>>> csc = torch.sparse_compressed_tensor(compressed_indices, plain_indices, values, layout=torch.sparse_csc)
>>> csc
tensor(ccol_indices=tensor([0, 2, 4]),
       row_indices=tensor([0, 1, 0, 1]),
       values=tensor([1, 2, 3, 4]), size=(2, 2), nnz=4,
       layout=torch.sparse_csc)
>>> (csr.transpose(0, 1).to_dense() == csc.to_dense()).all()
tensor(True)

Поддерживаемые операции

Операции линейной алгебры

В следующей таблице перечислены поддерживаемые операции линейной алгебры над разреженными матрицами, для которых форматы операндов могут различаться. Здесь T[layout] обозначает тензор с заданным форматом. Аналогично, M[layout] обозначает матрицу (двумерный тензор PyTorch), а V[layout] — вектор (одномерный тензор PyTorch). Кроме того, f обозначает скаляр (число с плавающей точкой или нульмерный тензор PyTorch), * — поэлементное умножение, а @ — матричное умножение.

Операция PyTorch

Разреженный градиент?

Сигнатура формата

torch.mv()

нет

M[sparse_coo] @ V[strided] -> V[strided]

torch.mv()

нет

M[sparse_csr] @ V[strided] -> V[strided]

torch.matmul()

нет

M[sparse_coo] @ M[strided] -> M[strided]

torch.matmul()

нет

M[sparse_csr] @ M[strided] -> M[strided]

torch.matmul()

нет

M[SparseSemiStructured] @ M[strided] -> M[strided]

torch.matmul()

нет

M[strided] @ M[SparseSemiStructured] -> M[strided]

torch.mm()

нет

M[sparse_coo] @ M[strided] -> M[strided]

torch.mm()

нет

M[SparseSemiStructured] @ M[strided] -> M[strided]

torch.mm()

нет

M[strided] @ M[SparseSemiStructured] -> M[strided]

torch.sparse.mm()

да

M[sparse_coo] @ M[strided] -> M[strided]

torch.smm()

нет

M[sparse_coo] @ M[strided] -> M[sparse_coo]

torch.hspmm()

нет

M[sparse_coo] @ M[strided] -> M[hybrid sparse_coo]

torch.bmm()

нет

T[sparse_coo] @ T[strided] -> T[strided]

torch.addmm()

нет

f * M[strided] + f * (M[sparse_coo] @ M[strided]) -> M[strided]

torch.addmm()

нет

f * M[strided] + f * (M[SparseSemiStructured] @ M[strided]) -> M[strided]

torch.addmm()

нет

f * M[strided] + f * (M[strided] @ M[SparseSemiStructured]) -> M[strided]

torch.sparse.addmm()

да

f * M[strided] + f * (M[sparse_coo] @ M[strided]) -> M[strided]

torch.sparse.spsolve()

нет

SOLVE(M[sparse_csr], V[strided]) -> V[strided]

torch.sspaddmm()

нет

f * M[sparse_coo] + f * (M[sparse_coo] @ M[strided]) -> M[sparse_coo]

torch.lobpcg()

нет

GENEIG(M[sparse_coo]) -> M[strided], M[strided]

torch.pca_lowrank()

да

PCA(M[sparse_coo]) -> M[strided], M[strided], M[strided]

torch.svd_lowrank()

да

SVD(M[sparse_coo]) -> M[strided], M[strided], M[strided]

Столбец «Разреженный градиент?» указывает, поддерживает ли операция PyTorch обратное распространение относительно аргумента-разреженной матрицы. Все операции PyTorch, кроме torch.smm(), поддерживают обратное распространение относительно аргументов-матриц со страйдовым форматом.

Примечание

В настоящее время PyTorch не поддерживает матричное умножение с сигнатурой формата M[strided] @ M[sparse_coo]. Однако приложения могут выполнять это умножение, используя соотношение между матрицами D @ S == (S.t() @ D.t()).t().

Методы Tensor и разреженные тензоры

Следующие методы Tensor связаны с разреженными тензорами:

Tensor.is_sparse

Возвращает True, если тензор использует разреженный формат хранения COO, и False в противном случае.

Tensor.is_sparse_csr

Возвращает True, если тензор использует разреженный формат хранения CSR, и False в противном случае.

Tensor.dense_dim

Возвращает число плотных измерений в разреженном тензоре self.

Tensor.sparse_dim

Возвращает число разреженных измерений в разреженном тензоре self.

Tensor.sparse_mask

Возвращает новый разреженный тензор со значениями из страйдового тензора self, отфильтрованными по индексам разреженного тензора mask.

Tensor.to_sparse

Возвращает разреженную копию тензора.

Tensor.to_sparse_coo

Преобразует тензор в координатный формат.

Tensor.to_sparse_csr

Преобразует тензор в формат хранения со сжатием по строкам (CSR).

Tensor.to_sparse_csc

Преобразует тензор в формат хранения со сжатием по столбцам (CSC).

Tensor.to_sparse_bsr

Преобразует тензор в формат хранения с блочным сжатием по строкам (BSR) с заданным размером блока.

Tensor.to_sparse_bsc

Преобразует тензор в формат хранения с блочным сжатием по столбцам (BSC) с заданным размером блока.

Tensor.to_dense

Создаёт страйдовую копию self, если self не является страйдовым тензором; в противном случае возвращает self.

Tensor.values

Возвращает тензор значений разреженного тензора COO.

Следующие методы предназначены для разреженных тензоров COO:

Tensor.coalesce

Возвращает объединённую копию self, если self является необъединённым тензором.

Tensor.sparse_resize_

Изменяет размер self разреженного тензора на заданный, а также задаёт число разреженных и плотных измерений.

Tensor.sparse_resize_and_clear_

Удаляет все указанные элементы из разреженного тензора self, а затем изменяет размер self на заданный и задаёт число разреженных и плотных измерений.

Tensor.is_coalesced

Возвращает True, если self — объединённый разреженный тензор COO, и False в противном случае.

Tensor.indices

Возвращает тензор индексов разреженного тензора COO.

Следующие методы предназначены для разреженных тензоров CSR и разреженных тензоров BSR:

Tensor.crow_indices

Возвращает тензор, содержащий сжатые индексы строк тензора self, если self — разреженный тензор CSR с форматом sparse_csr.

Tensor.col_indices

Возвращает тензор, содержащий индексы столбцов тензора self, если self — разреженный тензор CSR с форматом sparse_csr.

Следующие методы предназначены для разреженных тензоров CSC и разреженных тензоров BSC:

Tensor.row_indices

Tensor.ccol_indices

Следующие методы Tensor поддерживают разреженные тензоры COO:

add() add_() addmm() addmm_() any() asin() asin_() arcsin() arcsin_() bmm() clone() deg2rad() deg2rad_() detach() detach_() dim() div() div_() floor_divide() floor_divide_() get_device() index_select() isnan() log1p() log1p_() mm() mul() mul_() mv() narrow_copy() neg() neg_() negative() negative_() numel() rad2deg() rad2deg_() resize_as_() size() pow() sqrt() square() smm() sspaddmm() sub() sub_() t() t_() transpose() transpose_() zero_()

Функции Torch для разреженных тензоров

sparse_coo_tensor

Создаёт разреженный тензор в координатном формате COO с заданными значениями по указанным indices.

sparse_csr_tensor

Создаёт разреженный тензор в формате CSR (сжатие разреженных строк) с заданными значениями по указанным crow_indices и col_indices.

sparse_csc_tensor

Создаёт разреженный тензор в формате CSC (сжатие разреженных столбцов) с заданными значениями по указанным ccol_indices и row_indices.

sparse_bsr_tensor

Создаёт разреженный тензор в формате BSR (блочное сжатие разреженных строк) с заданными двумерными блоками по указанным crow_indices и col_indices.

sparse_bsc_tensor

Создаёт разреженный тензор в формате BSC (блочное сжатие разреженных столбцов) с заданными двумерными блоками по указанным ccol_indices и row_indices.

sparse_compressed_tensor

Создаёт разреженный тензор в формате сжатого хранения — CSR, CSC, BSR или BSC — с заданными значениями по указанным compressed_indices и plain_indices.

sparse.sum

Возвращает сумму элементов каждой строки заданного разреженного тензора.

sparse.addmm

При прямом проходе эта функция выполняет ту же операцию, что и torch.addmm(), но поддерживает обратное распространение для разреженных матриц COO и CSR mat1.

sparse.sampled_addmm

Выполняет умножение плотных матриц mat1 и mat2 в позициях, заданных шаблоном разреженности input.

sparse.mm

Выполняет умножение разреженной матрицы mat1

sspaddmm

Умножает разреженный тензор mat1 на плотный тензор mat2, а затем прибавляет к результату разреженный тензор input.

hspmm

Выполняет умножение разреженной матрицы COO mat1 на страйдовую матрицу mat2.

smm

Выполняет умножение разреженной матрицы input на плотную матрицу mat.

sparse.softmax

Применяет функцию softmax.

sparse.spsolve

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

sparse.log_softmax

Применяет функцию softmax, а затем логарифм.

sparse.spdiags

Создаёт разреженный двумерный тензор, размещая значения из строк diagonals на указанных диагоналях результирующего тензора

Другие функции

Следующие функции torch поддерживают разреженные тензоры:

cat() dstack() empty() empty_like() hstack() index_select() is_complex() is_floating_point() is_nonzero() is_same_size() is_signed() is_tensor() lobpcg() mm() native_norm() pca_lowrank() select() stack() svd_lowrank() unsqueeze() vstack() zeros() zeros_like()

Для управления проверкой инвариантов разреженных тензоров см.:

sparse.check_sparse_tensor_invariants

Средство для управления проверкой инвариантов разреженных тензоров.

О применении разреженных тензоров с функцией gradcheck() см.:

sparse.as_sparse_gradcheck

Декоратор функции, расширяющий gradcheck для разреженных тензоров.

Унарные функции, сохраняющие ноль

Мы стремимся поддерживать все «унарные функции, сохраняющие ноль»: функции от одного аргумента, которые отображают ноль в ноль.

Если вам нужна унарная функция, сохраняющая ноль, которой здесь нет, пожалуйста, создайте запрос на добавление этой функции. Как всегда, перед созданием запроса сначала воспользуйтесь поиском.

В настоящее время следующие операторы поддерживают входные тензоры sparse COO/CSR/CSC/BSR/CSR.

abs() asin() asinh() atan() atanh() ceil() conj_physical() floor() log1p() neg() round() sin() sinh() sign() sgn() signbit() tan() tanh() trunc() expm1() sqrt() angle() isinf() isposinf() isneginf() isnan() erf() erfinv()

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

Spec-Zone.ru

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