TensorFlow实现了稀疏张量乘法吗?

27

在 TensorFlow 中,稀疏张量与自身或密集张量相乘似乎无法正常工作。以下是一个例子:

from __future__ import print_function
import tensorflow as tf

x = tf.constant([[1.0,2.0],
                 [3.0,4.0]])
y = tf.SparseTensor(indices=[[0,0],[1,1]], values=[1.0,1.0], shape=[2,2])
z = tf.matmul(x,y)

sess = tf.Session()
sess.run(tf.initialize_all_variables())
print(sess.run([x, y, z]))

出现错误信息

TypeError: Input 'b' of 'MatMul' Op has type string that does not match type 
float32 of argument 'a'

当不进行乘法操作时,通过对两个张量进行评估可以看出它们的值均为float32类型。将y与自身相乘将返回类似的错误消息。将x与自身相乘则正常运行。

6个回答

38

目前,TensorFlow尚未实现 tf.SparseTensor 的通用乘法。不过,有三个部分解决方案可供选择,具体取决于您的数据特征:

  • 如果您有一个 tf.SparseTensor 和一个 tf.Tensor,则可以使用tf.sparse_tensor_dense_matmul()将它们相乘。如果其中一个张量在变为密集型时太大而无法放入内存中,则此方法比下一种方法更有效:文档有更多关于如何在这两种方法之间进行选择的指导。请注意,它接受 第一个参数为 tf.SparseTensor,因此要解决您的确切问题,您需要使用 adjoint_aadjoint_b 参数,并转置结果。

  • 如果您有两个稀疏张量并且需要将它们相乘,则最简单的(如果不是最高效的)方法是将它们转换为密集形式并使用 tf.matmul

    a = tf.SparseTensor(...)
    b = tf.SparseTensor(...)
    
    c = tf.matmul(tf.sparse_tensor_to_dense(a, 0.0),
                  tf.sparse_tensor_to_dense(b, 0.0),
                  a_is_sparse=True, b_is_sparse=True)
    

    请注意,可选的a_is_sparseb_is_sparse参数意味着"a(或b)具有稠密表示,但其中大量条目为零",这将触发使用不同的乘法算法。

  • 对于稀疏向量和(可能是大型和分片的)稠密矩阵的特殊情况,并且向量中的值为0或1,则tf.nn.embedding_lookup运算符可能更合适。 本教程详细讨论了何时使用嵌入以及如何调用运算符。

  • 对于稀疏矩阵和(可能是大型和分片的)稠密矩阵的特殊情况,tf.nn.embedding_lookup_sparse()可能是合适的。此函数接受一个或两个tf.SparseTensor对象,其中sp_ids表示非零值,而可选的sp_weights表示它们的值(否则默认为1)。


谢谢。这节省了我的时间。 - tobe
2
为什么没有涵盖 tf.sparse_matmul?它有何不同之处? - mrgloom
如何对两个稀疏张量进行逐元素乘法运算? - Qiqin Zhan

12

注意:此操作仅适用于两个矩阵的秩都为2的情况。如果这些矩阵的秩大于2,则此操作无法实现。 - Arka Mukherjee

5
在TF2.4.1中,您可以使用tensorflow.python.ops.linalg.sparse.sparse_csr_matrix_ops中的方法来将任意SparseTensor相乘(我认为最多可以有3个维度)。通常情况下,您需要将稀疏张量转换为CSR表示,然后使用以下类似的内容。
import tensorflow as tf
from tensorflow.python.ops.linalg.sparse import sparse_csr_matrix_ops


def tf_multiply(a: tf.SparseTensor, b: tf.SparseTensor):
    a_sm = sparse_csr_matrix_ops.sparse_tensor_to_csr_sparse_matrix(
        a.indices, a.values, a.dense_shape
    )

    b_sm = sparse_csr_matrix_ops.sparse_tensor_to_csr_sparse_matrix(
        b.indices, b.values, b.dense_shape
    )

    c_sm = sparse_csr_matrix_ops.sparse_matrix_sparse_mat_mul(
        a=a_sm, b=b_sm, type=tf.float32
    )

    c = sparse_csr_matrix_ops.csr_sparse_matrix_to_sparse_tensor(
        c_sm, tf.float32
    )

    return tf.SparseTensor(
        c.indices, c.values, dense_shape=c.dense_shape
    )

有一段时间我更喜欢使用scipy的乘法(通过py_function),因为在TF(2.3和2.4)中,这个乘法的性能不如scipy。最近我再次尝试,可能是我改变了代码,或者在2.4.1中进行了一些修复,使得使用TF稀疏乘法比使用scipy更快,无论是在CPU还是GPU上。


为什么需要 self - Luca Cappelletti
@LucaCappelletti 我将其实现为类的一个方法,但这里演示不需要self。谢谢。 - Jorge E. Cardona
完美运行(tf-2.6) - Marios V
是否可以基于python.ops开发一个版本,支持稀疏-密集张量乘法,适用于带有批处理维度的三阶张量,因为tf.sparse.sparse_dense_matmul不支持该操作? - Marios V

3
似乎是这样的:
tf.sparse_matmul(
    a,
    b,
    transpose_a=None,
    transpose_b=None,
    a_is_sparse=None,
    b_is_sparse=None,
    name=None
)

这不适用于两个 SparseTensors 的乘法。

abTensors 而不是 SparseTensors。我尝试过了,使用 SparseTensors 时无法工作。


1

tf.sparse_matmul用于乘法两个密集张量,而不是稀疏类型的数据结构。如果给定的矩阵(或两个矩阵)具有许多零值,则该函数只是张量乘法的优化版本。再次强调,它不接受稀疏张量数据类型,而接受密集张量数据类型。如果大部分数值为零,则可以加快计算速度。

据我所知,目前没有实现两个稀疏类型张量的乘法,但有一种稀疏和一种密集类型的乘法,即tf.sparse_tensor_dense_matmul(x, y)!


-2

我得到了以下错误信息:TypeError: 期望二进制或Unicode字符串,但得到了<tensorflow.python.framework.ops.SparseTensor object at 0x7f90873f4fd0> - sudo
pcejrowski!这是你的链接写的内容:“a”和“b”都必须是张量,而不是稀疏张量。与你的建议不同,a_is_sparse=True并不意味着矩阵是稀疏类型。它只是意味着矩阵a有许多零值,因此算法通过使用该先前信息来快速计算它!! - M. Balcilar

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