Tensorflow – Matmul de matriz de entrada con datos por lotes

Tengo algunos datos representados por input_x . Es un tensor de tamaño desconocido (debe ingresarse por lote) y cada artículo tiene un tamaño n . input_x somete a tf.nn.embedding_lookup , de modo que ahora embed tiene dimensiones [?, n, m] donde m es el tamaño de incrustación y ? se refiere al tamaño de lote desconocido.

Esto se describe aquí:

 input_x = tf.placeholder(tf.int32, [None, n], name="input_x") embed = tf.nn.embedding_lookup(W, input_x) 

Ahora estoy tratando de multiplicar cada muestra en mis datos de entrada (que ahora se expande incrustando dimensión) por una variable de matriz, U , y parece que no puedo saber cómo hacerlo.

Primero intenté usar tf.matmul pero da un error debido a la falta de coincidencia en las formas. Luego intenté lo siguiente, expandiendo la dimensión de U y aplicando batch_matmul (también probé la función desde tf.nn.math_ops. , El resultado fue el mismo):

 U = tf.Variable( ... ) U1 = tf.expand_dims(U,0) h=tf.batch_matmul(embed, U1) 

Esto pasa la comstackción inicial, pero luego, cuando se aplican los datos reales, aparece el siguiente error:

In[0].dim(0) and In[1].dim(0) must be the same: [64,58,128] vs [1,128,128]

También sé por qué está sucediendo esto: replicé la dimensión de U y ahora es 1 , pero el tamaño de minibatch, 64 , no se ajusta.

¿Cómo puedo realizar correctamente la multiplicación de matrices en mi entrada tensor-matriz (para un tamaño de lote desconocido)?

La operación matmul solo funciona en matrices (tensores 2D). Aquí hay dos enfoques principales para hacer esto, ambos suponen que U es un tensor 2D.

  1. Corte en rebordes los tensores 2D y multiplique cada uno de ellos con U individualmente. Probablemente esto sea más fácil de usar usando tf.scan() esta manera:

     h = tf.scan(lambda a, x: tf.matmul(x, U), embed) 
  2. Por otro lado, si la eficiencia es importante, puede ser mejor remodelar el embed para que sea un tensor 2D, de modo que la multiplicación se pueda realizar con un único matmul como este:

     embed = tf.reshape(embed, [-1, m]) h = tf.matmul(embed, U) h = tf.reshape(h, [-1, n, c]) 

    donde c es el número de columnas en U El último cambio de forma se asegurará de que h sea ​​un tensor 3D en el que la dimensión 0 se corresponda con el lote al igual que el x_input e embed originales.

Las respuestas anteriores son obsoletas. Actualmente tf.matmul() soporta tensores con rango> 2:

Las entradas deben ser matrices (o tensores de rango> 2, que representan lotes de matrices), con dimensiones internas coincidentes, posiblemente después de la transposición.

También se eliminó tf.matmul() y tf.matmul() es la forma correcta de hacer la multiplicación por lotes. La idea principal se puede entender a partir del siguiente código:

 import tensorflow as tf batch_size, n, m, k = 10, 3, 5, 2 A = tf.Variable(tf.random_normal(shape=(batch_size, n, m))) B = tf.Variable(tf.random_normal(shape=(batch_size, m, k))) tf.matmul(A, B) 

Ahora recibirá un tensor de la forma (batch_size, n, k) . Aquí está lo que está pasando aquí. Supongamos que tiene batch_size de matrices nxm y batch_size de matrices mxk . Ahora, para cada par de ellos, calculas nxm X mxk que te da una matriz nxk . Tendrás batch_size de ellos.

Tenga en cuenta que algo como esto también es válido:

 A = tf.Variable(tf.random_normal(shape=(a, b, n, m))) B = tf.Variable(tf.random_normal(shape=(a, b, m, k))) tf.matmul(A, B) 

y te dará una forma (a, b, n, k)

1. Quiero multiplicar un lote de matrices con un lote de matrices de la misma longitud, por pares

 M = tf.random_normal((batch_size, n, m)) N = tf.random_normal((batch_size, m, p)) # python >= 3.5 MN = M @ N # or the old way, MN = tf.matmul(M, N) # MN has shape (batch_size, n, p) 

2. Quiero multiplicar un lote de matrices con un lote de vectores de la misma longitud, por pares

Regresamos al caso 1 agregando y eliminando una dimensión a v .

 M = tf.random_normal((batch_size, n, m)) v = tf.random_normal((batch_size, m)) Mv = (M @ v[..., None])[..., 0] # Mv has shape (batch_size, n) 

3. Quiero multiplicar una sola matriz con un lote de matrices

En este caso, no podemos simplemente agregar una dimensión de lote de 1 a la matriz única, porque tf.matmul no transmite en la dimensión de lote.

3.1. La matriz única está en el lado derecho.

En ese caso, podemos tratar el lote de matriz como una matriz grande y única, utilizando una forma simple.

 M = tf.random_normal((batch_size, n, m)) N = tf.random_normal((m, p)) MN = tf.reshape(tf.reshape(M, [-1, m]) @ N, [-1, n, p]) # MN has shape (batch_size, n, p) 

3.2. La matriz única está en el lado izquierdo.

Este caso es más complicado. Podemos recurrir al caso 3.1 mediante la transposición de las matrices.

 MT = tf.matrix_transpose(M) NT = tf.matrix_transpose(N) NTMT = tf.reshape(tf.reshape(NT, [-1, m]) @ MT, [-1, p, n]) MN = tf.matrix_transpose(NTMT) 

Sin embargo, la transposición puede ser una operación costosa, y aquí se realiza dos veces en un lote completo de matrices. Puede ser mejor simplemente duplicar M para que coincida con la dimensión del lote:

 MN = tf.tile(M[None], [batch_size, 1, 1]) @ N 

El perfil le dirá qué opción funciona mejor para una combinación de hardware / problema dado.

4. Quiero multiplicar una sola matriz con un lote de vectores

Esto parece similar al caso 3.2, ya que la matriz única está a la izquierda, pero en realidad es más simple porque la transposición de un vector es esencialmente un no-op. Terminamos con

 M = tf.random_normal((n, m)) v = tf.random_normal((batch_size, m)) MT = tf.matrix_transpose(M) Mv = v @ MT 

¿Qué pasa con einsum ?

Todas las multiplicaciones anteriores podrían haberse escrito con la tf.einsum suiza tf.einsum . Por ejemplo, la primera solución para 3.2 podría escribirse simplemente como

 MN = tf.einsum('nm,bmp->bnp', M, N) 

Sin embargo, tenga en cuenta que einsum finalmente depende de la tranpose y matmul para el cálculo.

Entonces, aunque einsum es una forma muy conveniente de escribir multiplicaciones de matrices, oculta la complejidad de las operaciones que se encuentran debajo, por ejemplo, no es fácil adivinar cuántas veces una expresión de einsum transpondrá sus datos y, por lo tanto, qué tan costosa será la operación. . Además, puede ocultar el hecho de que podría haber varias alternativas para la misma operación (ver caso 3.2) y no necesariamente elegir la mejor opción.

Por esta razón, personalmente utilizaría fórmulas explícitas como las anteriores para transmitir mejor su respectiva complejidad. Aunque si sabes lo que estás haciendo y te gusta la simplicidad de la syntax de einsum , entonces hazlo.

Según lo contestado por @Stryke, hay dos maneras de lograr esto: 1. Escanear, y 2. Volver a formar

  1. tf.scan requiere funciones lambda y generalmente se utiliza para operaciones recursivas. Algunos ejemplos de los mismos están aquí: https://rdipietro.github.io/tensorflow-scan-examples/

  2. Personalmente prefiero remodelar, ya que es más intuitivo. Si está tratando de multiplicar matricialmente cada matriz en el tensor 3D por la matriz que es el tensor 2D, como Cijl = Aijk * Bkl, puede hacerlo con una simple remodelación.

     A' = tf.reshape(Aijk,[i*j,k]) C' = tf.matmul(A',Bkl) C = tf.reshape(C',[i,j,l]) 

Parece que en TensorFlow 1.11.0 los documentos para tf.matmul dicen incorrectamente que funciona para el rango> = 2.

En cambio, la mejor alternativa limpia que he encontrado es usar tf.tensordot(a, b, (-1, 0)) ( docs ).

Esta función obtiene el producto puntual de cualquier eje de la matriz a y cualquier eje de la matriz b en su forma general tf.tensordot(a, b, axis) . Proporcionar el axis como (-1, 0) obtiene el producto de puntos estándar de dos matrices.