2016-10-24 40 views
6

To pytanie istnieje również jako github issue. Chciałbym zbudować sieć neuronową w Keras, która zawiera zarówno skręty 2D, jak i warstwę LSTM.Keras: przekształć, aby połączyć lstm i konw.

Sieć powinna klasyfikować MNIST. Dane treningowe w MNIST to 60000 obrazów w skali szarości odręcznych cyfr od 0 do 9. Każdy obraz ma 28 x 28 pikseli.

Mam obrazy podzielony na cztery części (w lewo/w prawo, w górę/w dół) i uporządkowane je w czterech rzędów, aby uzyskać sekwencje dla LSTM.

|  |  |1 | 2| 
|image| -> ------- -> 4 sequences: |1|2|3|4|, |4|3|2|1|, |1|3|2|4|, |4|2|3|1| 
|  |  |3 | 4| 

jeden z małych podobrazów ma wymiar 14 x 14. Cztery sekwencje są ułożone razem na całej szerokości (nie powinno mieć znaczenia, czy szerokości lub wysokości).

Stwarza to wektor z kształtem [60000, 4, 1, 56, 14], gdzie:

  • 60000 jest numerem próbki
  • 4 oznacza liczbę elementów znajdujących się w sekwencji (# od timesteps)
  • 1 głębokość kolorów (skala szarości)
  • 56 i 14 są szerokość i wysokość

teraz należy zwrócić uwagę na modelu Keras . Problem polega na zmianie wymiarów wejściowych między CNN i LSTM. Szukałem w Internecie i okazało się to pytanie: Python keras how to change the size of input after convolution layer into lstm layer

Rozwiązaniem wydaje się być warstwa Reshape który spłaszcza obraz ale zachowuje timesteps (w przeciwieństwie do warstwy Spłaszcz który upadnie wszystko oprócz batch_size).

Oto mój kod do tej pory:

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")) 

Kod ten tworzy się komunikat o błędzie:

ValueError: total size of new array must be unchanged

Widocznie wejście do warstwy Zmień kształt jest nieprawidłowy. Jako alternatywę, starałem się przekazać timesteps do warstwy Zmień kształt, zbyt:

model.add(Reshape((4,56*14))) 

nie czuję się dobrze, aw każdym razie, błąd pozostaje taka sama.

Czy robię to we właściwy sposób? Czy warstwa Przekształcenia jest właściwym narzędziem do połączenia CNN i LSTM?

Istnieją dość złożone podejścia do tego problemu. Takie jak ten: https://github.com/fchollet/keras/pull/1456 A TimeDistributed warstwy, która wydaje się ukryć wymiar kroku czasu z następujących warstw.

Lub to: https://github.com/anayebi/keras-extra Zestaw specjalnych warstw do łączenia numerów CNN i LSTM.

Dlaczego są tak skomplikowane (przynajmniej wydawać się skomplikowane dla mnie) rozwiązań, jeśli prosta Reshape załatwia sprawę?

UPDATE:

żenująco, zapomniałem, że wymiary zostaną zmienione przez łączenie i (ze względu na brak wypełnienia) zwojów, too. kgrm poinformował mnie, żebym użył model.summary(), aby sprawdzić wymiary.

Dane wyjściowe warstwy przed warstwą Przekształcanie to (None, 32, 26, 5), Zmieniono przekształcenie na: model.add(Reshape((32*26*5,))).

Teraz ValueError nie ma, zamiast LSTM narzeka:

Exception: Input 0 is incompatible with layer lstm_5: expected ndim=3, found ndim=2

Wydaje się, że muszę zdać wymiar kroku czasu przez całą sieć. Jak mogę to zrobić ? Jeśli dodać go do input_shape splotu, narzeka też: Convolution2D(nb_filters, kernel_size[0], kernel_size[1], border_mode="valid", input_shape=[4, 1, 56,14])

Exception: Input 0 is incompatible with layer convolution2d_44: expected ndim=4, found ndim=5

Odpowiedz

5

Według Convolution2D definicja Twój wkład musi być 4-wymiarowej o wymiarach (samples, channels, rows, cols). To jest bezpośredni powód, dla którego popełniasz błąd.

Aby rozwiązać problem, należy użyć opakowania TimeDistributed. Pozwala to na używanie w danym czasie warstw statycznych (nie powtarzających się).