TensorFlow:带轴选项

时间:2018-06-15 20:28:57

标签: python tensorflow machine-learning deep-learning vectorization

在TensorFlow中,我可以使用tf.bincount获取数组中每个元素的计数:

x = tf.placeholder(tf.int32, [None])
freq = tf.bincount(x)
tf.Session().run(freq, feed_dict = {x:[2,3,1,3,7]})

返回

Out[45]: array([0, 1, 1, 2, 0, 0, 0, 1], dtype=int32)

有没有办法在2D张量上做到这一点?即。

x = tf.placeholder(tf.int32, [None, None])
freq = tf.axis_bincount(x, axis = 1)
tf.Session().run(freq, feed_dict = {x:[[2,3,1,3,7],[1,1,2,2,3]]})

返回

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

4 个答案:

答案 0 :(得分:1)

为numpy数组提供了一个解决方案:Apply bincount to each row of a 2D numpy array。 通过向每行添加row_id * (max + 1)使每一行都是唯一的,然后为展平的1d数组找到bincount,然后对其进行适当的重新整形。

TF进行以下更改:

x = tf.placeholder(tf.int32, [None, None])

max_x_plus_1 = tf.reduce_max(x)+1
ids = x + max_x_plus_1*tf.range(tf.shape(x)[0])[:,None]
out = tf.reshape(tf.bincount(tf.layers.flatten(ids), 
                 minlength=max_x_plus_1*tf.shape(x)[0]), [-1, N])

tf.Session().run(out, feed_dict = {x:[[2,3,1,3,7],[1,1,2,2,3]]})
#[[0, 1, 1, 2, 0, 0, 0, 1],
#[0, 2, 2, 1, 0, 0, 0, 0]]

答案 1 :(得分:0)

tf.bincount()接受一个数组作为参数,但它会聚合整个数组的计数,并且此刻不会沿某个轴工作。例如:

In [27]: arr
Out[27]: 
array([[2, 3, 1, 3, 7],
       [1, 1, 2, 2, 3]], dtype=int32)

In [28]: x = tf.placeholder(tf.int32, [None, None])
    ...: freq = tf.bincount(x)
    ...: tf.Session().run(freq, feed_dict = {x:arr})

# aggregates the count across the whole array
Out[28]: array([0, 3, 3, 3, 0, 0, 0, 1], dtype=int32)
# 0 occurs 0 times
# 1 occurs 3 times
# 2 occurs 3 times
# 3 occurs 3 times and so on..

所以,至少截至目前,无法将轴信息传递给tf.bincount()

但是,效率稍低的方法是一次将一行传递给tf.bincount()并获得结果。然后最终将这些得到的1D阵列组合成一个所需维数的数组。

我不确定这是否是最有效的方式,但无论如何这里是一种循环张量(沿轴0)的方法

In [3]: arr = np.array([[2, 3, 1, 3, 7], [1, 1, 2, 2, 3]], dtype=np.int32)
In [4]: sess = tf.InteractiveSession()

In [5]: for idx, row in enumerate(tf.unstack(arr)):
   ...:     freq = tf.bincount(row)
   ...:     print(freq.eval())
   ...:     
[0 1 1 2 0 0 0 1]
[0 2 2 1]

答案 2 :(得分:0)

我自己需要这个,并且为它编写了一个小函数,因为没有正式的实现。

def bincount(tensor, minlength=None, axis=None):
    if axis is None:
        return tf.bincount(tensor, minlength=minlength)
    else:
        if not hasattr(axis, "__len__"):
            axis = [axis]

        other_axis = [x for x in range(0, len(tensor.shape)) if x not in axis]
        swap = tf.transpose(tensor, [*other_axis, *axis])
        flat = tf.reshape(swap, [-1, *np.take(tensor.shape.as_list(), axis)])
        count = tf.map_fn(lambda x: tf.bincount(x, minlength=minlength), flat)
        res = tf.reshape(count, [*np.take([-1 if a is None else a for a in tensor.shape.as_list()], other_axis), minlength])
        return res

对于不同的边缘情况,有很多处理方法。

此解决方案的要点是以下部分:

swap = tf.transpose(tensor, [*other_axis, *axis])
flat = tf.reshape(swap, [-1, *np.take(tensor.shape.as_list(), axis)])
count = tf.map_fn(lambda x: tf.bincount(x, minlength=minlength), flat)
  • transpose操作将要bincount的所有轴移到张量的末端。例如,如果您有一个看起来像[100, 50, 20]且轴为[0, 1, 2]的矩阵,而您想为轴bincount使用1,则此操作会将轴1交换到末端您将得到一个[100, 20, 50]矩阵。
  • reshape操作将您不希望将bincount的所有其他轴展平为单个尺寸/轴。
  • map_fn操作将bincount映射到扁平尺寸/轴的每个条目上。

您必须指定minlength参数。这是必需的,因此所有bincount结果都具有相同的长度(否则矩阵将没有有效的形状)。这可能是您的tensor的最大值。对我来说,最好将其作为参数传递,因为我已经具有此值并且不需要检索它,但是您也可以使用tf.reduce_max(tensor)进行计算。

完整的解决方案还重新调整了形状,以恢复其他轴。 它还支持张量中的多个轴和单个None轴(用于批处理)。

答案 3 :(得分:0)

我发现做这件事的一种简单方法是利用广播将张量中的所有值与模式[0, 1, ..., length - 1]进行比较,然后计算沿所需轴的“命中”数。

即:

def bincount(arr, length, axis=-1):
  """Count the number of ocurrences of each value along an axis."""
  mask = tf.equal(arr[..., tf.newaxis], tf.range(length))
  return tf.math.count_nonzero(mask, axis=axis - 1 if axis < 0 else axis)

x = tf.convert_to_tensor([[2,3,1,3,7],[1,1,2,2,3]])
bincount(x, tf.reduce_max(x) + 1, axis=1)

返回:

<tf.Tensor: id=406, shape=(2, 8), dtype=int64, numpy=
array([[0, 1, 1, 2, 0, 0, 0, 1],
       [0, 2, 2, 1, 0, 0, 0, 0]])>