Długa pamięć krótkotrwała (ang. long short-term memory, LSTM) – architektura rekurencyjnej sieci neuronowej zaprojektowana do uczenia zależności w danych sekwencyjnych, w szczególności zależności oddzielonych wieloma krokami czasowymi. LSTM została wprowadzona przez Seppa Hochreitera i Jürgena Schmidhubera w 1997 roku jako odpowiedź na problem zanikających i eksplodujących gradientów w klasycznych sieciach rekurencyjnych.
LSTM jest jednym z najważniejszych wariantów sieci RNN. Przez wiele lat była szeroko stosowana w rozpoznawaniu mowy, przetwarzaniu języka naturalnego, rozpoznawaniu pisma ręcznego, analizie szeregów czasowych, modelowaniu sygnałów i innych zadaniach sekwencyjnych.
Motywacja Klasyczna rekurencyjna sieć neuronowa przetwarza sekwencję krok po kroku, aktualizując ukryty stan na podstawie bieżącego wejścia i poprzedniego stanu. Teoretycznie pozwala to modelować zależności długiego zasięgu, ale w praktyce trening takich sieci za pomocą propagacji wstecznej przez czas często prowadzi do zanikających albo eksplodujących gradientów. W efekcie sieć może mieć trudność z uczeniem zależności między elementami sekwencji odległymi o wiele kroków czasowych.
LSTM wprowadza specjalną komórkę pamięci oraz bramki sterujące przepływem informacji. Dzięki temu sieć może przechowywać pewne informacje przez długi czas, usuwać informacje niepotrzebne i decydować, które części stanu pamięci ujawnić w stanie ukrytym. Nazwa long short-term memory oznacza, że architektura miała zapewnić mechanizm pamięci krótkotrwałej zdolnej do działania na długich przedziałach czasowych.
Budowa komórki LSTM Typowa komórka LSTM ma dwa główne stany: stan komórki , pełniący rolę pamięci; stan ukryty , będący wyjściem komórki w danym kroku czasowym.
Przepływem informacji sterują bramki: bramka wejściowa – decyduje, jaka część nowej informacji zostanie zapisana w pamięci; bramka zapominania – decyduje, jaka część poprzedniego stanu komórki zostanie zachowana; bramka wyjściowa – decyduje, jaka część pamięci zostanie ujawniona jako stan ukryty.
Bramki są zwykle realizowane jako warstwy z funkcją sigmoidalną, której wartości należą do przedziału od 0 do 1. Wartość bliska 0 oznacza niemal całkowite zablokowanie informacji, a wartość bliska 1 – jej przepuszczenie.
Równania Jedna z najczęściej używanych wersji komórki LSTM jest opisana równaniami:
gdzie: oznacza wejście w chwili ; oznacza poprzedni stan ukryty; oznacza poprzedni stan komórki; jest bramką wejściową; jest bramką zapominania; jest bramką wyjściową; jest kandydatem nowej zawartości pamięci; oznacza funkcję sigmoidalną; oznacza mnożenie wektorów po współrzędnych.
W literaturze i implementacjach występują różne konwencje oznaczeń, ale podstawowa idea pozostaje taka sama: stan komórki jest aktualizowany przez połączenie zapamiętanej informacji z nową informacją kontrolowaną bramkami.
Mechanizm przepływu gradientu Jedną z najważniejszych cech LSTM jest istnienie ścieżki, po której informacja może być przenoszona przez kolejne kroki czasowe z mniejszą utratą niż w prostej sieci RNN. W równaniu
stan komórki zależy addytywnie od poprzedniego stanu . W uproszczeniu pochodna stanu komórki względem poprzedniego stanu ma składnik
Jeżeli bramka zapominania przyjmuje wartości bliskie 1, gradient może przepływać przez wiele kroków czasowych bez szybkiego zaniku. Jeżeli jest bliskie 0, komórka uczy się wygaszać wcześniejszą informację.
W pierwotnej terminologii Hochreitera i Schmidhubera rolę takiej stabilnej ścieżki odgrywał tzw. stały obieg błędu (ang. constant error carousel), dzięki któremu informacja o błędzie mogła być propagowana przez wiele kroków czasowych.
Liczba parametrów Dla wejścia o wymiarze i stanu ukrytego o wymiarze pojedyncza warstwa LSTM musi obliczyć cztery zestawy wielkości: bramkę wejściową, bramkę zapominania, kandydata stanu komórki i bramkę wyjściową. W typowej implementacji oznacza to macierze wag dla wejścia i stanu ukrytego oraz wyrazy wolne dla każdej z tych czterech części.
W uproszczonej konwencji z jednym wektorem wyrazów wolnych liczba parametrów jednej jednokierunkowej warstwy wynosi:
W niektórych implementacjach, na przykład w PyTorch, występują osobne wyrazy wolne dla części wejściowej i rekurencyjnej, co daje:
Dla sieci dwukierunkowej liczba parametrów jest odpowiednio większa, ponieważ utrzymywane są osobne parametry dla kierunku w przód i kierunku wstecz.
Rola bramki zapominania Pierwotna architektura LSTM z 1997 roku nie zawierała bramki zapominania w formie, która później stała się standardowa. Bramkę tę wprowadzili Felix Gers, Jürgen Schmidhuber i Fred Cummins w pracy z 2000 roku. Jej zadaniem jest umożliwienie komórce nauczenia się, kiedy zresetować albo osłabić wcześniejszą zawartość pamięci.
Bramka zapominania jest szczególnie ważna w strumieniach danych, które nie są naturalnie podzielone na niezależne epizody. Bez niej komórka może utrzymywać w pamięci informacje, które przestały być istotne dla dalszego przetwarzania.
Uczenie Sieci LSTM trenuje się zwykle metodami gradientowymi, takimi jak stochastyczny spadek gradientu lub jego warianty. Dla danych sekwencyjnych stosuje się propagację wsteczną przez czas (ang. backpropagation through time, BPTT), polegającą na rozwinięciu sieci rekurencyjnej wzdłuż kolejnych kroków czasowych i obliczeniu gradientów parametrów względem funkcji straty.
Architektura LSTM ułatwia przepływ gradientu przez stan komórki, ale nie usuwa wszystkich problemów treningu. W praktyce używa się takich technik jak przycinanie gradientów, regularyzacja, dropout, normalizacja, staranna inicjalizacja oraz dobór długości sekwencji używanej w BPTT.
Warianty Dwukierunkowa LSTM Dwukierunkowa LSTM (ang. bidirectional LSTM, BLSTM) przetwarza sekwencję w dwóch kierunkach: od początku do końca oraz od końca do początku. Wyjścia z obu kierunków są następnie łączone. Pozwala to używać informacji z przeszłości i przyszłości w zadaniach, w których dostępna jest cała sekwencja, na przykład przy klasyfikacji fonemów lub etykietowaniu tekstu.
LSTM z połączeniami podglądowymi Wariant z tzw. połączeniami podglądowymi (ang. peephole connections) pozwala bramkom korzystać bezpośrednio ze stanu komórki. Gers, Schraudolph i Schmidhuber pokazali, że taki wariant może ułatwiać uczenie precyzyjnych zależności czasowych.
Głębokie i warstwowe LSTM Sieci LSTM można układać w kilka warstw. Taki model nazywa się często głęboką LSTM. Kolejne warstwy mogą uczyć się reprezentacji sekwencji na różnych poziomach abstrakcji. Warianty głębokie były stosowane między innymi w rozpoznawaniu mowy na dużą skalę.
LSTM z projekcją W niektórych zastosowaniach używa się warstwy projekcyjnej, która zmniejsza wymiar stanu ukrytego przekazywanego dalej. Taka architektura może ograniczyć liczbę parametrów i koszt obliczeniowy przy zachowaniu dużego stanu komórki. Rozwiązania tego typu były badane w rozpoznawaniu mowy.
Konwolucyjna LSTM Konwolucyjna LSTM (ang. convolutional LSTM, ConvLSTM) zastępuje mnożenia macierzowe operacjami konwolucyjnymi. Jest używana do danych czasoprzestrzennych, takich jak sekwencje obrazów, mapy radarowe lub nagrania wideo. Shi i współautorzy zaproponowali ConvLSTM do prognozowania opadów w krótkim horyzoncie czasowym.
Związek z innymi architekturami Klasyczna RNN W klasycznej sieci RNN stan ukryty aktualizuje się bez wyraźnego, osobnego stanu pamięci i bez bramek kontrolujących przepływ informacji. LSTM rozbudowuje tę architekturę o mechanizm pamięci i bramkowania, co ułatwia uczenie dłuższych zależności.
GRU GRU (ang. gated recurrent unit) jest inną bramkowaną architekturą rekurencyjną, zaproponowaną w 2014 roku w kontekście modeli encoder–decoder dla tłumaczenia maszynowego. GRU ma mniej parametrów niż typowa LSTM, ponieważ nie rozdziela jawnie stanu komórki i stanu ukrytego oraz używa mniejszej liczby bramek.
Transformery Od końca lat 2010. w wielu zadaniach przetwarzania języka naturalnego i modelowania sekwencji duże znaczenie uzyskały transformery, oparte na mechanizmie uwagi i niewymagające rekurencji. Model Transformer został opisany w pracy Attention Is All You Need z 2017 roku jako architektura oparta wyłącznie na mechanizmach uwagi, bez rekurencji i konwolucji. LSTM pozostaje jednak użyteczna w wielu zadaniach, zwłaszcza tam, gdzie ważne są modele rekurencyjne, strumieniowe przetwarzanie danych, mniejsze modele lub ograniczenia obliczeniowe.
Implementacje w Pythonie i R Python W języku Python warstwy LSTM są dostępne w najpopularniejszych bibliotekach głębokiego uczenia, w tym w PyTorch, TensorFlow i Keras. W PyTorch podstawową klasą jest `torch.nn.LSTM`, która stosuje wielowarstwową sieć LSTM do sekwencji wejściowej. Dla ustawienia `batch_first=True` wejście ma zwykle kształt:
Warstwa zwraca sekwencję wyjść oraz końcowe stany i .
Przykładowy schemat modelu w PyTorch: import torch from torch import nn
class ModelLSTM(nn.Module): def __init__(self, input_size: int, hidden_size: int, output_size: int) -> None: super().__init__() self.lstm = nn.LSTM( input_size=input_size, hidden_size=hidden_size, batch_first=True, ) self.linear = nn.Linear(hidden_size, output_size)
def forward(self, x: torch.Tensor) -> torch.Tensor: output, (h_n, c_n) = self.lstm(x) last_output = output[:, -1, :] return self.linear(last_output)
W Keras odpowiednikiem jest warstwa `keras.layers.LSTM`. Przy standardowym użyciu oczekuje ona tensora trójwymiarowego, którego wymiary odpowiadają próbkom, krokom czasowym i cechom. Argument `return_sequences` decyduje, czy warstwa zwraca wyjście dla każdego kroku czasowego, czy tylko ostatnie wyjście; `return_state` pozwala dodatkowo zwrócić końcowy stan ukryty i stan komórki. Dokumentacja Keras wskazuje także, że przy odpowiednich ustawieniach i dostępności sprzętu warstwa może korzystać z szybkiej implementacji cuDNN.
Przykład w Keras: from keras import layers, models
model = models.Sequential([ layers.Input(shape=(None, 8)), layers.LSTM(64), layers.Dense(1) ])
R W języku R sieci LSTM można budować między innymi za pomocą pakietów `keras` i `torch`. Pakiet `keras` udostępnia funkcję `layer_lstm()`, będącą interfejsem do warstwy LSTM z Keras/TensorFlow.
Przykład w R z użyciem pakietu `keras`: library(keras)
model <- keras_model_sequential() |> layer_lstm(units = 64, input_shape = c(NULL, 8)) |> layer_dense(units = 1)
model |> compile( optimizer = "adam", loss = "mse" )
Pakiet `torch` dla R zawiera funkcję `nn_lstm()`, która działa analogicznie do modułu `torch.nn.LSTM` w PyTorch. Pozwala określić między innymi wymiar wejścia, liczbę jednostek ukrytych, liczbę warstw, dropout i dwukierunkowość.
Przykład w R z użyciem pakietu `torch`: library(torch)
model <- nn_module( initialize = function(input_size, hidden_size, output_size) { self$lstm <- nn_lstm( input_size = input_size, hidden_size = hidden_size, batch_first = TRUE ) self$linear <- nn_linear(hidden_size, output_size) }, forward = function(x) { lstm_out <- self$lstm(x) output <- lstm_out[] last_output <- output[ , dim(output), ] self$linear(last_output)
)
W praktyce dane wejściowe dla LSTM mają postać trójwymiarową: próbka, czas i cechy. Dla szeregów czasowych typowym etapem przygotowania danych jest więc utworzenie ruchomych okien czasowych, normalizacja cech oraz oddzielenie zbioru treningowego, walidacyjnego i testowego z zachowaniem kolejności czasowej.
Zastosowania LSTM jest stosowana do zadań, w których dane mają naturalną strukturę sekwencyjną. Przykłady obejmują: rozpoznawanie mowy; rozpoznawanie pisma ręcznego; modelowanie języka; etykietowanie sekwencji; analizę szeregów czasowych; predykcję sygnałów; generowanie tekstu; analizę danych biomedycznych; prognozowanie ruchu i danych czasoprzestrzennych.