What does tf.nn.embedding_lookup function do?

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

I cannot understand the duty of this function. Is it like a lookup table? Which means to return the parameters corresponding to each id (in ids)?

For instance, in the skip-gram model if we use tf.nn.embedding_lookup(embeddings, train_inputs), then for each train_input it finds the correspond embedding?

76050 次浏览

函数检索 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在列表中的分布方式。当矩阵可能太大而不能保持一个整体时,这种划分对于较大规模的问题是有用的。

是的,这个函数很难理解,直到你得到点。

在最简单的形式中,它类似于 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对应于 第二张量的 第一元素。索引2对应于 第三张量的 第一元素,依此类推。简单地索引 i对应于(i + 1) th 张量的第一个元素,对于所有索引 0..(n-1),假设 params 是 n张量列表。

现在,索引 n不能对应于张量 n + 1,因为列表 params只包含 n张量。所以指数 n对应于第一张量的 第二元素。类似地,指数 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]

再加上亚瑟 · 斯特恩的回答, params是 解释为一个大的嵌入张量的 分区。它可以是代表完全嵌入张量的单张量, 或者除了第一维以外的所有形状相同的 X 张量的列表, 表示分片嵌入张量。

编写函数 tf.nn.embedding_lookup时考虑到了嵌入(params)很大的事实,因此我们需要 partition_strategy

另一种看待它的方式是,假设将张量平坦化为一维数组,然后执行查找

张量0 = [1,2,3] ,张量1 = [4,5,6] ,张量2 = [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)所以张量1的第二个元素会显示出来,也就是6

当参数张量处于高维时,本征值仅指顶维。也许对大多数人来说这是显而易见的,但我必须运行以下代码才能理解:

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]]]

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

一个简单的嵌入矩阵(形状: vocabulary_size x embedding_dimension)如下所示。(即每个 将由一个由数字组成的 矢量表示,因此称为 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中的 文字,它将是我们的词汇表和 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)

在张量流中嵌入查找

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

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对应的索引。


请注意,embedding_dimension是一个超参数,人们必须为其应用程序进行调优,但是像 一个 href = “ https://en.wikipedia.org/wiki/Word2Vec”rel = “ noReferrer”> Word2Vec 翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳这样的流行模型使用 300维向量来表示每个单词。

额外阅读 < a href = “ http://mccormickml.com/2016/04/19/word2vec-lessons-the-Skip-gram-model/”rel = “ noReferrer”> word2vec Skip-gram 模型

既然我对这个函数也很感兴趣,我就说说我的看法吧。

在我看来,二维情况就像一个矩阵乘法(很容易推广到其他维度)。

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

但是您希望这个符号的表示不是作为 Nx1的矢量,而是作为一个维数为 Mx1的矢量,称为 < em > y

因此,要将 < em > x 转换为 < em > y ,可以使用并嵌入维数为 MxN 的矩阵 E:

Y = E < em > x .

这实际上就是 tf.nn. 嵌入 _ lookup (params,ids,...)正在做的事情,其中的微妙之處在于,身份证只是一个数字,表示1在一个热编码的向量 < em > x 中的位置。

下面是一个描述嵌入查找过程的图像。

Image: Embedding lookup process

简而言之,它获取嵌入层的相应行,由 ID 列表指定,并将其作为张量提供。它是通过以下过程实现的。

  1. 定义占位符 lookup_ids = tf.placeholder([10])
  2. 定义一个嵌入层 embeddings = tf.Variable([100,10],...)
  3. 定义张量流操作 embed_lookup = tf.embedding_lookup(embeddings, lookup_ids)
  4. 通过运行 lookup = session.run(embed_lookup, feed_dict={lookup_ids:[95,4,14]})获得结果

现有的解释是不够的。 该函数的主要目的是有效地检索给定词索引序列中每个词的向量。假设我们有以下嵌入矩阵:

embds = np.array([[0.2, 0.32,0.9],
[0.8, 0.62,0.19],
[0.0, -0.22,-1.9],
[1.2, 2.32,6.0],
[0.11, 0.10,5.9]])

假设我们有以下单词索引序列:

data=[[0,1],
[3,4]]

现在为了得到数据中每个单词的相应嵌入:

tf.nn.embedding_lookup(
embds, data
)

出去:

array([[[0.2 , 0.32, 0.9 ],
[0.8 , 0.62, 0.19]],


[[1.2 , 2.32, 6.  ],
[0.11, 0.1 , 5.9 ]]])>

注意 如果 embds 不是一个数组或张量,那么输出将不会是这样(我就不详细介绍了)。例如,如果 embds 是一个列表,那么输出将是:

array([[0.2 , 0.32],
[0.8 , 0.62]], dtype=float32)>