← Kembali ke artikel
August 15, 2026
5 menit baca

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

Multi-task learning (MTL) biasanya dijual dengan satu pernyataan: bagikan encoder di antara target yang berkorelasi dan tugas utama akan menjadi lebih baik. Dalam trading, target yang berkorelasi itu jelas — return, volume, dan volatilitas terealisasi semuanya berasal dari order flow yang sama — dan pernyataan itu hampir tidak pernah diuji. Pertanyaan yang menarik bukan apakah tugas-tugas tersebut berkaitan. Pertanyaannya adalah apakah gradient yang dibagikan selaras, dan apa yang terjadi pada fold ketika tidak selaras.

Artikel ini menempatkan dua hal di pusat pembahasan yang biasanya hanya diperlakukan sebagai catatan kaki dalam tulisan MTL:

  1. Penyeimbangan loss adalah eksperimen, bukan detail. Bobot tetap, pembobotan ketidakpastian Kendall, dan GradNorm adalah tiga model yang berbeda. Jalankan ketiganya pada fold yang sama dan laporkan bobot yang dipelajari bersama metrik tugas utama untuk masing-masing.
  2. Negative transfer dapat diukur sebelum metrik terlihat. Kesamaan kosinus antara gradient tugas pada encoder bersama memberi tahu Anda selama training apakah tugas tambahan menarik representasi ke arah yang diinginkan tugas utama. Beri tanda pada kosinus, lalu periksa apakah tandanya memprediksi hasil pada fold tersebut.

Hal lain dalam pipeline — proses volatilitas, training loop, kontrol kebocoran, protokol validasi — sudah dibahas di tempat lain di blog ini dan ditautkan, bukan diturunkan ulang.

Setup

Tiga target pasar yang muncul dari representasi laten bersama

Diberikan fitur input xRd\mathbf{x} \in \mathbb{R}^d (OHLCV, indikator teknikal, order flow), terdapat tiga target:

  • Tugas 1 (utama): return periode berikutnya y(1)=rt+1y^{(1)} = r_{t+1}
  • Tugas 2 (tambahan): log volume periode berikutnya y(2)=logVt+1y^{(2)} = \log V_{t+1}
  • Tugas 3 (tambahan): volatilitas terealisasi periode berikutnya y(3)=σt+1y^{(3)} = \sigma_{t+1}

Model multi-tugas menghasilkan ketiganya secara bersamaan, y^=fθ(x)\hat{\mathbf{y}} = f_\theta(\mathbf{x}), dan risiko multi-tugas adalah jumlah berbobot dari risiko setiap tugas:

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]

Seluruh artikel ini membahas wkw_k dan dampak gradient setiap tugas terhadap tugas lainnya.

Mengapa training bersama mungkin membantu, dalam satu paragraf. Tugas tambahan membatasi representasi bersama agar menjelaskan lebih dari satu fenomena pasar, yang sekaligus menjadi kontrol kapasitas dan bias induktif; dan karena volume serta volatilitas diamati langsung sementara "expected return" tidak, head tambahan memberikan sinyal gradient yang lebih bersih daripada head utama. Argumen untuk satu model yang menghasilkan banyak output dibahas panjang — bersama machinery interpretabilitasnya — dalam temporal fusion transformers untuk peramalan multi-horizon, yang membuat argumen encoder-bersama-banyak-head yang sama untuk quantile multi-horizon.

Arsitektur secara singkat

Hard parameter sharing: encoder bersama gϕg_\phi memberi input kepada KK head khusus tugas hψkh_{\psi_k}, sehingga y^(k)=hψk(gϕ(x))\hat{y}^{(k)} = h_{\psi_k}(g_\phi(\mathbf{x})). Inilah versi yang diukur di sini, karena inilah versi ketika konflik gradient pada ϕ\phi terdefinisi dengan baik.

Soft parameter sharing memberi setiap tugas encoder sendiri dengan penalti coupling λkjϕkϕj2\lambda \sum_{k \neq j} \|\phi_k - \phi_j\|^2 — lebih banyak parameter, lebih banyak fleksibilitas, dan tidak ada satu vektor parameter bersama untuk mengukur konflik. Cross-stitch networks berada di tengah, dengan mencampur fitur per tugas melalui matriks terpelajar αkj\alpha_{kj} pada setiap level. Keduanya layak dicoba jika hard sharing menunjukkan konflik, dan keduanya berada di luar cakupan pengukuran di bawah.

Eksperimen yang Penting: Tiga Skema Penyeimbangan Loss

Gradient tugas yang bersaing dan seimbang di sekitar inti neural bersama

Loss naif L=kwkL(k)\mathcal{L} = \sum_k w_k \mathcal{L}^{(k)} sensitif terhadap skala. Jika loss return berada sekitar 0.010.01 dan loss volume sekitar 1.01.0, volume menguasai gradient dan head return kelaparan. Tiga respons:

Bobot tetap. Tetapkan wk=1w_k = 1 setelah menstandarkan setiap target. Baseline yang jujur — jika ia menang, skema adaptif hanyalah formalitas.

Pembobotan ketidakpastian (Kendall et al., 2018). Pelajari skala noise homoskedastik σk\sigma_k untuk setiap tugas:

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

Tugas dengan ketidakpastian tinggi otomatis diberi bobot lebih rendah; suku logσk\log \sigma_k menghalangi solusi trivial σk\sigma_k \to \infty. Perhatikan bahwa σk\sigma_k ini adalah perangkat pembobotan loss saat training, bukan interval prediktif — untuk ketidakpastian yang benar-benar dapat digunakan dalam menentukan ukuran posisi, lihat conformal prediction.

GradNorm (Chen et al., 2018). Seimbangkan magnitudo gradient, bukan skala loss. Pada setiap langkah: hitung Gk=ϕwkL(k)2G_k = \|\nabla_\phi w_k \mathcal{L}^{(k)}\|_2 dan rata-rata Gˉ\bar{G}, hitung laju training relatif r~k=L(k)(t)/L(k)(0)rˉ\tilde{r}_k = \frac{\mathcal{L}^{(k)}(t)/\mathcal{L}^{(k)}(0)}{\bar{r}}, lalu perbarui 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|. Semua tugas kemudian training dengan laju yang sebanding terlepas dari skala loss.

Kode khusus MTL adalah head, forward yang mengembalikan daftar, dan agregasi loss. Stack Linear/BatchNorm/ReLU/Dropout, boilerplate Adam/cosine/clip, dan epoch loop adalah pola standar yang ditunjukkan dalam DeepLOB dan dihilangkan di sini.

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 memiliki parameter, jadi harus dimasukkan ke optimizer bersama model: optim.Adam(list(model.parameters()) + list(uw.parameters()), ...). Melupakan ini adalah cara paling umum untuk "menjalankan uncertainty weighting" tetapi diam-diam menjalankan bobot tetap.

Yang harus dilaporkan

Untuk setiap skema, pada setiap fold: bobot tugas akhir yang dipelajari, metrik tugas utama, dan — karena skema pembobotan adalah pilihan model — berapa banyak skema yang dibandingkan sebelum memilih satu.

Skema wreturnw_{\text{return}} wvolumew_{\text{volume}} wvolw_{\text{vol}} Metrik tugas utama vs single-task
Tetap (wk=1w_k = 1) 1.00 1.00 1.00
Pembobotan ketidakpastian
GradNorm

Tiga skema dikali beberapa fold sudah merupakan pencarian model kecil. Setiap peningkatan yang dilaporkan di sini harus bertahan dalam koreksi multiple-testing yang dijelaskan di deflated Sharpe dan multiple testing sebelum berarti apa pun.

Negative Transfer: Beri Tanda pada Gradient

Gradient tugas yang selaras dan berkonflik pada representasi bersama

Inilah bagian yang layak dipertahankan. Negative transfer terjadi ketika tugas tambahan membuat tugas utama lebih buruk, dan memiliki diagnostik langsung: sudut antara gradient tugas dalam ruang parameter bersama.

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}

Ukur hanya pada encoder bersama — head bersifat khusus tugas menurut konstruksi dan selalu "setuju" secara trivial.

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:]
    }

Panggil ini pada batch held-out dengan cadence tetap selama training, bukan sekali di akhir. Sebuah pasangan dapat mulai selaras lalu menyimpang ketika encoder menjadi khusus; satu angka pada akhir training menyembunyikan hal itu.

Temuan yang perlu dicari — dan dipublikasikan apa pun hasilnya:

Pasangan cos sim, training awal cos sim, training akhir MTL membantu tugas utama?
return ↔ volume
return ↔ volatility
volume ↔ volatility

Jika gradient volume dan volatilitas saling selaras sementara keduanya berkonflik dengan gradient return, kesimpulan yang tepat adalah bahwa dua tugas tambahan membentuk blok koheren yang tidak termasuk tugas return — dan perbaikannya adalah pengelompokan tugas, bukan kapasitas yang lebih besar. Ketika konflik nyata, solusi standar adalah PCGrad (Yu et al., 2020), yang memproyeksikan setiap gradient berkonflik ke bidang normal gradient lain; CAGrad (Liu et al., 2021), yang mencari arah descent yang tidak merugikan tugas mana pun; atau menghapus tugas tambahan sepenuhnya.

Perhatikan apa yang sengaja tidak ada: plot t-SNE dari representasi bersama yang diwarnai berdasarkan nilai target. Itu dekoratif — angka kosinus di atas menyatakan semua yang hendak ditunjukkan embedding, dan menyatakannya sebagai angka.

Protokol Validasi

Jendela validasi walk-forward terpisah di sepanjang garis waktu riset

Pengukuran di atas tidak berguna jika protokolnya ceroboh, dan MTL memperburuk jebakan biasa karena ada tiga target yang dapat bocor, bukan satu.

Data nyata, bukan simulator. Target harus berasal dari data OHLCV/trade aktual. GARCH toy yang di-hardcode menghasilkan volatilitas yang berkorelasi dengan return secara konstruksi, dan justru itulah yang sedang diuji — eksperimen akan mengukur generatornya sendiri. Jika Anda menginginkan proses volatilitas yang dipasang, peramalan volatilitas GARCH untuk kripto memasang GARCH(1,1) dengan maximum likelihood pada BTC/ETH nyata dan memvalidasi residual terstandarisasi, sementara GARCH asimetris dan efek leverage menjelaskan mengapa simulator respons simetris Gaussian sejak awal salah menyatakan volatilitas kripto. Data sintetis hanya dapat dipertanggungjawabkan jika menyediakan ground truth terkontrol — korelasi tugas yang diketahui dan ditetapkan penulis yang ingin Anda pulihkan — yang merupakan eksperimen berbeda dari yang ada di sini.

Scaler hanya dipasang pada train. Pasang feature scaler dan ketiga target scaler di dalam setiap fold training lalu terapkan pada validasi; fit_transform global sebelum splitting membocorkan momen test set ke training. Kegagalan persis ini dicatat dalam taksonomi look-ahead bias.

Fold walk-forward yang dipurge dan diberi embargo. Satu chronological split 80/20 tidak dapat membedakan peningkatan MTL dari efek fold — itulah seluruh argumen walk-forward optimization, yang menunjukkan tiga split menghasilkan tiga kesimpulan. Gunakan kembali generator purged_walk_forward expanding-window dari pemodelan spread dengan machine learning: generator itu membuang gap sebesar horizon baris di kedua sisi setiap batas, hal yang penting di sini karena window volatilitas terealisasi yang tumpang tindih bocor melewati batas bahkan ketika target return tidak.

Baseline klasik. Jaringan MTL yang mengalahkan tiga jaringan single-task belum membuktikan apa pun jika model gradient-boosting atau ridge per target mengalahkan keempatnya. Pasang satu model per target dengan LightGBM atau ridge pada fold dan fitur yang sama, lalu laporkan dalam tabel yang sama.

Model Metrik tugas utama Catatan
Ridge, per target Baseline klasik
LightGBM, per target Baseline klasik
MLP single-task, per target Tiga jaringan terpisah
MTL, skema loss terbaik Satu jaringan, tiga head

Kapan MTL Layak Digunakan di Sini

Ambang keputusan terukur untuk kompleksitas model multi-tugas

Kondisi ketika MTL seharusnya menang, dinyatakan sebagai hipotesis yang perlu diperiksa terhadap fold di atas, bukan sebagai checklist:

  • Label tambahan lebih bersih daripada label utama. Volume diamati langsung; "expected return" tidak. Jika head return terutama menyesuaikan noise, sinyal gradient dari head tambahan adalah satu-satunya bagian objective yang terdefinisi dengan baik.
  • Data training terbatas dibandingkan kapasitas encoder, sehingga constraint tambahan benar-benar melakukan regularisasi dan bukan sekadar berebut parameter.
  • Latency inferensi penting dan satu forward pass mengalahkan tiga.

Dan argumen tandingannya, yang juga dapat diuji: jika nilai cos_sim(return, ·) terus-menerus negatif, encoder bersama ditarik menjauh dari tugas utama dan head tambahan menjadi pajak, bukan regularizer.

Kesimpulan

Stream prediksi yang harmonis menuju kesimpulan multi-tugas

Return, volume, dan volatilitas berasal dari microstructure yang sama, jadi representasi bersama adalah prior yang wajar — tetapi prior bukan hasil. Dua hal yang benar-benar dapat ditetapkan oleh setup ini adalah skema penyeimbangan loss yang disukai data (dengan bobot terpelajar dilaporkan, bukan hanya pemenangnya) dan apakah gradient tugas pada encoder bersama selaras, diukur sepanjang training dan bukan diasumsikan hanya karena target berkorelasi.

Jika fold walk-forward yang dipurge menunjukkan jaringan MTL gagal mengalahkan model gradient-boosting per target, itulah temuannya dan harus dipublikasikan demikian — templatenya adalah hasil negatif yang jujur. Hasil negatif mengenai negative transfer tetap merupakan hasil tentang negative transfer.

Penafian: Informasi yang disediakan dalam artikel ini hanya untuk tujuan edukasi dan informasi serta tidak merupakan nasihat keuangan, investasi, atau trading. Trading mata uang kripto mengandung risiko kerugian yang signifikan.

Penulis

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

Selangkah Lebih Maju dari Pasar

Berlangganan newsletter kami untuk wawasan AI trading eksklusif, analisis pasar, dan pembaruan platform.

Kami menghormati privasi Anda. Berhenti berlangganan kapan saja.