使用稀疏张量#

在 TensorFlow.org 上查看 在 Google Colab 中运行 在 GitHub 上查看源代码 下载笔记本

使用包含大量零值的张量时,务必以节省空间和时间的方式存储它们。稀疏张量可以高效存储和处理包含大量零值的张量。稀疏张量广泛用于 TF-IDF 等编码方案,作为 NLP 应用中数据预处理的一部分,以及在计算机视觉应用中预处理具有大量暗像素的图像。

TensorFlow 中的稀疏张量#

TensorFlow 通过 tf.sparse.SparseTensor 对象表示稀疏张量。目前,TensorFlow 中的稀疏张量使用坐标列表 (COO) 格式进行编码。这种编码格式针对嵌入向量等超稀疏矩阵进行了优化。

稀疏张量的 COO 编码包括:

  • values:形状为 [N] 的一维张量,包含所有非零值。

  • indices:形状为 [N, rank] 的二维张量,包含非零值的索引。

  • dense_shape:形状为 [rank] 的一维张量,指定张量的形状。

tf.sparse.SparseTensor 上下文中的非零值是未显式编码的值。可以在 COO 稀疏矩阵的 values 中显式包含零值,但在稀疏张量中引用非零值时,通常不包含这些“显式零”。

注:tf.sparse.SparseTensor 不要求索引/值按任何特定顺序排列,但一些运算假定它们按行优先顺序排列。使用 tf.sparse.reorder 创建按规范行优先顺序排序的稀疏张量副本。

创建 tf.sparse.SparseTensor#

通过直接指定它们的 valuesindicesdense_shape 来构造稀疏张量。

from set_env import temp_dir
import tensorflow as tf
st1 = tf.sparse.SparseTensor(indices=[[0, 3], [2, 4]],
                      values=[10, 20],
                      dense_shape=[3, 10])
https://github.com/tensorflow/docs-l10n/blob/master/site/zh-cn/guide/images/sparse_tensor.png?raw=true

当您使用 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], [7, 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, 4]: 2
  [7, 0]: 38}>

使用 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 与其他 TensorFlow API 一起使用#

稀疏张量透明地与这些 TensorFlow API 一起使用:

  • tf.keras

  • tf.data

  • tf.Train.Example protobuf

  • 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.keras API 的一个子集支持稀疏张量,无需执行开销较大的类型转换或转换运算。可以利用 Keras API 将稀疏张量作为输入传递给 Keras 模型。调用 tf.keras.Inputtf.keras.layers.InputLayer 时设置 sparse=True。您可以在 Keras 层之间传递稀疏张量,也可以让 Keras 模型将它们作为输出返回。如果在模型中的 tf.keras.layers.Dense 层中使用稀疏张量,它们将输出密集张量。

下面的示例展示了如果仅使用支持稀疏输入的层,如何将稀疏张量作为输入传递给 Keras 模型。

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)
WARNING: All log messages before absl::InitializeLog() is called are written to STDERR
I0000 00:00:1729854472.678967 4109398 service.cc:146] XLA service 0x55fc4d6799b0 initialized for platform CUDA (this does not guarantee that XLA will be used). Devices:
I0000 00:00:1729854472.678997 4109398 service.cc:154]   StreamExecutor device (0): NVIDIA GeForce RTX 3090, Compute Capability 8.6
I0000 00:00:1729854472.679001 4109398 service.cc:154]   StreamExecutor device (1): NVIDIA GeForce RTX 2080 Ti, Compute Capability 7.5
---------------------------------------------------------------------------
InvalidArgumentError                      Traceback (most recent call last)
Cell In[15], line 14
      5 sparse_data = tf.sparse.SparseTensor(
      6     indices = [(0,0),(0,1),(0,2),
      7                (4,3),(5,0),(5,1)],
      8     values = [1,1,1,1,1,1],
      9     dense_shape = (6,4)
     10 )
     12 model(sparse_data)
---> 14 model.predict(sparse_data)

File /media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/keras/src/utils/traceback_utils.py:122, in filter_traceback.<locals>.error_handler(*args, **kwargs)
    119     filtered_tb = _process_traceback_frames(e.__traceback__)
    120     # To get the full stack trace, call:
    121     # `keras.config.disable_traceback_filtering()`
--> 122     raise e.with_traceback(filtered_tb) from None
    123 finally:
    124     del filtered_tb

File /media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/tensorflow/python/eager/execute.py:53, in quick_execute(op_name, num_outputs, inputs, attrs, ctx, name)
     51 try:
     52   ctx.ensure_initialized()
---> 53   tensors = pywrap_tfe.TFE_Py_Execute(ctx._handle, device_name, op_name,
     54                                       inputs, attrs, num_outputs)
     55 except core._NotOkStatusException as e:
     56   if name is not None:

InvalidArgumentError: Graph execution error:

Detected at node functional_1/dense_1/SparseFillEmptyRows/SparseFillEmptyRows defined at (most recent call last):
<stack traces unavailable>
Detected at node functional_1/dense_1/SparseFillEmptyRows/SparseFillEmptyRows defined at (most recent call last):
<stack traces unavailable>
Detected unsupported operations when trying to compile graph __inference_one_step_on_data_567[] on XLA_GPU_JIT: SparseFillEmptyRows (No registered 'SparseFillEmptyRows' OpKernel for XLA_GPU_JIT devices compatible with node {{node functional_1/dense_1/SparseFillEmptyRows/SparseFillEmptyRows}}){{node functional_1/dense_1/SparseFillEmptyRows/SparseFillEmptyRows}}
The op is created at: 
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/ipykernel_launcher.py", line 17, in <module>
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/traitlets/config/application.py", line 1075, in launch_instance
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/ipykernel/kernelapp.py", line 701, in start
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/tornado/platform/asyncio.py", line 205, in start
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/asyncio/base_events.py", line 639, in run_forever
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/asyncio/base_events.py", line 1985, in _run_once
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/asyncio/events.py", line 88, in _run
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/ipykernel/kernelbase.py", line 534, in dispatch_queue
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/ipykernel/kernelbase.py", line 523, in process_one
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/ipykernel/kernelbase.py", line 429, in dispatch_shell
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/ipykernel/kernelbase.py", line 767, in execute_request
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/ipykernel/ipkernel.py", line 429, in do_execute
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/ipykernel/zmqshell.py", line 549, in run_cell
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/IPython/core/interactiveshell.py", line 3075, in run_cell
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/IPython/core/interactiveshell.py", line 3130, in _run_cell
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/IPython/core/async_helpers.py", line 128, in _pseudo_sync_runner
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/IPython/core/interactiveshell.py", line 3334, in run_cell_async
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/IPython/core/interactiveshell.py", line 3517, in run_ast_nodes
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/IPython/core/interactiveshell.py", line 3577, in run_code
File "/tmp/ipykernel_4109124/3652816815.py", line 14, in <module>
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/keras/src/utils/traceback_utils.py", line 117, in error_handler
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/keras/src/backend/tensorflow/trainer.py", line 512, in predict
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/keras/src/backend/tensorflow/trainer.py", line 208, in one_step_on_data_distributed
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/keras/src/backend/tensorflow/trainer.py", line 198, in one_step_on_data
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/keras/src/backend/tensorflow/trainer.py", line 96, in predict_step
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/keras/src/utils/traceback_utils.py", line 117, in error_handler
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/keras/src/layers/layer.py", line 901, in __call__
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/keras/src/utils/traceback_utils.py", line 117, in error_handler
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/keras/src/ops/operation.py", line 46, in __call__
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/keras/src/utils/traceback_utils.py", line 156, in error_handler
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/keras/src/models/functional.py", line 175, in call
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/keras/src/ops/function.py", line 171, in _run_through_graph
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/keras/src/models/functional.py", line 560, in call
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/keras/src/utils/traceback_utils.py", line 117, in error_handler
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/keras/src/layers/layer.py", line 901, in __call__
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/keras/src/utils/traceback_utils.py", line 117, in error_handler
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/keras/src/ops/operation.py", line 46, in __call__
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/keras/src/utils/traceback_utils.py", line 156, in error_handler
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/keras/src/layers/core/dense.py", line 144, in call
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/keras/src/ops/numpy.py", line 3445, in matmul
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/keras/src/backend/tensorflow/numpy.py", line 463, in matmul
File "/media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/keras/src/backend/tensorflow/numpy.py", line 425, in embedding_lookup_sparse_dense_matmul
	tf2xla conversion failed while converting __inference_one_step_on_data_567[]. Run with TF_DUMP_GRAPH_PREFIX=/path/to/dump/dir and --vmodule=xla_compiler=2 to obtain a dump of the compiled functions.
	 [[StatefulPartitionedCall]] [Op:__inference_one_step_on_data_distributed_574]

tf.data#

tf.data API 可用于通过简单的可重用代码段构建复杂的输入流水线。它的核心数据结构是 tf.data.Dataset,表示一系列元素,每个元素包含一个或多个分量。

使用稀疏张量构建数据集#

使用用于从 tf.Tensor 或 NumPy 数组构建数据集的相同方法从稀疏张量构建数据集,例如 tf.data.Dataset.from_tensor_slices。此运算保留了数据的稀疏性。

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.batchDataset.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 是 TensorFlow 数据的标准 protobuf 编码。将稀疏张量与 tf.train.Example 搭配使用时,您可以:

  • 使用 tf.io.VarLenFeature 将可变长度数据读入 tf.sparse.SparseTensor。但是,您应当考虑改用 tf.io.RaggedFeature

  • 使用 tf.io.SparseFeature 将任意稀疏数据读入 tf.sparse.SparseTensor,它使用三个独立的特征键来存储 indicesvaluesdense_shape

tf.function#

tf.function 装饰器为 Python 函数预先计算 TensorFlow 计算图,这样可以大幅提升 TensorFlow 代码的性能。稀疏张量能够透明地与 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.SparseTensor 上的大多数运算都以相同的方式处理缺失值和显式零值。这是特意这样设计的 – tf.sparse.SparseTensor 的行为应当像密集张量一样。

但是,在少数情况下,区分零值和缺失值会十分有用。特别是,这样可以对训练数据中的缺失/未知数据进行编码。例如,考虑一个用例,其中包含一个分数张量(可以具有从 -Inf 到 +Inf 的任何浮点值),但缺少一些分数。可以使用稀疏张量对此张量进行编码,其中显式零是已知的零分数,但隐式零值实际上表示缺失数据,而不是零。

注:这通常不是 tf.sparse.SparseTensor 的预期用途;并且您可能还想考虑其他技术来对此进行编码,例如使用单独的掩码张量来识别已知/未知值的位置。但是,在使用这种方式时要格外小心,因为大多数稀疏运算将以相同的方式处理显式和隐式零值。

请注意,像 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)

补充阅读和资源#