Szereg czasowy to sekwencja obserwacji uporządkowanych chronologicznie, mierzonych w regularnych odstępach czasu.
Charakterystyczne cechy:
Tradycyjne modele (ARIMA, ES):
Dlaczego sieci neuronowe?
Sztuczny neuron:
Inspirowany biologicznym neuronem, przetwarza sygnały wejściowe i produkuje wyjście.
Działanie neuronu:
Matematyczny opis działania neuronu:
gdzie:
Klasyczna sieć feedforward (MLP):
Problem z szeregami czasowymi:
Przykład:
Jeśli chcemy przewidzieć cenę akcji, klasyczna sieć przyjmie np. ostatnie 10 cen jako wejście, ale nie rozumie, że są one uporządkowane chronologicznie.
Rekurencyjna Sieć Neuronowa (Recurrent Neural Network) to rodzaj sieci, która ma połączenia zwrotne, pozwalające na przetwarzanie sekwencji danych.
Kluczowa różnica:
RNN ma pamięć - zapamiętuje poprzednie stany i wykorzystuje je do przetwarzania bieżącego wejścia.
Mechanizm działania:
Stan ukryty w kroku t:
Wyjście w kroku t:
gdzie:
Wizualizacja RNN jako sekwencja:
Tę samą komórkę RNN można przedstawić jako sekwencję połączonych neuronów w czasie:
Krok t=1: x(1) → [RNN] → y(1), h(1)
Krok t=2: x(2), h(1) → [RNN] → y(2), h(2)
Krok t=3: x(3), h(2) → [RNN] → y(3), h(3)
...
Kluczowe obserwacje:
Problem:
Podczas uczenia sieci przez propagację wsteczną (backpropagation), gradienty są mnożone przez siebie w każdym kroku czasowym.
Konsekwencje:
Praktyczny przykład:
Jeśli chcemy przewidzieć cenę akcji za tydzień na podstawie cen z ostatnich 100 dni, klasyczny RNN będzie miał problem z zapamiętaniem tego, co działo się 90-100 dni temu.
Dlaczego to problem?
Rozwiązanie:
Sieci LSTM (Long Short-Term Memory) zostały zaprojektowane specjalnie do rozwiązania tego problemu!
LSTM (Long Short-Term Memory) to specjalny typ RNN zaprojektowany do zapamiętywania informacji przez długi czas.
Wynaleziono: 1997, Hochreiter i Schmidhuber
Kluczowa innowacja:
LSTM wprowadza stan komórki (cell state) - "pas transmisyjny" dla informacji, który przepływa przez całą sekwencję z minimalnymi zmianami.
Co zyskujemy dzięki LSTM?
Kiedy używać LSTM?
Komórka LSTM składa się z 4 głównych elementów:
1. Stan komórki (Cell State) - C(t)
2. Stan ukryty (Hidden State) - h(t)
3. Trzy bramki kontrolujące przepływ informacji:
Każda bramka to warstwa sigmoidalna (σ), która produkuje wartości 0-1:
Kluczowa idea:
Bramki uczą się automatycznie, które informacje są istotne, a które można zapomnieć.
Krok 1: Decyzja co zapomnieć
Forget gate analizuje poprzedni stan ukryty h(t-1) i bieżące wejście x(t), aby zdecydować, które informacje ze stanu komórki C(t-1) należy usunąć.
Wzór matematyczny:
gdzie:
Interpretacja wartości:
Przykład praktyczny:
W prognozowaniu pogody, jeśli wykryto zmianę sezonu, forget gate może "zapomnieć" wzorce z poprzedniego sezonu.
Analogia:
Wyobraź sobie, że czytasz książkę. Forget gate decyduje, które szczegóły z poprzednich rozdziałów są nadal istotne dla zrozumienia bieżącego fragmentu.
Krok 2: Decyzja co dodać do stanu
Input gate decyduje, jakie nowe informacje dodać do stanu komórki.
Dwa kroki:
a) Które wartości zaktualizować:
b) Kandydat na nowe wartości:
gdzie:
Aktualizacja stanu komórki:
gdzie ⊙ oznacza mnożenie element po elemencie (Hadamard)
Interpretacja:
Nowy stan = (co zachować ze starego stanu) + (jakie nowe informacje dodać)
Krok 3: Decyzja co wypuścić na wyjście
Output gate decyduje, która część stanu komórki zostanie wypuszczona jako stan ukryty h(t).
Wzory matematyczne:
1. Bramka wyjściowa:
2. Nowy stan ukryty:
Interpretacja:
Podsumowanie przepływu LSTM:
Kluczowa idea:
LSTM automatycznie uczy się, które informacje zachować, zaktualizować lub zapomnieć!
import numpy as np
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
# Wczytaj dane szeregu czasowego
df = pd.read_csv('stock_prices.csv')
data = df['Close'].values.reshape(-1, 1)
# Normalizacja danych do zakresu [0, 1]
scaler = MinMaxScaler(feature_range=(0, 1))
scaled_data = scaler.fit_transform(data)
# Utworzenie sekwencji (sliding window)
def create_sequences(data, seq_length):
X, y = [], []
for i in range(len(data) - seq_length):
X.append(data[i:i+seq_length])
y.append(data[i+seq_length])
return np.array(X), np.array(y)
# Okno czasowe = 60 dni
seq_length = 60
X, y = create_sequences(scaled_data, seq_length)
# Podział na zbiór treningowy i testowy (80/20)
train_size = int(len(X) * 0.8)
X_train, X_test = X[:train_size], X[train_size:]
y_train, y_test = y[:train_size], y[train_size:]
print(f"X_train shape: {X_train.shape}") # (samples, 60, 1)
print(f"y_train shape: {y_train.shape}") # (samples, 1)from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout
from tensorflow.keras.optimizers import Adam
# Budowa modelu
model = Sequential([
# Pierwsza warstwa LSTM (return_sequences=True dla kolejnej warstwy LSTM)
LSTM(units=50, return_sequences=True, input_shape=(seq_length, 1)),
Dropout(0.2), # Zapobiega przeuczeniu
# Druga warstwa LSTM
LSTM(units=50, return_sequences=False),
Dropout(0.2),
# Warstwa Dense (w pełni połączona)
Dense(units=25),
# Warstwa wyjściowa
Dense(units=1)
])
# Kompilacja modelu
model.compile(
optimizer=Adam(learning_rate=0.001),
loss='mean_squared_error',
metrics=['mae']
)
# Wyświetl architekturę
model.summary()from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
# Callbacks
early_stop = EarlyStopping(
monitor='val_loss',
patience=10,
restore_best_weights=True
)
checkpoint = ModelCheckpoint(
'best_model.h5',
monitor='val_loss',
save_best_only=True
)
# Trening modelu
history = model.fit(
X_train, y_train,
epochs=100,
batch_size=32,
validation_split=0.1,
callbacks=[early_stop, checkpoint],
verbose=1
)
# Ewaluacja na zbiorze testowym
test_loss, test_mae = model.evaluate(X_test, y_test)
print(f"Test Loss: {test_loss:.4f}")
print(f"Test MAE: {test_mae:.4f}")# Predykcja na zbiorze testowym
predictions = model.predict(X_test)
# Odwrócenie normalizacji
predictions = scaler.inverse_transform(predictions)
y_test_actual = scaler.inverse_transform(y_test)
# Wizualizacja wyników
import matplotlib.pyplot as plt
plt.figure(figsize=(14, 6))
plt.plot(y_test_actual, label='Rzeczywiste wartości', color='blue')
plt.plot(predictions, label='Prognoza LSTM', color='red')
plt.title('Prognoza cen akcji - LSTM')
plt.xlabel('Czas')
plt.ylabel('Cena')
plt.legend()
plt.grid(True)
plt.show()
# Metryki dokładności
from sklearn.metrics import mean_squared_error, mean_absolute_error
import math
rmse = math.sqrt(mean_squared_error(y_test_actual, predictions))
mae = mean_absolute_error(y_test_actual, predictions)
mape = np.mean(np.abs((y_test_actual - predictions) / y_test_actual)) * 100
print(f"RMSE: {rmse:.2f}")
print(f"MAE: {mae:.2f}")
print(f"MAPE: {mape:.2f}%")def predict_future(model, last_sequence, steps, scaler):
"""
Prognozuje 'steps' kroków w przyszłość.
Args:
model: wytrenowany model LSTM
last_sequence: ostatnie 'seq_length' obserwacji
steps: ile kroków w przyszłość przewidzieć
scaler: obiekt MinMaxScaler do denormalizacji
"""
predictions = []
current_sequence = last_sequence.copy()
for _ in range(steps):
# Prognoza następnego kroku
next_pred = model.predict(current_sequence.reshape(1, seq_length, 1))
predictions.append(next_pred[0, 0])
# Aktualizacja sekwencji (usuń pierwszy, dodaj prognozę)
current_sequence = np.append(current_sequence[1:], next_pred)
# Denormalizacja
predictions = scaler.inverse_transform(np.array(predictions).reshape(-1, 1))
return predictions
# Prognoza na 30 dni w przyszłość
last_60_days = scaled_data[-60:]
future_predictions = predict_future(model, last_60_days, steps=30, scaler=scaler)
print("Prognoza na najbliższe 30 dni:")
print(future_predictions.flatten())1. Bidirectional LSTM (Dwukierunkowy)
Przetwarza sekwencję w obu kierunkach (przód i tył), łącząc informacje z przeszłości i przyszłości.
Zastosowanie: Klasyfikacja sekwencji, gdy mamy dostęp do całej sekwencji.
2. Stacked LSTM (Wielowarstwowy)
Kilka warstw LSTM ułożonych jedna na drugiej. Głębsze sieci mogą uczyć się bardziej abstrakcyjnych reprezentacji.
3. GRU (Gated Recurrent Unit)
Uproszczona wersja LSTM z mniejszą liczbą parametrów:
4. Attention Mechanism
Pozwala modelowi "skupić się" na najważniejszych częściach sekwencji.
5. Encoder-Decoder LSTM
Architektura sekwencja-do-sekwencji (seq2seq), używana w tłumaczeniu maszynowym.
from tensorflow.keras.layers import Bidirectional
# Model z Bidirectional LSTM
model = Sequential([
# Warstwa Bidirectional LSTM
# Przetwarza sekwencję w obu kierunkach
Bidirectional(
LSTM(units=50, return_sequences=True),
input_shape=(seq_length, 1)
),
Dropout(0.2),
# Druga warstwa Bidirectional LSTM
Bidirectional(LSTM(units=50)),
Dropout(0.2),
Dense(units=25),
Dense(units=1)
])
# Uwaga: Bidirectional podwaja liczbę parametrów
# (2 × units, bo forward + backward)
model.compile(
optimizer='adam',
loss='mse',
metrics=['mae']
)
model.summary()from tensorflow.keras.layers import GRU
# Model z GRU zamiast LSTM
model_gru = Sequential([
# Warstwa GRU
GRU(units=50, return_sequences=True, input_shape=(seq_length, 1)),
Dropout(0.2),
# Druga warstwa GRU
GRU(units=50, return_sequences=False),
Dropout(0.2),
Dense(units=25),
Dense(units=1)
])
model_gru.compile(optimizer='adam', loss='mse')
# Porównanie liczby parametrów
print("LSTM parameters:", model.count_params())
print("GRU parameters:", model_gru.count_params())
# GRU ma ~25% mniej parametrów niż LSTM
# GRU jest szybszy w treningu, ale LSTM często
# osiąga lepsze wyniki na złożonych zadaniach1. Finanse
2. Energia i Utilities
3. Handel i E-commerce
4. Meteorologia
5. Healthcare
ARIMA vs LSTM
AspektARIMALSTMZłożonośćProsty, interpretowalnyZłożony, "czarna skrzynka"NieliniowośćTylko liniowe zależnościMoże uczyć się nieliniowych wzorcówDane wielowymiaroweTrudne (VARIMAX)Naturalne wsparcieDługoterminowe zależnościOgraniczoneBardzo dobre (LSTM)Wymaga stacjonarnościTAKNIECzas treninguSzybkiWolny (wymaga GPU)Ilość danychDziała na małych zbiorachWymaga dużo danych (1000+ obserwacji)Dobór parametrówWymaga ekspertyzyAutomatyczny (ale hyperparametry)
Kiedy użyć LSTM?
Kiedy użyć ARIMA?
Kluczowe hyperparametry do dostrojenia:
1. Architektura sieci
2. Sekwencja
3. Trening
4. Regularyzacja
Metody optymalizacji:
1. Przygotowanie danych
2. Podział danych
3. Walidacja
4. Unikaj przeuczenia
5. Ewaluacja