【问题标题】:Problem with multiple embedding layers in inner Keras model内部 Keras 模型中存在多个嵌入层的问题
【发布时间】:2020-03-02 04:19:30
【问题描述】:

我正在尝试构建一个 Keras 模型 model_B,它输出另一个 Keras 模型 model_A 的输出。现在,model_A 的输出是根据来自多个具有不同词汇量的Keras embedding 层的几个张量的串联计算的。型号model_Amodel_B 基本相同。

问题:当我训练 model_A 时,一切正常。但是,当我在同一数据集上训练 model_B 时,出现以下错误:

tensorflow.python.framework.errors_impl.InvalidArgumentError: indices[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])时,它可以工作,支持之前的假设。

【问题讨论】:

    标签: python tensorflow keras


    【解决方案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,您实际上会得到与 Lambda 提取 [:,0] 并将其馈送到嵌入层相对应的行为。我对这种行为差异的推测可能与在这种情况下调用 Model_A(X) 类初始化有关(而在第一个模型中,您只指定了输出层“h”并且没有调用 Model_A() 类作为输出 - 我相信其他评论也提出了这种差异)。

    我会说我通过在操作符初始化步骤中在文件“frameworks/constant_op.py”中放入一些打印语句来验证这种情况,并获得了与我上面所说的值和序列一致的调试语句。

    我希望这会有所帮助。

    【讨论】:

      【解决方案2】:

      由于某种奇怪的原因,循环张量会导致此错误。 您可以将切片替换为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可能使用model_B)在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)
      

      【讨论】:

      • 谢谢你,Möller,像往常一样快速而有见地。我知道使用tf.unstacktf.split 可以工作,但我仍然认为for-loop 行为是错误的并且非常不受欢迎。尽管如此,除非其他人能够在这种情况下崩溃为什么可以提供技术说明,否则赏金将是您的。 span>
      • 我很清楚。正在使用a 注册 lambda 函数。当您从Model B调用它时,您正在调用图层时,请在model_A(Y),lambda函数将获得a,它卡在循环的最后值。 span>
      • 您可以使用我的上一个示例进行测试,以在执行model_A(Y) 之前增加大A 的值。如果 lambda 注册了 A 并尝试使用它的值,你应该得到一些索引越界错误......
      猜你喜欢
      • 1970-01-01
      • 2018-09-23
      • 1970-01-01
      • 2020-06-20
      • 1970-01-01
      • 2020-01-23
      • 2021-09-02
      • 1970-01-01
      • 2019-05-17
      相关资源
      最近更新 更多