neural - tensorflow python tutorial




Calcolare la distanza a coppie in un batch senza replicare il tensore in Tensorflow? (3)

Voglio calcolare la distanza quadrata a coppie di un batch di feature 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 accetta come input due matrici di dimensioni (m, d) e (n, d) e calcola la distanza al quadrato tra ciascun vettore di riga. L'output è una matrice di dimensioni (m, n) con l'elemento 'd_ij = dist (x_i, y_j)'.

Il problema è che ho un batch di grandi dimensioni e funzionalità ad alta luminosità che replicano il tensore consumando molta memoria. Sto cercando un altro modo per implementarlo senza aumentare l'utilizzo della memoria e solo memorizzare il tensore della distanza finale. Tipo di doppio loop del tensore originale.


Ecco una soluzione più generale per due tensori delle 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 cambiarlo 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) .


Puoi usare un'algebra lineare per trasformarla in operazioni a matrice. Nota che hai bisogno della matrice D dove a[i] è la prima riga della tua 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 riga della matrice originale.

In un sistema che supporta le regole di trasmissione standard è possibile trattare r come un 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)

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 (OOM) per vettori molto grandi, mentre l'approccio di @YaroslavBulatov no. Quindi, penso che la decomposizione dell'operazione produca un footprint di memoria più piccolo (che pensavo che squared_difference avrebbe gestito meglio sotto il cofano).





tensorflow