Задача с несколькими слоями вложения во внутренней модели Кераса - PullRequest
2 голосов
/ 05 ноября 2019

Я пытаюсь построить модель Keras model_B, которая выводит выходные данные другой модели Keras model_A. Теперь вывод model_A вычисляется на основе конкатенации нескольких тензоров из нескольких Keras, встраивающих слои с различными размерами словарного запаса. Модели model_A и model_B по сути одинаковы.

Проблема: Когда я тренируюсь model_A, все работает нормально. Однако, когда я тренируюсь model_B в одном и том же наборе данных, я получаю следующую ошибку:

tenorflow.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]), это работает, поддерживая предыдущую гипотезу.

Ответы [ 2 ]

1 голос
/ 10 ноября 2019

Я полагаю, что происходит следующее:

(1) Когда вы выполняете начальный цикл for для функции Lambda, вы инициализируете постоянные тензоры, которые подаются в оператор "strided_slice", который извлекает либо[:, 0] или [:, 1] правильно. Использование глобальной переменной «a» в функции Lambda, вероятно, «рискованно», но в этом случае работает нормально. Более того, я считаю, что функция хранится в байт-коде как «lambda x: x [:, a]», поэтому она попытается найти любое значение «a» во время оценки. «a» может быть чем угодно, поэтому в некоторых случаях может быть проблематично.

(2) Когда вы строите первую модель (model_A), тензоры констант не реинициализируются, поэтому лямбда-функции(оператор strided_slice) имеет правильные значения (0 и 1), которые были инициализированы в цикле «for».

(3) При построении второй модели (model_B) постоянные тензоры равны переинициализирован. Однако в это время значение «a» теперь равно 1 (как указано в некоторых других комментариях), потому что это окончательное значение после исходного цикла «for». Фактически, вы можете установить a = 0 непосредственно перед определением model_B, и вы фактически получите поведение, которое соответствует как извлечению Lambdas [:, 0], так и подаче его во встроенные слои. Мои предположения об этой разнице в поведении, возможно, связаны с вызовом инициализации класса Model_A (X) в этом случае (тогда как в первой модели вы указали только выходной слой "h" и не вызывали класс Model_A () в качествевывод - это отличие, я полагаю, было также предложено в другом комментарии).

Я скажу, что я проверил это положение дел, вставив некоторые операторы печати в файл "frameworks / constant_op.py" во время операторашаг инициализации и полученные отладочные операторы со значениями и последовательностями, согласующимися с тем, что я изложил выше.

Надеюсь, это поможет.

1 голос
/ 07 ноября 2019

По какой-то странной причине зацикливание тензора вызывает эту ошибку. Вы можете заменить нарезку на 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 вместо значения, которое вы указали ранее (я полагаю, этого не должно происходить, но я однажды столкнулся с этой проблемой при загрузке модели: одинпеременные сохранили свое последнее значение при загрузке модели вместо того, чтобы иметь зацикленное значение)

Одна вещь, которая поддерживает это объяснение, это попытка использования констант вместо 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

Еще одна вещь, которая работает (и поддерживает объяснение, что лямбда может использовать последнее значение 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...