Comment faire un produit intérieur par lots dans Tensorflow?

10

J'ai deux tenseur a:[batch_size, dim] b:[batch_size, dim]. Je veux faire un produit intérieur pour chaque paire du lot, en générant c:[batch_size, 1], où c[i,0]=a[i,:].T*b[i,:]. Comment?

HenrySky
la source

Réponses:

9

Il n'y a pas de .dot_productméthode native . Cependant, un produit scalaire entre deux vecteurs est simplement additionné par multiplication élément par élément, de sorte que l'exemple suivant fonctionne:

import tensorflow as tf

# Arbitrarity, we'll use placeholders and allow batch size to vary,
# but fix vector dimensions.
# You can change this as you see fit
a = tf.placeholder(tf.float32, shape=(None, 3))
b = tf.placeholder(tf.float32, shape=(None, 3))

c = tf.reduce_sum( tf.multiply( a, b ), 1, keep_dims=True )

with tf.Session() as session:
    print( c.eval(
        feed_dict={ a: [[1,2,3],[4,5,6]], b: [[2,3,4],[5,6,7]] }
    ) )

La sortie est:

[[ 20.]
 [ 92.]]
Neil Slater
la source
Cela a résolu mon problème, merci!
HenrySky
1
tf.mul est maintenant tf.multiply. github.com/tensorflow/tensorflow/issues/7032
Rahul Jha
1
Il n'y a apparemment rien que les développeurs de TF aiment plus que de changer l'API ...
Emre
@sajedzarrinpour Merci. J'espère que cela est apparu entre 2016 et maintenant? Ajustera ma réponse de manière appropriée
Neil Slater
6

Une autre option qui mérite d'être étudiée est [tf.einsum][1]- c'est essentiellement une version simplifiée de la notation Einstein .

À suivre avec les exemples de Neil et Dumkar:

import tensorflow as tf

a = tf.placeholder(tf.float32, shape=(None, 3))
b = tf.placeholder(tf.float32, shape=(None, 3))

c = tf.einsum('ij,ij->i', a, b)

with tf.Session() as session:
    print( c.eval(
        feed_dict={ a: [[1,2,3],[4,5,6]], b: [[2,3,4],[5,6,7]] }
    ) )

Le premier argument de einsumest une équation représentant les axes à multiplier et à additionner. Les règles de base d'une équation sont:

  1. Les tenseurs d'entrée sont décrits par une chaîne séparée par des virgules d'étiquettes de dimension
  2. Des étiquettes répétées indiquent que les dimensions correspondantes seront multipliées
  3. Le tenseur de sortie est décrit par une autre chaîne d'étiquettes de dimension représentant les entrées (ou produits) correspondantes
  4. Les étiquettes manquantes dans la chaîne de sortie sont additionnées

Dans notre cas, cela ij,ij->isignifie que nos entrées seront 2 matrices de forme égale (i,j), et notre sortie sera un vecteur de forme (i,).

Une fois que vous aurez compris, vous constaterez que einsumgénéralisent un grand nombre d'autres opérations:

X = [[1, 2]]
Y = [[3, 4], [5, 6]]

einsum('ab->ba', X) == [[1],[2]]   # transpose
einsum('ab->a',  X) ==  [3]        # sum over last dimension
einsum('ab->',   X) ==   3         # sum over both dimensions

einsum('ab,bc->ac',  X, Y) == [[13,16]]          # matrix multiply
einsum('ab,bc->abc', X, Y) == [[[3,4],[10,12]]]  # multiply and broadcast

Malheureusement, einsumprend un coup de performance assez lourd par rapport à une multiplication + réduction manuelle. Lorsque les performances sont critiques, je recommanderais certainement de rester avec la solution de Neil.

wynn
la source
3

Prendre la diagonale de tf.tensordot fait aussi ce que vous voulez, si vous définissez par exemple

[[1], [1]]

J'ai adapté l'exemple de Neil Slater:

import tensorflow as tf

# Arbitrarity, we'll use placeholders and allow batch size to vary,
# but fix vector dimensions.
# You can change this as you see fit
a = tf.placeholder(tf.float32, shape=(None, 3))
b = tf.placeholder(tf.float32, shape=(None, 3))

c = tf.diag_part(tf.tensordot( a, b, axes=[[1],[1]]))

with tf.Session() as session:
    print( c.eval(
        feed_dict={ a: [[1,2,3],[4,5,6]], b: [[2,3,4],[5,6,7]] }
    ) )

qui donne désormais également:

[ 20.  92.]

Cela pourrait cependant être sous-optimal pour les grandes matrices (voir la discussion ici )

Dumkar
la source
1
La marche du progrès :-), je ne sais pas dans quelle version d'API cela a été ajouté? Je suggère d'élargir votre réponse avec un court exemple (peut-être basé sur le mien, mais il devrait être plus simple, car il n'en aura pas besoin reduce_sum)
Neil Slater
J'ai ajouté l'exemple! En fait, il donne également des produits scalaires hors diagonale si vous n'utilisez pas tf.diag_part, donc votre réponse sera probablement plus rapide. Je ne sais pas vraiment dans quelle version d'API tf.tensordot a été introduite, mais cela pourrait être il y a longtemps car il est également disponible en numpy.
dumkar
Cela ne prendrait-il pas beaucoup plus de mémoire que la multiplication et la somme par élément?
kbrose