web-dev-qa-db-fra.com

Tensorflow - matrice de matrice d'entrée avec données de lot

J'ai des données représentées par input_x. C'est un tenseur de taille inconnue (doit être saisi par lot) et chaque article est de taille n. input_x est soumis à tf.nn.embedding_lookup, de sorte que embed a maintenant les dimensions [?, n, m]m est la taille d'intégration et ? fait référence à la taille de lot inconnue.

Ceci est décrit ici:

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

J'essaie maintenant de multiplier chaque échantillon de mes données d'entrée (qui est maintenant développée par la dimension d'intégration) par une variable matricielle, U, et je n'arrive pas à comprendre comment faire.

J'ai d'abord essayé d'utiliser tf.matmul, mais cela donne une erreur due à une discordance dans les formes. J'ai ensuite essayé ce qui suit, en développant la dimension de U et en appliquant batch_matmul (j'ai également essayé la fonction à partir de tf.nn.math_ops., le résultat était le même):

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

Cela passe la compilation initiale, mais quand les données réelles sont appliquées, j'obtiens l'erreur suivante:

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

Je sais aussi pourquoi cela se produit - j'ai répliqué la dimension de U et elle porte maintenant le 1, mais la taille du minibatch, 64, ne convient pas.

Comment puis-je effectuer correctement cette multiplication matricielle sur mon entrée tenseur-matrice (pour une taille de lot inconnue)?

32
yoki

L'opération matmul ne fonctionne que sur les matrices (tenseurs 2D). Voici deux approches principales pour le faire, les deux supposent que U est un tenseur 2D.

  1. Découpez embed en deux tenseurs 2D et multipliez chacun d'eux avec U individuellement. C'est probablement plus facile à faire avec tf.scan() comme ceci:

    h = tf.scan(lambda a, x: tf.matmul(x, U), embed)
    
  2. D'un autre côté, si l'efficacité est importante, il peut être préférable de transformer embed en un tenseur 2D afin que la multiplication puisse être réalisée avec un seul matmul comme ceci:

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

    c est le nombre de colonnes dans U. La dernière refonte assurera que h est un tenseur 3D où la 0ème dimension correspond au lot de la même manière que les x_input et embed d'origine.

15
Styrke

Les réponses précédentes sont obsolètes. Actuellement tf.matmul() support des tenseurs de rang> 2:

Les entrées doivent être des matrices (ou des tenseurs de rang> 2, représentant des lots de matrices .__), avec des dimensions intérieures identiques, éventuellement après transposition.

De plus, tf.batch_matmul() a été supprimé et tf.matmul() est la bonne façon de procéder à la multiplication par lots. L'idée principale peut être comprise à partir du code suivant:

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)

Vous allez maintenant recevoir un tenseur de la forme (batch_size, n, k). Voici ce qui se passe ici. Supposons que vous avez batch_size de matrices nxm et batch_size de matrices mxk. Maintenant, pour chaque paire, vous calculez nxm X mxk qui vous donne une matrice nxk. Vous aurez batch_size d'entre eux.

Notez que quelque chose comme ceci est également valide:

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)

et vous donnera une forme (a, b, n, k)

67
Salvador Dali

1. Je veux multiplier un lot de matrices avec un lot de matrices de même longueur, deux par deux

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. Je veux multiplier un lot de matrices avec un lot de vecteurs de même longueur, deux par deux

Nous retombons dans le cas 1 en ajoutant et en supprimant une dimension dans 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. Je veux multiplier une seule matrice avec un lot de matrices

Dans ce cas, nous ne pouvons pas simplement ajouter une dimension de lot de 1 à la matrice unique, car tf.matmul ne diffuse pas dans la dimension de lot.

3.1. La matrice unique est sur le côté droit

Dans ce cas, nous pouvons traiter le lot de matrice comme une seule grande matrice, en utilisant une simple remise en forme.

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 matrice unique est sur le côté gauche

Cette affaire est plus compliquée. On peut revenir au cas 3.1 en transposant les 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)

Cependant, la transposition peut être une opération coûteuse et, ici, elle est effectuée deux fois sur un lot entier de matrices. Il peut être préférable de simplement dupliquer M pour correspondre à la dimension du lot:

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

Le profilage indiquera quelle option fonctionne le mieux pour une combinaison problème/matériel donnée.

4. Je veux multiplier une seule matrice avec un lot de vecteurs

Cela ressemble au cas 3.2 puisque la matrice unique est à gauche, mais en réalité c'est plus simple car la transposition d'un vecteur est essentiellement un no-op. On se retrouve avec

M = tf.random_normal((n, m))
v = tf.random_normal((batch_size, m))

MT = tf.matrix_transpose(M)
Mv = v @ MT

Qu'en est-il de einsum?

Toutes les multiplications précédentes auraient pu être écrites avec le couteau suisse tf.einsum . Par exemple, la première solution pour 3.2 pourrait être écrite simplement

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

Cependant, notez que einsum est finalement reposant sur tranpose et matmul pour le calcul.

Ainsi, même si einsum est un moyen très pratique d’écrire des multiplications matricielles, il cache la complexité des opérations sous-jacentes. Par exemple, il n’est pas facile de deviner combien de fois une expression einsum transposera vos données et donc combien elle sera coûteuse . En outre, cela peut masquer le fait qu'il peut exister plusieurs alternatives pour la même opération (voir cas 3.2) et ne pas nécessairement choisir la meilleure option.

Pour cette raison, j'utiliserais personnellement des formules explicites comme celles ci-dessus pour mieux rendre compte de leur complexité respective. Bien que si vous savez ce que vous faites et aimez la simplicité de la syntaxe einsum, alors foncez.

11
P-Gn

Comme @Stryke y a répondu, il existe deux méthodes pour y parvenir: 1. Numérisation, et 2. Remodelage

  1. tf.scan requiert les fonctions lambda et est généralement utilisé pour les opérations récursives. Quelques exemples pour les mêmes sont ici: https://rdipietro.github.io/tensorflow-scan-examples/

  2. Personnellement, je préfère remodeler, car c'est plus intuitif. Si vous essayez de multiplier chaque matrice du tenseur 3D par la matrice qui est le tenseur 2D, comme Cijl = Aijk * Bkl, vous pouvez le faire avec une simple refaçonnage.

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

Il semble que dans TensorFlow 1.11.0, les docs de tf.matmul indiquent à tort qu’il fonctionne pour un rang supérieur ou égal à 2.

Au lieu de cela, la meilleure alternative propre que j'ai trouvée consiste à utiliser tf.tensordot(a, b, (-1, 0)) ( docs ).

Cette fonction obtient le produit scalaire de n’importe quel axe du tableau a et de n’importe quel axe du tableau b sous sa forme générale tf.tensordot(a, b, axis). Si vous fournissez axis en tant que (-1, 0), vous obtenez le produit scalaire standard de deux matrices.

0
James Fletcher