Tensorflow Sparse Tensors объяснены с примерами

Tensorflow Sparse Tensors объяснены с примерами

19 июля 2025 г.

Обзор контента

  • Sparse Tensors в Tensorflow
  • Создание tf.sparse.sparsetensor
  • Манипулируя редкими тензорами
  • Использование tf.sparse.sparsetensor с другими API -интерфейсом tensorflow
  • tf.keras
  • tf.data
  • tf.train.example
  • tf.function
  • Дальнейшее чтение и ресурсы

Работая с тензорами, которые содержат много нулевых значений, важно хранить их в пространстве и временем. Разреженные тензоры обеспечивают эффективное хранение и обработку тензоров, которые содержат много нулевых значений. Sparsy Tensors широко используются в схемах кодирования, какTF-IDFВ рамках предварительной обработки данных в приложениях NLP и для предварительной обработки изображений с большим количеством темных пикселей в приложениях компьютерного зрения.

Sparse Tensors в Tensorflow

TensorFlow представляет редкие тензоры черезtf.sparse.SparseTensorобъект. В настоящее время разреженные тензоры в Tensorflow кодируются с использованием формата списка координат (COO). Этот формат кодирования оптимизирован для матриц гипер-склада, таких как встраивание.

Кодирование COO для разреженных тензоров состоит из:

  • values: 1D тензор с формой[N]содержащий все ненулевые значения.
  • indices: 2D тензор с формой[N, rank], содержащий индексы ненулевых значений.
  • dense_shape: 1D тензор с формой[rank], указав форму тензора.

Аненулевойценность в контекстеtf.sparse.SparseTensorэто значение, которое явно не закодировано. Можно явно включить нулевые значения вvaluesиз редкой матрицы COO, но эти «явные нули», как правило, не включены при ссылке на ненулевые значения в скудном тензоре.

Примечание:tf.sparse.SparseTensorне требует, чтобы индексы/значения были в каком-либо конкретном порядке, но несколько операций предполагают, что они находятся в порядке строя. Использоватьtf.sparse.reorderЧтобы создать копию разреженного тензора, которая отсортирована в каноническом порядке строк.

Создание аtf.sparse.SparseTensor

Построить разреженные тензоры, непосредственно указав ихvaluesВindices, иdense_shapeПолем

import tensorflow as tf

2024-10-25 01:24:09.202320: E external/local_xla/xla/stream_executor/cuda/cuda_fft.cc:477] Unable to register cuFFT factory: Attempting to register factory for plugin cuFFT when one has already been registered
WARNING: All log messages before absl::InitializeLog() is called are written to STDERR
E0000 00:00:1729819449.223893   16549 cuda_dnn.cc:8310] Unable to register cuDNN factory: Attempting to register factory for plugin cuDNN when one has already been registered
E0000 00:00:1729819449.230517   16549 cuda_blas.cc:1418] Unable to register cuBLAS factory: Attempting to register factory for plugin cuBLAS when one has already been registered

st1 = tf.sparse.SparseTensor(indices=[[0, 3], [2, 4]],
                      values=[10, 20],
                      dense_shape=[3, 10])

W0000 00:00:1729819451.911465   16549 gpu_device.cc:2344] Cannot dlopen some GPU libraries. Please make sure the missing libraries mentioned above are installed properly if you would like to use GPU. Follow the guide at https://www.tensorflow.org/install/gpu for how to download and setup the required libraries for your platform.
Skipping registering GPU devices...

Когда вы используетеprint()Функция для печати скудного тензора, он показывает содержимое трех компонентных тензоров:

print(st1)

SparseTensor(indices=tf.Tensor(
[[0 3]
 [2 4]], shape=(2, 2), dtype=int64), values=tf.Tensor([10 20], shape=(2,), dtype=int32), dense_shape=tf.Tensor([ 3 10], shape=(2,), dtype=int64))

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

def pprint_sparse_tensor(st):
  s = "<SparseTensor shape=%s \n values={" % (st.dense_shape.numpy().tolist(),)
  for (index, value) in zip(st.indices, st.values):
    s += f"\n  %s: %s" % (index.numpy().tolist(), value.numpy().tolist())
  return s + "}>"

print(pprint_sparse_tensor(st1))

<SparseTensor shape=[3, 10] 
 values={
  [0, 3]: 10
  [2, 4]: 20}>

Вы также можете построить разреженные тензоры из плотных тензоров, используяtf.sparse.from_denseи преобразовать их обратно в плотные тензоры, используяtf.sparse.to_denseПолем

st2 = tf.sparse.from_dense([[1, 0, 0, 8], [0, 0, 0, 0], [0, 0, 3, 0]])
print(pprint_sparse_tensor(st2))

<SparseTensor shape=[3, 4] 
 values={
  [0, 0]: 1
  [0, 3]: 8
  [2, 2]: 3}>

st3 = tf.sparse.to_dense(st2)
print(st3)

tf.Tensor(
[[1 0 0 8]
 [0 0 0 0]
 [0 0 3 0]], shape=(3, 4), dtype=int32)

Манипулируя редкими тензорами

Используйте утилиты вtf.sparseПакет для манипулирования редкими тензорами. ОПС какtf.math.addТо, что вы можете использовать для арифметических манипуляций с плотными тензорами, не работает с редкими тензорами.

Добавить разреженные тензоры той же формы, используяtf.sparse.addПолем

st_a = tf.sparse.SparseTensor(indices=[[0, 2], [3, 4]],
                       values=[31, 2], 
                       dense_shape=[4, 10])

st_b = tf.sparse.SparseTensor(indices=[[0, 2], [3, 0]],
                       values=[56, 38],
                       dense_shape=[4, 10])

st_sum = tf.sparse.add(st_a, st_b)

print(pprint_sparse_tensor(st_sum))

<SparseTensor shape=[4, 10] 
 values={
  [0, 2]: 87
  [3, 0]: 38
  [3, 4]: 2}>

Использоватьtf.sparse.sparse_dense_matmulумножить скудные тензоры с плотными матрицами.

st_c = tf.sparse.SparseTensor(indices=([0, 1], [1, 0], [1, 1]),
                       values=[13, 15, 17],
                       dense_shape=(2,2))

mb = tf.constant([[4], [6]])
product = tf.sparse.sparse_dense_matmul(st_c, mb)

print(product)

tf.Tensor(
[[ 78]
 [162]], shape=(2, 1), dtype=int32)

Собрать разреженные тензоры, используяtf.sparse.concatи разбери их, используяtf.sparse.sliceПолем

sparse_pattern_A = tf.sparse.SparseTensor(indices = [[2,4], [3,3], [3,4], [4,3], [4,4], [5,4]],
                         values = [1,1,1,1,1,1],
                         dense_shape = [8,5])
sparse_pattern_B = tf.sparse.SparseTensor(indices = [[0,2], [1,1], [1,3], [2,0], [2,4], [2,5], [3,5], 
                                              [4,5], [5,0], [5,4], [5,5], [6,1], [6,3], [7,2]],
                         values = [1,1,1,1,1,1,1,1,1,1,1,1,1,1],
                         dense_shape = [8,6])
sparse_pattern_C = tf.sparse.SparseTensor(indices = [[3,0], [4,0]],
                         values = [1,1],
                         dense_shape = [8,6])

sparse_patterns_list = [sparse_pattern_A, sparse_pattern_B, sparse_pattern_C]
sparse_pattern = tf.sparse.concat(axis=1, sp_inputs=sparse_patterns_list)
print(tf.sparse.to_dense(sparse_pattern))

tf.Tensor(
[[0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0]
 [0 0 0 0 1 1 0 0 0 1 1 0 0 0 0 0 0]
 [0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0]
 [0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0]
 [0 0 0 0 1 1 0 0 0 1 1 0 0 0 0 0 0]
 [0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0]], shape=(8, 17), dtype=int32)

sparse_slice_A = tf.sparse.slice(sparse_pattern_A, start = [0,0], size = [8,5])
sparse_slice_B = tf.sparse.slice(sparse_pattern_B, start = [0,5], size = [8,6])
sparse_slice_C = tf.sparse.slice(sparse_pattern_C, start = [0,10], size = [8,6])
print(tf.sparse.to_dense(sparse_slice_A))
print(tf.sparse.to_dense(sparse_slice_B))
print(tf.sparse.to_dense(sparse_slice_C))

tf.Tensor(
[[0 0 0 0 0]
 [0 0 0 0 0]
 [0 0 0 0 1]
 [0 0 0 1 1]
 [0 0 0 1 1]
 [0 0 0 0 1]
 [0 0 0 0 0]
 [0 0 0 0 0]], shape=(8, 5), dtype=int32)
tf.Tensor(
[[0]
 [0]
 [1]
 [1]
 [1]
 [1]
 [0]
 [0]], shape=(8, 1), dtype=int32)
tf.Tensor([], shape=(8, 0), dtype=int32)

Если вы используете TensorFlow 2.4 или выше, используйтеtf.sparse.map_valuesДля элементных операций на ненулевых значениях в разреженных тензорах.

st2_plus_5 = tf.sparse.map_values(tf.add, st2, 5)
print(tf.sparse.to_dense(st2_plus_5))

tf.Tensor(
[[ 6  0  0 13]
 [ 0  0  0  0]
 [ 0  0  8  0]], shape=(3, 4), dtype=int32)

Обратите внимание, что только ненулевые значения были изменены - нулевые значения остаются нулевыми.

Эквивалентно, вы можете следовать шаблону дизайна ниже для более ранних версий Tensorflow:

st2_plus_5 = tf.sparse.SparseTensor(
    st2.indices,
    st2.values + 5,
    st2.dense_shape)
print(tf.sparse.to_dense(st2_plus_5))

tf.Tensor(
[[ 6  0  0 13]
 [ 0  0  0  0]
 [ 0  0  8  0]], shape=(3, 4), dtype=int32)

С использованиемtf.sparse.SparseTensorс другими APIS TensorFlow

Sparsy Tensors работают прозрачно с этими API -интерфейсом Tensorflow:

  • tf.keras
  • tf.data
  • tf.Train.ExampleПротобуф
  • tf.function
  • tf.while_loop
  • tf.cond
  • tf.identity
  • tf.cast
  • tf.print
  • tf.saved_model
  • tf.io.serialize_sparse
  • tf.io.serialize_many_sparse
  • tf.io.deserialize_many_sparse
  • tf.math.abs
  • tf.math.negative
  • tf.math.sign
  • tf.math.square
  • tf.math.sqrt
  • tf.math.erf
  • tf.math.tanh
  • tf.math.bessel_i0e
  • tf.math.bessel_i1e

Примеры показаны ниже для нескольких из вышеперечисленных API.

tf.keras

Подмножествоtf.kerasAPI поддерживает разреженные тензоры без дорогого кастинга или конверсии. API Keras позволяет вам передавать разреженные тензоры в качестве входных данных в модель Keras. Наборsparse=TrueПри звонкеtf.keras.Inputилиtf.keras.layers.InputLayerПолем Вы можете пройти скудные тензоры между слоями кераса, а также модели Keras возвращают их в качестве выходов. Если вы используете разреженные тензоры вtf.keras.layers.DenseСлои В вашей модели они выводят плотные тензоры.

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

x = tf.keras.Input(shape=(4,), sparse=True)
y = tf.keras.layers.Dense(4)(x)
model = tf.keras.Model(x, y)

sparse_data = tf.sparse.SparseTensor(
    indices = [(0,0),(0,1),(0,2),
               (4,3),(5,0),(5,1)],
    values = [1,1,1,1,1,1],
    dense_shape = (6,4)
)

model(sparse_data)

model.predict(sparse_data)

1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 87ms/step
array([[ 1.8707037e-02,  7.7025330e-01,  2.2425324e-01, -1.9139588e+00],
       [ 0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00],
       [ 0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00],
       [ 0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00],
       [-6.2435389e-02, -4.5783034e-01,  1.2970567e-03, -1.8046319e-01],
       [-8.0019468e-01,  9.0452707e-01,  2.1884918e-02, -1.3622781e+00]],
      dtype=float32)

tf.data

Аtf.dataAPI позволяет создавать сложные входные трубопроводы из простых, многоразовых кусочков. Его основная структура данныхtf.data.Dataset, который представляет последовательность элементов, в которых каждый элемент состоит из одного или нескольких компонентов.

Создание наборов данных с редкими тензорами

Создайте наборы данных от Sparsy Tensors, используя те же методы, которые используются для их создания изtf.Tensors или numpy массивы, такие какtf.data.Dataset.from_tensor_slicesПолем Этот OP сохраняет разреженную (или редкую природу) данных.

dataset = tf.data.Dataset.from_tensor_slices(sparse_data)
for element in dataset: 
  print(pprint_sparse_tensor(element))

<SparseTensor shape=[4] 
 values={
  [0]: 1
  [1]: 1
  [2]: 1}>
<SparseTensor shape=[4] 
 values={}>
<SparseTensor shape=[4] 
 values={}>
<SparseTensor shape=[4] 
 values={}>
<SparseTensor shape=[4] 
 values={
  [3]: 1}>
<SparseTensor shape=[4] 
 values={
  [0]: 1
  [1]: 1}>

Пакетные и непредвзятые наборы данных с редкими тензорами

Вы можете привести к сочетанию (объединить элементы подряд в один элемент) и разоблачить наборы данных с редкими тензорами, используяDataset.batchиDataset.unbatchМетоды соответственно.

batched_dataset = dataset.batch(2)
for element in batched_dataset:
  print (pprint_sparse_tensor(element))

<SparseTensor shape=[2, 4] 
 values={
  [0, 0]: 1
  [0, 1]: 1
  [0, 2]: 1}>
<SparseTensor shape=[2, 4] 
 values={}>
<SparseTensor shape=[2, 4] 
 values={
  [0, 3]: 1
  [1, 0]: 1
  [1, 1]: 1}>

unbatched_dataset = batched_dataset.unbatch()
for element in unbatched_dataset:
  print (pprint_sparse_tensor(element))

<SparseTensor shape=[4] 
 values={
  [0]: 1
  [1]: 1
  [2]: 1}>
<SparseTensor shape=[4] 
 values={}>
<SparseTensor shape=[4] 
 values={}>
<SparseTensor shape=[4] 
 values={}>
<SparseTensor shape=[4] 
 values={
  [3]: 1}>
<SparseTensor shape=[4] 
 values={
  [0]: 1
  [1]: 1}>

Вы также можете использоватьtf.data.experimental.dense_to_sparse_batchдля составления элементов наборов данных различных форм в разреженные тензоры.

Преобразование наборов данных с редкими тензорами

Преобразовать и создавать разреженные тензоры в наборах данных, используяDataset.mapПолем

transform_dataset = dataset.map(lambda x: x*2)
for i in transform_dataset:
  print(pprint_sparse_tensor(i))

<SparseTensor shape=[4] 
 values={
  [0]: 2
  [1]: 2
  [2]: 2}>
<SparseTensor shape=[4] 
 values={}>
<SparseTensor shape=[4] 
 values={}>
<SparseTensor shape=[4] 
 values={}>
<SparseTensor shape=[4] 
 values={
  [3]: 2}>
<SparseTensor shape=[4] 
 values={
  [0]: 2
  [1]: 2}>

tf.train.example

tf.train.Exampleявляется стандартным кодированием Protobuf для данных TensorFlow. При использовании разреженных тензоров сtf.train.Example, ты можешь:

  • Прочитать данные с переменной длиной вtf.sparse.SparseTensorс использованиемtf.io.VarLenFeatureПолем Тем не менее, вам следует рассмотреть возможность использованияtf.io.RaggedFeatureвместо.
  • Прочитать произвольные разреженные данные вtf.sparse.SparseTensorс использованиемtf.io.SparseFeature, который использует три отдельных ключа функций для храненияindicesВvalues, иdense_shapeПолем

tf.function

Аtf.functionДекоратор предварительно считывает графики тензора для функций Python, что может существенно улучшить производительность вашего кода TensorFlow. Sparsy Tensors работают прозрачно с обоимиtf.functionиконкретные функцииПолем

@tf.function
def f(x,y):
  return tf.sparse.sparse_dense_matmul(x,y)

a = tf.sparse.SparseTensor(indices=[[0, 3], [2, 4]],
                    values=[15, 25],
                    dense_shape=[3, 10])

b = tf.sparse.to_dense(tf.sparse.transpose(a))

c = f(a,b)

print(c)

tf.Tensor(
[[225   0   0]
 [  0   0   0]
 [  0   0 625]], shape=(3, 3), dtype=int32)

Отличие пропущенных значений от нулевых значений

Большинство операцийtf.sparse.SparseTensorS Обработка пропущенных значений и явных нулевых значений одинаково. Это по дизайну -tf.sparse.SparseTensorПредполагается действовать как плотный тензор.

Тем не менее, есть несколько случаев, когда может быть полезно отличать нулевые значения от пропущенных значений. В частности, это позволяет одному способу кодировать отсутствующие/неизвестные данные в ваших учебных данных. Например, рассмотрим вариант использования, когда у вас есть тензор баллов (который может иметь какое -либо значение плавающей запятой от -inf до +inf), с некоторыми отсутствующими оценками. Вы можете кодировать этот тензор, используя разреженный тензор, где явные нули известны нулевые оценки, но неявные нулевые значения фактически представляют недостающие данные, а не ноль.

Примечание:Как правило, это не предполагаемое использованиеtf.sparse.SparseTensorS; И вы можете также рассмотреть другие методы кодирования этого, например, с использованием отдельного тензора маски, который идентифицирует местоположения известных/неизвестных значений. Тем не менее, проявляйте осторожность при использовании этого подхода, поскольку большинство разреженных операций будут одинаково рассматривать явные и неявные нулевые значения.

Обратите внимание, что некоторые операции любятtf.sparse.reduce_maxНе обращайтесь с недостающими значениями, как если бы они были нулю. Например, когда вы запускаете блок кода ниже, ожидаемый вывод0Полем Однако из -за этого исключения вывод-3Полем

print(tf.sparse.reduce_max(tf.sparse.from_dense([-5, 0, -3])))

tf.Tensor(-3, shape=(), dtype=int32)

Напротив, когда вы применяетеtf.math.reduce_maxК плотному тензору выходной выход равен 0, как и ожидалось.

print(tf.math.reduce_max([-5, 0, -3]))

tf.Tensor(0, shape=(), dtype=int32)

Дальнейшее чтение и ресурсы

  • СмТенсорский гидЧтобы узнать о тензорах.
  • ЧитатьРуководство с рваным тензоромЧтобы узнать, как работать с рваными тензорами, типом тензора, который позволяет вам работать с неравномерными данными.
  • Проверьте эту модель обнаружения объекта вTensorflow Model Gardenкоторый использует разреженные тензоры вtf.ExampleДекодер данных.

Первоначально опубликовано наTensorflowВеб -сайт, эта статья появляется здесь под новым заголовком и имеет лицензию в CC на 4.0. Образцы кода, разделенные по лицензии Apache 2.0.


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE