Глава 203: Прунинг торговых моделей
1. Введение
Прунинг (обрезка) нейронных сетей — одна из самых мощных техник создания эффективных, готовых к развертыванию торговых моделей. В мире алгоритмической торговли, особенно высокочастотной торговли (HFT), каждая микросекунда задержки инференса напрямую переводится в прибыль или убыток. Модель, которая на 10% быстрее делает предсказания, может захватить возможности, которые более медленные конкуренты упускают полностью.
Современные модели глубокого обучения, используемые в торговле, часто содержат миллионы параметров. Однако исследования последовательно показывают, что большая часть этих параметров вносит минимальный вклад в предсказательную способность модели. Прунинг использует это наблюдение: систематически удаляя избыточные или малозначимые веса, мы можем создавать модели, которые значительно меньше и быстрее, сохраняя при этом почти всю исходную точность.
Эта глава предоставляет всестороннее рассмотрение техник прунинга нейронных сетей применительно к торговым моделям. Мы рассматриваем математические основы, исследуем различные стратегии прунинга, обсуждаем практические сценарии развертывания (FPGA, периферийные устройства, колокированные серверы) и предоставляем полную реализацию на Rust, демонстрирующую прунинг на реальных рыночных данных с биржи Bybit.
Ключевое понимание, лежащее в основе прунинга, состоит в том, что переопределённые сети обучают разреженные решения — большая часть “знаний” сконцентрирована в относительно малом подмножестве весов. Наша цель — идентифицировать и сохранить это подмножество, отбросив остальное.
2. Математические основы
2.1 Прунинг по магнитуде весов
Простейший и наиболее широко используемый критерий прунинга — магнитуда (абсолютное значение) весов. Для нейронной сети с тензорами весов $W_l$ для каждого слоя $l$, прунинг по магнитуде удаляет веса, абсолютные значения которых ниже порога $\tau$:
$$M_{ij}^{(l)} = \begin{cases} 1 & \text{если } |W_{ij}^{(l)}| \geq \tau \ 0 & \text{если } |W_{ij}^{(l)}| < \tau \end{cases}$$
где $M^{(l)}$ — бинарная маска прунинга для слоя $l$. Обрезанная сеть вычисляет:
$$\hat{W}^{(l)} = W^{(l)} \odot M^{(l)}$$
где $\odot$ обозначает поэлементное умножение.
Глобальный прунинг устанавливает единый порог $\tau$ для всех слоев, определяемый желаемым уровнем разреженности $s \in [0, 1]$. Если мы хотим удалить $s \times 100%$ всех весов, $\tau$ выбирается как $s$-й процентиль абсолютных значений всех конкатенированных весов.
Послойный прунинг применяет отдельный порог $\tau_l$ к каждому слою независимо, гарантируя, что каждый слой достигает точно целевой разреженности. Это предотвращает патологический случай, когда глобальный прунинг удаляет почти все веса из малых слоев, едва затрагивая большие.
2.2 Прунинг на основе градиентов
Методы на основе градиентов используют градиент функции потерь для оценки важности каждого веса. Интуиция в том, что вес с большим градиентом активно используется при обучении и, следовательно, важен.
Чувствительность веса $W_{ij}^{(l)}$ может быть приближена как:
$$S_{ij}^{(l)} = \left| W_{ij}^{(l)} \cdot \frac{\partial \mathcal{L}}{\partial W_{ij}^{(l)}} \right|$$
Это связано с разложением Тейлора изменения потерь при удалении веса:
$$\Delta \mathcal{L} \approx \frac{\partial \mathcal{L}}{\partial W_{ij}} \cdot W_{ij} + \frac{1}{2} \frac{\partial^2 \mathcal{L}}{\partial (W_{ij})^2} \cdot (W_{ij})^2$$
Член первого порядка дает вышеуказанный критерий чувствительности. Включение члена второго порядка (гессиана) обеспечивает более точные оценки важности, но является вычислительно затратным.
2.3 Структурный и неструктурный прунинг
Неструктурный прунинг удаляет отдельные веса независимо от их положения в матрице весов. Это создает разреженные матрицы с нерегулярными паттернами разреженности. Хотя он достигает наивысших коэффициентов сжатия при заданном уровне точности, нерегулярные паттерны доступа к памяти затрудняют достижение реального ускорения инференса на стандартном оборудовании без специализированных библиотек разреженных матриц.
Структурный прунинг удаляет целые структурные единицы:
- Прунинг нейронов: удаляет целые строки/столбцы из матриц весов, что эквивалентно удалению нейронов из слоя.
- Прунинг фильтров: в свёрточных сетях удаляет целые фильтры (выходные каналы).
- Прунинг слоёв: удаляет целые слои из сети.
- Блочный прунинг: удаляет непрерывные блоки весов, создавая полуструктурную разреженность.
Преимущество структурного прунинга в том, что результирующая модель является стандартной плотной моделью (просто меньшей), поэтому не нужны специальные библиотеки разреженных вычислений, и ускорение реализуется на любом оборудовании.
2.4 Метрики разреженности
Мы количественно оцениваем степень прунинга с помощью метрики разреженности:
$$\text{sparsity} = 1 - \frac{\text{количество ненулевых весов}}{\text{общее количество весов}}$$
Разреженность 0.9 означает, что 90% весов были удалены. Мы также отслеживаем:
- Коэффициент сжатия: $\frac{\text{исходный размер}}{\text{размер после прунинга}}$
- Сокращение FLOPs: снижение числа операций с плавающей точкой при инференсе
- Сохранение точности: $\frac{\text{точность обрезанной модели}}{\text{точность исходной модели}} \times 100%$
3. Стратегии прунинга
3.1 Одноразовый прунинг
Одноразовый прунинг — самый простой подход: обучаем модель до сходимости, обрезаем её один раз до желаемой разреженности и, опционально, дообучаем оставшиеся веса. Процедура:
- Обучить плотную модель до сходимости
- Вычислить оценки важности для всех весов
- Удалить нижние $s%$ весов по важности
- Дообучить обрезанную модель в течение нескольких эпох
Одноразовый прунинг хорошо работает при умеренных уровнях разреженности (до ~50-70%), но качество быстро деградирует при более высокой разреженности. Он быстр и прост, что делает его подходящим, когда необходима быстрая компрессия модели и целевая разреженность умеренна.
3.2 Итеративный прунинг по магнитуде (IMP)
Итеративный прунинг по магнитуде достигает значительно более высоких уровней разреженности за счет постепенного прунинга в несколько раундов:
- Обучить плотную модель до сходимости
- Обрезать малую долю $p$ оставшихся весов (например, 20%)
- Переобучить обрезанную модель в течение $n$ эпох
- Повторять шаги 2-3 до достижения желаемой разреженности
На каждой итерации $t$ эффективная разреженность составляет:
$$s_t = 1 - (1 - p)^t$$
Например, при $p = 0.2$ (обрезка 20% оставшихся весов каждый раунд), после 10 раундов разреженность составляет $1 - 0.8^{10} \approx 0.893$ или примерно 89%.
IMP последовательно превосходит одноразовый прунинг при высоких уровнях разреженности, поскольку модель имеет возможность адаптировать оставшиеся веса после каждого шага прунинга. Фаза переобучения позволяет сети перераспределить информацию от обрезанных весов к сохранившимся.
3.3 Гипотеза лотерейного билета
Гипотеза лотерейного билета (Frankle и Carlin, 2019) утверждает, что плотные сети содержат разреженные подсети (“выигрышные билеты”), которые при обучении в изоляции от той же инициализации могут достичь производительности полной сети.
Процедура поиска лотерейных билетов:
- Инициализировать сеть весами $W_0$
- Обучить до сходимости, получив веса $W_T$
- Обрезать нижние $s%$ весов по магнитуде, создав маску $M$
- Сбросить оставшиеся веса к их начальным значениям: $W_0 \odot M$
- Переобучить эту разреженную сеть от исходной инициализации
Это имеет глубокие последствия для торговли: если мы можем идентифицировать выигрышный билет рано, мы можем обучать и развертывать малую, быструю модель с самого начала, экономя как вычисления на обучение, так и задержку инференса.
На практике поиск точных лотерейных билетов затратен. Практический компромисс — позднее перематывание: вместо сброса к $W_0$ сбрасываем к весам с ранних этапов обучения $W_k$, где $k$ — малое число эпох. Это ослабление делает технику более робастной и часто достаточно.
3.4 Расписание прунинга
Расписание прунинга определяет, как разреженность увеличивается со временем. Распространённые расписания:
- Линейное: $s_t = s_{\text{target}} \cdot \frac{t}{T}$
- Кубическое: $s_t = s_{\text{target}} \left(1 - \left(1 - \frac{t}{T}\right)^3\right)$
- Экспоненциальное: $s_t = s_{\text{target}} \cdot (1 - e^{-\alpha t})$
Кубическое расписание обычно предпочтительно, поскольку оно агрессивно обрезает в начале (когда избыточности больше всего) и замедляется позже (когда каждый оставшийся вес более важен).
4. Торговые применения
4.1 Снижение задержки в HFT
В высокочастотной торговле конвейер инференса должен выполняться за микросекунды. Типичный конвейер модели HFT включает:
- Приём рыночных данных (~1-5 мкс)
- Вычисление признаков (~2-10 мкс)
- Инференс модели (~5-50 мкс)
- Генерация ордеров (~1-5 мкс)
Инференс модели часто является узким местом. Обрезанная модель с разреженностью 90% может сократить время инференса в 3-5 раз при использовании структурного прунинга (что транслируется в меньшие плотные матрицы) или в 2-3 раза при неструктурном прунинге с оптимизированными разреженными ядрами.
Для торговой модели с 1М параметров, прунинг до 10% плотности дает модель на 100К параметров, которая:
- Полностью помещается в кэш L1 современных процессоров
- Требует в 10 раз меньше операций умножения-накопления
- Выдает предсказания с практически идентичной точностью
4.2 Развертывание на FPGA и периферийных устройствах
Торговые фирмы всё чаще развертывают модели на FPGA для ультранизколатентного инференса. FPGA имеют ограниченную встроенную память (обычно 1-10 МБ блочной RAM), что делает компрессию моделей необходимой.
Структурный прунинг особенно ценен для развертывания на FPGA, потому что:
- Меньшие модели помещаются во встроенную BRAM, избегая медленного доступа к внешней памяти
- Регулярные вычислительные паттерны эффективно отображаются на фабрику FPGA
- Сокращенное количество параметров означает меньше нужных блоков DSP
- Энергопотребление масштабируется с размером модели
Развертывание на периферийных устройствах для спутниковых или удаленных торговых систем также выигрывает от прунинга. Модели, работающие на процессорах ARM или специализированных AI-ускорителях, должны работать в строгих рамках мощности и памяти.
4.3 Снижение переобучения
Торговые модели печально известны склонностью к переобучению из-за низкого отношения сигнал/шум в финансовых данных. Прунинг действует как неявный регуляризатор:
- Удаление параметров снижает ёмкость модели, ограничивая пространство гипотез
- Сохранившиеся веса должны кодировать действительно предсказательные признаки
- Обрезанные модели лучше обобщаются на невиданные рыночные режимы
- Итеративный прунинг с переобучением заставляет модель находить робастные представления
Эмпирически обрезанные торговые модели часто превосходят свои плотные аналоги на данных вне выборки, даже когда плотная модель имеет более высокую точность на данных в выборке. Это один из наиболее убедительных аргументов в пользу прунинга в торговых приложениях.
5. Структурный прунинг
5.1 Прунинг фильтров и каналов
Для свёрточных архитектур, используемых при обработке рыночных данных (например, обработка ценовых рядов как одномерных сигналов), прунинг фильтров удаляет целые свёрточные фильтры. Важность фильтра $f$ в слое $l$ обычно измеряется как:
$$\text{Importance}(f) = |W_f^{(l)}|1 = \sum{i,j,k} |W_{f,i,j,k}^{(l)}|$$
Фильтры с наименьшей L1-нормой удаляются. Это напрямую сокращает количество выходных каналов, уменьшая последующие слои (поскольку они получают меньше входных каналов).
5.2 Прунинг нейронов в полносвязных слоях
Для полносвязных слоев, часто используемых в торговых моделях, прунинг нейронов удаляет целые нейроны, обнуляя все входящие и исходящие веса. Важность нейрона может быть измерена по:
- Магнитуде активации: нейроны, которые постоянно производят близкие к нулю активации, могут быть удалены
- Норме весов: нейроны с малыми нормами входящих весов имеют минимальное влияние
- Избыточности: нейроны, чьи активации сильно коррелированы с другими нейронами, избыточны
Удаление нейрона из слоя с $n$ нейронами уменьшает вычисления этого слоя на $\frac{1}{n}$ и удаляет одну строку из матрицы весов и один элемент из вектора смещений.
5.3 Прунинг слоёв
Для очень глубоких моделей целые слои могут быть удалены, если они вносят минимальный вклад в выход. Это измеряется сравнением входных и выходных представлений слоя:
$$\text{Importance}(l) = |h_l - h_{l-1}|_2$$
Если выход слоя очень похож на его вход (т.е. он вычисляет приблизительно тождественную функцию), его можно пропустить. Это наиболее применимо в остаточных архитектурах, где пропускающие соединения естественно обрабатывают удаление слоёв.
6. Пошаговое руководство по реализации
Наша реализация на Rust предоставляет полный фреймворк прунинга нейронных сетей. Ключевые компоненты:
6.1 Архитектура сети
Мы реализуем сеть прямого распространения с поддержкой масок прунинга. Каждый слой хранит как свои веса, так и бинарную маску:
pub struct PrunableLayer { pub weights: Array2<f64>, pub biases: Array1<f64>, pub mask: Array2<f64>, // 1.0 = активен, 0.0 = обрезан}Прямой проход применяет маску: effective_weights = weights * mask.
6.2 Прунинг по магнитуде
Функция magnitude_prune реализует глобальный прунинг по магнитуде:
- Собрать абсолютные значения всех немаскированных весов
- Отсортировать их для нахождения порога на целевом процентиле разреженности
- Установить записи маски в 0.0, где |вес| < порог
6.3 Итеративный прунинг по магнитуде
Функция iterative_prune реализует IMP:
- Начать с плотной обученной модели
- На каждом шаге обрезать долю оставшихся весов
- Переобучить в течение указанного количества эпох
- Повторять до достижения целевой разреженности
6.4 Структурный прунинг
Функция structured_prune удаляет целые нейроны:
- Вычислить L2-норму входящих весов каждого нейрона
- Ранжировать нейроны по важности
- Удалить наименее важные нейроны, убирая строки/столбцы
6.5 Интеграция с Bybit
Реализация получает реальные данные OHLCV с API Bybit для обучения и оценки моделей на фактических рыночных данных. Мы используем публичный эндпоинт kline, который не требует аутентификации.
Полная реализация предоставлена в rust/src/lib.rs с торговым примером в rust/examples/trading_example.rs.
7. Интеграция данных Bybit
Наша реализация подключается к API биржи Bybit для получения реальных рыночных данных для обучения и оценки модели. Интеграция использует публичный REST API эндпоинт для исторических свечей (kline).
7.1 Получение данных
Мы получаем данные OHLCV (Open, High, Low, Close, Volume) для BTCUSDT используя:
GET https://api.bybit.com/v5/market/kline?category=linear&symbol=BTCUSDT&interval=5&limit=200Это возвращает 5-минутные свечи, обеспечивая достаточную гранулярность для моделей краткосрочного прогнозирования цен.
7.2 Инженерия признаков
Из сырых данных OHLCV мы вычисляем признаки, включая:
- Доходности цен: $(close_t - close_{t-1}) / close_{t-1}$
- Нормализованный объём: $volume_t / \text{mean}(volume)$
- Диапазон high-low: $(high_t - low_t) / close_t$
- Разница open-close: $(close_t - open_t) / open_t$
7.3 Построение меток
Мы строим бинарные метки для предсказания направления цены: $$y_t = \begin{cases} 1 & \text{если } close_{t+1} > close_t \ 0 & \text{иначе} \end{cases}$$
Это предоставляет прямолинейную цель классификации, которая позволяет четко измерить влияние прунинга на точность предсказания.
8. Ключевые выводы
-
Прунинг необходим для производственных торговых моделей. Разрыв между исследовательскими моделями и развернутыми моделями часто преодолевается техниками компрессии, причем прунинг является наиболее эффективным.
-
Итеративный прунинг по магнитуде превосходит одноразовый прунинг. При высоких уровнях разреженности (>80%) IMP сохраняет значительно больше точности, позволяя модели адаптироваться после каждого шага прунинга.
-
Структурный прунинг обеспечивает реальное ускорение. Хотя неструктурный прунинг достигает более высоких коэффициентов сжатия на бумаге, структурный прунинг создает меньшие плотные модели, которые работают быстрее на любом оборудовании без специальной поддержки библиотек.
-
Прунинг действует как регуляризация. Для торговых моделей, где переобучение — основная проблема, прунинг может фактически улучшить производительность вне выборки. Обрезанная модель, которая лучше обобщается, более ценна, чем плотная модель, которая запоминает шум.
-
Компромисс точность-разреженность нелинеен. Модели обычно сохраняют >95% точности до 70-80% разреженности, затем постепенно деградируют до 90%, и наконец резко коллапсируют за пределами 95%. Оптимальная зона для торговых моделей обычно 80-90% разреженности.
-
Аппаратные ограничения определяют стратегию прунинга. Развертывание на FPGA предпочитает структурный прунинг и специфические паттерны разреженности. Развертывание на GPU может использовать неструктурную разреженность с разреженными тензорными ядрами. Развертывание на CPU выигрывает от обоих подходов.
-
Rust предоставляет идеальный язык реализации. Комбинация абстракций нулевой стоимости, отсутствия пауз сборщика мусора и предсказуемой производительности делает Rust отличным для реализации алгоритмов прунинга, которые будут использоваться в чувствительных к задержкам торговых системах.
-
Всегда валидируйте на данных вне выборки. Решения о прунинге, основанные на производительности в выборке, могут быть обманчивыми. Настоящий тест обрезанной модели — её производительность на невиданных рыночных данных, предпочтительно из другого рыночного режима, чем данные обучения.