Представьте себе: вы пытаетесь предсказать будущее, как современный Нострадамус, но вместо хрустальных шаров у вас есть блоки с управляемыми входами. Не переживайте, если ваше последнее предсказание касалось завтрашней погоды (спойлер: шёл дождь… снова), мы собираемся сделать так, чтобы вы выглядели компетентным!
1. Почему GRUs — ваш новый лучший друг
Блоки с управляемыми входами (GRUs) похожи на более молодого и быстрого брата LSTM, который не попал в семейную драму «затвора памяти». Они используют обновляющий и сбросной затворы, чтобы решить, какую информацию сохранить или отбросить — представьте их как вышибал в ночном клубе нейронной сети. Вот почему они хороши для временных рядов:
- Обучение на 18% быстрее, чем у LSTM без потери точности ([PDF-колдовство]);
- Идеально подходят для последовательных данных, таких как цены на акции, потребление энергии или ваш недельный цикл кофейной зависимости;
- Меньше параметров = меньше драмы с переобучением.
2. Подготовка данных: поваренная книга путешественника во времени
Мы будем использовать набор данных об энергии PJM East — потому что прогнозирование потребления энергии — это всё равно что быть электрическим экстрасенсом. Совет: никогда не доверяйте временным рядам, которые выглядят чище, чем ваша воскресная обувь.
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
# Загрузка данных с помощью pandas — швейцарский армейский нож для работы с данными
data = pd.read_csv('pjme_hourly.csv', index_col=, parse_dates=)
data = data.resample('D').mean().ffill() # Ежедневное повторное усреднение, потому что кому нужна 60-минутная тревога?
# Нормализация между 0 и 1, как ваши ожидания после 2020 года
scaler = MinMaxScaler()
scaled_data = scaler.fit_transform(data.values)
# Создание последовательностей — здесь происходит настоящее волшебство
def create_sequences(data, seq_length):
xs, ys = [], []
for i in range(len(data)-seq_length-1):
x = data[i:(i+seq_length)]
y = data[i+seq_length]
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
X, y = create_sequences(scaled_data, seq_length=7)
3. Создание модели GRU: версия PyTorch
Время собрать наших нейронных мстителей! Мы будем использовать PyTorch, потому что это как LEGO для глубокого обучения.
import torch
import torch.nn as nn
class GRUProphet(nn.Module):
def __init__(self, input_size=1, hidden_size=50, num_layers=2):
super().__init__()
self.gru = nn.GRU(input_size, hidden_size, num_layers, batch_first=True)
self.linear = nn.Linear(hidden_size, 1)
def forward(self, x):
# GRU возвращает: вывод, скрытое состояние
gru_out, _ = self.gru(x) # Мы игнорируем скрытое состояние, как неловкий зрительный контакт
last_time_step = gru_out[:, -1, :]
return self.linear(last_time_step)
model = GRUProphet()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
loss_fn = nn.MSELoss()
4. Обучение: где происходит магия (и переобучение)
Разделите свои данные, как мастер карате, и обучите эту модель. Помните: потеря валидации — вот кто говорит правду.
from sklearn.model_selection import train_test_split
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, shuffle=False)
# Преобразование в тензоры PyTorch
train_X = torch.Tensor(X_train).unsqueeze(-1)
train_y = torch.Tensor(y_train)
val_X = torch.Tensor(X_val).unsqueeze(-1)
val_y = torch.Tensor(y_val)
# Цикл обучения — идеальное время для перерыва на кофе ☕
for epoch in range(100):
model.train()
preds = model(train_X)
loss = loss_fn(preds, train_y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
# Проверка валидации
model.eval()
with torch.no_grad():
val_preds = model(val_X)
val_loss = loss_fn(val_preds, val_y)
if epoch % 10 == 0:
print(f"Epoch {epoch} | Train Loss: {loss.item():.4f} | Val Loss: {val_loss.item():.4f}")
5. Оценка: момент истины
Сравните свои прогнозы с фактическими значениями. Если они совпадают, станцуйте от радости. Если нет, вините гиперпараметры!
import matplotlib.pyplot as plt
# Обратное преобразование прогнозов
train_preds = scaler.inverse_transform(preds.detach().numpy())
val_preds = scaler.inverse_transform(val_preds.detach().numpy())
plt.figure(figsize=(12,6))
plt.plot(data.index[-len(val_y):], scaler.inverse_transform(y_val), label='Фактические')
plt.plot(data.index[-len(val_y):], val_preds, label='Прогнозируемые', alpha=0.7)
plt.title("Энергетическая схватка за прогнозирование потребления")
plt.legend()
plt.show()
6. Прогнозирование будущего
Потому что какой смысл быть магом времени, если вы не можете предсказать завтрашние цифры?
def predict_future(model, data, steps, seq_length=7):
model.eval()
predictions = []
current_seq = data[-seq_length:]
for _ in range(steps):
with torch.no_grad():
seq_tensor = torch.Tensor(current_seq[-seq_length:]).unsqueeze(0).unsqueeze(-1)
pred = model(seq_tensor)
predictions.append(pred.item())
current_seq = np.append(current_seq, pred.item())[1:]
return scaler.inverse_transform(np.array(predictions).reshape(-1,1))
next_week_energy = predict_future(model, scaled_data, steps=7)
print(f"Потребление энергии на следующей неделе: {next_week_energy.flatten()}")
Заключительные советы от профессионалов
- Настраивайте длину последовательности, как будто настраиваете гитару — 7 дней хорошо подходит для недельных паттернов;
- Добавьте больше слоёв, если ваши данные более драматичны, чем мыльная опера;
- Попробуйте разные скейлеры, когда ваши данные ведут себя как бунтующий подросток;
- Используйте дропаут, если ваша модель начинает запоминать, как тот ребёнок в школе. Помните, даже самые лучшие модели не могут предсказать, когда сломается ваша кофемашина. Для этого вам понадобится настоящий хрустальный шар… или договор на техническое обслуживание. Удачного прогнозирования! 🚀