稀疏张量(矩阵)从密集张量Tensorflow中生成

10
我正在创建一个卷积稀疏自编码器,需要将一个值填充的4D矩阵(其形状为[样本数,N,N,D])转换为稀疏矩阵。
对于每个样本,我有 D 个大小为 NxN 的特征图。我想要将每个 NxN 特征图转换为一个稀疏矩阵,其中最大值映射到1,其他所有值都映射为0。
我不希望在运行时执行此操作,而是在图形声明期间执行操作(因为我需要将结果稀疏矩阵用作其他图形操作的输入),但我不知道如何获取索引以构建稀疏矩阵。

你想在Tensorflow还是Python中进行这个转换?如果使用Python,可以使用以下函数将密集矩阵转换为稀疏矩阵(http://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.coo_matrix.html#scipy.sparse.coo_matrix)。您可以使用tf.SparseTensor(使用coo格式)来存储每个特征图,并使用列表来存储所有稀疏张量。 - Yao Zhang
具体来说,nonzero()函数(http://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.coo_matrix.nonzero.html#scipy.sparse.coo_matrix.nonzero)可以为非零元素提供索引。不确定这是否被认为是运行时方法。这可能是在图形声明之前进行的一些数据预处理。生成的4D密集矩阵是在运行时生成的还是仅仅是一些给定的输入数据? - Yao Zhang
我不想在运行时做那个(我知道如何使用numpy做那个),而是在图形声明时(因此使用Tensorflow)。 - user4706825
5个回答

17
你可以使用tf.wheretf.gather_nd来实现这个功能:
import numpy as np
import tensorflow as tf

# Make a tensor from a constant
a = np.reshape(np.arange(24), (3, 4, 2))
a_t = tf.constant(a)
# Find indices where the tensor is not zero
idx = tf.where(tf.not_equal(a_t, 0))
# Make the sparse tensor
# Use tf.shape(a_t, out_type=tf.int64) instead of a_t.get_shape()
# if tensor shape is dynamic
sparse = tf.SparseTensor(idx, tf.gather_nd(a_t, idx), a_t.get_shape())
# Make a dense tensor back from the sparse one, only to check result is correct
dense = tf.sparse_tensor_to_dense(sparse)
# Check result
with tf.Session() as sess:
    b = sess.run(dense)
np.all(a == b)
>>> True

1
@RocketPingu 不确定你的意思,这是将密集张量转换为稀疏张量。a_t在这里是一个常规的TensorFlow张量(在这种情况下是从tf.constant操作获取的,但也可以是任何其他操作的输出)。我已经添加了一些注释以增加清晰度。 - jdehesa
只是当我尝试在我的代码中使用它时,它给了我一个错误。更多信息请参见此处:https://dev59.com/zajja4cB1Zd3GeqP6CGz - Rocket Pingu
@RocketPingu 正如我在代码的某个注释中所提到的,如果张量的完整形状在图形创建时未知(即其形状是动态的而不是静态的),则应该使用 tf.shape(tensor) 而不是 tensor.get_shape()tensor.shape - jdehesa
我有一个问题。在 idx = tf.where(tf.not_equal(a_t, 0)) 中,为什么我们要寻找不等于0的索引?它可以是任意数字吗? - Rocket Pingu
@RocketPingu 不是的,正如您可以在文档中看到的那样,稀疏张量是通过其非零值来定义的(在TensorFlow中,但几乎所有定义了稀疏结构的代数包中也是如此)。如果一个矩阵或张量的大部分元素都是零,则称其为稀疏矩阵或稀疏张量,因此这种表示方法更加经济(尽管与之操作通常更加复杂)。 - jdehesa
显示剩余2条评论

3

将密集的numpy数组转换为tf.SparseTensor的简单代码:

def denseNDArrayToSparseTensor(arr):
  idx  = np.where(arr != 0.0)
  return tf.SparseTensor(np.vstack(idx).T, arr[idx], arr.shape)

1

自1.15版本起,Tensorflow提供了tf.sparse.from_dense。例如:

In [1]: import tensorflow as tf

In [2]: x = tf.eye(3) * 5

In [3]: x
Out[3]: 
<tf.Tensor: shape=(3, 3), dtype=float32, numpy=
array([[5., 0., 0.],
       [0., 5., 0.],
       [0., 0., 5.]], dtype=float32)>

应用 tf.sparse.from_dense:
In [4]: y = tf.sparse.from_dense(x)

In [5]: y.values
Out[5]: <tf.Tensor: shape=(3,), dtype=float32, numpy=array([5., 5., 5.], dtype=float32)>

In [6]: y.indices
Out[6]: 
<tf.Tensor: shape=(3, 2), dtype=int64, numpy=
array([[0, 0],
       [1, 1],
       [2, 2]])>

通过应用tf.sparse.to_dense来验证身份:

In [7]: tf.sparse.to_dense(y) == x
Out[7]: 
<tf.Tensor: shape=(3, 3), dtype=bool, numpy=
array([[ True,  True,  True],
       [ True,  True,  True],
       [ True,  True,  True]])>

如果您对当前的实现感兴趣:源代码 - hoefling

0

请注意,contrib 中有一个内置函数(取自from


0
在TF 2.3中,Tensorflow Probability有一个function可以实现此功能:
import tensorflow_probability as tfp

tfp.math.dense_to_sparse(x, ignore_value=None, name=None)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接