python tutorial Calcolare la distanza a coppie in un lotto senza replicare il tensore in Tensorflow?




tensorflow python tutorial (4)

Puoi usare dell'algebra lineare per trasformarla in operazioni mat. Nota che hai bisogno della matrice D dove a[i] è l'ultima riga della matrice originale e

D[i,j] = (a[i]-a[j])(a[i]-a[j])'

Puoi riscriverlo in

D[i,j] = r[i] - 2 a[i]a[j]' + r[j]

Dove r[i] è la norma quadrata della prima riga della matrice originale.

In un sistema che supporta le regole di trasmissione standard è possibile trattare r come vettore di colonna e scrivere D come

D = r - 2 A A' + r'

In TensorFlow puoi scrivere questo come

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)

risultato

array([[0, 2, 8],
       [2, 0, 2],
       [8, 2, 0]], dtype=int32)

Voglio calcolare la distanza quadrata a coppie di un lotto di funzionalità in Tensorflow. Ho una semplice implementazione usando le operazioni + e * affiancando il tensore originale:

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

Questa funzione prende come input due matrici di dimensioni (m, d) e (n, d) e calcola la distanza quadrata tra ciascun vettore di riga. L'output è una matrice di dimensione (m, n) con l'elemento 'd_ij = dist (x_i, y_j)'.

Il problema è che ho un grande batch e caratteristiche molto scure 'm, n, d' replicare il tensore consuma molta memoria. Sto cercando un altro modo per implementare questo senza aumentare l'utilizzo della memoria e solo memorizzare il tensore della distanza finale. Tipo di doppio avvolgimento del tensore originale.


Utilizzando 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

Una cosa che ho notato è che questa soluzione che utilizza tf.squared_difference mi dà memoria tf.squared_difference (OOM) per vettori di grandi dimensioni, mentre l'approccio di @YaroslavBulatov non lo è. Quindi, penso che la scomposizione dell'operazione produca un minor ingombro di memoria (che pensavo che la squared_difference avrebbe gestito meglio sotto il cofano).


Ecco una soluzione più generale per due tensori di coordinate A e 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

Si noti che questa è la distanza quadrata. Se vuoi cambiare questo alla distanza euclidea, esegui un tf.sqrt sul risultato. Se vuoi farlo, non dimenticare di aggiungere una piccola costante per compensare le instabilità in virgola mobile: dist = tf.sqrt(squared_dist(A, B) + 1e-6) .


Se vuoi calcolare altri metodi, cambia l'ordine dei moduli tf.

def compute_euclidean_distance(x, y):
    size_x = x.shape.dims[0]
    size_y = y.shape.dims[0]
    for i in range(size_x):
        tile_one = tf.reshape(tf.tile(x[i], [size_y]), [size_y, -1])
        eu_one = tf.expand_dims(tf.sqrt(tf.reduce_sum(tf.pow(tf.subtract(tile_one, y), 2), axis=1)), axis=0)
        if i == 0:
            d = eu_one
        else:
            d = tf.concat([d, eu_one], axis=0)
return d




tensorflow