Proces, który zaimplementowaliśmy przy użyciu biblioteki Numpy, jest odpowiednikiem prostej warstwy o nazwie layer_simple_rnn pakietu Keras:
layer_simple_rnn(units = 32)
Pomiędzy tymi implementacjami jest jednak pewna mała różnica: warstwa layer_simple_rnn dzieli sekwencje na wsady (operację taką wykonują wszystkie warstwy Keras) — nie przetwarza pojedynczych sekwencji tak, jak to miało miejsce w przykładzie zaimplementowanym za pomocą prostego kodu R. W związku z tym warstwa ta przyjmuje obiekty wejściowe o kształcie (rozmiar_wsadu, kroki_czasu, cechy_wejściowe), a nie (kroki_czasu, cechy_wejściowe).
Warstwa layer_simple_rnn, podobnie jak wszystkie rekurencyjne warstwy pakietu Keras, może być uruchomiona w dwóch trybach: może zwracać pełne sekwencje kolejnych obiektów wyjściowych dla każdego kroku czasu (trójwymiarowe tensory o kształcie (rozmiar_wsadu, kroki_czasu, cechy_wyjściowe)) lub tylko ostatnie obiekty wyjściowe poszczególnych sekwencji wejściowych (dwuwymiarowe tensory o kształcie (rozmiar_wsadu, cechy_wyjściowe)). Wybór trybu pracy jest dokonywany za pomocą argumentu return_sequences. Przeanalizujmy przykład, w którym zastosowano warstwę SimpleRNN, a dane wyjściowe są zwracane tylko podczas przetwarzania ostatniego kroku czasu:
r
r library(keras) model <- keras_model_sequential() %>% layer_embedding(input_dim = 10000, output_dim = 32) %>% layer_simple_rnn(units = 32) summary(model)
_______________________________________________________________________________________________________________
Layer (type) Output Shape Param #
===============================================================================================================
embedding_6 (Embedding) (None, None, 32) 320000
_______________________________________________________________________________________________________________
simple_rnn_8 (SimpleRNN) (None, 32) 2080
===============================================================================================================
Total params: 322,080
Trainable params: 322,080
Non-trainable params: 0
_______________________________________________________________________________________________________________
r
r model <- keras_model_sequential() %>% layer_embedding(input_dim = 10000, output_dim = 32) %>% layer_simple_rnn(units = 32, return_sequences = TRUE) summary(model)
_______________________________________________________________________________________________________________
Layer (type) Output Shape Param #
===============================================================================================================
embedding_7 (Embedding) (None, None, 32) 320000
_______________________________________________________________________________________________________________
simple_rnn_9 (SimpleRNN) (None, None, 32) 2080
===============================================================================================================
Total params: 322,080
Trainable params: 322,080
Non-trainable params: 0
_______________________________________________________________________________________________________________
Czasami warto utworzyć stos składający się z kilku warstw rekurencyjnych. Zwiększa to siłę tworzenia reprezentacji przez sieć. Przy takiej konfiguracji wszystkie warstwy pośrednie muszą zwracać pełną sekwencję obiektów wyjściowych:
r
r model <- keras_model_sequential() %>% layer_embedding(input_dim = 10000, output_dim = 32) %>% layer_simple_rnn(units = 32, return_sequences = TRUE) %>% layer_simple_rnn(units = 32, return_sequences = TRUE) %>% layer_simple_rnn(units = 32, return_sequences = TRUE) %>% layer_simple_rnn(units = 32) # This last layer only returns the last outputs. summary(model)
_______________________________________________________________________________________________________________
Layer (type) Output Shape Param #
===============================================================================================================
embedding_8 (Embedding) (None, None, 32) 320000
_______________________________________________________________________________________________________________
simple_rnn_10 (SimpleRNN) (None, None, 32) 2080
_______________________________________________________________________________________________________________
simple_rnn_11 (SimpleRNN) (None, None, 32) 2080
_______________________________________________________________________________________________________________
simple_rnn_12 (SimpleRNN) (None, None, 32) 2080
_______________________________________________________________________________________________________________
simple_rnn_13 (SimpleRNN) (None, 32) 2080
===============================================================================================================
Total params: 328,320
Trainable params: 328,320
Non-trainable params: 0
_______________________________________________________________________________________________________________
Teraz użyjmy takiego modelu w celu rozwiązania problemu klasyfikacji recenzji filmów. Zacznijmy od wstępnej obróbki danych:
r
r library(keras) max_features <- 10000 # Number of words to consider as features maxlen <- 500 # Cuts off texts after this many words (among the max_features most common words) batch_size <- 32 cat(data…)
Loading data...
r
r imdb <- dataset_imdb(num_words = max_features) c(c(input_train, y_train), c(input_test, y_test)) %<-% imdb cat(length(input_train), sequences)
25000 train sequences
r
r cat(length(input_test), sequences)
25000 test sequences
r
r cat(sequences (samples x time))
Pad sequences (samples x time)
r
r input_train <- pad_sequences(input_train, maxlen = maxlen) input_test <- pad_sequences(input_test, maxlen = maxlen) cat(_train shape:, dim(input_train), \n)
input_train shape: 25000 500
r
r cat(_test shape:, dim(input_test), \n)
input_test shape: 25000 500
Przeprowadźmy proces trenowania prostej rekurencyjnej sieci przy użyciu warstwy layer_embedding i warstwy layer_simple_rnn.
r
r model <- keras_model_sequential() %>% layer_embedding(input_dim = max_features, output_dim = 32) %>% layer_simple_rnn(units = 32) %>% layer_dense(units = 1, activation = ) model %>% compile( optimizer = , loss = _crossentropy, metrics = c() ) history <- model %>% fit( input_train, y_train, epochs = 10, batch_size = 128, validation_split = 0.2 )
Teraz możemy wyświetlić wykresy dokładności i straty w procesach trenowania i walidacji:
r
r plot(history)
Pierwsze maksymalnie uproszczone rozwiązanie, przedstawione w rozdziale 3., doprowadziło do uzyskania dokładności testowej na poziomie 88%. Niestety ta mała rekurencyjna sieć działa o wiele słabiej od wspomnianego rozwiązania (uzyskujemy dokładność walidacyjną na poziomie zaledwie 85%). Problem ten wynika częściowo z tego, że nie przetwarzamy pełnych sekwencji, a tylko 500 pierwszych słów, a więc sieć rekurencyjna ma dostęp do mniejszej ilości informacji niż model przedstawiony w rozdziale 3. Ponadto warstwa layer_simple_rnn nie sprawdza się najlepiej podczas przetwarzania długich sekwencji takich jak tekst. W takiej sytuacji lepiej sprawdzają się inne warstwy rekurencyjne. Przyjrzyjmy się bardziej zaawansowanym warstwom tego typu.
Czas przyjrzeć się praktycznemu przykładowi zastosowania warstwy layer_lstm. Skonfigurujemy model, w którym znajdzie się taka warstwa, i wytrenujemy go na zbiorze danych IMDB (patrz rysunek 6.13). Przypomina on zaprezentowany wcześniej model z warstwą layer_simple_rnn. Określimy tylko liczbę wymiarów obiektu wyjściowego warstwy layer_lstm. Pozostałe argumenty tej warstwy (jest ich wiele) pozostawimy przy wartościach domyślnych. Ustawienia domyślne pakietu Keras są przemyślane i zwykle „po prostu działają” bez konieczności poświęcania dużej ilości czasu na ręczne dostrajanie parametrów.
r
r model <- keras_model_sequential() %>% layer_embedding(input_dim = max_features, output_dim = 32) %>% layer_lstm(units = 32) %>% layer_dense(units = 1, activation = ) model %>% compile( optimizer = , loss = _crossentropy, metrics = c() ) history <- model %>% fit( input_train, y_train, epochs = 10, batch_size = 128, validation_split = 0.2 )
r
r plot(history)