Search code examples
pythontensorflowembedding

How can I select a row from a SparseTensor in TensorFlow?


Say, if I have two SparseTensors as following:

[[1, 0, 0, 0],
 [2, 0, 0, 0],
 [1, 2, 0, 0]]

and

[[1.0, 0, 0, 0],
 [1.0, 0, 0, 0],
 [0.3, 0.7, 0, 0]]

and I want to extract the first two rows out of them. I need both indices and values of non-zeros entries as SparseTensors so that I can pass the result to tf.nn.embedding_lookup_sparse. How can I do this?

My application is: I want to use word embeddings, which is quite straight forward in TensorFlow. But now I want to use sparse embeddings, i.e.: for common words, they have their own embeddings. For rare words, their embeddings are a sparse linear combination of embeddings of common words. So I need two cookbooks to indicate how sparse embeddings are composed. In the aforementioned example, the cookbook says: For the first word, it's embedding consists of its own embedding with weight 1.0. Things are similar for the second word. For the last word, it says: the embedding of this word is a linear combination of the embeddings of the first two words, and the corresponding weights are 0.3 and 0.7 respectively. I need to extract a row, then feed the indices and weights to tf.nn.embedding_lookup_sparse to obtain the final embeddings. How can I do that in TensorFlow?

Or I need to work around it, i.e.: preprocess my data and deal with the cookbook out of TensorFlow?


Solution

  • I checked in with one of the engineers here who knows more about this area, and here's what he passed on:

    I am not sure if we have an efficient implementation of the this, but here is a not-so-optimal implementation using dynamic_partition and gather ops.

    def sparse_slice(indices, values, needed_row_ids):
       num_rows = tf.shape(indices)[0]
       partitions = tf.cast(tf.equal(indices[:,0], needed_row_ids), tf.int32)
       rows_to_gather = tf.dynamic_partition(tf.range(num_rows), partitions, 2)[1]
       slice_indices = tf.gather(indices, rows_to_gather)
       slice_values = tf.gather(values, rows_to_gather)
       return slice_indices, slice_values
    
    with tf.Session().as_default():
      indices = tf.constant([[0,0], [1, 0], [2, 0], [2, 1]])
      values = tf.constant([1.0, 1.0, 0.3, 0.7], dtype=tf.float32)
      needed_row_ids = tf.constant([1])
      slice_indices, slice_values = sparse_slice(indices, values, needed_row_ids)
      print(slice_indices.eval(), slice_values.eval())
    

    Update:

    The engineer sent on an example to help with multiple rows too, thanks for pointing that out!

    def sparse_slice(indices, values, needed_row_ids):
      needed_row_ids = tf.reshape(needed_row_ids, [1, -1])
      num_rows = tf.shape(indices)[0]
      partitions = tf.cast(tf.reduce_any(tf.equal(tf.reshape(indices[:,0], [-1, 1]), needed_row_ids), 1), tf.int32)
      rows_to_gather = tf.dynamic_partition(tf.range(num_rows), partitions, 2)[1]
      slice_indices = tf.gather(indices, rows_to_gather)
      slice_values = tf.gather(values, rows_to_gather)
      return slice_indices, slice_values
    
    with tf.Session().as_default():
      indices = tf.constant([[0,0], [1, 0], [2, 0], [2, 1]])
      values = tf.constant([1.0, 1.0, 0.3, 0.7], dtype=tf.float32)
      needed_row_ids = tf.constant([0, 2])