¿Cómo hacer lotes de productos internos en Tensorflow?

10

Tengo dos tensor a:[batch_size, dim] b:[batch_size, dim]. Quiero hacer un producto interno para cada par en el lote, generando c:[batch_size, 1], dónde c[i,0]=a[i,:].T*b[i,:]. ¿Cómo?

HenrySky
fuente

Respuestas:

9

No hay un .dot_productmétodo nativo . Sin embargo, un producto de punto entre dos vectores solo se suma por elementos, por lo que funciona el siguiente ejemplo:

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]] }
    ) )

El resultado es:

[[ 20.]
 [ 92.]]
Neil Slater
fuente
¡Resolvió mi problema, gracias!
HenrySky
1
tf.mul ahora es tf.multiply. github.com/tensorflow/tensorflow/issues/7032
Rahul Jha
1
Al parecer, no hay nada que los desarrolladores de TF adoren más que cambiar la API ...
Emre
@sajedzarrinpour Gracias. Espero que haya aparecido en algún momento entre 2016 y ahora. Ajustará mi respuesta apropiadamente
Neil Slater
6

Otra opción que vale la pena ver es [tf.einsum][1]: es esencialmente una versión simplificada de la notación de Einstein .

Siguiendo junto con los ejemplos de Neil y 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]] }
    ) )

El primer argumento einsumes una ecuación que representa los ejes que se multiplicarán y se sumarán. Las reglas básicas para una ecuación son:

  1. Los tensores de entrada se describen mediante una cadena separada por comas de etiquetas de dimensión
  2. Las etiquetas repetidas indican que las dimensiones correspondientes se multiplicarán
  3. El tensor de salida se describe mediante otra cadena de etiquetas de dimensión que representan las entradas (o productos) correspondientes.
  4. Las etiquetas que faltan en la cadena de salida se suman

En nuestro caso, ij,ij->isignifica que nuestras entradas serán 2 matrices de igual forma (i,j), y nuestra salida será un vector de forma (i,).

Una vez que lo domines, encontrarás que einsum generaliza una gran cantidad de otras operaciones:

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

Desafortunadamente, einsumtiene un impacto de rendimiento bastante considerable en comparación con una multiplicación manual + reducción. Cuando el rendimiento es crítico, definitivamente recomendaría seguir con la solución de Neil.

wynn
fuente
3

Tomar la diagonal de tf.tensordot también hace lo que quieres, si configuras el eje para, por ejemplo,

[[1], [1]]

He adaptado el ejemplo 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]] }
    ) )

que ahora también da:

[ 20.  92.]

Sin embargo, esto podría ser subóptimo para matrices grandes (ver discusión aquí )

dumkar
fuente
1
La marcha del progreso :-), ¿no estoy seguro de en qué versión de API se agregó? Sugiero ampliar su respuesta con un breve ejemplo (quizás basado en el mío, pero debería ser más simple, ya que no necesitará el reduce_sum)
Neil Slater
¡Agregué el ejemplo! En realidad, también proporciona productos de punto fuera de la diagonal si no usa tf.diag_part, por lo que su respuesta probablemente será más rápida. No estoy seguro de en qué versión de API se introdujo tf.tensordot, pero podría ser hace mucho tiempo ya que también está disponible en numpy.
dumkar
¿No tomaría esto mucha más memoria que la multiplicación y suma por elementos?
kbrose