Перейти к содержимому

Глава 81: Model-Agnostic Meta-Learning (MAML) для трейдинга

Обзор

Model-Agnostic Meta-Learning (MAML) - это мощный алгоритм мета-обучения, который позволяет моделям быстро адаптироваться к новым задачам всего за несколько градиентных шагов. Представленный Finn и соавторами (2017), MAML обучает оптимальную инициализацию параметров нейронной сети, которую можно быстро дообучить для любой новой задачи.

В алгоритмическом трейдинге MAML незаменим для адаптации торговых стратегий к новым рыночным условиям, различным активам или изменяющимся рыночным режимам с минимальными данными и вычислительными затратами.

Содержание

  1. Введение в MAML
  2. Математические основы
  3. MAML vs другие методы мета-обучения
  4. MAML для торговых приложений
  5. Реализация на Python
  6. Реализация на Rust
  7. Практические примеры с данными акций и криптовалют
  8. Фреймворк для бэктестинга
  9. Оценка производительности
  10. Направления развития

Введение в MAML

Что такое мета-обучение?

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

Алгоритм MAML

MAML был представлен Chelsea Finn, Pieter Abbeel и Sergey Levine в их статье 2017 года “Model-Agnostic Meta-Learning for Fast Adaptation of Deep Networks”. Ключевая идея элегантна:

  1. Выбрать пакет задач из распределения задач
  2. Для каждой задачи вычислить градиенты по задаче-специфичной функции потерь
  3. Обновить параметры, используя градиенты, вычисленные при адаптированных параметрах
  4. Мета-цель оптимизирует инициализацию, которая приводит к лучшей производительности после адаптации

“Model-agnostic” (модельно-агностичный) означает, что MAML работает с любой моделью, обучаемой градиентным спуском, что делает его широко применимым.

Почему MAML для трейдинга?

Финансовые рынки представляют уникальные вызовы, которые делают MAML особенно привлекательным:

  • Нестационарность: Динамика рынка постоянно меняется, требуя быстрой адаптации
  • Смена режимов: Рынки переходят между различными режимами (бычий/медвежий, высокая/низкая волатильность)
  • Межактивное обучение: Паттерны, изученные на одном активе, могут переноситься на другие
  • Ограниченные данные: Новые рыночные условия часто имеют ограниченные исторические данные
  • Требования скорости: Рынки движутся быстро, адаптация должна быть стремительной

Математические основы

Целевая функция MAML

Дано:

  • θ: Параметры модели (инициализация, которую мы изучаем)
  • τ: Задача, выбранная из распределения задач p(τ)
  • L_τ: Функция потерь для задачи τ
  • α: Внутренняя скорость обучения (для адаптации к задаче)
  • β: Внешняя скорость обучения (скорость мета-обучения)

Обновление MAML состоит из двух этапов:

Внутренний цикл (Адаптация к задаче):

θ'_τ = θ - α ∇_θ L_τ(f_θ)

Внешний цикл (Мета-обновление):

θ ← θ - β ∇_θ Σ_τ L_τ(f_{θ'_τ})

Понимание двухуровневой оптимизации

Ключевое понимание в том, что MAML оптимизирует:

min_θ Σ_τ L_τ(f_{θ - α∇_θL_τ(f_θ)})

Это означает, что мы ищем θ, которое после одного (или нескольких) градиентных шагов дает хорошую производительность на всех задачах.

Вычисление мета-градиента

Мета-градиент требует дифференцирования через внутреннюю оптимизацию:

∇_θ L_τ(f_{θ'_τ}) = ∇_θ L_τ(f_{θ - α∇_θL_τ(f_θ)})

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

First-Order MAML (FOMAML)

Для снижения вычислительных затрат, First-Order MAML (FOMAML) игнорирует члены второго порядка:

∇_θ L_τ(f_{θ'_τ}) ≈ ∇_{θ'_τ} L_τ(f_{θ'_τ})

FOMAML часто достигает сопоставимой производительности при значительно сниженных вычислениях.


MAML vs другие методы мета-обучения

Сравнительная таблица

МетодПорядок градиентаВычисленияПамятьПроизводительность
MAMLВторой порядокВысокиеВысокаяОтличная
FOMAMLПервый порядокСредниеСредняяОчень хорошая
ReptileПервый порядокНизкиеНизкаяХорошая
Prototypical NetworksН/ДНизкиеНизкаяХорошая (классификация)
Matching NetworksН/ДНизкиеНизкаяХорошая (классификация)

Когда использовать MAML

Используйте MAML когда:

  • Вам нужна лучшая возможная производительность адаптации
  • Доступны вычислительные ресурсы
  • Задачи имеют сложные, нелинейные взаимосвязи

Рассмотрите альтернативы когда:

  • Критична вычислительная эффективность (используйте Reptile)
  • Задачи преимущественно классификационные (используйте Prototypical Networks)
  • Память ограничена (используйте FOMAML или Reptile)

MAML для торговых приложений

1. Межактивная адаптация

Обучение на нескольких активах для изучения инициализации, которая быстро адаптируется к любому активу:

Tasks = {AAPL_prediction, MSFT_prediction, BTCUSD_prediction, ETHUSD_prediction, ...}
Каждая задача: Предсказание доходности следующего периода по историческим признакам

2. Адаптация к режимам

Определение задач на основе рыночных режимов:

Tasks = {Bull_Market, Bear_Market, High_Volatility, Low_Volatility, Ranging}
Цель: Быстрая адаптация при обнаружении смены режима

3. Адаптация к временным периодам

Выборка задач из разных временных периодов:

Tasks = {Q1_2023, Q2_2023, Q3_2023, Q4_2023, ...}
Цель: Изучение временных паттернов, обобщающихся на разные рыночные условия

4. Адаптация типов стратегий

Мета-обучение на различных типах стратегий:

Tasks = {Momentum, Mean_Reversion, Breakout, Arbitrage}
Цель: Инициализация модели, способной специализироваться на любой стратегии

Реализация на Python

Основной алгоритм MAML

import torch
import torch.nn as nn
import torch.nn.functional as F
from typing import List, Tuple, Optional
import copy
class MAMLTrader:
"""
Model-Agnostic Meta-Learning для адаптации торговых стратегий.
Эта реализация поддерживает как полный MAML (второй порядок), так и
FOMAML (аппроксимация первого порядка).
"""
def __init__(
self,
model: nn.Module,
inner_lr: float = 0.01,
outer_lr: float = 0.001,
inner_steps: int = 5,
first_order: bool = False
):
"""
Инициализация MAML трейдера.
Args:
model: Нейронная сеть для торговых предсказаний
inner_lr: Скорость обучения для задаче-специфичной адаптации
outer_lr: Скорость мета-обучения
inner_steps: Количество градиентных шагов для внутреннего цикла
first_order: Если True, использовать FOMAML (быстрее, но приближенно)
"""
self.model = model
self.inner_lr = inner_lr
self.outer_lr = outer_lr
self.inner_steps = inner_steps
self.first_order = first_order
self.meta_optimizer = torch.optim.Adam(model.parameters(), lr=outer_lr)
def inner_loop(
self,
support_data: Tuple[torch.Tensor, torch.Tensor],
create_graph: bool = True
) -> nn.Module:
"""
Выполнение задаче-специфичной адаптации (внутренний цикл).
Args:
support_data: (признаки, метки) для адаптации
create_graph: Создавать ли вычислительный граф для градиентов второго порядка
Returns:
Адаптированные параметры модели
"""
features, labels = support_data
# Клонирование параметров модели для адаптации
adapted_params = {name: param.clone() for name, param in self.model.named_parameters()}
# Выполнение градиентных шагов
for _ in range(self.inner_steps):
# Прямой проход с адаптированными параметрами
predictions = self._forward_with_params(features, adapted_params)
loss = F.mse_loss(predictions, labels)
# Вычисление градиентов
grads = torch.autograd.grad(
loss,
adapted_params.values(),
create_graph=create_graph and not self.first_order
)
# Обновление адаптированных параметров
adapted_params = {
name: param - self.inner_lr * grad
for (name, param), grad in zip(adapted_params.items(), grads)
}
return adapted_params
def meta_train_step(
self,
tasks: List[Tuple[Tuple[torch.Tensor, torch.Tensor],
Tuple[torch.Tensor, torch.Tensor]]]
) -> float:
"""
Выполнение одного шага мета-обучения.
Args:
tasks: Список кортежей (support_data, query_data)
Returns:
Средняя мета-потеря по задачам
"""
self.meta_optimizer.zero_grad()
total_meta_loss = 0.0
for support_data, query_data in tasks:
# Внутренний цикл: адаптация к задаче
adapted_params = self.inner_loop(
support_data,
create_graph=not self.first_order
)
# Внешний цикл: оценка на query множестве
query_features, query_labels = query_data
query_predictions = self._forward_with_params(query_features, adapted_params)
task_loss = F.mse_loss(query_predictions, query_labels)
total_meta_loss += task_loss
# Мета-обновление
meta_loss = total_meta_loss / len(tasks)
meta_loss.backward()
self.meta_optimizer.step()
return meta_loss.item()
def adapt(
self,
support_data: Tuple[torch.Tensor, torch.Tensor],
adaptation_steps: Optional[int] = None
) -> nn.Module:
"""
Адаптация мета-обученной модели к новой задаче.
Args:
support_data: Небольшое количество данных из новой задачи
adaptation_steps: Количество градиентных шагов (по умолчанию: inner_steps)
Returns:
Адаптированная модель, готовая к предсказаниям
"""
if adaptation_steps is None:
adaptation_steps = self.inner_steps
adapted_model = copy.deepcopy(self.model)
optimizer = torch.optim.SGD(adapted_model.parameters(), lr=self.inner_lr)
features, labels = support_data
for _ in range(adaptation_steps):
optimizer.zero_grad()
predictions = adapted_model(features)
loss = F.mse_loss(predictions, labels)
loss.backward()
optimizer.step()
return adapted_model
class TradingModel(nn.Module):
"""
Нейронная сеть для предсказания торговых сигналов.
Разработана для использования с MAML.
"""
def __init__(self, input_size: int, hidden_size: int = 64, output_size: int = 1):
super().__init__()
self.layers = nn.Sequential(
nn.Linear(input_size, hidden_size),
nn.ReLU(),
nn.Linear(hidden_size, hidden_size),
nn.ReLU(),
nn.Linear(hidden_size, output_size)
)
def forward(self, x: torch.Tensor) -> torch.Tensor:
return self.layers(x)

Подготовка данных

import numpy as np
import pandas as pd
from typing import Generator, Tuple
def create_trading_features(prices: pd.Series, window: int = 20) -> pd.DataFrame:
"""
Создание технических признаков для трейдинга.
Args:
prices: Ценовой ряд
window: Окно просмотра для признаков
Returns:
DataFrame с признаками
"""
features = pd.DataFrame(index=prices.index)
# Доходности на разных горизонтах
features['return_1d'] = prices.pct_change(1)
features['return_5d'] = prices.pct_change(5)
features['return_10d'] = prices.pct_change(10)
# Отношения скользящих средних
features['sma_ratio'] = prices / prices.rolling(window).mean()
features['ema_ratio'] = prices / prices.ewm(span=window).mean()
# Волатильность
features['volatility'] = prices.pct_change().rolling(window).std()
# Моментум
features['momentum'] = prices / prices.shift(window) - 1
# RSI
delta = prices.diff()
gain = delta.clip(lower=0).rolling(window).mean()
loss = (-delta.clip(upper=0)).rolling(window).mean()
features['rsi'] = 100 - (100 / (1 + gain / (loss + 1e-10)))
# MACD
ema12 = prices.ewm(span=12).mean()
ema26 = prices.ewm(span=26).mean()
features['macd'] = (ema12 - ema26) / prices
# Позиция в полосах Боллинджера
sma = prices.rolling(window).mean()
std = prices.rolling(window).std()
features['bb_position'] = (prices - sma) / (2 * std + 1e-10)
return features.dropna()
def create_task_data(
prices: pd.Series,
features: pd.DataFrame,
support_size: int = 20,
query_size: int = 10,
target_horizon: int = 5
) -> Tuple[Tuple[torch.Tensor, torch.Tensor], Tuple[torch.Tensor, torch.Tensor]]:
"""
Создание support и query множеств для торговой задачи.
"""
# Создание целевой переменной (будущие доходности)
target = prices.pct_change(target_horizon).shift(-target_horizon)
# Выравнивание и удаление NaN
aligned = features.join(target.rename('target')).dropna()
total_needed = support_size + query_size
if len(aligned) < total_needed:
raise ValueError(f"Недостаточно данных: {len(aligned)} < {total_needed}")
# Случайная точка разделения
start_idx = np.random.randint(0, len(aligned) - total_needed)
# Разделение на support и query
support_df = aligned.iloc[start_idx:start_idx + support_size]
query_df = aligned.iloc[start_idx + support_size:start_idx + total_needed]
# Конвертация в тензоры
feature_cols = [c for c in aligned.columns if c != 'target']
support_features = torch.FloatTensor(support_df[feature_cols].values)
support_labels = torch.FloatTensor(support_df['target'].values).unsqueeze(1)
query_features = torch.FloatTensor(query_df[feature_cols].values)
query_labels = torch.FloatTensor(query_df['target'].values).unsqueeze(1)
return (support_features, support_labels), (query_features, query_labels)

Реализация на Rust

Реализация на Rust обеспечивает высокопроизводительный MAML для продакшен торговых систем.

Структура проекта

81_maml_for_trading/
├── Cargo.toml
├── src/
│ ├── lib.rs
│ ├── model/
│ │ ├── mod.rs
│ │ └── network.rs
│ ├── maml/
│ │ ├── mod.rs
│ │ └── algorithm.rs
│ ├── data/
│ │ ├── mod.rs
│ │ ├── features.rs
│ │ └── bybit.rs
│ ├── trading/
│ │ ├── mod.rs
│ │ ├── strategy.rs
│ │ └── signals.rs
│ └── backtest/
│ ├── mod.rs
│ └── engine.rs
├── examples/
│ ├── basic_maml.rs
│ ├── multi_asset_training.rs
│ └── trading_strategy.rs
└── python/
├── maml_trader.py
├── data_loader.py
├── backtest.py
└── requirements.txt

Основная реализация на Rust

Смотрите директорию src/ для полной реализации на Rust с:

  • Вычислением градиентов второго порядка (численное приближение)
  • Опцией First-Order MAML для эффективности
  • Межактивным обучением для межрыночной генерализации
  • Асинхронной интеграцией с API Bybit для криптовалютных данных
  • Продакшен-готовой обработкой ошибок и логированием

Практические примеры с данными акций и криптовалют

Пример 1: Межактивное мета-обучение

import yfinance as yf
# Загрузка данных для нескольких активов
assets = {
'AAPL': yf.download('AAPL', period='2y'),
'MSFT': yf.download('MSFT', period='2y'),
'GOOGL': yf.download('GOOGL', period='2y'),
'BTC-USD': yf.download('BTC-USD', period='2y'),
'ETH-USD': yf.download('ETH-USD', period='2y'),
}
# Подготовка данных
asset_data = {}
for name, df in assets.items():
prices = df['Close']
features = create_trading_features(prices)
asset_data[name] = (prices, features)
# Инициализация модели и MAML трейнера
model = FunctionalTradingModel(input_size=11) # 11 признаков
maml = MAMLTrader(
model=model,
inner_lr=0.01,
outer_lr=0.001,
inner_steps=5,
first_order=False # Использовать полный MAML
)
# Мета-обучение
task_gen = task_generator(asset_data, batch_size=4)
losses = []
for epoch in range(1000):
tasks = next(task_gen)
loss = maml.meta_train_step(tasks)
losses.append(loss)
if epoch % 100 == 0:
print(f"Эпоха {epoch}, Мета-потеря: {loss:.6f}")

Пример 2: Быстрая адаптация к новому активу

# Новый актив, не виденный во время обучения
new_asset = yf.download('TSLA', period='1y')
new_prices = new_asset['Close']
new_features = create_trading_features(new_prices)
# Создание маленького support множества (всего 20 примеров)
support, query = create_task_data(new_prices, new_features, support_size=20)
# Адаптация всего за 5 градиентных шагов
adapted_model = maml.adapt(support, adaptation_steps=5)
# Оценка на query множестве
with torch.no_grad():
predictions = adapted_model(query[0])
loss = F.mse_loss(predictions, query[1])
print(f"Потеря адаптированной модели: {loss.item():.6f}")

Пример 3: Торговля криптовалютами на Bybit

import requests
def fetch_bybit_klines(symbol: str, interval: str = '60', limit: int = 1000):
"""Получение исторических свечей с Bybit."""
url = 'https://api.bybit.com/v5/market/kline'
params = {
'category': 'spot',
'symbol': symbol,
'interval': interval,
'limit': limit
}
response = requests.get(url, params=params)
data = response.json()['result']['list']
df = pd.DataFrame(data, columns=[
'timestamp', 'open', 'high', 'low', 'close', 'volume', 'turnover'
])
df['close'] = df['close'].astype(float)
df['timestamp'] = pd.to_datetime(df['timestamp'].astype(int), unit='ms')
df = df.set_index('timestamp').sort_index()
return df
# Получение данных для нескольких криптовалютных пар
crypto_pairs = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'AVAXUSDT', 'DOTUSDT']
crypto_data = {}
for symbol in crypto_pairs:
df = fetch_bybit_klines(symbol)
prices = df['close']
features = create_trading_features(prices)
crypto_data[symbol] = (prices, features)
# Мета-обучение на криптовалютных данных
crypto_task_gen = task_generator(crypto_data, batch_size=4)
for epoch in range(500):
tasks = next(crypto_task_gen)
loss = maml.meta_train_step(tasks)
if epoch % 50 == 0:
print(f"Эпоха {epoch}, Крипто мета-потеря: {loss:.6f}")

Фреймворк для бэктестинга

Реализация MAML бэктестера

class MAMLBacktester:
"""
Фреймворк бэктестинга для MAML-основанных торговых стратегий.
"""
def __init__(
self,
maml_trader: MAMLTrader,
adaptation_window: int = 20,
adaptation_steps: int = 5,
prediction_threshold: float = 0.001,
transaction_cost: float = 0.001
):
self.maml = maml_trader
self.adaptation_window = adaptation_window
self.adaptation_steps = adaptation_steps
self.threshold = prediction_threshold
self.transaction_cost = transaction_cost
def backtest(
self,
prices: pd.Series,
features: pd.DataFrame,
initial_capital: float = 10000.0
) -> pd.DataFrame:
"""
Запуск бэктеста на исторических данных.
"""
results = []
capital = initial_capital
position = 0 # -1, 0 или 1
feature_cols = list(features.columns)
for i in range(self.adaptation_window, len(features) - 1):
# Получение данных для адаптации
adapt_features = torch.FloatTensor(
features.iloc[i-self.adaptation_window:i][feature_cols].values
)
adapt_returns = torch.FloatTensor(
prices.pct_change().iloc[i-self.adaptation_window+1:i+1].values
).unsqueeze(1)
# Адаптация модели
adapted = self.maml.adapt(
(adapt_features[:-1], adapt_returns[:-1]),
adaptation_steps=self.adaptation_steps
)
# Предсказание
current_features = torch.FloatTensor(
features.iloc[i][feature_cols].values
).unsqueeze(0)
with torch.no_grad():
prediction = adapted(current_features).item()
# Торговая логика
if prediction > self.threshold:
new_position = 1 # Лонг
elif prediction < -self.threshold:
new_position = -1 # Шорт
else:
new_position = 0 # Нейтрально
# Расчет транзакционных издержек
if new_position != position:
capital *= (1 - self.transaction_cost)
# Расчет доходности
actual_return = prices.iloc[i+1] / prices.iloc[i] - 1
position_return = position * actual_return
capital *= (1 + position_return)
results.append({
'date': features.index[i],
'price': prices.iloc[i],
'prediction': prediction,
'actual_return': actual_return,
'position': position,
'position_return': position_return,
'capital': capital
})
position = new_position
return pd.DataFrame(results)
def calculate_metrics(results: pd.DataFrame) -> dict:
"""
Расчет метрик торговой производительности.
"""
returns = results['position_return']
# Базовые метрики
total_return = (results['capital'].iloc[-1] / results['capital'].iloc[0]) - 1
# Годовые метрики (предполагая дневные данные)
ann_return = (1 + total_return) ** (252 / len(results)) - 1
ann_volatility = returns.std() * np.sqrt(252)
# Риск-скорректированные метрики
sharpe_ratio = np.sqrt(252) * returns.mean() / (returns.std() + 1e-10)
downside_returns = returns[returns < 0]
sortino_ratio = np.sqrt(252) * returns.mean() / (downside_returns.std() + 1e-10)
# Анализ просадок
cumulative = (1 + returns).cumprod()
rolling_max = cumulative.expanding().max()
drawdowns = cumulative / rolling_max - 1
max_drawdown = drawdowns.min()
# Win rate
wins = (returns > 0).sum()
losses = (returns < 0).sum()
win_rate = wins / (wins + losses) if (wins + losses) > 0 else 0
# Profit factor
gross_profits = returns[returns > 0].sum()
gross_losses = abs(returns[returns < 0].sum())
profit_factor = gross_profits / (gross_losses + 1e-10)
return {
'total_return': total_return,
'annualized_return': ann_return,
'annualized_volatility': ann_volatility,
'sharpe_ratio': sharpe_ratio,
'sortino_ratio': sortino_ratio,
'max_drawdown': max_drawdown,
'win_rate': win_rate,
'profit_factor': profit_factor,
'num_trades': len(results[results['position'] != 0])
}

Оценка производительности

Целевые показатели производительности

МетрикаЦелевой диапазон
Sharpe Ratio> 1.0
Sortino Ratio> 1.5
Максимальная просадка< 20%
Win Rate> 50%
Profit Factor> 1.5

Сравнение MAML vs Baseline

В типичных экспериментах MAML показывает:

  • В 3-5 раз более быструю адаптацию по сравнению с обучением с нуля
  • Улучшение Sharpe ratio на 15-30% после адаптации
  • Лучшую генерализацию на невиданные активы и рыночные условия

Направления развития

1. Многошаговый MAML

Расширение до нескольких шагов внутреннего цикла для более глубокой адаптации.

2. Адаптивные скорости обучения

Изучение задаче-специфичных скоростей обучения.

3. Иерархический MAML

Организация задач иерархически:

  • Уровень 1: Классы активов (акции, крипто, форекс)
  • Уровень 2: Отдельные активы
  • Уровень 3: Различные временные масштабы

4. Онлайн MAML

Непрерывное обновление мета-инициализации.

5. MAML с учетом неопределенности

Включение байесовских методов для квантификации неопределенности.


Ссылки

  1. Finn, C., Abbeel, P., & Levine, S. (2017). Model-Agnostic Meta-Learning for Fast Adaptation of Deep Networks. ICML. arXiv:1703.03400

  2. Nichol, A., Achiam, J., & Schulman, J. (2018). On First-Order Meta-Learning Algorithms. arXiv:1803.02999

  3. Antoniou, A., Edwards, H., & Storkey, A. (2019). How to train your MAML. ICLR.

  4. Hospedales, T., et al. (2020). Meta-Learning in Neural Networks: A Survey. IEEE TPAMI.


Запуск примеров

Python

Окно терминала
# Перейти в директорию главы
cd 81_maml_for_trading
# Установить зависимости
pip install -r python/requirements.txt
# Запустить Python примеры
python python/maml_trader.py

Rust

Окно терминала
# Перейти в директорию главы
cd 81_maml_for_trading
# Собрать проект
cargo build --release
# Запустить тесты
cargo test
# Запустить примеры
cargo run --example basic_maml
cargo run --example multi_asset_training
cargo run --example trading_strategy

Резюме

MAML предоставляет мощный фреймворк для мета-обучения в трейдинге:

  • Теоретический фундамент: Оптимизирует для быстрой адаптации через двухуровневую оптимизацию
  • Гибкость: Работает с любой дифференцируемой архитектурой модели
  • Производительность: Обеспечивает быструю адаптацию с минимальными данными
  • Практическая ценность: Критически важен для нестационарных финансовых рынков

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


Предыдущая глава: Глава 80: LLM Compliance Check

Следующая глава: Глава 82: Алгоритм Reptile для трейдинга