Long Short-Term Memory - praktycznie
LSTM (Long Short-Term Memory) to specjalny typ rekurencyjnej sieci neuronowej (RNN).
Problem tradycyjnych RNN:
Rozwiązanie LSTM:
LSTM doskonale sprawdza się w problemach sekwencyjnych:
LSTM składa się z komórki pamięci i trzech bramek:
Stan komórki (Cell State): główna pamięć, przepływa przez całą sekwencję z minimalnymi zmianami.
# Utwórz środowisko wirtualne
python -m venv lstm_env
# Aktywuj (Linux/Mac)
source lstm_env/bin/activate
# Aktywuj (Windows)
lstm_env\Scripts\activate
# Zainstaluj TensorFlow
pip install tensorflow
# Biblioteki pomocnicze
pip install numpy pandas matplotlib scikit-learn
# Sprawdź wersję TensorFlow
python -c "import tensorflow as tf; print(tf.__version__)"
# Opcjonalnie: Jupyter Notebook
pip install jupyterimport numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# TensorFlow i Keras
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
# Preprocessing
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
# Sprawdź dostępność GPU
print("GPU dostępne:", tf.config.list_physical_devices('GPU'))
print("TensorFlow version:", tf.__version__)
# Ustaw seed dla powtarzalności
np.random.seed(42)
tf.random.set_seed(42)LSTM wymaga danych w formacie 3D:
(samples, timesteps, features)
Przykład:
Jeśli mamy 1000 sekwencji, każda po 50 kroków czasowych, z 3 cechami:
shape = (1000, 50, 3)
Funkcja do tworzenia sekwencji z szeregu czasowego:
def create_sequences(data, seq_length):
"""
Tworzy sekwencje dla LSTM
data: array 1D lub 2D z danymi
seq_length: długość sekwencji (timesteps)
Returns: X (sequences), y (targets)
"""
X, y = [], []
for i in range(len(data) - seq_length):
# Sekwencja wejściowa
X.append(data[i:i + seq_length])
# Wartość docelowa (następny krok)
y.append(data[i + seq_length])
return np.array(X), np.array(y)
# Przykład użycia
data = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
X, y = create_sequences(data, seq_length=3)
print("X shape:", X.shape) # (7, 3)
print("y shape:", y.shape) # (7,)
print("Pierwsza sekwencja X:", X[0]) # [1, 2, 3]
print("Pierwszy target y:", y[0]) # 4LSTM działa najlepiej z danymi znormalizowanymi do zakresu [0, 1] lub [-1, 1].
from sklearn.preprocessing import MinMaxScaler
# Przykładowe dane - ceny akcji
data = np.array([100, 102, 105, 103, 107, 110, 108]).reshape(-1, 1)
# Inicjalizacja skalera
scaler = MinMaxScaler(feature_range=(0, 1))
# Dopasuj i transformuj dane treningowe
data_scaled = scaler.fit_transform(data)
print("Oryginalne:", data.flatten())
print("Znormalizowane:", data_scaled.flatten())
# WAŻNE: Zapisz scaler do późniejszego odwrócenia transformacji
# Po predykcji musisz użyć: scaler.inverse_transform()
# Tworzenie sekwencji z znormalizowanych danych
X, y = create_sequences(data_scaled, seq_length=3)
# Reshape dla LSTM: dodaj wymiar features
X = X.reshape(X.shape[0], X.shape[1], 1)
print("X shape dla LSTM:", X.shape) # (4, 3, 1)# Dla szeregów czasowych: NIE mieszamy danych!
# Podział chronologiczny
train_size = int(len(X) * 0.8)
X_train = X[:train_size]
y_train = y[:train_size]
X_test = X[train_size:]
y_test = y[train_size:]
print(f"Dane treningowe: {X_train.shape}")
print(f"Dane testowe: {X_test.shape}")
# BŁĄD: Nie używaj train_test_split z shuffle=True!
# from sklearn.model_selection import train_test_split
# X_train, X_test, y_train, y_test = train_test_split(
# X, y, test_size=0.2, shuffle=True # ZŁE dla szeregów!
# )
# Opcjonalnie: walidacja
val_size = int(len(X_train) * 0.2)
X_val = X_train[-val_size:]
y_val = y_train[-val_size:]
X_train = X_train[:-val_size]
y_train = y_train[:-val_size]from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense
# Inicjalizacja modelu
model = Sequential()
# Warstwa LSTM
# units=50 - liczba jednostek LSTM (neuronów)
# input_shape=(timesteps, features)
model.add(LSTM(units=50, input_shape=(X_train.shape[1], X_train.shape[2])))
# Warstwa wyjściowa Dense
# 1 neuron - predykcja pojedynczej wartości
model.add(Dense(units=1))
# Podsumowanie modelu
model.summary()
# Kompilacja modelu
model.compile(
optimizer='adam',
loss='mean_squared_error',
metrics=['mae']
)Najważniejsze parametry LSTM:
Pozostałe ważne parametry:
Używaj return_sequences=True dla wszystkich warstw LSTM oprócz ostatniej:
from tensorflow.keras.layers import LSTM, Dense, Dropout
model = Sequential()
# Pierwsza warstwa LSTM
# return_sequences=True - zwraca pełną sekwencję dla następnej warstwy
model.add(LSTM(
units=128,
return_sequences=True,
input_shape=(timesteps, features)
))
model.add(Dropout(0.2))
# Druga warstwa LSTM
model.add(LSTM(units=64, return_sequences=True))
model.add(Dropout(0.2))
# Trzecia warstwa LSTM
# return_sequences=False - ostatnia warstwa, zwraca tylko końcowe wyjście
model.add(LSTM(units=32))
model.add(Dropout(0.2))
# Warstwy Dense
model.add(Dense(units=16, activation='relu'))
model.add(Dense(units=1)) # Wyjście
model.compile(optimizer='adam', loss='mse', metrics=['mae'])Bidirectional LSTM przetwarza sekwencję w obu kierunkach (od początku i od końca).
Kiedy używać:
from tensorflow.keras.layers import Bidirectional, LSTM, Dense
model = Sequential()
# Bidirectional LSTM
# Podwaja liczbę parametrów (forward + backward)
model.add(Bidirectional(
LSTM(units=64, return_sequences=True),
input_shape=(timesteps, features)
))
model.add(Bidirectional(LSTM(units=32)))
model.add(Dense(units=1))
model.compile(optimizer='adam', loss='mse')
# Output shape po Bidirectional(LSTM(64)):
# (batch_size, 128) - 64*2 jednostek# Dla regresji (przewidywanie wartości ciągłych)
model.compile(
optimizer='adam',
loss='mean_squared_error', # lub 'mse'
metrics=['mae', 'mse']
)
# Dla klasyfikacji binarnej
model.compile(
optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy']
)
# Dla klasyfikacji wieloklasowej
model.compile(
optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy']
)
# Dostosowany learning rate
from tensorflow.keras.optimizers import Adam
optimizer = Adam(learning_rate=0.001)
model.compile(optimizer=optimizer, loss='mse', metrics=['mae'])# Trening modelu
history = model.fit(
X_train, y_train,
epochs=50, # Liczba epok
batch_size=32, # Rozmiar batcha
validation_data=(X_val, y_val), # Dane walidacyjne
verbose=1 # 1=progress bar, 0=cichy, 2=jedna linia na epokę
)
# Parametry:
# - epochs: ile razy przejść przez cały zbiór treningowy
# - batch_size: ile próbek w jednym batchu (32, 64, 128)
# - validation_data: opcjonalnie dane do walidacji
# - validation_split: alternatywnie % danych treningowych (np. 0.2)
# Historia treningu
print("Loss treningowy:", history.history['loss'])
print("Loss walidacyjny:", history.history['val_loss'])from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
# EarlyStopping - zatrzymaj gdy nie ma poprawy
early_stop = EarlyStopping(
monitor='val_loss', # Monitoruj loss walidacyjny
patience=10, # Czekaj 10 epok bez poprawy
restore_best_weights=True, # Przywróć najlepsze wagi
verbose=1
)
# ModelCheckpoint - zapisz najlepszy model
checkpoint = ModelCheckpoint(
'best_model.h5', # Nazwa pliku
monitor='val_loss',
save_best_only=True, # Zapisz tylko najlepszy
verbose=1
)
# Trening z callbacks
history = model.fit(
X_train, y_train,
epochs=100,
batch_size=32,
validation_data=(X_val, y_val),
callbacks=[early_stop, checkpoint],
verbose=1
)
print(f"Trening zatrzymany po {len(history.history['loss'])} epokach")import matplotlib.pyplot as plt
# Wykres loss
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(history.history['loss'], label='Train Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.title('Model Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.grid(True)
# Wykres MAE
plt.subplot(1, 2, 2)
plt.plot(history.history['mae'], label='Train MAE')
plt.plot(history.history['val_mae'], label='Validation MAE')
plt.title('Model MAE')
plt.xlabel('Epoch')
plt.ylabel('MAE')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
# Diagnoza: jeśli val_loss rośnie a train_loss maleje = overfitting!# Ewaluacja na zbiorze testowym
test_loss, test_mae = model.evaluate(X_test, y_test, verbose=0)
print(f"Test Loss: {test_loss:.4f}")
print(f"Test MAE: {test_mae:.4f}")
# Predykcja
predictions = model.predict(X_test)
# Odwrócenie normalizacji
predictions = scaler.inverse_transform(predictions)
y_test_actual = scaler.inverse_transform(y_test.reshape(-1, 1))
# Metryki
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
mse = mean_squared_error(y_test_actual, predictions)
mae = mean_absolute_error(y_test_actual, predictions)
rmse = np.sqrt(mse)
r2 = r2_score(y_test_actual, predictions)
print(f"MSE: {mse:.4f}")
print(f"MAE: {mae:.4f}")
print(f"RMSE: {rmse:.4f}")
print(f"R²: {r2:.4f}")import matplotlib.pyplot as plt
plt.figure(figsize=(14, 5))
# Wykres porównania
plt.plot(y_test_actual, label='Rzeczywiste wartości', marker='o')
plt.plot(predictions, label='Predykcje', marker='x')
plt.title('LSTM - Predykcje vs Rzeczywiste wartości')
plt.xlabel('Próbka')
plt.ylabel('Wartość')
plt.legend()
plt.grid(True)
plt.show()
# Scatter plot
plt.figure(figsize=(8, 8))
plt.scatter(y_test_actual, predictions, alpha=0.5)
plt.plot([y_test_actual.min(), y_test_actual.max()],
[y_test_actual.min(), y_test_actual.max()],
'r--', lw=2)
plt.xlabel('Rzeczywiste wartości')
plt.ylabel('Predykcje')
plt.title('Predykcje vs Rzeczywiste')
plt.grid(True)
plt.show()def predict_future(model, last_sequence, n_steps, scaler):
"""
Predykcja n kroków w przyszłość
model: wytrenowany model LSTM
last_sequence: ostatnia znana sekwencja (znormalizowana)
n_steps: ile kroków przewidzieć
scaler: do odwrócenia normalizacji
"""
predictions = []
current_sequence = last_sequence.copy()
for _ in range(n_steps):
# Reshape do formatu (1, timesteps, features)
input_seq = current_sequence.reshape(1, timesteps, features)
# Predykcja następnej wartości
next_pred = model.predict(input_seq, verbose=0)
predictions.append(next_pred[0, 0])
# Aktualizuj sekwencję: usuń pierwszy element, dodaj predykcję
current_sequence = np.append(current_sequence[1:], next_pred)
# Odwróć normalizację
predictions = scaler.inverse_transform(np.array(predictions).reshape(-1, 1))
return predictions
# Użycie
future_predictions = predict_future(model, X_test[-1], n_steps=10, scaler=scaler)
print("Predykcje na 10 kroków:", future_predictions.flatten())import pandas as pd
import numpy as np
from sklearn.preprocessing import MinMaxScaler
# 1. Wczytaj dane
df = pd.read_csv('stock_prices.csv')
data = df['Close'].values.reshape(-1, 1)
# 2. Normalizacja
scaler = MinMaxScaler()
data_scaled = scaler.fit_transform(data)
# 3. Tworzenie sekwencji (60 dni -> przewiduj następny)
seq_length = 60
X, y = create_sequences(data_scaled, seq_length)
X = X.reshape(X.shape[0], X.shape[1], 1)
# 4. Podział danych
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:]from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dropout, Dense
from tensorflow.keras.callbacks import EarlyStopping
# 5. Budowa modelu
model = Sequential([
LSTM(128, return_sequences=True, input_shape=(60, 1)),
Dropout(0.3),
LSTM(64, return_sequences=True),
Dropout(0.3),
LSTM(32),
Dropout(0.2),
Dense(1)
])
model.compile(optimizer='adam', loss='mse', metrics=['mae'])
# 6. Trening
early_stop = EarlyStopping(monitor='val_loss', patience=15,
restore_best_weights=True)
history = model.fit(
X_train, y_train,
epochs=100,
batch_size=32,
validation_split=0.1,
callbacks=[early_stop],
verbose=1
)
# 7. Predykcja i odwrócenie normalizacji
predictions = model.predict(X_test)
predictions = scaler.inverse_transform(predictions)
y_test_actual = scaler.inverse_transform(y_test)from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.layers import Embedding, LSTM, Dense
# 1. Przykładowe dane
texts = ["Film był świetny!", "Okropny film", "Nieźle", "Fantastyczny!"]
labels = [1, 0, 1, 1] # 1=pozytywny, 0=negatywny
# 2. Tokenizacja
tokenizer = Tokenizer(num_words=5000)
tokenizer.fit_on_texts(texts)
sequences = tokenizer.texts_to_sequences(texts)
# 3. Padding - wyrównaj długości
max_length = 20
X = pad_sequences(sequences, maxlen=max_length, padding='post')
y = np.array(labels)
# 4. Model z Embedding
vocab_size = 5000
embedding_dim = 128
model = Sequential([
Embedding(vocab_size, embedding_dim, input_length=max_length),
LSTM(64, dropout=0.2),
Dense(1, activation='sigmoid')
])
model.compile(optimizer='adam', loss='binary_crossentropy',
metrics=['accuracy'])
model.fit(X, y, epochs=10, batch_size=32)import pandas as pd
# 1. Dane pogodowe (wiele cech)
df = pd.read_csv('weather.csv')
features = ['temperature', 'humidity', 'pressure', 'wind_speed']
data = df[features].values
# 2. Normalizacja dla każdej cechy
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
data_scaled = scaler.fit_transform(data)
# 3. Sekwencje (7 dni -> przewiduj temperaturę)
seq_length = 7
X, y = [], []
for i in range(len(data_scaled) - seq_length):
X.append(data_scaled[i:i+seq_length])
y.append(data_scaled[i+seq_length, 0]) # Tylko temperatura
X = np.array(X) # shape: (samples, 7, 4)
y = np.array(y) # shape: (samples,)
# 4. Model wielocechowy
model = Sequential([
LSTM(64, return_sequences=True, input_shape=(7, 4)),
LSTM(32),
Dense(16, activation='relu'),
Dense(1)
])
model.compile(optimizer='adam', loss='mse')
model.fit(X, y, epochs=50, batch_size=32, validation_split=0.2)Objawy:
Rozwiązania:
Objawy:
Rozwiązania:
# Przykład: zwiększenie pojemności modelu
model = Sequential([
LSTM(256, return_sequences=True, input_shape=(timesteps, features)),
LSTM(128, return_sequences=True),
LSTM(64),
Dense(32, activation='relu'),
Dense(1)
])Objawy:
Rozwiązania:
# 1. Gradient Clipping
from tensorflow.keras.optimizers import Adam
optimizer = Adam(learning_rate=0.001, clipnorm=1.0)
model.compile(optimizer=optimizer, loss='mse')
# 2. Batch Normalization
from tensorflow.keras.layers import BatchNormalization
model = Sequential([
LSTM(64, return_sequences=True, input_shape=(timesteps, features)),
BatchNormalization(),
LSTM(32),
BatchNormalization(),
Dense(1)
])
# 3. Zmiana learning rate
optimizer = Adam(learning_rate=0.01) # Większy LR
# 4. Learning Rate Schedule
from tensorflow.keras.callbacks import ReduceLROnPlateau
reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.5,
patience=5, min_lr=1e-7)
model.fit(X_train, y_train, callbacks=[reduce_lr])Kluczowe hiperparametry do dostrojenia:
Metody optymalizacji:
# Zapisz cały model (architektura + wagi + optimizer)
model.save('lstm_model.h5')
# lub w nowym formacie:
model.save('lstm_model.keras')
# Zapisz tylko wagi
model.save_weights('lstm_weights.h5')
# Zapisz jako SavedModel (TensorFlow format)
model.save('saved_model/')
# Załaduj model
from tensorflow.keras.models import load_model
model = load_model('lstm_model.h5')
# Załaduj tylko wagi (wymaga wcześniejszego zbudowania modelu)
model.load_weights('lstm_weights.h5')
# Zapisz scaler (ważne!)
import pickle
with open('scaler.pkl', 'wb') as f:
pickle.dump(scaler, f)
# Załaduj scaler
with open('scaler.pkl', 'rb') as f:
scaler = pickle.load(f)
# Predykcja załadowanym modelem
predictions = model.predict(X_test)LSTM jest bardzo wymagający obliczeniowo - GPU może przyspieszyć trening 10-50x!
import tensorflow as tf
# Sprawdź dostępność GPU
print("GPU dostępne:", len(tf.config.list_physical_devices('GPU')) > 0)
print("Urządzenia:", tf.config.list_physical_devices())
# Wymuś użycie CPU (do testów)
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '-1'
# Wymuś użycie GPU 0
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
# Ogranicz pamięć GPU
gpus = tf.config.list_physical_devices('GPU')
if gpus:
try:
tf.config.set_logical_device_configuration(
gpus[0],
[tf.config.LogicalDeviceConfiguration(memory_limit=4096)]
)
except RuntimeError as e:
print(e)
# Mixed Precision - szybsze obliczenia na GPU
from tensorflow.keras import mixed_precision
policy = mixed_precision.Policy('mixed_float16')
mixed_precision.set_global_policy(policy)1. Przygotowanie danych:
2. Architektura modelu:
3. Trening:
4. Ewaluacja:
Dokumentacja:
Dalsze tematy do nauki:
Praktyka:
import numpy as np
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout
from tensorflow.keras.callbacks import EarlyStopping
# 1. Dane
data = np.sin(np.linspace(0, 100, 1000)).reshape(-1, 1)
# 2. Normalizacja
scaler = MinMaxScaler()
data_scaled = scaler.fit_transform(data)
# 3. Sekwencje
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)
X, y = create_sequences(data_scaled, 50)
X = X.reshape(X.shape[0], X.shape[1], 1)# 4. Podział
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:]
# 5. Model
model = Sequential([
LSTM(64, return_sequences=True, input_shape=(50, 1)),
Dropout(0.2),
LSTM(32),
Dropout(0.2),
Dense(1)
])
model.compile(optimizer='adam', loss='mse', metrics=['mae'])
# 6. Trening
early_stop = EarlyStopping(monitor='val_loss', patience=10,
restore_best_weights=True)
history = model.fit(X_train, y_train, epochs=100, batch_size=32,
validation_split=0.1, callbacks=[early_stop])
# 7. Ewaluacja
predictions = model.predict(X_test)
predictions = scaler.inverse_transform(predictions)
y_test_actual = scaler.inverse_transform(y_test)
# 8. Zapisz
model.save('lstm_sine.h5')
print("Model gotowy!")Co się nauczyliśmy:
Pamiętaj:
Następny krok: Wybierz problem i zacznij praktykę!
"The future depends on what you do today" - Przyszłość zależy od tego, co robisz dzisiaj
Powodzenia w budowaniu modeli LSTM!