Como fazer produto interno em lote no Tensorflow?

10

Eu tenho dois tensores a:[batch_size, dim] b:[batch_size, dim]. Quero fazer um produto interno para cada par do lote, gerando c:[batch_size, 1], onde c[i,0]=a[i,:].T*b[i,:]. Quão?

HenrySky
fonte

Respostas:

9

Não há .dot_productmétodo nativo . No entanto, um produto escalar entre dois vetores é somado por multiplicação por elementos, portanto, o exemplo a seguir funciona:

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

A saída é:

[[ 20.]
 [ 92.]]
Neil Slater
fonte
Resolveu o meu problema, thx!
HenrySky
1
tf.mul agora é tf.multiply. github.com/tensorflow/tensorflow/issues/7032
Rahul Jha
1
Aparentemente, não há nada que os desenvolvedores de TF adorem mais do que mudar a API ...
Emre
@sajedzarrinpour Thanks. Espero que tenha aparecido algum tempo entre 2016 e agora? Ajustarei minha resposta adequadamente
Neil Slater
6

Outra opção que vale a pena conferir é [tf.einsum][1]- é essencialmente uma versão simplificada do Einstein Notation .

Seguindo os exemplos de Neil e 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]] }
    ) )

O primeiro argumento para einsumé uma equação que representa os eixos a serem multiplicados e somados. As regras básicas para uma equação são:

  1. Os tensores de entrada são descritos por uma sequência de rótulos de dimensão separados por vírgula
  2. Etiquetas repetidas indicam que as dimensões correspondentes serão multiplicadas
  3. O tensor de saída é descrito por outra sequência de rótulos de dimensão que representam entradas (ou produtos) correspondentes
  4. Os rótulos ausentes da sequência de saída são somados

No nosso caso, ij,ij->isignifica que nossas entradas serão 2 matrizes de forma igual (i,j)e nossa saída será um vetor de forma (i,).

Depois de pegar o jeito, você verá que einsumgeneraliza um grande número de outras operações:

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

Infelizmente, é einsumpreciso um desempenho considerável quando comparado a uma multiplicação manual + redução. Onde o desempenho é crítico, eu recomendaria definitivamente continuar com a solução de Neil.

wynn
fonte
3

Tomar a diagonal de tf.tensordot também faz o que você deseja, se você definir o eixo como, por exemplo,

[[1], [1]]

Eu adaptei o exemplo 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 agora também fornece:

[ 20.  92.]

Isso pode ser abaixo do ideal para matrizes grandes (veja a discussão aqui )

dumkar
fonte
1
A marcha do progresso :-), não tenho certeza em qual versão da API foi adicionada? Eu sugiro expandir a sua resposta com um exemplo curto (talvez com base na minha, mas deve ser mais simples, uma vez que não será necessário o reduce_sum)
Neil Slater
Eu adicionei o exemplo! Na verdade, ele também fornece produtos de pontos fora da diagonal se você não usar tf.diag_part, portanto sua resposta provavelmente será mais rápida. Não tenho muita certeza de qual versão da API tf.tensordot foi introduzida, mas pode ser há muito tempo, já que também está disponível em numpy.
dumkar
Isso não exigiria muito mais memória do que a multiplicação e soma por elementos?
Kbrose