Карты значимости для трейдинга: визуализация интерпретируемости моделей в финансовых прогнозах
Карты значимости (saliency maps) — это мощные методы визуализации, которые показывают, какие входные признаки оказывают наибольшее влияние на предсказания нейронной сети. Изначально разработанные для компьютерного зрения для выделения важных областей на изображениях, карты значимости были адаптированы для анализа временных рядов в трейдинге, помогая трейдерам и исследователям понять, почему модели делают конкретные предсказания о движении рынка.
В алгоритмической торговле интерпретируемость модели критически важна по нескольким причинам:
- Управление рисками: понимание факторов, определяющих предсказания, помогает выявить потенциальные сбои модели
- Соответствие регуляторным требованиям: финансовые регуляторы всё чаще требуют объяснимости в автоматизированных торговых системах
- Разработка стратегий: выводы из карт значимости могут подсказать новые торговые гипотезы
- Отладка модели: визуализация важности признаков помогает обнаружить переобучение или ложные корреляции
В этой главе рассматривается теория и реализация карт значимости для финансовых временных рядов с практическим применением на данных фондового рынка и криптовалют.
Содержание
- Введение в карты значимости
- Типы методов значимости
- Карты значимости для временных рядов
- Реализация на PyTorch
- Торговая стратегия на основе значимости
- Бэктестинг стратегии
- Реализация на Rust
- Литература
Введение в карты значимости
Карта значимости — это визуализация, показывающая, какие части входных данных оказывают наибольшее влияние на выход модели. Для нейронной сети с выходом y и входом x значимость вычисляется как градиент выхода по входу:
S(x) = ∂y/∂xВ контексте трейдинга, если наша модель предсказывает направление цены на основе исторических данных OHLCV (Open, High, Low, Close, Volume), карта значимости показывает, какие временные шаги и какие признаки (цена, объём, технические индикаторы) модель считает наиболее важными для своего предсказания.
Почему карты значимости важны для трейдинга
Традиционные методы оценки важности признаков, такие как permutation importance или значения SHAP, дают глобальные выводы, но могут упустить временную динамику. Карты значимости предлагают:
- Объяснения на уровне отдельных случаев: понимание конкретных торговых сигналов
- Временное разрешение: видеть, какие временные шаги важнее всего
- Взаимодействия признаков: определять, когда комбинации признаков вызывают сигналы
- Вычисление в реальном времени: градиенты быстро вычисляются во время live-торговли
Типы методов значимости
Ванильные градиенты
Простейший метод значимости вычисляет градиент оценки выходного класса по входу:
saliency = torch.autograd.grad(output, input)[0]Абсолютное значение градиента указывает на чувствительность предсказания к каждому входному признаку. Большие градиенты предполагают более важные признаки.
Ограничения:
- Может быть зашумлённым из-за резких градиентов в сетях с ReLU
- Может выделять нерелевантные признаки, которые случайно имеют большие локальные градиенты
Градиент × Вход
Этот метод умножает градиент на само значение входа:
S(x) = x × ∂y/∂xЭта модификация гарантирует, что признаки с нулевыми входными значениями имеют нулевую значимость, обеспечивая лучшую атрибуцию. Она учитывает и чувствительность (градиент), и фактический вклад (величину входа).
Интегрированные градиенты
Интегрированные градиенты (IG) решают проблему насыщения градиентов путём интегрирования градиентов вдоль пути от базовой линии (обычно нуля) до фактического входа:
IG(x) = (x - x') × ∫₀¹ ∂F(x' + α(x - x'))/∂x dαГде x’ — базовый вход. Этот метод удовлетворяет важным аксиомам:
- Чувствительность: если изменение признака меняет предсказание, он получает ненулевую атрибуцию
- Инвариантность реализации: атрибуции одинаковы для функционально эквивалентных сетей
- Полнота: атрибуции суммируются в разницу между выходом на x и базовой линии
SmoothGrad
SmoothGrad снижает шум в градиентной значимости путём усреднения градиентов по нескольким зашумлённым версиям входа:
SmoothGrad(x) = (1/n) × Σ ∂y/∂(x + N(0, σ²))Это создаёт визуально более чистые карты значимости, сохраняя наиболее важные атрибуции.
Карты значимости для временных рядов
Адаптация карт значимости к финансовым временным рядам требует учёта временной структуры:
Представление входных данных
Для торговой модели вход обычно имеет форму (batch, sequence_length, features):
- sequence_length: количество исторических временных шагов (например, 60 дней)
- features: данные OHLCV, технические индикаторы, фундаментальные данные
Визуализация временной значимости
Карта значимости имеет ту же форму, что и вход. Её можно визуализировать как:
- Тепловая карта: временные шаги × признаки, показывающие важность
- Агрегированная временная важность: сумма абсолютной значимости по признакам для каждого временного шага
- Агрегированная важность признаков: сумма абсолютной значимости по времени для каждого признака
Важные соображения
- Нормализация: масштабирование значений значимости для визуализации (например, min-max или процентили)
- Знак: положительные градиенты направлены к бычьим предсказаниям; отрицательные — к медвежьим
- Выбор базовой линии: для интегрированных градиентов выбирайте подходящие базовые линии (ноль, историческое среднее или рыночно-нейтральное состояние)
Реализация на PyTorch
Пример кода: построение торговой модели
Ноутбук 01_saliency_trading_model.ipynb демонстрирует построение нейронной сети для предсказания направления цены:
import torchimport torch.nn as nn
class TradingLSTM(nn.Module): """LSTM-модель для предсказания направления цены."""
def __init__(self, input_size, hidden_size, num_layers, dropout=0.2): super().__init__() self.lstm = nn.LSTM( input_size=input_size, hidden_size=hidden_size, num_layers=num_layers, batch_first=True, dropout=dropout if num_layers > 1 else 0 ) self.fc = nn.Linear(hidden_size, 1) self.sigmoid = nn.Sigmoid()
def forward(self, x): lstm_out, _ = self.lstm(x) last_hidden = lstm_out[:, -1, :] output = self.sigmoid(self.fc(last_hidden)) return outputПример кода: вычисление карт значимости
Ноутбук 02_saliency_computation.ipynb показывает, как вычислять различные карты значимости:
class SaliencyComputer: """Вычисление карт значимости для торговых моделей."""
def __init__(self, model): self.model = model self.model.eval()
def vanilla_gradient(self, x): """Вычисление ванильной градиентной значимости.""" x = x.clone().requires_grad_(True) output = self.model(x) output.backward(torch.ones_like(output)) return x.grad.abs()
def gradient_x_input(self, x): """Вычисление значимости градиент × вход.""" x = x.clone().requires_grad_(True) output = self.model(x) output.backward(torch.ones_like(output)) return (x.grad * x).abs()
def integrated_gradients(self, x, baseline=None, steps=50): """Вычисление интегрированных градиентов.""" if baseline is None: baseline = torch.zeros_like(x)
# Генерация интерполированных входов alphas = torch.linspace(0, 1, steps).view(-1, 1, 1, 1) interpolated = baseline + alphas * (x - baseline) interpolated = interpolated.view(-1, x.shape[1], x.shape[2]) interpolated.requires_grad_(True)
# Вычисление градиентов outputs = self.model(interpolated) outputs.sum().backward()
# Усреднение градиентов и умножение на (x - baseline) avg_grads = interpolated.grad.view(steps, -1, x.shape[1], x.shape[2]).mean(0) ig = (x - baseline) * avg_grads return ig.abs()Торговая стратегия на основе значимости
Анализ важности признаков
Карты значимости позволяют проводить динамический анализ важности признаков:
- Определение смены режима: когда модель внезапно начинает фокусироваться на других признаках, это может сигнализировать о смене рыночного режима
- Валидация торговых сигналов: предсказания с высокой уверенностью должны иметь чёткие, интерпретируемые паттерны значимости
- Фильтрация шума: игнорирование сигналов, где значимость размыта или сфокусирована на нерелевантных признаках
Адаптивный отбор признаков
Ноутбук 03_adaptive_strategy.ipynb реализует стратегию, которая:
- Вычисляет карты значимости для каждого предсказания
- Определяет top-k наиболее важных признаков для данного предсказания
- Торгует только когда важные признаки согласуются с доменными знаниями
- Корректирует размер позиции на основе концентрации значимости
def saliency_weighted_signal(model, x, feature_names, threshold=0.7): """Генерация торгового сигнала, взвешенного по интерпретируемости значимости."""
# Получение предсказания и значимости saliency = compute_saliency(model, x) prediction = model(x).item()
# Агрегация значимости по признакам feature_importance = saliency.mean(dim=1).squeeze() # Среднее по времени
# Проверка интерпретируемости топ-признаков top_features = feature_importance.topk(3).indices interpretable = check_feature_interpretability(top_features, feature_names)
# Вычисление концентрации (на основе энтропии) concentration = 1 - entropy(feature_importance.softmax(dim=0))
if interpretable and concentration > threshold: return prediction, concentration else: return 0.5, 0 # Нет сигналаБэктестинг стратегии
Ноутбук 04_backtest.ipynb демонстрирует бэктестинг с метриками эффективности:
Ключевые метрики
| Метрика | Описание |
|---|---|
| Коэффициент Шарпа | Доходность с поправкой на риск: (Return - Rf) / Std |
| Коэффициент Сортино | Доходность с поправкой на падение |
| Максимальная просадка | Наибольшее падение от пика до дна |
| Win Rate | Процент прибыльных сделок |
| Profit Factor | Валовая прибыль / Валовой убыток |
Сравнение с базовой стратегией
Мы сравниваем:
- Базовая модель: торговля по всем сигналам
- С фильтрацией по значимости: торговля только когда значимость интерпретируема
- С весами по значимости: размер позиции на основе концентрации значимости
Реализация на Rust
Директория rust_examples/ содержит production-ready реализацию на Rust с:
- Высокопроизводительным инференсом: оптимизировано для торговли с низкой задержкой
- Интеграцией с API Bybit: данные о криптовалютах в реальном времени
- Вычислением значимости: градиентная атрибуция на Rust
Подробности см. в rust_examples/README.md.
Запуск примеров на Rust
cd rust_examples
# Получение данных с Bybitcargo run --example fetch_data
# Обучение модели и вычисление значимостиcargo run --example saliency_trading
# Запуск бэктестаcargo run --example backtestЛитература
-
Deep Inside Convolutional Networks: Visualising Image Classification Models and Saliency Maps
- Симонян К., Ведальди А., Зиссерман А. (2013)
- URL: https://arxiv.org/abs/1312.6034
- Представлены ванильные градиентные карты значимости
-
Axiomatic Attribution for Deep Networks (Integrated Gradients)
- Сундарараджан М., Тали А., Янь К. (2017)
- URL: https://arxiv.org/abs/1703.01365
- Предложен метод интегрированных градиентов
-
SmoothGrad: Removing Noise by Adding Noise
- Смилков Д., Торат Н., Ким Б., Виегас Ф., Ваттенберг М. (2017)
- URL: https://arxiv.org/abs/1706.03825
- Представлен SmoothGrad для более чистых атрибуций
-
Interpretable Machine Learning for Finance
- Молнар К. (2022)
- URL: https://christophm.github.io/interpretable-ml-book/
- Всеобъемлющее руководство по интерпретируемости ML
-
Explainable AI for Algorithmic Trading
- Чен Дж. и др. (2021)
- Применение методов интерпретируемости к трейдингу