ChatGPT解决这个技术问题 Extra ChatGPT

tf.nn.embedding_lookup 函数有什么作用?

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

我无法理解此功能的职责。它像查找表吗?即返回每个id对应的参数(在ids中)?

例如,在 skip-gram 模型中,如果我们使用 tf.nn.embedding_lookup(embeddings, train_inputs),那么对于每个 train_input,它会找到对应的嵌入吗?

“它像查找表吗?” tldr - 是的。对于每个 x (ids),给我关联的 y (params)。

n
nbro

是的,这个功能很难理解,直到你明白了这一点。

在最简单的形式中,它类似于 tf.gather。它根据 ids 指定的索引返回 params 的元素。

例如(假设您在 tf.InteractiveSession() 内)

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

将返回 [10 20 30 40],因为 params 的第一个元素(索引 0)是 10,params 的第二个元素(索引 1)是 20,等等。

相似地,

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

将返回 [20 20 40]

embedding_lookup 不止于此。 params 参数可以是张量的列表,而不是单个张量。

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)

在这种情况下,ids 中指定的索引根据 分区策略 对应于张量的元素,其中默认分区策略是“mod”。

在 'mod' 策略中,索引 0 对应于列表中第一个张量的第一个元素。索引 1 对应于 second 张量的 first 元素。索引 2 对应于 third 张量的 first 元素,以此类推。简单的索引 i 对应于第 (i+1) 个张量的第一个元素,对于所有索引 0..(n-1),假设 params 是 n 张量的列表。

现在,索引 n 不能对应张量 n+1,因为列表 params 仅包含 n 个张量。所以索引 n 对应于第一个张量的 second 元素。类似地,索引 n+1 对应于第二张量的第二个元素,依此类推。

所以,在代码中

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)

索引 0 对应于第一个张量的第一个元素:1

索引 1 对应于第二张量的第一个元素:10

索引 2 对应于第一个张量的第二个元素:2

索引 3 对应于第二张量的第二个元素:20

因此,结果将是:

[ 2  1  2 10  2 20]

注意:您可以使用 partition_strategy='div',会得到 [10, 1, 10, 2, 10, 20],即 id=1 是第一个参数的第二个元素。基本上:partition_strategy=mod(默认)id%len(params):params 中的参数索引 id//len(params):上述参数 partition_strategy=*div* 中元素的索引,反之亦然
@asher-stern 你能解释为什么“mod”策略是默认的吗?似乎“div”策略更类似于标准张量切片(通过给定索引选择行)。在“div”的情况下是否存在一些性能问题?
n
nbro

embedding_lookup 函数检索 params 张量的行。该行为类似于在 numpy 中对数组使用索引。例如

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 参数也可以是张量列表,在这种情况下 ids 将分布在张量中。例如,给定一个包含 3 个张量 [2, 64] 的列表,默认行为是它们将表示 ids[0, 3][1, 4][2, 5]

partition_strategy 控制 ids 在列表中的分布方式。当矩阵可能太大而无法保持在一块时,分区对于更大规模的问题很有用。


为什么他们会这样称呼它而不是 select_rows
@LenarHoyt,因为这种查找的想法来自词嵌入。并且“行”是单词的表示(嵌入)到向量空间中-并且在它们本身中很有用。通常比实际的网络更重要。
tensorflow是如何学习embedding结构的?此功能是否也管理该过程?
@vgoklani,不,embedding_lookup 只是提供了一种方便(且并行)的方式来检索与 ids 中的 id 对应的嵌入。 params 张量通常是作为训练过程的一部分学习的 tf 变量 - 一个 tf 变量,其组件直接或间接用于由优化器优化的损失函数(例如 tf.l2_loss) (例如 tf.train.AdamOptimizer)。
@Rafał Józefowicz 为什么“默认行为是它们将代表 id:[0, 3], [1, 4], [2, 5]。”?你能解释一下吗?
C
Community

是的,tf.nn.embedding_lookup() 函数的目的是在 嵌入矩阵 中执行 查找 并返回单词的嵌入(或简单地说是向量表示)。

一个简单的嵌入矩阵(形状:vocabulary_size x embedding_dimension)如下所示。 (即每个 word 将由一个 vector 数字表示;因此名称为 word2vec

嵌入矩阵

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 

我拆分了上面的嵌入矩阵并只加载了 vocab 中的 words,这将是我们的词汇表和 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)

在 TensorFlow 中嵌入查找

现在我们将看到如何对一些任意输入句子执行嵌入查找。

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)

观察我们如何使用词汇表中的单词索引从原始嵌入矩阵(带有单词)中获得嵌入。

通常,这样的嵌入查找由第一层(称为嵌入层)执行,然后将这些嵌入传递给 RNN/LSTM/GRU 层进行进一步处理。

旁注:通常词汇表也会有一个特殊的 unk 标记。因此,如果我们的词汇表中不存在来自输入句子的标记,则将在嵌入矩阵中查找对应于 unk 的索引。

PS 请注意,embedding_dimension 是一个必须针对其应用程序进行调整的超参数,但 Word2VecGloVe 等流行模型使用300 表示每个单词的维度向量。

额外阅读 word2vec skip-gram model


F
Fabian N.

这是描述嵌入查找过程的图像。

https://i.stack.imgur.com/5wFii.jpg

定义占位符lookup_ids = tf.placeholder([10]) 定义嵌入层embeddings = tf.Variable([100,10],...) 定义tensorflow操作 embed_lookup = tf.embedding_lookup(embeddings,lookup_ids) 得到结果通过运行 lookup = session.run(embed_lookup, feed_dict={lookup_ids:[95,4,14]})


Y
Yan Zhao

当 params 张量为高维度时,ids 仅指最高维度。也许对大多数人来说这很明显,但我必须运行以下代码才能理解:

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)

只是尝试“div”策略,对于一个张量,它没有任何区别。

这是输出:

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

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

 [[11 11]
  [12 12]
  [13 13]
  [14 14]]]

S
Shanmugam Ramasamy

另一种看待它的方法是,假设您将张量展平为一维数组,然后您正在执行查找

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

展平的张量如下 [1,4,7,2,5,8,3,6,9]

现在,当您查找 [0,3,4,1,7] 时,它会得到 [1,2,5,4,6]

(i,e) 例如,如果查找值为 7,并且我们有 3 个张量(或具有 3 行的张量),那么,

7 / 3 :(提醒为 1,商为 2)因此将显示 Tensor1 的第二个元素,即 6


j
joaoaccarvalho

因为我也对这个功能很感兴趣,所以我会给我两分钱。

我在 2D 情况下看到它的方式就像矩阵乘法一样(很容易推广到其他维度)。

考虑一个包含 N 个符号的词汇表。然后,您可以将符号 x 表示为维度为 Nx1 的向量,单热编码。

但是你想要这个符号的表示不是作为 Nx1 的向量,而是作为一个维度为 Mx1 的向量,称为 y。

因此,要将 x 转换为 y,您可以使用和嵌入矩阵 E,尺寸为 MxN:

y = E x。

这本质上就是 tf.nn.embedding_lookup(params, ids, ...) 正在做的事情,其中 ids 只是一个数字,它表示 1 在 one-hot-encoded 向量 x 中的位置。


a
aerin

添加到 Asher Stern 的答案中,params 被解释为大型嵌入张量的分区。它可以是表示完整嵌入张量的单个张量,也可以是除第一个维度外所有形状相同的 X 张量列表,表示分片嵌入张量。

函数 tf.nn.embedding_lookup 是考虑到嵌入(参数)很大的事实而编写的。因此我们需要partition_strategy