← Мақалаларға оралу
August 15, 2026
5 мин оқу

Multi-Task Learning for Simultaneous Price, Volume, and Volatility Prediction

Multi-Task Learning for Simultaneous Price, Volume, and Volatility Prediction
#deep-learning
#multi-task
#shared-representation
#prediction
#auxiliary

Көптапсырмалы оқыту (MTL) әдетте мынадай тұжырыммен ұсынылады: өзара байланысты мақсаттар үшін бір кодерді бөліссеңіз, негізгі тапсырманың нәтижесі жақсарады. Саудада өзара байланысты мақсаттар анық — кірістілік, көлем және іске асқан құбылмалылықтың бәрі бір order flow-дан шығады — бірақ бұл тұжырым дерлік ешқашан тексерілмейді. Қызықты сұрақ тапсырмалардың өзара байланысты-болмағаны емес. Мәселе ортақ градиенттердің келісуінде және олар келіспейтін fold-тарда не болатынында.

Бұл мақала MTL туралы жазбалардың көпшілігі ескертпе ретінде ғана қарастыратын екі нәрсені ортаға қояды:

  1. Loss balancing — егжей-тегжей емес, эксперимент. Fixed weights, Kendall uncertainty weighting және GradNorm — үш түрлі model. Үшеуін бірдей fold-тарда іске қосып, әрқайсысы үшін learned weights-ті primary-task metric-пен бірге хабарлаңыз.
  2. Negative transfer-ді metric-ті көрмей тұрып өлшеуге болады. Shared encoder-дегі task gradient-терінің cosine similarity-і training кезінде auxiliary task-тер representation-ды primary task қажет ететін бағыттан алыстатып жатқанын көрсетеді. Cosine таңбасын жазып алып, оның сол fold-тағы нәтижені болжағанын тексеріңіз.

Pipeline-ның қалған бөлігі — volatility process, training loop, leakage controls және validation protocol — блогтың басқа материалдарында қамтылған, сондықтан мұнда қайта шығарылмай, сілтеме арқылы берілген.

Баптау

Ортақ latent representation-нан пайда болатын үш нарықтық мақсат

Input features xRd\mathbf{x} \in \mathbb{R}^d (OHLCV, technical indicators, order flow) берілсін, үш target бар:

  • Task 1 (primary): келесі period return y(1)=rt+1y^{(1)} = r_{t+1}
  • Task 2 (auxiliary): келесі period log volume y(2)=logVt+1y^{(2)} = \log V_{t+1}
  • Task 3 (auxiliary): келесі period realized volatility y(3)=σt+1y^{(3)} = \sigma_{t+1}

Multi-task model үшеуін бір уақытта шығарады, y^=fθ(x)\hat{\mathbf{y}} = f_\theta(\mathbf{x}), ал multi-task risk әр task risk-інің weighted sum-ы болады:

RMTL(θ)=k=1KwkE[(k)(fθ(k)(x),y(k))]\mathcal{R}_{\text{MTL}}(\theta) = \sum_{k=1}^{K} w_k \cdot \mathbb{E}\bigl[\ell^{(k)}(f_\theta^{(k)}(\mathbf{x}), y^{(k)})\bigr]

Мақаланың негізгі тақырыбы — wkw_k және әр task-тің gradient-тері бір-біріне қалай әсер ететіні.

Бірлескен training неге көмектесуі мүмкін — бір абзацта. Auxiliary task-тер shared representation-ды бірнеше нарықтық құбылысты түсіндіруге мәжбүрлейді, бұл бір мезетте capacity control да, inductive bias та болады; volume мен volatility тікелей бақыланатын, ал "expected return" бақыланбайтындықтан, auxiliary head-тер primary head-ке қарағанда тазарақ gradient signal береді. Бір model-дің көп output шығаруының пайдасы — interpretability machinery-мен бірге — көп горизонтты болжауға арналған temporal fusion transformer-лер материалында егжей-тегжейлі талқыланған; ол multi-horizon quantile-дері үшін дәл осы shared encoder және көп head идеясын қолданады.

Архитектураға қысқаша шолу

Hard parameter sharing: shared encoder gϕg_\phi KK task-specific head hψkh_{\psi_k}-ті қоректендіреді, сондықтан y^(k)=hψk(gϕ(x))\hat{y}^{(k)} = h_{\psi_k}(g_\phi(\mathbf{x})). Бұл жерде өлшенетін нұсқа осы, өйткені дәл осы нұсқада ϕ\phi бойынша gradient conflict анық мағынаға ие.

Soft parameter sharing әр task-ке λkjϕkϕj2\lambda \sum_{k \neq j} \|\phi_k - \phi_j\|^2 coupling penalty-і бар жеке encoder береді — parameter көбірек, икемділік жоғарырақ, бірақ conflict өлшенетін бір shared parameter vector жоқ. Cross-stitch networks осы екі тәсілдің арасында тұрады: әр level-де learned matrix αkj\alpha_{kj} арқылы task-specific feature-лерді араластырады. Hard sharing conflict көрсетсе, екеуін де сынап көруге болады, бірақ төмендегі measurement scope-ына екеуі де кірмейді.

Маңызды эксперимент: loss balancing-тің үш схемасы

Ортақ neural core айналасында теңестірілген бәсекелес task gradient-тері

Naive loss L=kwkL(k)\mathcal{L} = \sum_k w_k \mathcal{L}^{(k)} scale-ға сезімтал. Return loss шамамен 0.010.01, ал volume loss шамамен 1.01.0 болса, gradient-ті volume иеленеді де, return head gradient signal-сыз қалады. Үш жауап бар:

Fixed weights. Әр target-ты standardize еткеннен кейін wk=1w_k = 1 деп қойыңыз. Бұл — адал baseline: егер ол жеңсе, adaptive scheme-дер тек рәсім болып шығады.

Uncertainty weighting (Kendall et al., 2018). Әр task үшін homoscedastic noise scale σk\sigma_k-ті үйреніңіз:

LMTL=k=1K12σk2L(k)+logσk\mathcal{L}_{\text{MTL}} = \sum_{k=1}^{K} \frac{1}{2\sigma_k^2} \mathcal{L}^{(k)} + \log \sigma_k

Uncertainty-і жоғары task-тер автоматты түрде төмен weight алады; logσk\log \sigma_k мүшесі тривиалды σk\sigma_k \to \infty шешіміне жол бермейді. Бұл σk\sigma_k predictive interval емес, training кезіндегі loss-weighting құралы екенін ескеріңіз — position size есептеуге болатын uncertainty үшін conformal prediction материалын қараңыз.

GradNorm (Chen et al., 2018). Loss scale-дерін емес, gradient magnitude-терін теңестіріңіз. Әр қадамда Gk=ϕwkL(k)2G_k = \|\nabla_\phi w_k \mathcal{L}^{(k)}\|_2 және орташа Gˉ\bar{G} мәнін, relative training rate r~k=L(k)(t)/L(k)(0)rˉ\tilde{r}_k = \frac{\mathcal{L}^{(k)}(t)/\mathcal{L}^{(k)}(0)}{\bar{r}} мәнін есептеп, wkwkηwwkkGkGˉr~kαw_k \leftarrow w_k - \eta_w \nabla_{w_k} \sum_k |G_k - \bar{G} \cdot \tilde{r}_k^\alpha| жаңартуын жасаңыз. Сонда loss scale-іне қарамастан барлық task салыстырмалы жылдамдықпен training-нен өтеді.

MTL-ге тән code — head-тер, list қайтаратын forward және loss aggregation. Linear/BatchNorm/ReLU/Dropout stack-і, Adam/cosine/clip boilerplate-і және epoch loop-ы DeepLOB материалында көрсетілген стандартты pattern, сондықтан мұнда қысқартылды.

import torch
import torch.nn as nn


class MultiTaskTradingModel(nn.Module):
    """Hard parameter sharing: one encoder, K heads."""

    def __init__(self, encoder: nn.Module, repr_dim: int, n_tasks: int = 3):
        super().__init__()
        self.shared_encoder = encoder          # any MLP/CNN/GRU trunk
        self.task_heads = nn.ModuleList(
            nn.Linear(repr_dim, 1) for _ in range(n_tasks)
        )

    def forward(self, x):
        h = self.shared_encoder(x)
        return [head(h).squeeze(-1) for head in self.task_heads]

    def shared_repr(self, x):
        return self.shared_encoder(x)


class UncertaintyWeightedLoss(nn.Module):
    """Kendall et al. (2018) homoscedastic weighting."""

    def __init__(self, n_tasks: int = 3):
        super().__init__()
        self.log_vars = nn.Parameter(torch.zeros(n_tasks))  # log(sigma^2)

    def forward(self, losses: list) -> torch.Tensor:
        return sum(
            torch.exp(-self.log_vars[i]) * loss + self.log_vars[i]
            for i, loss in enumerate(losses)
        )

    def get_weights(self) -> list:
        with torch.no_grad():
            return [torch.exp(-lv).item() for lv in self.log_vars]

UncertaintyWeightedLoss parameter-лерге ие, сондықтан ол model-мен бірге optimizer-ге қосылуы керек: optim.Adam(list(model.parameters()) + list(uw.parameters()), ...). Мұны ұмыту — "uncertainty weighting іске қосылды" деп ойлап, іс жүзінде fixed weights-ті үнсіз іске қосудың ең жиі кездесетін жолы.

Нені хабарлау керек

Әр scheme үшін, әр fold-та: learned final task weights, primary-task metric және weighting scheme model choice болғандықтан, біреуін таңдағанға дейін қанша scheme салыстырылғаны.

Схема wreturnw_{\text{return}} wvolumew_{\text{volume}} wvolw_{\text{vol}} Single-task-пен салыстырғандағы primary-task metric
Fixed (wk=1w_k = 1) 1.00 1.00 1.00
Uncertainty weighting
GradNorm

Үш scheme-ді бірнеше fold-та сынау — шағын model search-тің өзі. Мұнда хабарланған кез келген improvement ештеңе білдірмес бұрын deflated Sharpe және multiple testing материалында сипатталған multiple-testing correction-нан өтуі керек.

Теріс тасымал: градиент таңбасын белгілеу

Ортақ representation-да бағыттас және қайшылықты task gradient-тері

Сақтауға тұрарлық бөлік осы. Negative transfer — auxiliary task-тер primary task нәтижесін нашарлататын жағдай, оның тікелей диагностикасы бар: shared parameter space ішіндегі task gradient-тері арасындағы бұрыш.

cos(ϕL(k),ϕL(j))<0    conflicting tasks\cos\bigl(\nabla_\phi \mathcal{L}^{(k)}, \nabla_\phi \mathcal{L}^{(j)}\bigr) < 0 \implies \text{conflicting tasks}

Өлшемді тек shared encoder-де жасаңыз — head-тер құрылымы бойынша task-specific және сондықтан әрқашан тривиалды түрде "келіседі".

import torch.nn.functional as F


def shared_grad(model, x, y, task_idx, criterion=nn.MSELoss()):
    """Gradient of task `task_idx` w.r.t. the shared encoder, flattened."""
    model.zero_grad(set_to_none=True)
    loss = criterion(model(x)[task_idx], y)
    loss.backward()
    return torch.cat([
        p.grad.detach().flatten()
        for p in model.shared_encoder.parameters()
        if p.grad is not None
    ])


def task_conflict(model, x, y_by_task, task_names):
    """Pairwise cosine similarity between per-task shared-encoder gradients."""
    grads = {
        name: shared_grad(model, x, y_by_task[name], i)
        for i, name in enumerate(task_names)
    }
    return {
        (a, b): F.cosine_similarity(
            grads[a].unsqueeze(0), grads[b].unsqueeze(0)
        ).item()
        for i, a in enumerate(task_names)
        for b in task_names[i + 1:]
    }

Мұны training соңында бір рет емес, training кезінде fixed cadence бойынша held-out batch-та есептеңіз. Encoder маманданған сайын жұп бастапқыда aligned болып, кейін diverge етуі мүмкін; training соңындағы жалғыз сан мұны жасырады.

Ізделетін және кез келген нәтиже жағдайында жарияланатын finding:

Жұп cos sim, ерте training cos sim, кеш training MTL primary task-ке көмектесті ме?
return ↔ volume
return ↔ volatility
volume ↔ volatility

Егер volume және volatility gradient-тері өзара келісіп, екеуі де return gradient-іне қайшы келсе, дұрыс қорытынды — екі auxiliary task return task кірмейтін coherent block құрайды; шешім capacity-ді ұлғайту емес, task grouping болады. Conflict нақты болса, стандартты remedy-лер: PCGrad (Yu et al., 2020), ол қайшылықты gradient-ті екіншісінің normal plane-іне project етеді; CAGrad (Liu et al., 2021), ол ешбір task-ке зиян келтірмейтін descent direction іздейді; немесе auxiliary task-ті толық алып тастау.

Әдейі жоқ нәрсеге назар аударыңыз: target value бойынша боялған shared representation-ның t-SNE plot-ы. Ол тек сәндік — жоғарыдағы cosine сандары embedding нені меңзейтін болса, соның бәрін сан ретінде айтып тұр.

Валидация протоколы

Research timeline бойындағы бөлінген walk-forward validation терезелері

Жоғарыдағы measurement салақ protocol жағдайында түкке тұрмайды, ал MTL әдеттегі қателерді күшейтеді, өйткені бір target орнына leakage-ке ұшырайтын үшеуі бар.

Simulator емес, real data. Target-тер нақты OHLCV/trade data-дан алынуы керек. Hardcoded GARCH toy return-мен құрылымы бойынша корреляцияланған volatility жасайды, ал бұл дәл тексеріліп отырған нәрсе — experiment өз generator-ін өлшейтін болады. Fitted volatility process қажет болса, crypto үшін GARCH volatility forecasting real BTC/ETH-те maximum likelihood арқылы GARCH(1,1)-ді fit жасап, standardized residual-дерді тексереді, ал asymmetric GARCH және leverage effect Gaussian symmetric-response simulator-дің crypto volatility-ді неге бұрмалайтынын түсіндіреді. Synthetic data controlled ground truth бергенде ғана орынды — яғни қалпына келтіргіңіз келетін, автор алдын ала орнатқан белгілі task correlation болғанда; бұл мұндағы experiment-тен бөлек.

Scalers тек train-ге fit етіледі. Feature scaler-ді және үш target scaler-дің бәрін әр training fold ішінде fit жасап, validation-ға қолданыңыз; split-ке дейін жасалған global fit_transform test-set moments-ін training-ге leakage етеді. Бұл нақты қате look-ahead bias taxonomy материалында сипатталған.

Purged, embargo-сы бар walk-forward fold-тар. Бір 80/20 chronological split MTL improvement-ін fold effect-тен ажырата алмайды — walk-forward optimization дәл осыны талдап, үш split үш түрлі conclusion беретінін көрсетеді. Machine learning арқылы spread modeling материалындағы expanding-window purged_walk_forward generator-ін қайта пайдаланыңыз: ол әр boundary-дің екі жағынан horizon жолдық gap алып тастайды, бұл жерде маңызды, өйткені overlapping realized-volatility window-лері return target leakage жасамаса да boundary арқылы leakage жасайды.

Classical baseline. Үш single-task net-тен жақсы MTL net per-target gradient-boosting немесе ridge model-і төрт модельдің бәрін жеңсе, ештеңе дәлелдемейді. Сол fold-тар мен сол feature-лерде әр target үшін LightGBM немесе ridge арқылы жеке model fit жасап, оны сол table-де хабарлаңыз.

Үлгі Primary-task metric Ескертпелер
Ridge, per target Классикалық baseline
LightGBM, per target Классикалық baseline
Single-task MLP, per target Үш бөлек net
MTL, best loss scheme Бір net, үш head

Бұл жерде MTL қашан ақталады

Multi-task model complexity үшін өлшенген decision threshold

MTL жоғарыдағы fold-тарда checklist ретінде емес, тексерілетін hypothesis ретінде жеңуі тиіс жағдайлар:

  • Auxiliary label-дер primary label-ден таза. Volume тікелей бақыланады, ал "expected return" бақыланбайды. Return head негізінен noise-ты fit жасап жатса, auxiliary head-терден келетін gradient signal objective-тің дұрыс қойылған жалғыз бөлігі болады.
  • Training data encoder capacity-іне қарағанда аз, сондықтан auxiliary constraint parameter үшін жай бәсекелеспей, шынайы regularization жасайды.
  • Inference latency маңызды және бір forward pass үшеуінен жылдам.

Қарсы жағдай да дәл сондай тексеріледі: өлшенген cos_sim(return, ·) мәндері тұрақты түрде теріс болса, shared encoder primary task-тен алыстатылып жатыр, ал auxiliary head-тер regularizer емес, қосымша шығын болып тұр.

Қорытынды

Үйлескен prediction stream-дері көптапсырмалы қорытындыға келіп тоғысуда

Return, volume және volatility бір microstructure-дан шығады, сондықтан shared representation — орынды prior; бірақ prior нәтиже емес. Бұл setup нақты анықтай алатын екі нәрсе: data қай loss-balancing scheme-ді қалайтыны (тек жеңімпазды атау емес, learned weights-ті хабарлау арқылы) және target-тердің correlation-ына сүйенбей, training бойы өлшенген shared encoder-дегі task gradient-терінің келісуі.

Егер purged walk-forward fold-тар MTL net-і per-target gradient-boosting model-ін жеңе алмайтынын көрсетсе, бұл — finding және оны сол күйінде жариялау керек: үлгі ретінде адал теріс нәтиже материалын қараңыз. Negative transfer бойынша negative result те negative transfer туралы нәтиже болып қала береді.

blog.disclaimer

Authors

Eugen Soloviov
Eugen Soloviov

Trading-systems engineer

Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.

Newsletter

Нарықтан бір қадам алда болыңыз

AI сауда талдаулары, нарық аналитикасы және платформа жаңалықтары үшін біздің ақпараттық бюллетеньге жазылыңыз.

Біз сіздің жекелігіңізді құрметтейміз. Кез келген уақытта жазылымнан шығуға болады.