Вопрос или проблема
Я реализую в PyTorch модель LSTM для предсказания, увеличится или уменьшится закрывающая стоимость акции через 5 и 10 минут. Конкретно, я использую 24 года данных с интервалом в 5 минут с 19 признаками, разделенных на блоки по одной неделе на прогноз (используя 7 различных акций). Проблема, с которой я столкнулся, заключается в том, что, как ни старайся, модель LSTM, похоже, предсказывает значения вокруг одного определенного значения, чтобы всегда минимизировать потерю, что не снижается слишком сильно.
Я предварительно подготавливаю входные данные и цели в torch.tensors с размерностью [batch_size, sequence_len, features] (в моем случае это [32, 2016, 19]), нормализую их между 0 и 1 и подаю в мою модель LSTM, которая структурирована следующим образом:
class MultiInputOutputLSTM(nn.Module):
def __init__(self, input_size, hidden_size, num_layers, output_size, dropout, lr, batch_size):
super(MultiInputOutputLSTM, self).__init__()
self.input_size = input_size
self.hidden_size = hidden_size
self.num_layers = num_layers
self.dropout = dropout
self.batch_size = batch_size
self.loss_list = []
self.accuracy = 0
self.predictions_list = [0]
self.lstm = nn.LSTM(input_size = self.input_size, hidden_size = self.hidden_size, num_layers = self.num_layers, dropout = self.dropout, batch_first=True)
self.fc = nn.Linear(hidden_size, output_size, bias=True)
self.sigmoid = nn.Sigmoid()
self.criterion = nn.BCEWithLogitsLoss()
self.optimizer = torch.optim.RMSprop(self.parameters(), lr = lr, alpha=0.9, weight_decay=1e-4, momentum=0.5)
self.scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(self.optimizer, 'min')
def forward(self, x):
h0 = torch.zeros(self.num_layers, self.batch_size, self.hidden_size)
c0 = torch.zeros(self.num_layers, self.batch_size, self.hidden_size)
lstm_out, _ = self.lstm(x, (h0, c0))
output = self.fc(lstm_out[:, -1, :])
return output
def train_step(self ,x, y):
self.train()
predictions_1 = torch.round(self.forward(x))
predictions = self.forward(x)
if (predictions_1.detach().cpu().numpy()[0] == y.detach().cpu().numpy()[0]).all():
self.accuracy += 1
self.predictions_list.append(predictions.detach().cpu().numpy()[0][0])
penalty = torch.mean((predictions-0.5)**2)
loss = self.criterion(predictions, y) + penalty
self.scheduler.step(loss)
self.optimizer.zero_grad()
self.loss_list.append(loss.item())
mean_loss = sum(self.loss_list)/len(self.loss_list)
loss.backward()
torch.nn.utils.clip_grad_norm_(self.parameters(), max_norm=6)
self.optimizer.step()
return loss.item(), mean_loss, self.accuracy, predictions.detach().cpu().numpy()[0], y.detach().cpu().numpy()[0]
Цели равны 1, если цена растет, 0 в противном случае.
Гиперпараметры такие:
input_size = 19
hidden_size = 3
num_layers = 5
output_size = 2
lr = 0.001
num_epochs = 5
batch_size = 32
dropout = 0.3
Сначала я создаю dataframe из CSV файла: затем я делю его на цели и входные данные, и с последними вычисляю различные торговые сигналы.
Затем я преобразую данные в подходящий вид для модели LSTM, разделяя их на недельные блоки.
Модель должна предсказывать значения, близкие к единице, если цена растет, и близкие к нулю, если цена остается неизменной/снижается: однако, она создает одно предсказание и затем постепенно снижается к отметке 0.5, оставаясь на месте до конца процесса обучения. Разделение на обучающую и тестовую выборки составляет 85-15%.
Вот список того, что я пробовал: уменьшение или увеличение скорости обучения (от 0.00001 до 0.1), размера выхода (с большим и меньшим количеством прогнозов), batch_size (от 1 до 256), num_layers (от 1 до 5), input_size (с 1, 2, 3… 19 признаками), dropout (от 0 до 0.7) и num_epochs (от 1 до 100).
Я пробовал Adam оптимизатор, затем SDG, затем RMSProp оптимизатор, изменяя alpha, momentum и weight_decay.
Я пробовал менять входные данные, чтобы лучше соответствовать целям (заменяя каждый элемент на 0 и 1: 1, если элемент больше предыдущего, 0 в противном случае) или использовать приросты между элементами.
Также пробовал BCELoss (с слоем активации sigmoid в self.forward()), L1Loss, MSELoss, CrossEntropyLoss, и даже регулировал потери, добавляя штраф penalty = torch.mean((y-predictions)**(-2))
, но это ничего не изменило, значения все равно колеблются вокруг 0.5. Средняя потеря снижается с 1.174 до 1.040.
Сейчас я пытаюсь сильно штрафовать значения вокруг 0.5 с помощью penalty = torch.mean((predictions-0.5)**(-2))
, но предсказания идут в одном направлении и остаются около 0 или 1, не обучаясь.
Что я могу сделать, чтобы решить эти проблемы? (кто-то предложил, что это может быть исчезающий градиент, но я действительно не знаю, как решить эту проблему)
Ответ или решение
Проблема, с которой вы столкнулись при реализации модели LSTM в PyTorch для предсказания изменения стоимости акций, типична для задач временных рядов и классификации. Вы описали, что ваша модель, независимо от попыток изменить гиперпараметры или стратегию обучения, кажется, стремится к предсказаниям, близким к постоянному значению, главным образом к 0.5, вместо того чтобы правильно разбирать входные данные и делать значимые прогнозы. Рассмотрим, почему это может происходить, и обсудим возможные решения.
Теория
Начнем с понимания природы задачи. Ваша модель решает задачу бинарной классификации: нужно определить, возрастет или уменьшится цена. Столкновение с постоянными предсказаниями вокруг одного значения может быть вызвано несколькими факторами, такими как дисбаланс данных или проблемы с архитектурой и гиперпараметрами модели.
-
Проблема дисбаланса классов. Если ваши данные сильно дисбалансированы, т. е. один класс значительно преобладает над другим, модель может легко минимизировать функцию потерь, просто предсказывая более частый класс.
-
Ванишинг градиент. В LSTM этот феномен менее выражен, чем в других архитектурах, таких как стандартные рекуррентные нейронные сети (RNN), однако при глубоких сетях или при использовании маленьких значений градиентов он все еще может быть проблемой.
-
Архитектура и гиперпараметры. Открытые архитектуры могут плохо справляться с задачей, особенно если емкость модели (например, количество нейронов в скрытых слоях) не соотносится с размерностью и сложностью данных. Кроме того, выбор функции активации, функции потерь и оптимизатора может существенно влиять на производительность.
Пример
На практике, эффективное решение проблемы требует применения комплексного подхода:
-
Анализ и предобработка данных:
- Подтвердите баланс классов в данных. Если один из классов представлен непропорционально, стоит рассмотреть стратегии сэмплинга, такие как увеличение или уменьшение чрезмерно представленных классов, или применение методов взвешивания потерь.
- Нормализуйте и стандартизируйте входные данные. Убедитесь, что все признаки находятся в сопоставимых масштабах.
-
Проверка архитектуры модели:
- Попробуйте увеличить или уменьшить
hidden_size
. Это может помочь модели лучше улавливать сложные зависимости. - Увеличьте количество эпох обучения, чтобы дать возможность модели проявить свои способности.
- Измените количество слоев LSTM. Иногда менее глубокая модель может лучше справляться с задачей.
- Попробуйте увеличить или уменьшить
-
Выбор функций потерь и оптимизации:
- Используйте взвешенную кросс-энтропию, чтобы компенсировать дисбаланс в данных.
- Попробуйте использовать оптимизатор AdamW, который часто лучше справляется с задачами, где присутствует проблема переобучения и где важна правильная настройка коэффициентов регуляризации.
Применение
На основании приведенной теории и примеров, разработайте план действий:
-
Пересмотрите баланс классов в ваших данных. Если данные очень дисбалансированы, внедрите взвешивание классов в функции потерь или примените методы повторной выборки.
-
Испытайте настройки параметров модели. Рассмотрите возможность увеличения
hidden_size
для LSTM, хотя важно помнить, что слишком большие размеры могут привести к переобучению. -
Проверяйте архитектуру и оптимизатор:
- Проверьте, чтобы
dropout
не был слишком высоким; это может приводить к "забыванию" всей полной зависимости данных. - Пробуйте различные стратегии регуляризации, такие как L2-регуляризация в оптимизаторе Adam или AdamW.
- Проверьте, чтобы
-
Если структура входных данных использует признаки, недопонимание связи которых с выводом может вредить модели, проведите переработку: может быть полезно сократить количество используемых признаков для сокрытия информации.
-
Рассмотрите возможность использования более сложной архитектуры, такой как двойные LSTM с разными фильтрами или внедрение дополнительных слоев регуляризации.
Ваши текущие усилия, направленные на уменьшение потерь и корректировку параметров, означают, что вы движетесь в правильном направлении. Главное, чтобы каждый шаг был подкреплен обоснованным анализом и проверялся экспериментально. Внедрение стратегий, описанных выше, может помочь вам найти более устойчивое решение для вашей задачи предсказания направления цен на акции.