首页 > 解决方案 > 如何在张量流中创建给定索引的二进制矩阵

问题描述

假设我有一个带有两个样本索引的 tf 张量:

x = [[2,3,5], [5,7,5]]

我想创建一个具有特定形状的张量(samples, 10),其中每个样本的索引x设置为 1,其余的设置为 0,如下所示:

output = [[0, 0, 1, 1, 0, 1, 0, 0, 0, 0],
          [0, 0, 0, 0, 0, 1, 0, 1, 0, 0]]

在不创建大量中间矩阵的情况下,最好的方法是什么?

我得到的最接近的是 using tf.scatter_nd,但我无法弄清楚如何正确转换xupdates除了手动添加这样的附加信息:

>>> tf.cast(tf.scatter_nd([[0,2], [0,3], [0,5], [1,5], [1,7], [1,5]], [1, 1, 1, 1, 1, 1] , 
[2, 10]) > 0, dtype="int64")
<tf.Tensor: id=1191, shape=(2, 10), dtype=int64, numpy=
array([[0, 0, 1, 1, 0, 1, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 1, 0, 1, 0, 0]])>

此外,这种方法首先会聚合重复的索引,这使得中间布尔矩阵成为必要。(虽然我可以忍受,但主要问题是从x一个形状的矩阵开始,(samples, 10)其中每个样本的不存在索引为 0。)

谢谢你的帮助!:)

标签: pythontensorflow

解决方案


我找到了一个解决方案(tensorflow 2.2.0):

class BinarizeSequence(tf.keras.layers.Layer):
    """
    Transforms an integer sequence into a binary representation
    with shape (samples, vocab_size).

    Example:
    In: [[2,3,5], [5,7,5]]
    Out: [[0, 0, 1, 1, 0, 1, 0, 0, 0, 0],
          [0, 0, 0, 0, 0, 1, 0, 1, 0, 0]]

    By default the output is returned as SparseTensor.
    Use dense_output=True if you need a dense representation.
    """

    def __init__(self, vocab_size, dense_output=False, **kwargs):
        super(BinarizeSequence, self).__init__(**kwargs)
        self.vocab_size = vocab_size
        self.dense_output = dense_output

    def get_config(self):
        config = super().get_config().copy()
        config.update(
            {"vocab_size": self.vocab_size, "dense_output": self.dense_output}
        )
        return config

    def call(self, x, mask=None):
        # create indices for binarized representation
        x = tf.cast(x, dtype=tf.int32)
        x_1d = tf.reshape(x, [-1])
        sample_dim = tf.repeat(
            tf.range(tf.shape(x)[0], dtype=tf.int32), tf.shape(x)[1]
        )
        indices = tf.transpose(tf.stack([sample_dim, x_1d]))

        # only keep unique indices
        # (see https://stackoverflow.com/a/42245425/979377)
        indices64 = tf.bitcast(indices, type=tf.int64)
        unique64, idx = tf.unique(indices64)
        unique_indices = tf.bitcast(unique64, type=tf.int32)

        # build binarized representation
        updates = tf.ones(tf.shape(unique_indices)[0])
        output_shape = [tf.shape(x)[0], self.vocab_size]
        if self.dense_output:
            output = tf.scatter_nd(unique_indices, updates, output_shape)
        else:
            output = tf.sparse.SparseTensor(
                tf.cast(unique_indices, tf.int64), updates, output_shape
            )
        return output

推荐阅读