我正在尝试构建一个Keras模型model_B
,该模型输出另一个Keras模型model_A
的输出。现在,model_A
的输出是根据来自多个Keras embedding层且具有不同词汇量的几个张量的级联来计算的。模型model_A
和model_B
基本相同。
问题:我训练model_A
时,一切正常。但是,当我在同一数据集上训练model_B
时,出现以下错误:
tensorflow.python.framework.errors_impl.InvalidArgumentError: index [1] = 3不在[0,2)[[{{node model_1 / embedding_1 / embedding_lookup}}]]
从本质上讲,该错误是说一个单词的索引超出了预期的词汇范围,但事实并非如此。有人可以弄清楚为什么会这样吗?
以下是该问题的可复制示例:
from keras.layers import Input, Dense, Lambda, Concatenate, Embedding
from keras.models import Model
import numpy as np
# Constants
A = 2
vocab_sizes = [2, 4]
# Architecture
X = Input(shape=(A,))
embeddings = []
for a in range(A):
X_a = Lambda(lambda x: x[:, a])(X)
embedding = Embedding(input_dim=vocab_sizes[a],
output_dim=1)(X_a)
embeddings.append(embedding)
h = Concatenate()(embeddings)
h = Dense(1)(h)
# Model A
model_A = Model(inputs=X, outputs=h)
model_A.compile('sgd', 'mse')
# Model B
Y = Input(shape=(A,))
model_B = Model(inputs=Y, outputs=model_A(Y))
model_B.compile('sgd', 'mse')
# Dummy dataset
x = np.array([[vocab_sizes[0] - 1, vocab_sizes[1] - 1]])
y = np.array([1])
# Train models
model_A.fit(x, y, epochs=10) # Works well
model_B.fit(x, y, epochs=10) # Fails
根据上述错误,似乎输入x[:, 1]
被错误地馈送到词汇量为2的第一嵌入层,而不是第二词汇层。有趣的是,当我交换词汇量(例如设置vocab_sizes = [4, 2]
)时,它可以工作,并支持先前的假设。
答案 0 :(得分:1)
出于某些奇怪的原因,张量循环导致此错误。
您可以用tf.split
代替切片,使用必要的调整,效果会很好:
额外进口:
import tensorflow as tf
from keras.layers import Flatten
# Architecture
X = Input(shape=(A,))
X_as = Lambda(lambda x: tf.split(x, A, axis=1))(X)
embeddings = []
for a, x in enumerate(X_as):
embedding = Embedding(input_dim=vocab_sizes[a],
output_dim=1)(x)
embeddings.append(embedding)
h = Concatenate(axis=1)(embeddings)
h = Flatten()(h)
h = Dense(1)(h)
为什么会这样?
好吧,很难猜测。我的假设是系统正在尝试使用实际变量a
而不是您之前提供的值来应用lambda层(我想这应该不会发生,但是在加载模型时,我曾有过一次这个问题:加载模型时,其中一个变量保留了最后一个值,而不是具有循环值)
支持此解释的一件事是尝试使用常量而不是a
:
#Architecture
X = Input(shape=(A,))
embeddings = []
X_a1 = Lambda(lambda x: x[:, 0], name = 'lamb_'+str(0))(X)
X_a2 = Lambda(lambda x: x[:, 1], name = 'lamb_'+str(1))(X)
xs = [X_a1, X_a2]
for a, X_a in enumerate(xs):
embedding = Embedding(input_dim=vocab_sizes[a],
output_dim=1)(X_a)
embeddings.append(embedding)
h = Concatenate()(embeddings)
h = Dense(1)(h)
要避免使用tf.split
另一种有效的方法(并支持Lambda可能在代码中将a
的最后一个值model_B
使用Lambda
的原因)使整个循环在a
层内,这样#Architecture
X = Input(shape=(A,))
X_as = Lambda(lambda x: [x[:, a] for a in range(A)])(X)
embeddings = []
for a, X_a in enumerate(X_as):
embedding = Embedding(input_dim=vocab_sizes[a],
output_dim=1)(X_a)
embeddings.append(embedding)
h = Concatenate()(embeddings)
h = Dense(1)(h)
不会得到任何意外的值:
class MyApplicationLoader extends GuiceApplicationLoader() {
override def builder(context: ApplicationLoader.Context):
GuiceApplicationBuilder = {
val builder = super.builder(context)
Global.Injector = builder.injector()
builder
}
}
答案 1 :(得分:1)
我相信正在发生以下事情:
(1)在Lambda函数上执行初始“ for循环”时,您正在初始化常量张量,该张量将馈入“ strided_slice”运算符中,该运算符可正确提取[:,0]或[:,1] 。在Lambda函数中使用全局变量“ a”可能是“风险”,但在这种情况下可以正常工作。此外,我相信该函数将以“ lambda x:x [:, a]”的形式存储在字节码中,因此它将尝试在求值时查找“ a”的值。 “ a”可能是任何东西,因此在某些情况下可能会引起问题。
(2)构建第一个模型(model_A)时,常数张量不会重新初始化,因此lambda函数(strided_slice运算符)具有已初始化的正确值(0和1)在“ for循环”中。
(3)建立第二个模型(model_B)时,常数张量 被重新初始化。但是,此时,“ a”的值现在为1(如其他注释中所述),因为这是原始“ for循环”之后的最终值。实际上,您可以在定义model_B之前将a = 0设置为0,并且实际上您将获得与Lambda提取[:,0]并将其馈送到嵌入层相对应的行为。我对这种行为差异的推测可能与这种情况下调用Model_A(X)类初始化有关(而在第一个模型中,您仅将输出层指定为“ h”,而没有将Model_A()类称为输出-我认为其他评论也暗示了这种差异。
我要说的是,我在操作员初始化步骤中通过在文件“ frameworks / constant_op.py”中放入一些打印语句来验证了这种状况,并获得了调试语句,其值和序列与我上面所述的一致。
我希望这会有所帮助。