结论:我的脑子坏掉了orz
起因是在逐步检查模型的时候发现这样的小模型:
1 | input = Input(shape=(5,)) |
不进行训练,而直接进行预测
1 | input = np.array([[1, 1, 2, 0, 3]]) |
得到的值是:
1 | array([[[1., 1., 1.], |
然后我就惊呆了,觉得怎么会这样,居然初始化的是输出而不是权重 Σ(°Д°;
吓得我赶紧去看了看源码,实际上初始化不管是Dense
层还是Embedding
层都是initializers.py
里完成的,拿上面用的ones
为例
1 | class Ones(Initializer): |
然后看这里面用的shape
就能证明初始化的到底是输出还是权重矩阵。这里的shape
来自embeddings.py
1 | def build(self, input_shape): |
这shape
不是妥妥的权重矩阵嘛……
然后我就只能倒回去看看之前的小模型,然后就发现权重矩阵全为 1 输出算出来就该全是 1 ……
把input = [1, 1, 2, 0, 3]
one-hot 展开一下:
1 | [ |
然后上面那个例子里的 Embedding 矩阵是这样:
1 | [ |
这两个矩阵乘一下不是全是 1 是什么orz
当然实际上 Embedding 层在实现的时候不是通过矩阵乘法,而是通过K.gather()
来实现的
1 | def call(self, inputs): |
然后K.gather()
在文档中的定义为
Retrieves the elements of indices
indices
in the tensorreference
.
源码中对应到 tensorflow 的 tf.nn.embedding_lookup(reference, indices)
,theano 和 CNTK 的实现要更复杂一点,就不贴了。
反正完成的功能就是在给定张量中检索给定下标的向量。
所以也就知道 Embedding 类不是通过将 input 转换为 one-hot 向量之后再进行向量乘法来获得输出的,而是直接检索 input 的每个值对应到 Embedding 矩阵中的向量。这也符合之前在看 Embedding 实现原理的时候提到的,向量乘法运算量太大了(矩阵很大),通过检索的方法更有效率。
所以 Embedding 权重矩阵全为 1 的时候,无论查到哪一列向量不都全是 1 嘛……
v1.5.2