Керас: неправильная форма ввода в нейронной сети LSTM

Я пытаюсь обучить рекуррентную нейронную сеть LSTM для классификации последовательностей.

Мои данные имеют следующий вид:

Input: [1,5,2,3,6,2, ...] -> Output: 1
Input: [2,10,4,6,12,4, ...] -> Output: 1
Input: [4,1,7,1,9,2, ...] -> Output: 2
Input: [1,3,5,9,10,20, ...] -> Output: 3
.
.
.

Итак, в основном я хочу предоставить последовательность в качестве ввода и получить целое число в качестве вывода.

Каждая входная последовательность имеет длину = 2000 чисел с плавающей запятой, и у меня есть около 1485 образцов для обучения.

На выходе получается целое число от 1 до 10.

Вот что я пытался сделать:

# Get the training numpy 2D array for the input (1485X 2000). 
# Each element is an input sequence of length 2000
# eg: [ [1,2,3...], [4,5,6...], ... ]
x_train = get_training_x() 

# Get the training numpy 2D array for the outputs (1485 X 1). 
# Each element is an integer output for the corresponding input from x_train
# eg: [ 1, 2, 3, ...]
y_train = get_training_y()

# Create the model
model = Sequential()
model.add(LSTM(100, input_shape=(x_train.shape)))
model.add(Dense(1, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
print(model.summary())
model.fit(x_train, y_train, nb_epoch=3, batch_size=64)

Я получаю следующую ошибку:

Error when checking input: expected lstm_1_input to have 3 dimensions, but got array with shape (1485, 2000)

Вместо этого я попытался использовать это:

model.add(LSTM(100, input_shape=(1485, 1, 2000)))

Но на этот раз произошла другая ошибка:

ValueError: Input 0 is incompatible with layer lstm_1: expected ndim=3, found ndim=4

Может ли кто-нибудь объяснить, какова моя форма ввода? а что я делаю не так?

Спасибо


person Youssef    schedule 21.03.2018    source источник


Ответы (3)


Учитывая формат вашего ввода и вывода, вы можете использовать части подхода, принятого одним из официальных Примеры Keras. В частности, поскольку вы не создаете двоичный классификатор, а скорее прогнозируете целое число, вы можете использовать быстрое кодирование для кодирования y_train с помощью to_categorical().

# Number of elements in each sample
num_vals = x_train.shape[1]

# Convert all samples in y_train to one-hot encoding
y_train = to_categorical(y_train)

# Get number of possible values for model inputs and outputs
num_x_tokens = np.amax(x_train) + 1
num_y_tokens = y_train.shape[1]

model = Sequential()
model.add(Embedding(num_x_tokens, 100))
model.add(LSTM(100))
model.add(Dense(num_y_tokens, activation='sigmoid'))

model.compile(loss='binary_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])

model.fit(x_train, y_train,
              batch_size=64,
              epochs=3)

num_x_tokens в приведенном выше коде будет максимальным размером элемента в одном из ваших входных образцов (например, если у вас есть два образца [1, 7, 2] и [3, 5, 4], тогда num_x_tokens равно 7). Если вы используете numpy, вы можете найти это с помощью np.amax(x_train). Точно так же num_y_tokens - это количество категорий в y_train.

После обучения вы можете выполнять прогнозы, используя приведенный ниже код. Использование np.argmax эффективно меняет to_categorical в этой конфигурации.

model_out = model.predict(x_test)
model_out = np.argmax(model_out, axis=1)

Вы можете импортировать to_categorical, используя from keras.utils import to_categorical, Embedding, используя from keras.layers import Embedding, и numpy, используя import numpy as np.

Кроме того, вам не нужно делать print(model.summary()). model.summary() достаточно, чтобы распечатать резюме.

ИЗМЕНИТЬ

Если это тот случай, когда ввод имеет форму [[0.12, 0.31, ...], [0.22, 0.95, ...], ...] (скажем, сгенерирован с помощью x_train = np.random.rand(num_samples, num_vals)), вы можете использовать x_train = np.reshape(x_train, (num_samples, num_vals, 1)), чтобы изменить форму массива, чтобы ввести его в слой LSTM. Код для обучения модели в этом случае будет:

num_samples = x_train.shape[0]
num_vals    = x_train.shape[1] # Number of elements in each sample

# Reshape for what LSTM expects
x_train = np.reshape(x_train, (num_samples, num_vals, 1))
y_train = to_categorical(y_train)

# Get number of possible values for model outputs
num_y_tokens = y_train.shape[1]

model = Sequential()
model.add(LSTM(100, input_shape=(num_vals, 1)))
model.add(Dense(num_y_tokens, activation='sigmoid'))

model.compile(loss='binary_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])

model.fit(x_train, y_train,
              batch_size=64,
              epochs=3)

num_vals - это длина каждого массива выборок в x_train. np.reshape(x_train, (num_samples, num_vals, 1)) изменяет каждый образец с [0.12, 0.31, ...] формы на [[0.12], [0.31], ...] форму, которая является формой, которую затем принимает LSTM (input_shape=(num_vals, 1)). Дополнительный 1 в этом случае кажется странным, но необходимо добавить дополнительное измерение во входные данные для LSTM, поскольку он ожидает, что каждый образец будет иметь как минимум два измерения, обычно называемых (timesteps, data_dim), или в данном случае (num_vals, 1).

Чтобы узнать, как еще используются LSTM в Keras, вы можете обратиться к:

Руководство по последовательной модели Keras (содержит несколько примеров LSTM)

Примеры Keras (ищите *.py файлы с lstm в их имени)

person vasilyrud    schedule 23.03.2018
comment
Добавление слоя Embedding устранило проблему с моей формой ввода, однако у меня есть другая проблема. Я не знаю num_x_tokens. Я не могу взять максимум, потому что там все значения с плавающей запятой и значения от 0 до 1 (пример в вопросе мог быть ошибочным, извините за это). И я не знаю количество входных токенов. Есть идеи, как можно использовать слой встраивания, не зная номера токена? (Я думаю, что может быть миллионы разных входных данных, учитывая, что это случайное число с плавающей запятой между 0 и 1). Спасибо - person Youssef; 24.03.2018
comment
В этом случае предположим, что вы определили x_train = np.random.rand(num_samples, num_vals). Это будет форма [[0.12, 0.31, ...], [0.22, 0.95, ...], ...]. Вы можете использовать x_train = np.reshape(x_train, (num_samples, num_vals, 1)), чтобы изменить форму массива, а затем ввести его в слой LSTM с помощью model.add(LSTM(100, input_shape=(num_vals, 1))) (а также удалить слой Embedding). @Youssef правильно ли я интерпретировал ваш формат ввода? - person vasilyrud; 24.03.2018
comment
Потрясающие! это сработало отлично. Не могли бы вы добавить свой комментарий к своему ответу, и я его приму. Было бы также здорово, если бы вы могли объяснить, что представляет 1 как в reshape, так и в input_shape. Это размер входа? [число_валлов x 1]? - person Youssef; 24.03.2018

попробуйте изменить форму тренировочных данных на:

x_train=x_train.reshape(x_train.shape[0], 1, x_train.shape[1])
person Ioannis Nasios    schedule 21.03.2018
comment
Я получаю ту же ошибку, что и позже: ValueError: Input 0 is incompatible with layer lstm_1: expected ndim=3, found ndim=4. Это все еще используется model.add(LSTM(100, input_shape=x_train.shape)) Спасибо - person Youssef; 21.03.2018
comment
тоже измените свой input_shape - person Ioannis Nasios; 21.03.2018

input_shape=(None, x_train.shape[1], 1), где None - размер пакета, x_train.shape[1] - длина каждой последовательности функций, а 1 - длина каждой функции. (Не уверен, нужен ли размер партии для модели Sequential).

А затем измените форму ваших данных на x_train = x_train.reshape(-1, x_train.shape[1], 1).

person Maxim Egorushkin    schedule 23.03.2018
comment
Здравствуйте, теперь я получаю эту ошибку: ValueError: Input 0 is incompatible with layer lstm_1: expected ndim=3, found ndim=4 Спасибо - person Youssef; 24.03.2018
comment
Я сделал именно то, что вы описали, используя: input_shape = (None, x_train.shape[1], 1) x_train = x_train.reshape(-1, x_train.shape[1], 1) А затем изменил слой LSTM, чтобы он принял эту форму ввода model.add(LSTM(100, input_shape=input_shape)) - person Youssef; 24.03.2018
comment
@Youssef Попробуйте удалить None из input_shape. - person Maxim Egorushkin; 24.03.2018