شارپ کی بنیاد پر توثیق اور ارلی اسٹاپنگ کے ساتھ نیورل ٹریڈنگ ماڈلز کی تربیت
خلاصہ
یہ تربیتی یوٹیلٹی ڈیپ پی ایم طرز کے ماڈل کے لیے اصلاحی عمل بیان کرتی ہے۔ یہ مضبوط شارپ پر مبنی نقصان سے ماڈل کے پیرامیٹرز کو اپ ڈیٹ کرنے کے لیے ایڈم ڈبلیو استعمال کرتی ہے، جس میں فارورڈ ریٹرنز، اتار چڑھاؤ کی اسکیلنگ، ماسکس، ٹرانزیکشن لاگت اور ترتیب دیے گئے برن اِن اور سافٹ من کنٹرولز شامل ہیں۔ غیر متناہی نقصان کی قدروں کو نظرانداز کیا جاتا ہے، اور ہر آپٹیمائزر مرحلے سے پہلے گریڈینٹس کو محدود کیا جا سکتا ہے۔
مقررہ وقفوں پر ماڈل کی جانچ جمع شدہ توثیقی ریٹرنز پر ہوتی ہے۔ یوٹیلٹی تربیتی مقصد اور شارپ پیمائشوں کے ساتھ توثیقی شارپ بھی درج کرتی ہے، بہترین توثیقی اسکور والی حالت محفوظ رکھتی ہے اور ترتیب دیے گئے برن اِن کے بعد ایکسپونینشلی اسموڈ توثیقی پیمائش سے ارلی اسٹاپنگ کرتی ہے۔ جمع شدہ پیمائش بیچز کے درست ریٹرنز کو جوڑتی ہے اور اوسط کو ریٹرن کے پھیلاؤ کے لحاظ سے سالانہ بناتی ہے۔ اگر درست مشاہدات بہت کم ہوں تو یہ کوئی اسکور نہیں دیتی۔ یہ تجرباتی ثبوت کے بجائے نفاذ کی رہنمائی ہے: اس میں ڈیٹا سیٹ، نتائج یا ٹریڈنگ کارکردگی متعین نہیں، اور توثیقی اسکور منتخب ڈیٹا تقسیم اور ترتیب پر منحصر رہتا ہے۔
اہم خیالات
- تربیتی مقصد میں اتار چڑھاؤ کی اسکیلنگ، ماسکس اور اختیاری لاگت ایڈجسٹمنٹ کے ساتھ مضبوط شارپ لاس شامل ہے۔
- غیر متناہی نقصان کی قدریں آپٹیمائزیشن کا مرحلہ چھوڑنے کا سبب بنتی ہیں۔
- ترتیب دیا گیا زیادہ سے زیادہ گریڈینٹ نورم مثبت ہو تو گریڈینٹ کلپنگ لاگو ہوتی ہے۔
- توثیقی شارپ ڈیٹا لوڈر کے جمع شدہ ریٹرنز سے نکالی جاتی ہے؛ مختصر یا خالی نمونوں کو دستیاب نہیں سمجھا جاتا۔
- ارلی اسٹاپنگ توثیقی اسکور کو ایکسپونینشل اوسط سے ہموار کرتی ہے اور ترتیب دیا گیا برن اِن رکھتی ہے۔
- واپس کیا گیا ماڈل اسٹیٹ وہ ہے جس کی مشاہدہ شدہ توثیقی شارپ سب سے زیادہ ہو۔
ٹیگز
مکمل متن
# train.py
```py
"""Training utilities for DeePM-style models."""
from __future__ import annotations
from dataclasses import dataclass
import torch
from torch import nn
from torch.optim import AdamW
from torch.utils.data import DataLoader
from .configs import TrainingConfig, validate_training_config
from .dataset import StaticAssetMetadata
from .losses import robust_sharpe_loss
from .utils import set_seed
@dataclass(frozen=True, slots=True)
class TrainHistory:
steps: list[int]
train_objective: list[float]
train_sharpe_pool: list[float]
val_sharpe_pool: list[float]
class EarlyStopping:
"""Validation-metric early stopping with EMA smoothing."""
def __init__(
self,
*,
alpha: float,
min_delta: float,
patience: int,
burn_in_iters: int,
) -> None:
self.alpha = float(alpha)
self.min_delta = float(min_delta)
self.patience = int(patience)
self.burn_in_iters = int(burn_in_iters)
self._ema: float | None = None
self._best_ema: float = float("-inf")
self._bad_count: int = 0
def update(self, value: float, step: int) -> tuple[bool, float, float]:
"""Update state. Returns (stop, ema, best_ema)."""
if self._ema is None:
self._ema = float(value)
else:
self._ema = self.alpha * float(value) + (1.0 - self.alpha) * self._ema
if step < self.burn_in_iters:
return False, self._ema, self._best_ema
if self._ema >= self._best_ema + self.min_delta:
self._best_ema = self._ema
self._bad_count = 0
else:
self._bad_count += 1
return self._bad_count >= self.patience, self._ema, self._best_ema
@torch.no_grad()
def evaluate_pooled_sharpe(
model: nn.Module,
loader: DataLoader,
*,
static_meta: StaticAssetMetadata,
cfg: TrainingConfig,
device: torch.device,
) -> float:
"""Compute pooled Sharpe over the entire loader."""
model.eval()
returns: list[torch.Tensor] = []
for x, y, v, m in loader:
x = x.to(device=device, dtype=torch.float32)
y = y.to(device=device, dtype=torch.float32)
v = v.to(device=device, dtype=torch.float32)
m = m.to(device=device, dtype=torch.float32)
p = model(
x,
mask=m,
asset_ids=static_meta.asset_ids.to(device),
group_ids=None if static_meta.group_ids is None else static_meta.group_ids.to(device),
costs=None if static_meta.costs is None else static_meta.costs.to(device),
)
out = robust_sharpe_loss(
p=p,
y_fwd1=y,
vol_scale=v,
mask=m,
costs=None if static_meta.costs is None else static_meta.costs.to(device),
burn_in=cfg.burn_in,
gamma_cost=cfg.gamma_cost,
annualization_factor=cfg.annualization_factor,
eps=cfg.sharpe_eps,
tau=cfg.softmin_tau,
lambda_soft=cfg.softmin_lambda,
)
returns.append(out.net_returns[:, cfg.burn_in :].reshape(-1).cpu())
if not returns:
return float("nan")
r = torch.cat(returns)
r = r[~torch.isnan(r)]
if r.numel() < 10:
return float("nan")
mu = r.mean()
var = r.var(unbiased=False)
sharpe = (cfg.annualization_factor**0.5) * mu / torch.sqrt(var + cfg.sharpe_eps)
return float(sharpe.item())
def train_model(
model: nn.Module,
*,
train_loader: DataLoader,
val_loader: DataLoader,
static_meta: StaticAssetMetadata,
cfg: TrainingConfig,
) -> tuple[dict[str, torch.Tensor], TrainHistory]:
"""Train a DeePM-style model and return the best state dict."""
validate_training_config(cfg)
set_seed(cfg.seed)
device = torch.device(cfg.device)
model.to(device)
optimizer = AdamW(model.parameters(), lr=cfg.learning_rate, weight_decay=cfg.weight_decay)
stopper = EarlyStopping(
alpha=cfg.metric_ema_alpha,
min_delta=cfg.metric_min_delta,
patience=cfg.early_stopping_patience,
burn_in_iters=cfg.early_stopping_burn_in_iters,
)
history = TrainHistory(steps=[], train_objective=[], train_sharpe_pool=[], val_sharpe_pool=[])
best_state: dict[str, torch.Tensor] = {}
best_val_sharpe: float = float("-inf")
train_iter = iter(train_loader)
for step in range(1, cfg.max_iters + 1):
model.train()
try:
x, y, v, m = next(train_iter)
except StopIteration:
train_iter = iter(train_loader)
x, y, v, m = next(train_iter)
x = x.to(device=device, dtype=torch.float32)
y = y.to(device=device, dtype=torch.float32)
v = v.to(device=device, dtype=torch.float32)
m = m.to(device=device, dtype=torch.float32)
optimizer.zero_grad(set_to_none=True)
p = model(
x,
mask=m,
asset_ids=static_meta.asset_ids.to(device),
group_ids=None if static_meta.group_ids is None else static_meta.group_ids.to(device),
costs=None if static_meta.costs is None else static_meta.costs.to(device),
)
out = robust_sharpe_loss(
p=p,
y_fwd1=y,
vol_scale=v,
mask=m,
costs=None if static_meta.costs is None else static_meta.costs.to(device),
burn_in=cfg.burn_in,
gamma_cost=cfg.gamma_cost,
annualization_factor=cfg.annualization_factor,
eps=cfg.sharpe_eps,
tau=cfg.softmin_tau,
lambda_soft=cfg.softmin_lambda,
)
if torch.isnan(out.loss) or torch.isinf(out.loss):
optimizer.zero_grad(set_to_none=True)
continue
out.loss.backward()
if cfg.max_grad_norm > 0:
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=cfg.max_grad_norm)
optimizer.step()
if step % cfg.eval_every != 0:
continue
val_sharpe = evaluate_pooled_sharpe(
model, val_loader, static_meta=static_meta, cfg=cfg, device=device
)
stop, _ema, _best_ema = stopper.update(val_sharpe, step)
history.steps.append(step)
history.train_objective.append(float(out.objective.item()))
history.train_sharpe_pool.append(float(out.sharpe_pool.item()))
history.val_sharpe_pool.append(val_sharpe)
if val_sharpe > best_val_sharpe:
best_val_sharpe = val_sharpe
best_state = {k: v.detach().cpu().clone() for k, v in model.state_dict().items()}
if stop:
break
return best_state, history
```ماخذ کا حوالہ دیتے ہوئے مکمل متن دکھایا گیا ہے، ماخذ کے لائسنس کے تحت۔ لائسنس: MIT
یہ خلاصہ اصل ماخذ سے Stratmill کے تحقیقی ایجنٹ نے لکھا ہے؛ یہ ماخذ کی نقل نہیں۔