torch.sparse
Предупреждение
API PyTorch для разреженных тензоров находится в стадии бета-тестирования и может быть изменён в ближайшем будущем. Мы приветствуем предложения по добавлению функций, сообщения об ошибках и общие предложения в виде проблем на Github.
Почему и когда использовать разреженность
По умолчанию PyTorch хранит torch.Tensor хранит элементы непрерывно в физической памяти. Это приводит к эффективной реализации различных алгоритмов обработки массивов, которые требуют быстрого доступа к элементам.
Теперь некоторые пользователи могут решить представлять данные, такие как матрицы смежности графов, обрезные веса или облака точек, с помощью тензоров, чьи элементы в основном равны нулю. Мы признаём, что это важные приложения и стремимся обеспечить оптимизацию производительности для этих случаев использования с помощью разреженных форматов хранения.
За последние годы были разработаны различные форматы разреженного хранения, такие как COO, CSR/CSC, LIL и т. д. Хотя они отличаются по точным макетам, они все сжимают данные с помощью эффективного представления элементов с нулевым значением. Мы называем несжатые значения заданными в отличие от незаданных, сжатых элементов.
Благодаря сжатию повторяющихся нулей разреженные форматы хранения предназначены для экономии памяти и вычислительных ресурсов на различных процессорах и графических процессорах. Особенно при высокой степени разреженности или сильно структурированной разреженности это может иметь значительные последствия для производительности. Таким образом, разреженные форматы хранения могут рассматриваться как оптимизация производительности.
Как и многие другие оптимизации производительности, разреженные форматы хранения не всегда выгодны. При попытке использовать разреженные форматы для вашего случая использования вы можете обнаружить, что время выполнения уменьшается, а не увеличивается.
Пожалуйста, не стесняйтесь открыть вопрос на Github, если вы аналитически ожидали значительного увеличения производительности, но вместо этого измерили ухудшение. Это поможет нам расставить приоритеты в реализации эффективных ядер и более широкой оптимизации производительности.
Мы делаем лёгким перебор различных макетов разреженности и преобразование между ними, не высказывая предпочтений относительно того, что лучше для вашего конкретного приложения.
Обзор функциональности
Мы хотим, чтобы создание разреженного тензора из заданного плотного тензора было простым, предоставив процедуры преобразования для каждого макета.
В следующем примере мы преобразуем 2D-тензор с плотными (шагающими) макетом по умолчанию в 2D-тензор, поддерживаемый макетом памяти 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. Дополнительную информацию см. в ссылках.
Обратите внимание, что мы предоставляем небольшие обобщения этих форматов.
Пакеты: Устройства, такие как графические процессоры, требуют пакетов для оптимальной производительности, и поэтому мы поддерживаем размерности пакетов.
В настоящее время мы предлагаем очень простую версию пакетов, где каждый компонент разреженного формата сам по себе упакован в пакеты. Это также требует одинакового числа заданных элементов на элемент пакета. В этом примере мы создаём 3D (пакетированный) CSR-тензор из 3D-плотного тензора.
>>> 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)
Размеры плотных данных: С другой стороны, некоторые данные, такие как вложения графов, могут лучше рассматриваться как разреженные наборы векторов, а не скаляров.
В этом примере мы создаём 3D гибридный COO-тензор с 2 разреженными и 1 плотной размерностью из 3D-шагающего тензора. Если вся строка в 3D-шагающем тензоре равна нулю, она не сохраняется. Однако, если какие-либо значения в строке не равны нулю, они сохраняются полностью. Это уменьшает количество индексов, так как нам нужен один индекс на строку вместо одного на элемент. Но это также увеличивает объём хранения значений. Поскольку сохраняются только строки, которые полностью равны нулю, а наличие ненулевых элементов приводит к сохранению всей строки.
>>> 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)
Обзор операторов
В принципе, операции над тензором с разреженными форматами хранения ведут себя так же, как операции над тензором с шагающими (или другими) форматами хранения. Особенности хранения, то есть физическое расположение данных, влияют на производительность операции, но не должны влиять на семантику.
Мы активно расширяем покрытие операторов для разреженных тензоров. Пользователи пока не должны ожидать поддержки на том же уровне, что и для плотных тензоров. См. нашу документацию по операторам операторов для получения списка.
>>> 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, которые, в свою очередь, также поддерживаются и управляются разреженными форматами хранения и ядрами.
Также обратите внимание, что пока пользователь не имеет выбора макета выходных данных. Например, добавление разреженного тензора к обычному шагающему тензору приводит к шагающему тензору. Некоторые пользователи могут предпочесть, чтобы макет оставался разреженным, потому что они знают, что результат по-прежнему будет достаточно разреженным.
>>> a + b.to_sparse()
tensor([[0., 3.],
[3., 0.]])
Мы признаём, что доступ к ядрам, которые могут эффективно создавать разные макеты выходных данных, может быть очень полезным. Последующая операция может значительно выиграть от получения определённого макета. Мы работаем над API для управления макетом результата и понимаем, что это важная функция для планирования более оптимального пути выполнения для любой модели.
Разреженные тензоры COO
PyTorch реализует так называемый формат координат, или формат COO, как один из форматов хранения для реализации разреженных тензоров. В формате COO указанные элементы хранятся как кортежи индексов элементов и соответствующих значений. В частности,
- индексы указанных элементов собираются в тензоре
indicesразмера(ndim, nse)и с типом элементаtorch.int64, - соответствующие значения собираются в тензоре
valuesразмера(nse,)и с произвольным целочисленным или с плавающей точкой типом элемента,
где ndim — размерность тензора, а nse — количество указанных элементов.
Примечание
Потребление памяти разреженным тензором COO составляет не менее (ndim *
8 + <size of element type in bytes>) * nse байтов (плюс постоянные накладные расходы на хранение других данных тензора).
Потребление памяти обычным тензором составляет не менее 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 байт при использовании стандартного макета обычного тензора. Заметьте 200-кратную экономию памяти при использовании формата хранения COO.
Создание
Разреженный 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)и с произвольным целочисленным или с плавающей точкой типом элемента.
Примечание
Мы используем тензор размерности (M + K) для обозначения N-мерного гибридного разреженного тензора, где 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- значения хранятся как смежные тензоры.
Примечание
Плотность всегда следует за разреженностью, то есть смешивание плотных и разреженных измерений не поддерживается.
Несвязанные разреженные COO-тензоры
Формат PyTorch разреженного COO-тензора допускает разреженные несвязанные тензоры, где в индексах могут быть дублирующиеся координаты; в этом случае интерпретация состоит в том, что значение в этом индексе является суммой всех дублирующихся значений. Например, можно указать несколько значений, 3 и 4, для одного индекса 1, что приводит к 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 расширяют поддержкой пакетов разреженных тензоров, позволяя использовать значения тензоров с несколькими измерениями и храня значения разреженного тензора в плотных блоках.
Примечание
Мы используем тензор размерности (B + M + K), чтобы обозначить N-мерный гибридный разреженный сжатый тензор, где 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— количество обычных измерений (ортогональных сжатым измерениям, например, столбцы или строки).
Примечание
Обобщение разреженных сжатых схем на N-мерные тензоры может привести к некоторой путанице относительно подсчета указанных элементов. Когда разреженный сжатый тензор содержит измерения пакета, количество указанных элементов будет соответствовать количеству таких элементов на пакет. Когда разреженный сжатый тензор имеет плотные измерения, теперь рассматриваемый элемент — это K-мерный массив. Также для разреженных сжатых схем блоков 2-мерный блок считается указанным элементом. Рассмотрим, например, 3-мерный разреженный сжатый тензор с одним измерением пакета длиной b, и формой блока p, q. Если этот тензор имеет n указанных элементов, то фактически у нас есть n блоков, указанных на пакет. Этот тензор имел бы values с формой (b, n, p, q). Это толкование количества указанных элементов вытекает из того, что все разреженные сжатые схемы получены из сжатия 2-мерной матрицы. Измерения пакета рассматриваются как стекирование разреженных матриц, плотные измерения изменяют значение элемента с простого скалярного значения на массив со своими измерениями.
Разреженный тензор CSR
Основное преимущество формата CSR перед форматом COO заключается в более эффективном использовании памяти и значительно более быстрых операциях вычислений, таких как умножение разреженной матрицы на вектор, с использованием бэкэндов MKL и MAGMA.
В простейшем случае (0 + 2 + 0)-мерный разреженный CSR-тензор состоит из трех 1-мерных тензоров: crow_indices, col_indices и values:
- Тензор
crow_indicesсостоит из сжатых индексов строк. Это 1-мерный тензор размеромnrows + 1(количество строк плюс 1). Последний элементcrow_indices— это количество указанных элементов,nse. Этот тензор кодирует индекс вvaluesиcol_indicesв зависимости от того, где начинается данная строка. Каждое последующее число в тензоре, вычтенное из предыдущего, обозначает количество элементов в данной строке. - Тензор
col_indicesсодержит индексы столбцов каждого элемента. Это 1-мерный тензор размеромnse. - Тензор
valuesсодержит значения элементов CSR-тензора. Это 1-мерный тензор размеромnse.
Примечание
Тензоры индексов crow_indices и col_indices должны иметь тип элемента либо torch.int64 (по умолчанию), либо torch.int32. Если вы хотите использовать операции с матрицами, поддерживаемые MKL, используйте torch.int32. Это связано с тем, что по умолчанию PyTorch связан с MKL LP64, который использует 32-битный индексирование.
В общем случае (B + 2 + K)-мерный разреженный CSR-тензор состоит из двух (B + 1)-мерных тензоров индексов crow_indices и col_indices, и (1 + K)-мерного тензора values, при этом
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-тензора. Заметьте экономию в 1,6 и 310 раз при использовании формата хранения CSR по сравнению с форматами COO и с шагом соответственно.
Создание 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 должен быть указан явно.
Самый простой способ создания 2-мерного разреженного CSR-тензора из строкового или разреженного COO-тензора — использовать метод torch.Tensor.to_sparse_csr(). Любые нули в (строковом) тензоре будут интерпретироваться как отсутствующие значения в разреженном тензоре:
>>> 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 для хранения 2-мерных тензоров с расширением для поддержки пакетов разреженных 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-тензора имеет аргумент сжатых индексов столбцов перед аргументом индексов строк.
В (0 + 2 + 0)-мерный разреженный CSC-тензоры можно преобразовать любой двумерный тензор, используя метод torch.Tensor.to_sparse_csc(). Любые нули в (строковом) тензоре будут интерпретироваться как отсутствующие значения в разреженном тензоре:
>>> 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] обозначает матрицу (2-мерный тензор PyTorch), а V[layout] обозначает вектор (1-мерный тензор PyTorch). Кроме того, f обозначает скаляр (число с плавающей точкой или 0-мерный тензор PyTorch), * — поэлементное умножение, а @ — умножение матриц.
Операция PyTorch | Разряженный градиент? | Подпись макета |
|---|---|---|
нет |
| |
нет |
| |
нет |
| |
нет |
| |
нет |
| |
да |
| |
нет |
| |
нет |
| |
нет |
| |
нет |
| |
да |
| |
нет |
| |
нет |
| |
да |
| |
да |
|
где столбец «Разряженный градиент?» указывает, поддерживает ли операция PyTorch обратное распространение по аргументу разряженной матрицы. Все операции PyTorch, кроме torch.smm(), поддерживают обратное распространение по аргументам матриц со смещениями.
Примечание
В настоящее время PyTorch не поддерживает умножение матриц со схемой макета M[strided] @ M[sparse_coo]. Однако приложения по-прежнему могут вычислять это, используя матричное соотношение D @
S == (S.t() @ D.t()).t().
Методы тензоров и разреженные тензоры
Следующие методы тензоров относятся к разреженным тензорам:
Является | |
Является | |
Возвращает количество плотных измерений в разреженном тензоре | |
Возвращает количество разреженных измерений в разреженном тензоре | |
Возвращает новый разреженный тензор со значениями из стредного тензора | |
Возвращает разреженную копию тензора. | |
Преобразует тензор в формат координат. | |
Преобразует тензор в формат сжатой строки (CSR). | |
Преобразует тензор в формат сжатого столбца (CSC). | |
Преобразует тензор CSR в формат сжатой разреженной строки (BSR) заданного размера блока. | |
Преобразует тензор CSR в формат сжатого разреженного столбца (BSC) заданного размера блока. | |
Создает стредную копию | |
Возвращает тензор значений разреженного тензора COO. |
Следующие методы тензоров специфичны для разреженных тензоров COO:
Возвращает сжатую копию | |
Изменяет размер разреженного тензора до желаемого размера и количества разреженных и плотных измерений. | |
Удаляет все указанные элементы из разреженного тензора | |
Возвращает | |
Возвращает тензор индексов разреженного тензора COO. |
Следующие методы специфичны для разреженных тензоров CSR и разреженных тензоров BSR:
Возвращает тензор, содержащий сжатые индексы строк | |
Возвращает тензор, содержащий индексы столбцов |
Следующие методы специфичны для разреженных тензоров CSC и разреженных тензоров BSC:
Следующие методы тензора поддерживают разреженные тензоры 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 (координат) со значениями в указанных |
sparse_csr_tensor
| Создаёт разреженный тензор в формате CSR (сжатая разреженная строка) со значениями в указанных |
sparse_csc_tensor
| Создаёт разреженный тензор в формате CSC (сжатая разреженная колонка) со значениями в указанных |
sparse_bsr_tensor
| Создаёт разреженный тензор в формате BSR (блочно-сжатая разреженная строка) с 2-мерными блоками в указанных |
sparse_bsc_tensor
| Создаёт разреженный тензор в формате BSC (блочно-сжатая разреженная колонка) с 2-мерными блоками в указанных |
sparse_compressed_tensor
| Создаёт разреженный тензор в сжатом формате - CSR, CSC, BSR или BSC - со значениями в указанных |
Возвращает сумму каждой строки разреженного тензора | |
Эта функция выполняет точно такие же действия, что и | |
Выполняет умножение матриц плотных матриц | |
Выполняет умножение матриц для разреженной матрицы | |
sspaddmm
| Умножает разреженный тензор |
hspmm
| Выполняет умножение матриц разреженной матрицы COO |
smm
| Выполняет умножение матриц разреженной матрицы |
Применяет функцию softmax. | |
Применяет функцию softmax, а затем логарифм. | |
Создаёт разреженный 2D тензор, помещая значения из строк |
Другие функции
Следующие функции 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()
Унарные функции
Мы стремимся поддерживать все унарные функции, сохраняющие нулевое значение.
Если вы обнаружите, что у нас отсутствует необходимая унарная функция, сохраняющая нулевое значение, пожалуйста, откройте запрос на новую функцию. Как всегда, пожалуйста, сначала используйте функцию поиска, прежде чем открывать запрос.
Следующие операторы в настоящее время поддерживают входные тензоры разреженных типов 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()
© 2024, PyTorch Contributors
PyTorch has a BSD-style license, as found in the LICENSE file.
https://pytorch.org/docs/1.13/sparse.html