Was macht die Funktion tf.nn.embedding_lookup?

158
tf.nn.embedding_lookup(params, ids, partition_strategy='mod', name=None)

Ich kann die Pflicht dieser Funktion nicht verstehen. Ist es wie eine Nachschlagetabelle? Was bedeutet, die Parameter zurückzugeben, die jeder ID entsprechen (in IDs)?

skip-gramWenn wir zum Beispiel im Modell verwenden tf.nn.embedding_lookup(embeddings, train_inputs), findet es dann für jedes train_inputdie entsprechende Einbettung?

Poorya Pzm
quelle
"Ist es wie eine Nachschlagetabelle?" tldr - Ja. Geben Sie für jedes x (ids) das assoziierte y (params) an.
David Refaeli

Antworten:

147

embedding_lookupFunktion ruft Zeilen des paramsTensors ab. Das Verhalten ähnelt der Verwendung der Indizierung mit Arrays in Numpy. Z.B

matrix = np.random.random([1024, 64])  # 64-dimensional embeddings
ids = np.array([0, 5, 17, 33])
print matrix[ids]  # prints a matrix of shape [4, 64] 

paramsArgument kann auch eine Liste von Tensoren sein, in welchem ​​Fall die idsunter den Tensoren verteilt werden. Zum Beispiel, da eine Liste von 3 Tensoren [2, 64], das Standardverhalten ist , dass sie vertreten ids: [0, 3], [1, 4], [2, 5].

partition_strategySteuert die idsVerteilung der Daten auf die Liste. Die Partitionierung ist nützlich bei größeren Problemen, wenn die Matrix möglicherweise zu groß ist, um sie in einem Stück zu halten.

Rafał Józefowicz
quelle
21
Warum sollten sie es so nennen und nicht select_rows?
Lenar Hoyt
12
@LenarHoyt, weil diese Idee einer Suche von Word Embeddings stammt. und die "Zeilen" sind die Darstellungen (Einbettungen) der Wörter in einen Vektorraum - und sind in einem von ihnen selbst nützlich. Oft mehr als das eigentliche Netzwerk.
Lyndon White
2
Wie lernt Tensorflow die Einbettungsstruktur? Verwaltet diese Funktion auch diesen Prozess?
Vgoklani
19
@vgoklani, nein, embedding_lookupbietet einfach eine bequeme (und parallele) Möglichkeit, Einbettungen abzurufen, die der ID in entsprechen ids. Der paramsTensor ist normalerweise eine tf-Variable, die im Rahmen des Trainingsprozesses gelernt wird - eine tf-Variable, deren Komponenten direkt oder indirekt in einer Verlustfunktion (z. B. tf.l2_loss) verwendet werden, die von einem Optimierer (z. B. tf.train.AdamOptimizer) optimiert wird .
Shobhit
5
@ Rafał Józefowicz Warum "ist das Standardverhalten, dass sie IDs darstellen: [0, 3], [1, 4], [2, 5]."? Könntest du erklären?
Aerin
219

Ja, diese Funktion ist schwer zu verstehen, bis Sie den Punkt bekommen.

In seiner einfachsten Form ähnelt es tf.gather. Es gibt die Elemente von paramsgemäß den durch angegebenen Indizes zurück ids.

Zum Beispiel (vorausgesetzt du bist drinnen tf.InteractiveSession())

params = tf.constant([10,20,30,40])
ids = tf.constant([0,1,2,3])
print tf.nn.embedding_lookup(params,ids).eval()

würde zurückgeben [10 20 30 40], weil das erste Element (Index 0) von Parametern ist 10, das zweite Element von Parametern (Index 1) ist 20usw.

Ähnlich,

params = tf.constant([10,20,30,40])
ids = tf.constant([1,1,3])
print tf.nn.embedding_lookup(params,ids).eval()

würde zurückkehren [20 20 40].

Aber embedding_lookupist mehr als das. Das paramsArgument kann eine Liste von Tensoren sein und nicht ein einzelner Tensor.

params1 = tf.constant([1,2])
params2 = tf.constant([10,20])
ids = tf.constant([2,0,2,1,2,3])
result = tf.nn.embedding_lookup([params1, params2], ids)

In einem solchen Fall idsentsprechen die in angegebenen Indizes Elementen von Tensoren gemäß einer Partitionsstrategie , wobei die Standardpartitionsstrategie 'mod' ist.

In der 'mod'-Strategie entspricht Index 0 dem ersten Element des ersten Tensors in der Liste. Index 1 entspricht dem ersten Element des zweiten Tensors. Index 2 entspricht dem ersten Element des dritten Tensors und so weiter. Einfach Index ientspricht für alle Indizes dem ersten Element des (i + 1) -ten Tensors 0..(n-1), vorausgesetzt, params ist eine Liste von nTensoren.

Der Index nkann jetzt nicht dem Tensor n + 1 entsprechen, da die Liste paramsnur nTensoren enthält . Der Index nentspricht also dem zweiten Element des ersten Tensors. In ähnlicher Weise n+1entspricht der Index dem zweiten Element des zweiten Tensors usw.

Also im Code

params1 = tf.constant([1,2])
params2 = tf.constant([10,20])
ids = tf.constant([2,0,2,1,2,3])
result = tf.nn.embedding_lookup([params1, params2], ids)

Index 0 entspricht dem ersten Element des ersten Tensors: 1

Index 1 entspricht dem ersten Element des zweiten Tensors: 10

Index 2 entspricht dem zweiten Element des ersten Tensors: 2

Index 3 entspricht dem zweiten Element des zweiten Tensors: 20

Das Ergebnis wäre also:

[ 2  1  2 10  2 20]
Asher Stern
quelle
8
ein Hinweis: Sie können verwenden partition_strategy='div'und würden erhalten [10, 1, 10, 2, 10, 20], dh id=1ist das zweite Element des ersten Parameters. Grundsätzlich: partition_strategy=mod(Standard) id%len(params): Index des Parameters in Params id//len(params): Index des Elements im obigen Parameter partition_strategy=*div*umgekehrt
Mario Alemi
3
@ asher-stern könntest du erklären, warum "mod" -Strategie Standard ist? Es scheint, dass die "div" -Strategie dem Standard-Tensor-Slicing (Auswahlreihen nach gegebenen Indizes) ähnlicher ist. Gibt es Leistungsprobleme bei "div"?
svetlov.vsevolod
46

Ja, der Zweck der tf.nn.embedding_lookup()Funktion besteht darin, eine Suche in der Einbettungsmatrix durchzuführen und die Einbettungen (oder in einfachen Worten die Vektordarstellung) von Wörtern zurückzugeben.

Eine einfache Einbettungsmatrix (mit Form :) vocabulary_size x embedding_dimensionwürde wie folgt aussehen. (dh jedes Wort wird durch einen Vektor von Zahlen dargestellt; daher der Name word2vec )


Matrix einbetten

the 0.418 0.24968 -0.41242 0.1217 0.34527 -0.044457 -0.49688 -0.17862
like 0.36808 0.20834 -0.22319 0.046283 0.20098 0.27515 -0.77127 -0.76804
between 0.7503 0.71623 -0.27033 0.20059 -0.17008 0.68568 -0.061672 -0.054638
did 0.042523 -0.21172 0.044739 -0.19248 0.26224 0.0043991 -0.88195 0.55184
just 0.17698 0.065221 0.28548 -0.4243 0.7499 -0.14892 -0.66786 0.11788
national -1.1105 0.94945 -0.17078 0.93037 -0.2477 -0.70633 -0.8649 -0.56118
day 0.11626 0.53897 -0.39514 -0.26027 0.57706 -0.79198 -0.88374 0.30119
country -0.13531 0.15485 -0.07309 0.034013 -0.054457 -0.20541 -0.60086 -0.22407
under 0.13721 -0.295 -0.05916 -0.59235 0.02301 0.21884 -0.34254 -0.70213
such 0.61012 0.33512 -0.53499 0.36139 -0.39866 0.70627 -0.18699 -0.77246
second -0.29809 0.28069 0.087102 0.54455 0.70003 0.44778 -0.72565 0.62309 

Ich habe die obige Einbettungsmatrix aufgeteilt und nur die Wörter geladen, in vocabdenen sich unser Vokabular und die entsprechenden Vektoren im embArray befinden.

vocab = ['the','like','between','did','just','national','day','country','under','such','second']

emb = np.array([[0.418, 0.24968, -0.41242, 0.1217, 0.34527, -0.044457, -0.49688, -0.17862],
   [0.36808, 0.20834, -0.22319, 0.046283, 0.20098, 0.27515, -0.77127, -0.76804],
   [0.7503, 0.71623, -0.27033, 0.20059, -0.17008, 0.68568, -0.061672, -0.054638],
   [0.042523, -0.21172, 0.044739, -0.19248, 0.26224, 0.0043991, -0.88195, 0.55184],
   [0.17698, 0.065221, 0.28548, -0.4243, 0.7499, -0.14892, -0.66786, 0.11788],
   [-1.1105, 0.94945, -0.17078, 0.93037, -0.2477, -0.70633, -0.8649, -0.56118],
   [0.11626, 0.53897, -0.39514, -0.26027, 0.57706, -0.79198, -0.88374, 0.30119],
   [-0.13531, 0.15485, -0.07309, 0.034013, -0.054457, -0.20541, -0.60086, -0.22407],
   [ 0.13721, -0.295, -0.05916, -0.59235, 0.02301, 0.21884, -0.34254, -0.70213],
   [ 0.61012, 0.33512, -0.53499, 0.36139, -0.39866, 0.70627, -0.18699, -0.77246 ],
   [ -0.29809, 0.28069, 0.087102, 0.54455, 0.70003, 0.44778, -0.72565, 0.62309 ]])


emb.shape
# (11, 8)

Einbetten der Suche in TensorFlow

Jetzt werden wir sehen, wie wir eine Einbettungssuche für einen beliebigen Eingabesatz durchführen können.

In [54]: from collections import OrderedDict

# embedding as TF tensor (for now constant; could be tf.Variable() during training)
In [55]: tf_embedding = tf.constant(emb, dtype=tf.float32)

# input for which we need the embedding
In [56]: input_str = "like the country"

# build index based on our `vocabulary`
In [57]: word_to_idx = OrderedDict({w:vocab.index(w) for w in input_str.split() if w in vocab})

# lookup in embedding matrix & return the vectors for the input words
In [58]: tf.nn.embedding_lookup(tf_embedding, list(word_to_idx.values())).eval()
Out[58]: 
array([[ 0.36807999,  0.20834   , -0.22318999,  0.046283  ,  0.20097999,
         0.27515   , -0.77126998, -0.76804   ],
       [ 0.41800001,  0.24968   , -0.41242   ,  0.1217    ,  0.34527001,
        -0.044457  , -0.49687999, -0.17862   ],
       [-0.13530999,  0.15485001, -0.07309   ,  0.034013  , -0.054457  ,
        -0.20541   , -0.60086   , -0.22407   ]], dtype=float32)

Beobachten Sie anhand der Wortindizes in unserem Wortschatz, wie wir die Einbettungen aus unserer ursprünglichen Einbettungsmatrix (mit Wörtern) erhalten haben .

Normalerweise wird eine solche Einbettungssuche von der ersten Schicht (als Einbettungsschicht bezeichnet ) durchgeführt, die diese Einbettungen dann zur weiteren Verarbeitung an RNN / LSTM / GRU-Schichten weiterleitet.


Randnotiz : Normalerweise hat das Vokabular auch einen speziellen unkToken. Wenn also ein Token aus unserem Eingabesatz nicht in unserem Vokabular vorhanden ist, wird der entsprechende Index unkin der Einbettungsmatrix nachgeschlagen.


PS Beachten Sie, dass dies embedding_dimensionein Hyperparameter ist, den man für seine Anwendung anpassen muss, aber beliebte Modelle wie Word2Vec und GloVe verwenden 300Dimensionsvektoren zur Darstellung jedes Wortes.

Bonus Lesen word2vec Skip-Gramm-Modell

kmario23
quelle
17

Hier ist ein Bild, das den Prozess des Einbettens der Suche zeigt.

Bild: Einbetten des Suchprozesses

Kurz gesagt, es erhält die entsprechenden Zeilen einer Einbettungsschicht, die durch eine Liste von IDs angegeben werden, und stellt diese als Tensor bereit. Dies wird durch den folgenden Prozess erreicht.

  1. Definieren Sie einen Platzhalter lookup_ids = tf.placeholder([10])
  2. Definieren Sie eine Einbettungsebene embeddings = tf.Variable([100,10],...)
  3. Definieren Sie den Tensorflow-Vorgang embed_lookup = tf.embedding_lookup(embeddings, lookup_ids)
  4. Erhalten Sie die Ergebnisse durch Ausführen lookup = session.run(embed_lookup, feed_dict={lookup_ids:[95,4,14]})
thushv89
quelle
6

Wenn sich der Parametertensor in hohen Dimensionen befindet, beziehen sich die IDs nur auf die obere Dimension. Vielleicht ist es für die meisten Leute offensichtlich, aber ich muss den folgenden Code ausführen, um das zu verstehen:

embeddings = tf.constant([[[1,1],[2,2],[3,3],[4,4]],[[11,11],[12,12],[13,13],[14,14]],
                          [[21,21],[22,22],[23,23],[24,24]]])
ids=tf.constant([0,2,1])
embed = tf.nn.embedding_lookup(embeddings, ids, partition_strategy='div')

with tf.Session() as session:
    result = session.run(embed)
    print (result)

Nur die 'div'-Strategie auszuprobieren und für einen Tensor macht es keinen Unterschied.

Hier ist die Ausgabe:

[[[ 1  1]
  [ 2  2]
  [ 3  3]
  [ 4  4]]

 [[21 21]
  [22 22]
  [23 23]
  [24 24]]

 [[11 11]
  [12 12]
  [13 13]
  [14 14]]]
Yan Zhao
quelle
3

Eine andere Sichtweise ist, dass Sie die Tensoren auf ein eindimensionales Array reduzieren und dann eine Suche durchführen

(zB) Tensor0 = [1,2,3], Tensor1 = [4,5,6], Tensor2 = [7,8,9]

Der abgeflachte Tensor ist wie folgt [1,4,7,2,5,8,3,6,9]

Wenn Sie nun [0,3,4,1,7] nachschlagen, wird [1,2,5,4,6] angezeigt.

(i, e) Wenn der Suchwert beispielsweise 7 ist und wir 3 Tensoren (oder einen Tensor mit 3 Zeilen) haben, dann

7/3: (Erinnerung ist 1, Quotient ist 2) Es wird also das 2. Element von Tensor1 angezeigt, nämlich 6

Shanmugam Ramasamy
quelle
2

Da ich auch von dieser Funktion fasziniert war, gebe ich meine zwei Cent.

Ich sehe es im 2D-Fall nur als Matrixmultiplikation (es ist einfach, es auf andere Dimensionen zu verallgemeinern).

Betrachten Sie ein Vokabular mit N Symbolen. Dann können Sie ein Symbol x als einen Vektor der Dimensionen Nx1 darstellen, der einmalig codiert ist.

Sie möchten dieses Symbol jedoch nicht als Vektor von Nx1 darstellen, sondern als Vektor mit den Dimensionen Mx1, genannt y .

Um x in y umzuwandeln , können Sie die Matrix E mit den Dimensionen MxN verwenden und einbetten :

y = E x .

Dies ist im Wesentlichen das, was tf.nn.embedding_lookup (params, ids, ...) tut, mit der Nuance, dass ids nur eine Zahl sind, die die Position der 1 im One-Hot-Coded-Vektor x darstellt .

joaoaccarvalho
quelle
0

Das Hinzufügen zu Asher Sterns Antwort paramswird als Partitionierung eines großen Einbettungstensors interpretiert . Es kann sich um einen einzelnen Tensor handeln, der den vollständigen Einbettungstensor darstellt, oder um eine Liste von X-Tensoren, die bis auf die erste Dimension alle dieselbe Form haben und Sharded-Einbettungstensoren darstellen.

Die Funktion tf.nn.embedding_lookupwird unter Berücksichtigung der Tatsache geschrieben, dass die Einbettung (Parameter) groß ist. Deshalb brauchen wir partition_strategy.

Aerin
quelle