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]
où 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)?
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.
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)
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])
où 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.
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)
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)
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)
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.
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)
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.
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
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.
Comme @Stryke y a répondu, il existe deux méthodes pour y parvenir: 1. Numérisation, et 2. Remodelage
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/
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])
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.