在Tensorflow中计算批量中的成对距离而不复制张量?

jra*_*ary 30 python tensorflow

我想计算Tensorflow中一批特征的成对平方距离.我通过平铺原始张量使用+和*操作有一个简单的实现:

def pairwise_l2_norm2(x, y, scope=None):
    with tf.op_scope([x, y], scope, 'pairwise_l2_norm2'):
        size_x = tf.shape(x)[0]
        size_y = tf.shape(y)[0]
        xx = tf.expand_dims(x, -1)
        xx = tf.tile(xx, tf.pack([1, 1, size_y]))

        yy = tf.expand_dims(y, -1)
        yy = tf.tile(yy, tf.pack([1, 1, size_x]))
        yy = tf.transpose(yy, perm=[2, 1, 0])

        diff = tf.sub(xx, yy)
        square_diff = tf.square(diff)

        square_dist = tf.reduce_sum(square_diff, 1)

        return square_dist
Run Code Online (Sandbox Code Playgroud)

该函数将两个大小为(m,d)和(n,d)的矩阵作为输入,并计算每个行向量之间的平方距离.输出是大小为(m,n)的矩阵,其元素为'd_ij = dist(x_i,y_j)'.

问题是我有一个大批量和高昏暗的功能'm,n,d'复制张量消耗了大量的内存.我正在寻找另一种方法来实现它,而不增加内存使用量,只是存储最终的距离张量.一种双循环原始张量.

Yar*_*tov 59

您可以使用一些线性代数将其转换为矩阵运算.注意你需要的矩阵D在哪里a[i]i原始矩阵的第一行和

D[i,j] = (a[i]-a[j])(a[i]-a[j])'
Run Code Online (Sandbox Code Playgroud)

你可以把它重写成

D[i,j] = r[i] - 2 a[i]a[j]' + r[j]
Run Code Online (Sandbox Code Playgroud)

原矩阵的第一行的r[i]平方范数在哪里i.

在支持标准广播规则的系统中,您可以将其r视为列向量并写D

D = r - 2 A A' + r'
Run Code Online (Sandbox Code Playgroud)

在TensorFlow中,您可以将其写为

A = tf.constant([[1, 1], [2, 2], [3, 3]])
r = tf.reduce_sum(A*A, 1)

# turn r into column vector
r = tf.reshape(r, [-1, 1])
D = r - 2*tf.matmul(A, tf.transpose(A)) + tf.transpose(r)
sess = tf.Session()
sess.run(D)
Run Code Online (Sandbox Code Playgroud)

结果

array([[0, 2, 8],
       [2, 0, 2],
       [8, 2, 0]], dtype=int32)
Run Code Online (Sandbox Code Playgroud)

  • ^ 我最近对此进行了测试(在 GPU 上,使用 TF 2.0)。`tf.matmul(a, b, transpose_b=True)` 始终比 `tf.matmul(a, tf.transpose(b))` 快约 40%。 (3认同)
  • 我不知道这会带来多少性能上的改善,但是`tf.matmul`具有用于动态转置数组的参数(`transpose_a`和`transpose_b`)。 (2认同)

Yam*_*eko 15

使用squared_difference:

def squared_dist(A): 
    expanded_a = tf.expand_dims(A, 1)
    expanded_b = tf.expand_dims(A, 0)
    distances = tf.reduce_sum(tf.squared_difference(expanded_a, expanded_b), 2)
    return distances
Run Code Online (Sandbox Code Playgroud)

我注意到的一件事是,这个解决方案使用tf.squared_difference了非常大的向量给我内存(OOM),而@YaroslavBulatov的方法没有.因此,我认为分解操作会产生更小的内存占用(我认为它squared_difference可以在引擎盖下更好地处理).

  • 感谢另一个解决方案内存不足的信息.很高兴知道.+1的答案很棒 (2认同)
  • 此解决方案的计算效率也较低。但是当不可能使用矩阵乘法时它非常有用(例如对于绝对距离) (2认同)

Aug*_*tin 9

这是两个坐标张量A和的更通用的解决方案B

def squared_dist(A, B):
  assert A.shape.as_list() == B.shape.as_list()

  row_norms_A = tf.reduce_sum(tf.square(A), axis=1)
  row_norms_A = tf.reshape(row_norms_A, [-1, 1])  # Column vector.

  row_norms_B = tf.reduce_sum(tf.square(B), axis=1)
  row_norms_B = tf.reshape(row_norms_B, [1, -1])  # Row vector.

  return row_norms_A - 2 * tf.matmul(A, tf.transpose(B)) + row_norms_B
Run Code Online (Sandbox Code Playgroud)

请注意,这是平方距离。如果要将其更改为欧几里得距离,请tf.sqrt对结果执行 a 。如果你想要做的是,不要忘记添加一个很小的常数,以补偿浮点不稳定:dist = tf.sqrt(squared_dist(A, B) + 1e-6)