web-dev-qa-db-fra.com

Que fait la fonction tf.nn.embedding_lookup?

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

Je ne peux pas comprendre le devoir de cette fonction. Est-ce comme une table de consultation? Que signifie renvoyer les paramètres correspondant à chaque id (en ids)?

Par exemple, dans le modèle skip-gram si nous utilisons tf.nn.embedding_lookup(embeddings, train_inputs), alors pour chaque train_input, il trouve l’incorporation correspondante?

123
Poorya Pzm

La fonction embedding_lookup récupère les lignes du tenseur params. Le comportement est similaire à l'utilisation de l'indexation avec des tableaux dans numpy. Par exemple.

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] 

L'argument params peut également être une liste de tenseurs, auquel cas la ids sera répartie entre les tenseurs. Par exemple, avec une liste de 3 tenseurs [2, 64], le comportement par défaut est qu'ils représenteront ids: [0, 3], [1, 4], [2, 5]

partition_strategy contrôle la façon dont la variable ids est répartie dans la liste. Le partitionnement est utile pour les problèmes à grande échelle lorsque la matrice peut être trop volumineuse pour rester en un seul morceau.

127
Rafał Józefowicz

Oui, cette fonction est difficile à comprendre tant que vous n’avez pas compris.

Dans sa forme la plus simple, il ressemble à tf.gather. Il retourne les éléments de params en fonction des index spécifiés par ids.

Par exemple (en supposant que vous êtes dans tf.InteractiveSession())

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

renverrait [10 20 30 40], car le premier élément (index 0) de params est 10, le deuxième élément de params (index 1) est 20, etc.

De même, 

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

renverrait [20 20 40].

Mais embedding_lookup est plus que cela. L'argument params peut être un liste de tenseurs plutôt qu'un seul tenseur.

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)

Dans un tel cas, les index, spécifiés dans ids, correspondent à des éléments de tenseurs selon une stratégie de partition, où la stratégie de partition par défaut est 'mod'.

Dans la stratégie "mod", l'index 0 correspond au premier élément du premier tenseur de la liste. L'indice 1 correspond à l'élément premier du tenseur deuxième. L'indice 2 correspond à l'élément premier du tenseur troisième, etc. Simplement index i correspond au premier élément du (i + 1) ème tenseur, pour tous les index 0..(n-1), en supposant que params est une liste de n tenseurs.

Maintenant, index n ne peut pas correspondre au tenseur n + 1, car la liste params ne contient que n tenseurs. Donc, index n correspond à l’élément second du premier tenseur. De même, index n+1 correspond au deuxième élément du deuxième tenseur, etc.

Donc, dans le 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)

l'indice 0 correspond au premier élément du premier tenseur: 1

l'indice 1 correspond au premier élément du deuxième tenseur: 10

l'indice 2 correspond au deuxième élément du premier tenseur: 2

l'indice 3 correspond au deuxième élément du deuxième tenseur: 20

Ainsi, le résultat serait:

[ 2  1  2 10  2 20]
193
Asher Stern

Oui, le but de la fonction tf.nn.embedding_lookup() est d'exécuter une recherche dans la matrice d'intégration et de renvoyer les imbrications (ou, en termes simples, la représentation vectorielle) mots.

Une matrice d’incorporation simple (de forme: vocabulary_size x embedding_dimension) ressemblerait à celle ci-dessous. (c'est-à-dire que chaque Word sera représenté par un vecteur de nombres; d'où le nom Word2vec)


Matrice d'intégration

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 

J'ai scindé la matrice d'intégration ci-dessus et chargé uniquement les mots dans vocab qui seront notre vocabulaire et les vecteurs correspondants dans le tableau emb.

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)

Recherche de l'intégration dans TensorFlow

Nous allons maintenant voir comment effectuer recherche d'intégration pour certaines phrases d'entrée arbitraires.

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)

Observez comment nous avons obtenu les incorporations de notre matrice d'intégration originale (avec des mots) en utilisant les indices de mots de notre vocabulaire.

Habituellement, une telle recherche d’incorporation est effectuée par la première couche (appelée couche d’incorporation), qui transmet ensuite ces incorporations aux couches RNN/LSTM/GRU pour un traitement ultérieur.


Note latérale _: le vocabulaire comporte généralement un jeton unk spécial. Ainsi, si un jeton de notre phrase d'entrée n'est pas présent dans notre vocabulaire, l'index correspondant à unk sera recherché dans la matrice d'intégration.


P.S. Notez que embedding_dimension est un hyperparamètre que vous devez ajuster pour leur application, mais des modèles populaires tels que Word2Vec et - GloVe utilisent le vecteur dimension 300 pour représenter chaque mot.

Lecture en bonusModèle de saut de gramme Word2vec

32
kmario23

Voici une image décrivant le processus d'intégration de la recherche. 

Image: Embedding lookup process

De manière concise, il obtient les lignes correspondantes d’un calque d’incorporation, spécifiées par une liste d’ID et les fournit en tant que tenseur. Ceci est réalisé par le processus suivant.

  1. Définir un espace réservé lookup_ids = tf.placeholder([10])
  2. Définir une couche d'intégration embeddings = tf.Variable([100,10],...)
  3. Définir le fonctionnement du tensorflow embed_lookup = tf.embedding_lookup(embeddings, lookup_ids)
  4. Obtenez les résultats en exécutant lookup = session.run(embed_lookup, feed_dict={lookup_ids:[95,4,14]})
7
thushv89

Lorsque le tenseur de paramètres est dans des dimensions élevées, les ID ne font référence qu'à la dimension supérieure. C'est peut-être évident pour la plupart des gens, mais je dois exécuter le code suivant pour comprendre cela:

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)

Essayer simplement la stratégie "div" et pour un tenseur, cela ne fait aucune différence.

Voici la sortie:

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

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

 [[11 11]
  [12 12]
  [13 13]
  [14 14]]]
4
Yan Zhao

Une autre façon de voir les choses est de supposer que vous aplatissez les tenseurs en un tableau à une dimension, puis que vous effectuez une recherche.

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

Le tenseur aplati sera comme suit [1,4,7,2,5,8,3,6,9]

Maintenant, lorsque vous recherchez [0,3,4,1,7], vous obtiendrez [1,2,5,4,6]

(i, e) si la valeur de recherche est 7 par exemple, et que nous avons 3 tenseurs (ou un tenseur à 3 lignes), alors, 

7/3: (Le rappel est 1, le quotient est 2) Donc le 2ème élément de Tensor1 sera montré, qui est 6 

3
Shanmugam Ramasamy

Puisque cette fonction m'a également intriguée, je vais donner deux centimes.

La façon dont je le vois dans le cas 2D est juste comme une multiplication matricielle (il est facile de généraliser à d'autres dimensions).

Considérons un vocabulaire avec N symboles . Ensuite, vous pouvez représenter un symbole x sous la forme d'un vecteur de dimensions Nx1, codé à chaud.

Mais vous voulez une représentation de ce symbole non pas en tant que vecteur de Nx1, mais en tant que vecteur de dimension Mx1, appelé y.

Ainsi, pour transformer x en y, vous pouvez utiliser et incorporer la matrice E, aux dimensions MxN: 

y = Ex.

C'est essentiellement ce que fait tf.nn.embedding_lookup (params, ids, ...), avec la nuance que ids ne sont qu'un nombre qui représente la position du 1 dans le vecteur codé à chaud X.

1
joaoaccarvalho

Ajoutant à la réponse de Asher Stern, params est interprété comme un partitionnement d'un grand tenseur d'inclusion. Il peut s'agir d'un seul tenseur représentant le tenseur d'inclusion complet, ou une liste de X tenseurs ayant tous la même forme, à l'exception de la première dimension, représentant des tenseurs d'inclusion fragmentés. 

La fonction tf.nn.embedding_lookup est écrite en tenant compte du fait que l’incorporation (params) sera grande. Nous avons donc besoin de partition_strategy.

0
Aerin