Важность криптографических функций для глубокого обучения с подкреплением

Эта статья написана Берендом Гортом и Брюсом Янгом, членами основной команды проекта с открытым исходным кодом AI4Finance. Этот проект представляет собой сообщество с открытым исходным кодом, которое делится инструментами искусственного интеллекта для финансов и является частью Колумбийского университета в Нью-Йорке. Ссылка на гитхаб:



Результаты

В моей новой статье мы представляем некоторые результаты с использованием этого пайплайна:



Введение

Целью этого руководства является выполнение анализа важности функций для нейронной сети, которая используется во время глубокого обучения с подкреплением (DRL). Для выбора лучших функций используются традиционные методы машинного обучения.

Проще говоря, этот конвейер создан для того, чтобы:

  1. Определите функции, обеспечивающие высокую предсказуемость повышения или понижения цены (в этой записной книжке)
  2. Создайте соответствующую сеть для DRL (в этом блокноте)
  3. Вставьте их в ДХО (не в этот блокнот)

Конвейер представляет собой комбинацию идей, представленных Лопесом де Прадо, М. (2018) в Достижениях в области финансового машинного обучения. Этот учебник следует аналогичной методологии, но отличается тем, что нейронная сеть используется для классификации бездействия, короткого или длительного. Эта статья разделена на пять основных разделов:

  • Глава 2: Адаптированный загрузчик Binance
  • Глава 3: Метод тройного барьера
  • Ch5: Дробная дифференциация
  • Ch6: Моделирование (в нашем случае с помощью нейронной сети)
  • Глава 8. Важность функции

Функциональный блокнот Google Colab доступен в репозитории FinRL-Meta фонда AI4Finance:



Ch2: Адаптированный загрузчик Binance

Если у вас нет опыта работы с данными по свечам (или клинам), мы рекомендуем это видео:

Во-первых, требуется неограниченный объем данных. Этот процессор не имеет ограничений на количество клинов, которые вы хотите скачать. Однако для этого требуются ключи API данных от Binance.

Ладно, вернемся к процессору. Класс имеет пять важных методов:

  1. бегать()
  2. download_data()
  3. add_technical_indicator()
  4. df_to_array()
  5. get_binance_bars()

Чтобы использовать этот класс, нужно просто создать его экземпляр, а затем применить метод run(). Поэтому мы объясним метод запуска в последовательном порядке. и иди оттуда. Экземпляру BinanceProcessor() требуется только информация API.

После этого вы можете вызвать метод запуска экземпляра. Метод запуска требует:

  • ticker_list: список торговых пар, которые вы хотите скачать. Они должны быть доступны на любом из рынков Binance.
  • start_date / end_date: обе строки в правильном формате (Y%-M%-D% h%:m%:s%). Указание начала загрузки и окончания
  • time_interval: выборка данных между датами начала и окончания. Формат должен соответствовать Документации Binance.
  • technical_indicator_list: в настоящее время добавление самоопределяемых технических индикаторов для загрузчика Binance еще не поддерживается. Это означает, что для анализа дополнительных функций необходимо вручную добавить их в процессор. Кроме того, в процессор могут быть включены другие источники данных, чтобы иметь дополнительные функции. Это выходит за рамки данной статьи и оставлено читателю в качестве упражнения.
  • if_vix=Ложь. Игнорируйте это пока.

Теперь читатель понимает входные данные метода run(). После этого вызывается функция download_data().

data = self.download_data(ticker_list, start_date, end_date, time_interval)

Который проходит через ticker_list и создает отдельный фрейм данных для каждого тикера и добавляет их вместе. Для этой записной книжки Google Colab рекомендуется ограничить анализ одной криптовалютой. Остальной код еще не создан для мультикриптографического анализа.

Цикл по ticker_list применяет get_binance_bars() для каждого тикера в ticker_list. Этот метод использует клиент Binance для получения klines, отбрасывает те, которые нас не интересуют, и изменяет метку временной метки на индекс. Хорошо, теперь мы вернемся к методу run(). Мы получили наш фрейм данных. Чистим его и применяем метод add_technical_indicators.

data = self.add_technical_indicator(data, technical_indicator_list)

Здесь в дело вступает TA-lib (библиотека технического анализа). Здесь представлен список поддерживаемых индикаторов. Вы можете добавить любые функции TA-lib в процессор данных, такие как индекс относительной силы (RSI), схождение-расхождение скользящих средних (MACD), индекс товарного канала (CCI) и индекс направленного движения (DX). Большинство этих индикаторов рассчитываются по клинам (или данным Open-High-Low-Close-Volume, OHLCV). Эта часть оставлена ​​в качестве упражнения для читателя.

Фрейм данных pandas теперь готов для дальнейшего анализа. Метод df_to_array() преобразует фрейм данных в массивы Numpy, которые могут быть прямым входом для алгоритмов глубокого обучения с подкреплением (DRL), представленных в AI4Finance GitHub. Это не является целью данной статьи, поэтому наши данные готовы сейчас как есть.

Глава 3: Метод тройного барьера (стр. 43, Лопес де Прадо, М.)

Рассмотрим матрицу признаков X с I строками. Матрица характеристик существует из данных OHLCV и добавлены технические индикаторы. Пример:

Давайте сначала поработаем над вашей интуицией. Останься со мной здесь. Рассмотрим график BTC/USDT ниже. Рисуем красную коробку 30-08-2017, цена 4580. Как будем рисовать коробку пока не важно. На коробке есть три важные строки:

  • Если цена сначала достигает нижней части поля, мы обозначаем наблюдение y = 0 (цена достигает стоп-лосса).
  • Если цена сначала достигает верхней части поля, мы обозначаем наблюдение y = 2 (цена достигает фиксации прибыли).
  • Если цена достигает правой стороны поля, мы помечаем наблюдение y = 1, время ожидания ставки истекло (значительных изменений цены во временном интервале поля нет).

Представьте, что мы выполнили эту маркировку, теперь у нас есть что предсказывать на основе имеющихся у нас функций! 0: открывать короткие позиции, 1: ничего не делать, 2: открывать длинные позиции.

Теперь давайте найдем разумный способ получить форму коробки. Эквивалентное правило маркировки в математической форме:

Здесь 𝜏 — заранее определенный порог, который мы определим позже. t(i,0) — временной индекс X(i)-го наблюдения. t(i,0) + h — номер h-го бара после t(i,0). Теперь на английском; с определенной даты, например 30 августа 2017 г., добавьте hточки данных. Если возврат идет ниже нижней полосы, описанной ранее, пометьте его как 0. Если абсолютное значение возврата остается между верхней и нижней полосой, тег равен 1. Наконец, если доход превышает верхнюю полосу, пометьте его как 2.

Следовательно, r(t, h) — доходность цены за горизонт бара h:

Мы хотим установить горизонтальные полосы фиксации прибыли / стоп-лосса в зависимости от дневной волатильности. Волатильность — это мера того, насколько сильно цена колеблется вверх и вниз, и если мы не приспосабливаемся к этим колебаниям цены до установив поле, наше поле будет слишком большим или слишком маленьким для того, чтобы цена попадала в поле достаточно последовательно.

Приведенная ниже функция перебирает временные метки и соответствующую волатильность, созданную приведенным выше фрагментом. Количество переданных точек данных — это количество наблюдений, прошедших с момента первого наблюдения.

Затем давайте сначала создадим значения барьера. Вертикальный барьер будет отметкой времени, а два других барьера — ценой.

  • Вертикальный барьер – это тайм-аут, определяемый параметром t_final с точки зрения количества переданных точек данных. Поэтому, если точек данных все еще достаточно и установлен тайм-аут, вертикальный барьер — это просто индекс (метка времени) индекса волатильности плюс тайм-аут.

  • Затем верхний барьер устанавливается во втором операторе if. Верхний барьер рассчитывается как динамическая функция дневной волатильности, от текущей цены, умноженной на множитель. Этот множитель является постоянным и устанавливается заранее.

  • Для нижнего барьера счет такой же, как и для верхнего барьера, но в отрицательном направлении.

Пример недавно созданных барьеров фрейма данных:

Хорошо, теперь мы получили наши цели. Мы можем начать маркировать соответственно. Эта функция следует правилу маркировки, определенному ранее. Во-первых, мы получаем дату начала и тайм-аута, что соответствует индексу и установленному вертикальному барьеру. Затем находим цены на экстремумах ящика. После этого проверяем условие, были ли попадания в верхний или нижний барьер. Наконец, если не было ударов по барьеру, он ударил по вертикальному барьеру.

Ch5: Дробное дифференцирование (стр. 75), ! НЕОБЯЗАТЕЛЬНО!

Эта часть представляет собой пример преобразования данных, эффект от которого вы можете измерить. Вы также можете пропустить к Главе 8: Важность функции и использовать фрейм данных, который мы создали в предыдущих разделах. Это пример преобразования данных, эффект от которого мы можем количественно оценить позже в следующем разделе.

В заключительной части, глава 8: Важность функции формализована ошибка потери журнала. Следовательно, мы можем напрямую измерить влияние преобразования данных, такого как дробное дифференцирование, на ошибку потери журнала! Конечно, вы можете протестировать свои преобразования данных.

Хорошо, давайте начнем наше дробное дифференцирование:

Машинное обучение любит стационарные данные. Итак, если у нас есть нестационарные данные (например, цена BTC с 2015 года, которую можно аппроксимировать экспоненциальной функцией), мы можем их дифференцировать. Однако, поскольку цены всегда представляют собой временной ряд, текущая цена зависит от долгой истории предыдущих уровней, это называется памятью.

Итак, нам нужны стационарные данные путем их дифференцирования. Однако полная дифференциация данных приведет к потере информации. Поэтому мы хотим дробно дифференцировать данные, то есть дифференцировать данные не с целым числом (например, дифференцирование первого порядка; 1), а с дробью, например 0,20.

ЦЕЛЬ: Нам нужны стационарные данные, сохраняя при этом как можно больше памяти о прошлом!

Дробное дифференцирование использовало биномиальное разложение для оценки исходных данных, но с более низким порядком. Цель состоит в том, чтобы сделать данные более стационарными, сохраняя при этом как можно больше памяти:

Итак, мы различаем здесь w.r.t. д . Давайте посмотрим, как действительное (не целое → дробное) положительное d сохраняет память. Этот арифметический ряд состоит из скалярного произведения. Приведенному выше ряду даются определенные имена переменных и, наконец, они упрощаются; это просто преобразование биномиального расширения:

Таким образом, веса могут быть описаны как:

Хорошо, так о чем все это? См. следующий рисунок. По оси Y отложено значение весов w. Легенда описывает дробную дифференцированную переменную d. Наконец, по оси x находится итератор k.

Единственное, что вы должны понять об этой фигуре, это три вещи:

  • Если d = 0 → все веса равны нулю, кроме w_0. Поэтому никакого дифференцирования не происходит, и оценка равняется исходному ряду.
  • Если d = 1 → w_0 = 1 и w_1 = -1. Остальные веса равны нулю! Посмотрите на уравнения ниже, выглядит знакомым? Поскольку разница в шаге между X(t) и X(t-1) равна единице, это производная первого порядка!

  • Чем меньше d, тем больше весов по-прежнему имеет ненулевое значение при увеличении k. Это означает, что память сохраняется (поскольку учитываются члены более высокого порядка).

Вот и все, вы только что поняли дробное дифференцирование (в двух словах). Итак, теперь возникает новый важный вопрос: какое значение d приемлемо?

Применяя метод fracdiff окна фиксированной ширины (FFD) к временному ряду, мы можем вычислить минимальный коэффициент d, чтобы результирующий дробно-дифференцированный ряд { X ̃ t } t=l,…,T был стационарным. Этот коэффициент d количественно определяет объем памяти, который необходимо удалить для достижения стационарного состояния.

Мы будем использовать статистический тест ADF, чтобы отслеживать, есть ли во временном ряду единичный корень, который указывает, является ли временной ряд стационарным или нет. Если вам интересно, посмотрите это видео.

Следующая таблица + рисунок отображают статистику ADF по ценам журнала BTC/USDT. По оси x отображается значение d, используемое для создания ряда, на основе которого вычислялась статистика ADF.

Исходный ряд имеет статистику ADF, равную –2,0 (синяя линия слева), а серия возвратов имеет статистику ADF, равную –110,35 (синяя линия, правая сторона). При доверительном уровне 95% (красная пунктирная линия) критическое значение теста составляет 0,1832 (где синий цвет пересекает красную пунктирную линию). На левой оси ординат показана корреляция между исходным рядом (d = 0) и дифференцированным рядом при различных значениях d. При d = 0,1832 корреляция между дифференцированным рядом и исходным рядом еще очень высока и составляет 0,993. После этого мы потеряем много памяти.

Это подтверждает, что процедура, представленная в этой главе, успешно достигла стационарности без потери слишком большого объема памяти. Напротив, корреляция между исходным рядом и рядом результатов составляет всего 0,011806, что показывает, что стандартное целочисленное дифференцирование почти полностью стирает память ряда.

Ch6: Моделирование классификатора для прогнозирования меток 0, 1, 2 с помощью нейронной сети

Вот здесь-то и вступает в игру глубокое обучение с подкреплением (DRL). Если у вас нет опыта использования DRL в криптографии, этот видеоурок (также сделанный мной) подробно объясняет, как инфраструктура AI4Finance DRL может использоваться для эффективного обучения агентов в криптопространстве. Это очень мощная структура, в которую легко попасть.

! ПРИМЕЧАНИЕ: мы хотим подчеркнуть здесь, что понимание видео ниже НЕ обязательно для понимания/подписки на этот средний пост!

Итак, AI4Finance предлагает структуру предварительно закодированных агентов DRL. В файле net.py определены все нейронные сети, соответствующие каждому агенту.



Мы хотим протестировать нейронную сеть на ее производительность прогнозирования при использовании функций, которые мы определили ранее. Например, меня интересует ActorPPO, я нахожу его в файле net.py. Я сделал копию (немного в другом формате) этой конкретной нейронной сети. Поскольку у нас есть проблема с несколькими классами, мы не делаем окончательный вывод tanh(), а вместо этого продаем/ничего не делаем/покупаем.

После этого, в следующей части, есть много руководств, и мы рекомендуем вам, если вы хотите получить более подробную информацию, найти их в Интернете.

Нам нужен фиктивный класс, который превращает данные в итерируемые элементы, чтобы PyTorch DataLoader мог выполнить итерацию по ним и получить длину:

Код ниже

  • Сделайте простой пробный сплит поезда.
  • MinMaxScaler() для масштабирования входных данных от 0 до 1. fit_transform() для данных поезда и transform() для тестовых данных (погуглите, вы этого не знаете).
  • Создайте два экземпляра ClassifierDataset().
  • Передайте их классу PyTorch DataLoader, чтобы получить итерируемый элемент
  • Критерием является CrossEntropyLoss() для проблем с несколькими классами (помните, что в конце уже есть softmax). Мы используем оптимизатор Adam:
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model_NN1.parameters(), lr=0.0001)

Замечания о функциях обучения и тестирования приведены ниже:

  • output = model(data), вывод здесь представляет собой матрицу batch_size * классов. Размерность классов - это логарифмическая вероятность короткого/ничего не делать/длинного.
  • nn.CrossEntropyLoss()критерийтребуетлогарифмические вероятности в качестве входных данных и цель в виде вектора классов (0, 1, 2) .
  • Остальное говорит само за себя

Тогда петля поезда выглядит очень просто

model_NN1.to(device)
# State fold (no PurgedKFold build yet, ignore this
fold = 0
for epoch in range(1, epochs + 1):
correct_train, train_loss = train(fold, model_NN1, device, train_loader, optimizer, epoch)
test(fold, model_NN1, device, test_loader, correct_train, train_loss)

Важное заключительное примечание:

Теперь, например, вы можете тренироваться 300 эпох с определенной скоростью обучения. Точность обучения продолжает медленно приближаться к 100% (как и ожидалось). Однако точность теста не превышает 60% после эпохи 150. Это означает, что после эпохи 150 мы полностью переобучаем набор поездов. Если вы добавите несколько базовых слоев в существующую сеть ActorPPO, точность уже может достигать 75%.

Главный вопрос:

Можем ли мы теперь сделать вывод, что нейронная сеть ActorPPO в ElegantRL во время обучения DRL с этими специфическими функциями недостаточно велика для захвата всей информации в заданном наборе данных?

Я оставляю это читателю (и себе) в качестве гипотезы, поскольку это само по себе может стать целым исследовательским вопросом.

Ch8: Важность функции

Теперь измерение важности признаков с помощью обученной нейронной сети объясняется довольно просто. Выполнение этого в PyTorch делает его менее понятным.

Методика следующая:

  1. Для каждого столбца в ваших функциях перетасуйте этот столбец.
  2. Сделайте прогноз с перетасованным столбцом.
  3. Найдите ошибку log_loss между реальной целью и вновь предсказанной (с перетасованными столбцами)
  4. Добавить эти ошибки в список
  5. Сортировать список
  6. Определите, какая ошибка является самой высокой из-за перетасовки столбцов
  7. Измеряйте важность функции

Прочтите приведенный ниже код.

Окончательный результат указан ниже:

Теперь вы можете начать играть с этим пайплайном!

  • Вы хотите дробные дифференцированные функции?
  • Какие технические индикаторы вам нужны?
  • Какие преобразования данных хорошо справляются с увеличением ошибки при перемешивании?
  • Каков должен быть размер вашей сети?
  • Какие слои вы хотите использовать в своей нейронной сети для агента DRL?

Результаты

В моей новой статье мы представляем некоторые результаты



Спасибо, что рассказали о важности криптографических функций для DRL!

~ Беренд и Брюс

Ссылки

0: Лопес де Прадо, М. (2018). Достижения в области финансового машинного обучения. Джон Уайли и сыновья.

1: Фонд AI4Finance

2: Обнаружение оптимальных торговых правил с маркировкой тройного барьера

3: Разметка данных, метод тройного барьера

4: Финансовое машинное обучение, часть 1: метки

5: Метамаркировка: решение проблемы нестационарности и определения размера позиции

6: Достижения в области финансового машинного обучения