如果输入值大于 input_dim,keras 嵌入层如何工作?

How does keras Embedding layer works if input value greater than input_dim?

如果输入值大于 input_dim,嵌入层如何工作?

为什么 keras 不引发异常?

from keras.models import Sequential
from keras.layers import Embedding

model = Sequential()
model.add(Embedding(1, 2, trainable=True, mask_zero=False))
input_array = [5]

model.compile("rmsprop", "mse")

output_array = model.predict(input_array)

outpur_array
#array([[[0., 0.]]], dtype=float32)

输入值 = 5 input_dim = 1

文档说明输入值 (5) 必须小于 input_dim(1)。在我的示例中它是错误的,但代码仍然没有引发异常

谢谢!

Embeding层就是一个Dense层,没什么问题。您只是在对数据执行简单的线性或仿射变换。 input/output 维度是任意的,使用较低的输出维度在实践中更常见的原因在于高维数据点通常在其各自的输入维度中具有较低维度的流形,大部分有价值的信息都在于那个多方面的。这是 PCA 或任何其他降维算法背后的几何思想。

相比之下,过完备自动编码器是实际案例的一个很好的例子,其中需要更高的输出维度,并且模型被迫通过其他方式学习有用的表示,而不是通过投影到较低维度的子空间,通过稀疏性和套索正则化等约束例如。

嵌入层使用形状为 (input_dim, output_dim) 的查找矩阵。其中要学习的输入暗数嵌入向量。当我传递索引时,图层通过嵌入矩阵中的索引获取向量。

感谢您指出我对 input_length 和 input_dim 感到困惑。

首先,如果使用tensorflow.keras会出错。

tensorflow

from tensorflow.keras.models import Model
from tensorflow.keras.layers import Embedding, Input
import numpy as np

ip = Input(shape = (3,))
emb = Embedding(1, 2, trainable=True, mask_zero=True)(ip)

model = Model(ip, emb)
input_array = np.array([[5, 3, 1], [1, 2, 3]])

model.compile("rmsprop", "mse")

output_array = model.predict(input_array)

print(output_array)

print(output_array.shape)

model.summary()

但是如果我使用 keras 2.3.1,我不会收到任何错误。

喀拉斯 2.3.1

from keras.models import Model
from keras.layers import Embedding, Input
import numpy as np

ip = Input(shape = (3,))
emb = Embedding(1, 2, trainable=True, mask_zero=True)(ip)

model = Model(ip, emb)
input_array = np.array([[5, 3, 1], [1, 2, 3]])

model.compile("rmsprop", "mse")

output_array = model.predict(input_array)

print(output_array)

print(output_array.shape)

model.summary()

那么,keras坏了吗?首先要注意的是 keras 和 tensorflow.keras 有不同的嵌入层实现。为了验证这一点,让我们转到 keras 嵌入层。

https://github.com/keras-team/keras/blob/master/keras/layers/embeddings.py#L16

现在让我们看看调用函数。

    def call(self, inputs):
        if K.dtype(inputs) != 'int32':
            inputs = K.cast(inputs, 'int32')
        out = K.gather(self.embeddings, inputs)
        return out

N.B:如果您想要 keras 2.3.1 的确切源代码,请转到此处下载源代码:https://github.com/keras-team/keras/releases

但是如果我们去tensorflow实现,就不一样了。

https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/ops/embedding_ops.py

只是为了验证,调用函数的写法不同。

  def call(self, inputs):
    dtype = K.dtype(inputs)
    if dtype != 'int32' and dtype != 'int64':
      inputs = math_ops.cast(inputs, 'int32')
    out = embedding_ops.embedding_lookup(self.embeddings, inputs)
    return out

现在,我们可以更深入地挖掘以找到不同的行为并查明 keras 不会抛出错误而 tensorflow.keras 会抛出错误的来源,但让我们简单说明一下。 keras嵌入层是不是做错了什么?

让我们像以前一样设计一个简单的网络并观察权重矩阵。

from keras.models import Model
from keras.layers import Embedding, Input
import numpy as np

ip = Input(shape = (3,))
emb = Embedding(1, 2, trainable=True, mask_zero=True)(ip)

model = Model(ip, emb)
input_array = np.array([[5, 3, 1], [1, 2, 3]])

model.compile("rmsprop", "mse")

output_array = model.predict(input_array)

print(output_array)

print(output_array.shape)

model.summary()

模型给出以下输出。

[[[0. 0.]
  [0. 0.]
  [0. 0.]]

 [[0. 0.]
  [0. 0.]
  [0. 0.]]]
(2, 3, 2)
Model: "model_18"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_21 (InputLayer)        (None, 3)                 0         
_________________________________________________________________
embedding_33 (Embedding)     (None, 3, 2)              2         
=================================================================
Total params: 2
Trainable params: 2
Non-trainable params: 0

好的,我们得到了一堆零,但默认的 weight_initializer 不是零!

那么,现在让我们观察权重矩阵。

import keras.backend as K

w = model.layers[1].get_weights()
print(w)

[array([[ 0.03680499, -0.04904002]], dtype=float32)]

其实也不全是零

那么,为什么我们得到零?

让我们更改模型的输入。

作为 input_dim = 1 的唯一词汇索引,是 0。让我们将 0 作为输入之一传递。

from keras.models import Model
from keras.layers import Embedding, Input
import numpy as np

ip = Input(shape = (3,))
emb = Embedding(1, 2, trainable=True, mask_zero=True)(ip)

model = Model(ip, emb)
input_array = np.array([[5, 0, 1], [1, 2, 0]])

model.compile("rmsprop", "mse")

output_array = model.predict(input_array)

print(output_array)

print(output_array.shape)

model.summary()

现在,我们得到了超过 0 的位置的非零向量。

[[[ 0.          0.        ]
  [-0.04339869 -0.04900574]
  [ 0.          0.        ]]

 [[ 0.          0.        ]
  [ 0.          0.        ]
  [-0.04339869 -0.04900574]]]
(2, 3, 2)
Model: "model_19"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_22 (InputLayer)        (None, 3)                 0         
_________________________________________________________________
embedding_34 (Embedding)     (None, 3, 2)              2         
=================================================================
Total params: 2
Trainable params: 2
Non-trainable params: 0

简而言之,Keras 使用零向量映射任何词汇表外的单词索引,这是合理的,因为对于那些位置,正向传递将确保所有贡献都是 NIL(尽管偏差可能起作用)。这有点违反直觉,因为将词汇标记传递给模型似乎是一种开销(而不是仅仅在预处理步骤中删除它们)和不好的做法。

教训是完全避免使用 Keras 并转向 tensorflow.keras,因为他们清楚地提到 2.2 版本后将减少支持和修复小错误。

keras github repo 的相关问题:https://github.com/keras-team/keras/issues/13989