Keras: изменить форму, чтобы соединить lstm и conv
Этот вопрос также существует как проблема github. Я хотел бы построить нейронную сеть в Керасе, которая содержит как двумерные свертки, так и слой LSTM.
Сеть должна классифицировать MNIST. Обучающие данные в MNIST представляют собой 60000 полутоновых изображений рукописных цифр от 0 до 9. Каждое изображение имеет размер 28x28 пикселей.
Я разделил изображения на четыре части (влево / вправо, вверх / вниз) и переставил их в четыре порядка, чтобы получить последовательности для LSTM.
| | |1 | 2|
|image| -> ------- -> 4 sequences: |1|2|3|4|, |4|3|2|1|, |1|3|2|4|, |4|2|3|1|
| | |3 | 4|
Одно из маленьких подизображений имеет размер 14 x 14. Четыре последовательности сгруппированы по ширине (не имеет значения, ширина или высота).
Это создает вектор с формой [60000, 4, 1, 56, 14] где:
- 60000 это количество образцов
- 4 - количество элементов в последовательности (количество временных шагов)
- 1 - глубина цвета (оттенки серого)
- 56 и 14 ширина и высота
Теперь это должно быть дано модели Кераса. Проблема состоит в том, чтобы изменить входные размеры между CNN и LSTM. Я искал в Интернете и нашел этот вопрос: Python keras, как изменить размер ввода после слоя свертки в слой lstm
Решением кажется слой Reshape, который выравнивает изображение, но сохраняет временные шаги (в отличие от слоя Flatten, который разрушит все, кроме batch_size).
Вот мой код до сих пор:
nb_filters=32
kernel_size=(3,3)
pool_size=(2,2)
nb_classes=10
batch_size=64
model=Sequential()
model.add(Convolution2D(nb_filters, kernel_size[0], kernel_size[1],
border_mode="valid", input_shape=[1,56,14]))
model.add(Activation("relu"))
model.add(Convolution2D(nb_filters, kernel_size[0], kernel_size[1]))
model.add(Activation("relu"))
model.add(MaxPooling2D(pool_size=pool_size))
model.add(Reshape((56*14,)))
model.add(Dropout(0.25))
model.add(LSTM(5))
model.add(Dense(50))
model.add(Dense(nb_classes))
model.add(Activation("softmax"))
Этот код создает сообщение об ошибке:
ValueError: общий размер нового массива должен быть неизменным
Видимо, ввод в слой Reshape неправильный. В качестве альтернативы я также попытался передать временные шаги слою Reshape:
model.add(Reshape((4,56*14)))
Это не правильно и в любом случае ошибка остается прежней.
Я делаю это правильно? Является ли слой Reshape подходящим инструментом для соединения CNN и LSTM?
Есть довольно сложные подходы к этой проблеме. Например: https://github.com/fchollet/keras/pull/1456 Слой TimeDistributed, который, кажется, скрывает измерение временного шага от следующих слоев.
Или это: https://github.com/anayebi/keras-extra Набор специальных слоев для объединения CNN и LSTM.
Почему существуют такие сложные (по крайней мере, мне кажутся сложными) решения, если простая Reshape добивается цели?
ОБНОВЛЕНИЕ:
Смущающе, я забыл, что размеры будут меняться в результате объединения и (из-за отсутствия заполнения) сверток тоже. кгрм посоветовал мне использовать model.summary()
проверить размеры.
Выход слоя перед слоем Reshape (None, 32, 26, 5)
Я изменил форму: model.add(Reshape((32*26*5,)))
,
Теперь ошибка ValueError исчезла, вместо этого LSTM жалуется:
Исключение: вход 0 несовместим со слоем lstm_5: ожидаемый ndim=3, найденный ndim=2
Кажется, мне нужно передать измерение временного шага по всей сети. Как я могу это сделать? Если я добавлю его в input_shape в Convolution, он тоже будет жаловаться: Convolution2D(nb_filters, kernel_size[0], kernel_size[1], border_mode="valid", input_shape=[4, 1, 56,14])
Исключение: вход 0 несовместим со сверточным слоем2d_44: ожидаемый ndim=4, найденный ndim=5
1 ответ
Согласно определению Convolution2D, ваш ввод должен быть четырехмерным с размерами (samples, channels, rows, cols)
, Это прямая причина, почему вы получаете ошибку.
Чтобы решить эту проблему, вы должны использовать оболочку TimeDistributed. Это позволяет вам использовать статические (не повторяющиеся) слои во времени.