neural - tensorflow python
¿Qué hace la función tf.nn.embedding_lookup? (8)
tf.nn.embedding_lookup(params, ids, partition_strategy=''mod'', name=None)
No puedo entender el deber de esta función. ¿Es como una tabla de búsqueda? ¿Qué significa devolver los parámetros correspondientes a cada id (en ids)?
Por ejemplo, en el modelo de
skip-gram
si utilizamos
tf.nn.embedding_lookup(embeddings, train_inputs)
, ¿para cada
train_input
encuentra la incrustación correspondiente?
Además de la respuesta de Asher Stern,
params
se interpreta como una
partición
de un gran tensor de inserción.
Puede ser un solo tensor que represente el tensor de incrustación completo, o una lista de tensores X todos de la misma forma, excepto la primera dimensión, que representa los tensores de incrustación fragmentados.
La función
tf.nn.embedding_lookup
está escrita considerando el hecho de que la incrustación (params) será grande.
Por lo tanto, necesitamos la
partition_strategy
.
Aquí hay una imagen que representa el proceso de incrustación de búsqueda.
De forma concisa, obtiene las filas correspondientes de una capa de incrustación, especificada por una lista de ID y la proporciona como tensor.
Se logra a través del siguiente proceso.
-
Definir un marcador de posición
lookup_ids = tf.placeholder([10])
-
Definir una capa de
embeddings = tf.Variable([100,10],...)
-
Defina la operación de
embed_lookup = tf.embedding_lookup(embeddings, lookup_ids)
-
Obtenga los resultados ejecutando
lookup = session.run(embed_lookup, feed_dict={lookup_ids:[95,4,14]})
Como también estaba intrigada por esta función, daré mis dos centavos.
La forma en que lo veo en el caso 2D es solo como una multiplicación matricial (es fácil generalizar a otras dimensiones).
Considere un vocabulario con N símbolos. Luego, puede representar un símbolo x como un vector de dimensiones Nx1, codificado en caliente.
Pero desea una representación de este símbolo no como un vector de Nx1, sino como uno con dimensiones Mx1, llamado y .
Entonces, para transformar x en y , puede usar e incrustar la matriz E , con dimensiones MxN:
y = E x .
Esto es esencialmente lo que está haciendo tf.nn.embedding_lookup (params, ids, ...), con el matiz de que los identificadores son solo un número que representa la posición del 1 en el vector x codificado en caliente.
Cuando el tensor de parámetros está en dimensiones altas, los identificadores solo se refieren a la dimensión superior. Tal vez sea obvio para la mayoría de las personas, pero tengo que ejecutar el siguiente código para entender eso:
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)
Simplemente probando la estrategia ''div'' y para un tensor, no hay diferencia.
Aquí está la salida:
[[[ 1 1]
[ 2 2]
[ 3 3]
[ 4 4]]
[[21 21]
[22 22]
[23 23]
[24 24]]
[[11 11]
[12 12]
[13 13]
[14 14]]]
Otra forma de verlo es, suponga que aplana los tensores a una matriz unidimensional, y luego está realizando una búsqueda
(p. ej.) Tensor0 = [1,2,3], Tensor1 = [4,5,6], Tensor2 = [7,8,9]
El tensor aplanado será el siguiente [1,4,7,2,5,8,3,6,9]
Ahora, cuando realices una búsqueda de [0,3,4,1,7], aparecerá [1,2,5,4,6]
(i, e) si el valor de búsqueda es 7, por ejemplo, y tenemos 3 tensores (o un tensor con 3 filas), entonces,
7/3: (el recordatorio es 1, el cociente es 2) Entonces se mostrará el segundo elemento del Tensor1, que es 6
Sí, el propósito de la función
tf.nn.embedding_lookup()
es realizar una
búsqueda
en la
matriz de incrustación
y devolver las incrustaciones (o en términos simples la representación vectorial) de las palabras.
Una matriz de incrustación simple (de forma:
vocabulary_size x embedding_dimension
) se vería a continuación.
(es decir, cada
palabra
estará representada por un
vector
de números; de ahí el nombre
word2vec
)
Matriz de incrustación
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
Dividí la matriz de incrustación anterior y cargué solo las
palabras
en
vocab
que serán nuestro vocabulario y los vectores correspondientes en la matriz de incrustación.
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)
Búsqueda de incrustación en TensorFlow
Ahora veremos cómo podemos realizar búsquedas de incrustación para alguna oración de entrada arbitraria.
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)
Observe cómo obtuvimos las incrustaciones de nuestra matriz de incrustación original (con palabras) utilizando los índices de palabras en nuestro vocabulario.
Por lo general, dicha búsqueda de incrustación es realizada por la primera capa (llamada capa de incrustación ) que luego pasa estas incrustaciones a las capas RNN / LSTM / GRU para su posterior procesamiento.
Nota al
margen: por lo general, el vocabulario también tendrá un token especial de
unk
.
Entonces, si un token de nuestra oración de entrada no está presente en nuestro vocabulario, entonces el índice correspondiente a
unk
buscará en la matriz de incrustación.
PD
Tenga en cuenta que
embedding_dimension
es un hiperparámetro que uno tiene que ajustar para su aplicación, pero los modelos populares como
Word2Vec
y
GloVe
usan un vector de
300
dimensiones para representar cada palabra.
Sí, esta función es difícil de entender, hasta que entienda el punto.
En su forma más simple, es similar a
tf.gather
.
Devuelve los elementos de los
params
acuerdo con los índices especificados por los
ids
.
Por ejemplo (suponiendo que está dentro de
tf.InteractiveSession()
)
params = tf.constant([10,20,30,40])
ids = tf.constant([0,1,2,3])
print tf.nn.embedding_lookup(params,ids).eval()
devolvería
[10 20 30 40]
, porque el primer elemento (índice 0) de los parámetros es
10
, el segundo elemento de los parámetros (índice 1) es
20
, etc.
Similar,
params = tf.constant([10,20,30,40])
ids = tf.constant([1,1,3])
print tf.nn.embedding_lookup(params,ids).eval()
volvería
[20 20 40]
.
Pero
embedding_lookup
es más que eso.
El argumento
params
puede ser una
lista
de tensores, en lugar de un solo 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)
En tal caso, los índices, especificados en
ids
, corresponden a elementos de tensores de acuerdo con una
estrategia de partición
, donde la estrategia de partición predeterminada es ''mod''.
En la estrategia ''mod'', el índice 0 corresponde al primer elemento del primer tensor de la lista.
El índice 1 corresponde al
primer
elemento del
segundo
tensor.
El índice 2 corresponde al
primer
elemento del
tercer
tensor, y así sucesivamente.
Simplemente el índice
i
corresponde al primer elemento del tensor (i + 1) th, para todos los índices
0..(n-1)
, suponiendo que params es una lista de
n
tensores.
Ahora, el índice
n
no puede corresponder al tensor n + 1, porque los
params
la lista contienen solo
n
tensores.
Entonces el índice
n
corresponde al
segundo
elemento del primer tensor.
Del mismo modo, el índice
n+1
corresponde al segundo elemento del segundo tensor, etc.
Entonces, en el código
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)
El índice 0 corresponde al primer elemento del primer tensor: 1
El índice 1 corresponde al primer elemento del segundo tensor: 10
El índice 2 corresponde al segundo elemento del primer tensor: 2
El índice 3 corresponde al segundo elemento del segundo tensor: 20
Por lo tanto, el resultado sería:
[ 2 1 2 10 2 20]
embedding_lookup
función
embedding_lookup
recupera filas del tensor de
params
.
El comportamiento es similar al uso de indexación con matrices en numpy.
P.ej
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]
params
argumento
params
también puede ser una lista de tensores, en cuyo caso los
ids
se distribuirán entre los tensores.
Por ejemplo, dada una lista de 3 tensores
[2, 64]
, el comportamiento predeterminado es que representarán
ids
:
[0, 3]
,
[1, 4]
,
[2, 5]
.
partition_strategy
controla la forma en que se distribuyen los
ids
entre la lista.
La partición es útil para problemas de mayor escala cuando la matriz puede ser demasiado grande para mantenerla en una sola pieza.