tf.nn.embedding_lookup_sparse(
    params,
    sp_ids,
    sp_weights,
    partition_strategy='mod',
    name=None,
    combiner=None,
    max_norm=None
)



主要的作用是接收一个稀疏矩阵,返回一个embedding,这个embedding是在params表中查找到的embedding加上权重以后的结果。

  • params参数在前面tf.nn.embedding_lookup中已经有说明
  • sp_ids接收一个tf.SparseTensor类,在前面tf.SparseTensor中有详细说明
  • sp_weights为每个值的权重,None时默认为1
  • partition_strategy暂时没弄明白什么意思
  • max_norm暂时没弄明白什么意思
  • combiner是每个embedding查找后合并的方式,默认为求sqrtn,sqrtn的求法会在例子中有详细说明。那我们快来看下例子吧!

例子1: 权重为None,也就是默认每个value的权重都为1。权重也是一个SparseTensor类,indices和dense_shape都和ids一样,只有values不一样,但values的大小和ids是一一对应的



import tensorflow as tf

x = tf.sparse_placeholder(tf.float32)

params = tf.constant([[0.1, 0.4, 0.5, 7.0, 6.4, 1.2, 0.5, 0.3, 3.3, 2.0],
                      [0.3, 0.4, 0.9, 0.8, 0.5, 0.3, 0.7, 0.5, 0.8, 3.2],
                      [0.4, 0.9, 1.1, 4.3, 3.4, 0.2, 0.3, 0.2, 0.5, 0.1]])

ids = tf.SparseTensor(indices=[[0, 1],
                               [0, 3],
                               [1, 2],
                               [1, 3]],
                      values=[2, 1, 1, 1],
                      dense_shape=[2, 4])

with tf.Session() as sess:
    lookup = sess.run(
        tf.nn.embedding_lookup_sparse(params, ids, None,
                                      partition_strategy="div"))

print(lookup)



首先看看我们的ids长什么样子:



[
 [0, 2, 0, 1]
 [0, 0, 1, 1]
]



一个2*4的矩阵。在tf.nn.embedding_lookup中我也举的是同样的数据例子。

weights长什么样子(默认为None时):



[
 [0, 1, 0, 1]
 [0, 0, 1, 1]
]



重点来了:

tf.nn.embedding_lookup中该矩阵的0也是需要从params中找索引的,但是这里,这个矩阵的0是没有实际意义的,不能拿来找索引!也就是说我们第一行只找id为2和id为1的embedding,第二行只找id为1和id为1的embedding。找到的结果如下:(代码中打印不方便,这里手动写一下)



[
  [
    [0.4, 0.9, 1.1, 4.3, 3.4, 0.2, 0.3, 0.2, 0.5, 0.1]
    [0.3, 0.4, 0.9, 0.8, 0.5, 0.3, 0.7, 0.5, 0.8, 3.2]
  ]
  [
    [0.1, 0.4, 0.5, 7.0, 6.4, 1.2, 0.5, 0.3, 3.3, 2.0]
    [0.1, 0.4, 0.5, 7.0, 6.4, 1.2, 0.5, 0.3, 3.3, 2.0]
  ]
]



先来说说sqrtn的算法

(ps:公式是我自己写的,算法也是自己揣摩的,如果错误欢迎指正。算法揣摩的依据来源于源代码注释内mean模式的算法,还有程序输出的结果对比这两个参考):





什么意思呢?

我们来拿原矩阵的第一行[0,2,0,1]来详细举例:

  1. 用id在params中找到对应的embedding

[0,2,0,1]对应着:



[
  [0.4, 0.9, 1.1, 4.3, 3.4, 0.2, 0.3, 0.2, 0.5, 0.1]
  [0.3, 0.4, 0.9, 0.8, 0.5, 0.3, 0.7, 0.5, 0.8, 3.2]
]



而[0,2,0,1]中,id=2的权重取默认值1,id=1的权重也取默认值为1,所以在计算输出的embedding时,两行都对应乘1即可。

2. 分别给每一个乘上它自己对应的权重



[
  [0.4*1, 0.9*1, 1.1*1, 4.3*1, 3.4*1, 0.2*1, 0.3*1, 0.2*1, 0.5*1, 0.1*1]
  [0.3*1, 0.4*1, 0.9*1, 0.8*1, 0.5*1, 0.3*1, 0.7*1, 0.5*1, 0.8*1, 3.2*1]
]



3. 把结果对应位置加起来,这个作为分子



[
  [0.7, 1.3, 2.0, 5.1, 3.9, 0.5, 1.0, 0.7, 1.3, 3.3]
]



4. 计算分母,各自的权重平方后取平方根,再求和。

(这里跟官方注释中解释的不一样。官方注释中所写的是权重平方先求和再取平方根,但是从代码输出结果来看这样的算法并不正确)

这里按照我自己的方式计算为:





5. 用第3步的结果除以第四步的结果得到



[
  [0.35,0.65,1.0,2.55,1.95,0.25,0.5,0.35,0.65,1.65]
]



这是原矩阵的第一行[0,2,0,1]的最终输出的embedding的结果。第二行同理,这里不再赘述。

代码如下(和前面开篇的代码段一样):



import tensorflow as tf

x = tf.sparse_placeholder(tf.float32)

params = tf.constant([[0.1, 0.4, 0.5, 7.0, 6.4, 1.2, 0.5, 0.3, 3.3, 2.0],
                      [0.3, 0.4, 0.9, 0.8, 0.5, 0.3, 0.7, 0.5, 0.8, 3.2],
                      [0.4, 0.9, 1.1, 4.3, 3.4, 0.2, 0.3, 0.2, 0.5, 0.1]])

ids = tf.SparseTensor(indices=[[0, 1],
                               [0, 3],
                               [1, 2],
                               [1, 3]],
                      values=[2, 1, 1, 1],
                      dense_shape=[2, 4])

with tf.Session() as sess:
    lookup = sess.run(
        tf.nn.embedding_lookup_sparse(params, ids, None,
                                      partition_strategy="div"))

print(lookup)



结果:




GCN的权重矩阵 权重矩阵计算_默认值


例子2: 自己设置权重参数再来算一遍

现在设置权重参数为[3,4,0.5,2]。需要注意有多少个values就要设置多少个权重,这两个是一一对应的。

weights长什么样子(自己配的):


[
 [0, 3, 0, 4]
 [0, 0, 0.5, 2]
]


还是拿上面的例子来算:

  1. 用id在params中找到对应的embedding

[0,2,0,1]对应着:


[
  [0.4, 0.9, 1.1, 4.3, 3.4, 0.2, 0.3, 0.2, 0.5, 0.1]
  [0.3, 0.4, 0.9, 0.8, 0.5, 0.3, 0.7, 0.5, 0.8, 3.2]
]


而[0,2,0,1]中,id=2的权重取默认值1,id=1的权重也取默认值为1,所以在计算输出的embedding时,两行都对应乘1即可。

2. 分别给每一个乘上它自己对应的权重,注意这里权重改变了,一个是3一个是4


[
  [0.4*3, 0.9*3, 1.1*3, 4.3*3, 3.4*3, 0.2*3, 0.3*3, 0.2*3, 0.5*3, 0.1*3]
  [0.3*4, 0.4*4, 0.9*4, 0.8*4, 0.5*4, 0.3*4, 0.7*4, 0.5*4, 0.8*4, 3.2*4]
]


3. 把结果对应位置加起来,这个作为分子


[
  [2.4, 4.3, 6.9, 16.1, 12.2, 1。8, 1.0, 0.7, 1.3, 3.3]
]


4. 计算分母,各自的权重平方后取平方根,再求和。

(这里跟官方注释中解释的不一样。官方注释中所写的是权重平方先求和再取平方根,但是从代码输出结果来看这样的算法并不正确)

这里按照我自己的方式计算为:


5. 用第3步的结果除以第四步的结果得到


[
  [0.34285715,0.61428565,0.9857143,2.3,1.7428572,0.25714287,0.5285714,0.37142855,
   0.67142856,1.8714286]
]


这是原矩阵的第一行[0,2,0,1]的最终输出的embedding的结果。第二行同理,这里不再赘述。

代码如下:


import tensorflow as tf

x = tf.sparse_placeholder(tf.float32)

params = tf.constant([[0.1, 0.4, 0.5, 7.0, 6.4, 1.2, 0.5, 0.3, 3.3, 2.0],
                      [0.3, 0.4, 0.9, 0.8, 0.5, 0.3, 0.7, 0.5, 0.8, 3.2],
                      [0.4, 0.9, 1.1, 4.3, 3.4, 0.2, 0.3, 0.2, 0.5, 0.1]])

ids = tf.SparseTensor(indices=[[0, 1],
                               [0, 3],
                               [1, 2],
                               [1, 3]],
                      values=[2, 1, 1, 1],
                      dense_shape=[2, 4])
sp_weights = tf.SparseTensor(indices=[[0, 1],
                                      [0, 3],
                                      [1, 2],
                                      [1, 3]],
                             values=[3, 4, 0.5, 2],
                             dense_shape=[2, 4])

with tf.Session() as sess:
    lookup = sess.run(
        tf.nn.embedding_lookup_sparse(params, ids, sp_weights,
                                      partition_strategy="div"))

print(lookup)


结果如下:


GCN的权重矩阵 权重矩阵计算_tf计算矩阵维度_02


最后

combiner方式求mean和求sum都比较简单,注意加权求和,加权求平均就可以了,不再说明了。