web-dev-qa-db-de.com

Tensorflow - Matmul der Eingangsmatrix mit Chargendaten

Ich habe einige Daten, die durch input_x dargestellt werden. Es ist ein Tensor unbekannter Größe (sollte chargenweise eingegeben werden) und jedes Element hat die Größe n. input_x erfährt tf.nn.embedding_lookup, sodass embed jetzt die Abmessungen [?, n, m] hat, wobei m die Einbettungsgröße ist und ? sich auf die unbekannte Stapelgröße bezieht.

Dies ist hier beschrieben:

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

Ich versuche jetzt, jedes Sample in meinen Eingabedaten (die jetzt durch Einbetten der Dimension erweitert werden) mit einer Matrixvariablen, U, zu multiplizieren, und ich kann anscheinend nicht verstehen, wie das geht.

Ich habe zum ersten Mal versucht, tf.matmul zu verwenden, aber es wird ein Fehler angezeigt, weil die Formen nicht übereinstimmen. Ich habe dann Folgendes versucht, indem ich die Dimension von U erweitert und batch_matmul angewendet habe (ich habe auch die Funktion von tf.nn.math_ops. ausprobiert, das Ergebnis war dasselbe):

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

Hiermit wird die erste Kompilierung bestanden, aber wenn die tatsächlichen Daten angewendet werden, wird der folgende Fehler angezeigt:

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

Ich weiß auch, warum dies geschieht - ich habe die Dimension von U repliziert und es ist jetzt 1, aber die Minibatch-Größe 64 passt nicht.

Wie kann ich diese Matrixmultiplikation auf meinem Tensor-Matrix-Eingang korrekt durchführen (für unbekannte Chargengröße)?

32
yoki

Die Operation matmul funktioniert nur bei Matrizen (2D-Tensoren). Hier sind zwei Hauptansätze dafür, beide gehen davon aus, dass U ein 2D-Tensor ist.

  1. Schneiden Sie embed in 2D-Tensoren und multiplizieren Sie diese einzeln mit U. Dies ist wahrscheinlich am einfachsten, wenn Sie tf.scan() wie folgt verwenden:

    h = tf.scan(lambda a, x: tf.matmul(x, U), embed)
    
  2. Auf der anderen Seite, wenn die Effizienz wichtig ist, kann es besser sein, embed in einen 2D-Tensor umzuformen, damit die Multiplikation mit einer einzelnen matmul wie folgt erfolgen kann:

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

    dabei ist c die Anzahl der Spalten in U. Die letzte Umformung stellt sicher, dass h ein 3D-Tensor ist, bei dem die 0-te Dimension genau wie der ursprüngliche x_input und embed dem Stapel entspricht.

15
Styrke

Vorherige Antworten sind veraltet. Derzeit unterstützen tf.matmul() Tensoren mit Rang> 2:

Die Eingaben müssen Matrizen sein (oder Tensoren mit Rang> 2, die __. Stapel von Matrizen repräsentieren) mit übereinstimmenden inneren Abmessungen, möglicherweise nach. Umsetzung.

Außerdem wurde tf.batch_matmul() entfernt, und tf.matmul() ist der richtige Weg für die Stapelvervielfachung. Die Grundidee kann aus dem folgenden Code verstanden werden:

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)

Jetzt erhalten Sie einen Tensor der Form (batch_size, n, k). Hier ist was hier los ist. Angenommen, Sie haben batch_size von Matrizen nxm und batch_size von Matrizen mxk. Jetzt berechnen Sie für jedes Paar nxm X mxk, wodurch Sie eine nxk-Matrix erhalten. Sie werden batch_size von ihnen haben.

Beachten Sie, dass so etwas auch gültig ist:

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)

und wird dir eine Form geben (a, b, n, k)

67
Salvador Dali

1. Ich möchte eine Charge von Matrizen mit einer Charge von Matrizen derselben Länge paarweise multiplizieren

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. Ich möchte eine Charge von Matrizen mit einer Charge von Vektoren gleicher Länge paarweise multiplizieren

Wir gehen auf Fall 1 zurück, indem wir eine Dimension zu v hinzufügen und entfernen.

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. Ich möchte eine einzelne Matrix mit einem Stapel von Matrizen multiplizieren

In diesem Fall können wir der Einzelmatrix nicht einfach eine Batch-Dimension von 1 hinzufügen, da tf.matmul nicht in der Batch-Dimension sendet.

3.1. Die Einzelmatrix befindet sich auf der rechten Seite

In diesem Fall können wir die Matrixcharge mit einer einfachen Umformung als eine einzige große Matrix behandeln.

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. Die Einzelmatrix befindet sich auf der linken Seite

Dieser Fall ist komplizierter. Wir können auf den Fall 3.1 zurückgreifen, indem wir die Matrizen transponieren.

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)

Die Transposition kann jedoch eine kostspielige Operation sein, und hier wird die gesamte Charge von Matrizen zweimal durchgeführt. Es ist möglicherweise besser, einfach M zu duplizieren, um der Batch-Dimension zu entsprechen:

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

Die Profilerstellung gibt an, welche Option für eine gegebene Problem/Hardware-Kombination besser geeignet ist.

4. Ich möchte eine einzelne Matrix mit einem Stapel Vektoren multiplizieren

Dies sieht ähnlich zu Fall 3.2 aus, da sich die einzelne Matrix auf der linken Seite befindet, sie ist jedoch einfacher, da das Versetzen eines Vektors im Wesentlichen ein No-Op ist. Wir enden mit

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

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

Was ist mit einsum?

Alle vorherigen Multiplikationen hätten mit dem tf.einsum Schweizer Taschenmesser geschrieben werden können. Zum Beispiel könnte die erste Lösung für 3.2 einfach als geschrieben werden

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

Beachten Sie jedoch, dass einsum letztendlich auf tranpose und matmul angewiesen ist.

Auch wenn einsum eine sehr bequeme Methode zum Schreiben von Matrixmultiplikationen ist, verdeckt dies die Komplexität der darunter liegenden Operationen. Beispielsweise ist es nicht einfach zu erraten, wie oft ein einsum-Ausdruck Ihre Daten transponiert und wie teuer die Operation ist . Es kann auch überdeckt werden, dass es für dieselbe Operation mehrere Alternativen gibt (siehe Fall 3.2) und nicht unbedingt die bessere Option gewählt wird.

Aus diesem Grund würde ich persönlich explizite Formeln wie die obigen verwenden, um ihre jeweilige Komplexität besser zu vermitteln. Wenn Sie wissen, was Sie tun, und die Einfachheit der einsum-Syntax mögen, dann sollten Sie dies unbedingt tun.

11
P-Gn

Wie von @Stryke beantwortet, gibt es zwei Möglichkeiten, dies zu erreichen: 1. Scannen und 2. Umformen

  1. tf.scan benötigt Lambda-Funktionen und wird im Allgemeinen für rekursive Operationen verwendet. Einige Beispiele dafür sind hier: https://rdipietro.github.io/tensorflow-scan-examples/

  2. Ich persönlich bevorzuge die Umgestaltung, da es intuitiver ist. Wenn Sie versuchen, jede Matrix im 3D-Tensor mit der 2D-Tensor-Matrix (z. B. Cijl = Aijk * Bkl) zu multiplizieren, können Sie dies mit einer einfachen Umformung tun.

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

Es scheint, dass in TensorFlow 1.11.0 die docs für tf.matmul falsch sagen, dass es für rank> = 2 funktioniert.

Stattdessen ist die beste saubere Alternative, die ich gefunden habe, tf.tensordot(a, b, (-1, 0)) ( docs ).

Diese Funktion erhält das Punktprodukt einer beliebigen Achse des Arrays a und einer beliebigen Achse des Arrays b in ihrer allgemeinen Form tf.tensordot(a, b, axis). Wenn Sie axis als (-1, 0) angeben, wird das Standardpunktprodukt von zwei Arrays erhalten.

0
James Fletcher