将张量组织成批量动态形状的张量

时间:2017-09-17 18:06:44

标签: tensorflow tensorflow-serving

我有以下情况:

  • 我想使用Tensorflow服务部署面部检测器模型:https://www.tensorflow.org/serving/
  • 在Tensorflow服务中,有一个名为--enable_batching的命令行选项。这会导致模型服务器自动批处理请求以最大化吞吐量。我想要启用它。
  • 我的模型采用一组图像(称为图像),这是一个形状张量(batch_size, 640, 480, 3)
  • 该模型有两个输出:(number_of_faces, 4)(number_of_faces,)。第一个输出将被称为 faces 。最后一个输出,我们可以称之为分区,是相应面的原始批次中的索引。例如,如果我传递了一批4个图像并获得7个面孔,那么我可能会将此张量设为[0, 0, 1, 2, 2, 2, 3]。前两个面对应第一个图像,第三个面对应第二个图像,第三个图像有3个面等等。

我的问题是:

  • 为了使--enable_batching标志起作用,我的模型的输出需要使第0个维度与输入相同。也就是说,我需要一个具有以下形状的张量:(batch_size, ...)。我想这是模型服务器可以知道哪个grpc连接将批量发送给每个输出。
  • 我想要做的是将面部检测器的输出张量从此形状(number_of_faces, 4)转换为此形状(batch_size, None, 4)。也就是说,一批批次,其中每批可以有可变数量的面(例如,批次中的一个图像可能没有面,另一个可能有3个)。

我尝试了什么:

  • tf.dynamic_partition。从表面上看,这个功能看起来很完美。但是,在意识到num_partitions参数不能是一个张量,只有一个整数后,我遇到了困难:

    tensorflow_serving_output = tf.dynamic_partition(faces, partitions, batch_size)

如果tf.dynamic_partition函数接受num_partition的张量值,那么我的问题似乎就会得到解决。但是,我回到原点,因为事实并非如此。

谢谢大家的帮助!如果有什么不清楚,请告诉我

P.S。以下是预期过程的直观表示:

Image batch to prediction batch

1 个答案:

答案 0 :(得分:1)

我最终使用TensorArraytf.while_loop找到了解决方案:

def batch_reconstructor(tensor, partitions, batch_size):
    """
    Take a tensor of shape (batch_size, 4) and a 1-D partitions tensor as well as the scalar batch_size
    And reconstruct a TensorArray that preserves the original batching

    From the partitions, we can get the maximum amount of tensors within a batch. This will inform the padding we need to use.
    Params:
        - tensor: The tensor to convert to a batch
        - partitions: A list of batch indices. The tensor at position i corresponds to batch # partitions[i]
    """
    tfarr = tf.TensorArray(tf.int32, size=batch_size, infer_shape=False)

    _, _, count = tf.unique_with_counts(partitions)
    maximum_tensor_size = tf.cast(tf.reduce_max(count), tf.int32)

    padding_tensor_index = tf.cast(tf.gather(tf.shape(tensor), 0), tf.int32)

    padding_tensor = tf.expand_dims(tf.cast(tf.fill([4], -1), tf.float32), axis=0) # fill with [-1, -1, -1, -1]
    tensor = tf.concat([tensor, padding_tensor], axis=0)

    def cond(i, acc):
        return tf.less(i, batch_size)

    def body(i, acc):
        partition_indices = tf.reshape(tf.cast(tf.where(tf.equal(partitions, i)), tf.int32), [-1])

        partition_size = tf.gather(tf.shape(partition_indices), 0)

        # concat the partition_indices with padding_size * padding_tensor_index
        padding_size = tf.subtract(maximum_tensor_size, partition_size)
        padding_indices = tf.reshape(tf.fill([padding_size], padding_tensor_index), [-1])

        partition_indices = tf.concat([partition_indices, padding_indices], axis=0)

        return (tf.add(i, 1), acc.write(i, tf.gather(tensor, partition_indices)))

    _, reconstructed = tf.while_loop(
        cond,
        body,
        (tf.constant(0), tfarr),
        name='batch_reconstructor'
    )

    reconstructed = reconstructed.stack()
    return reconstructed